feat(mosq): Added support for TLS transport using ESP-TLS

This commit is contained in:
David Cermak
2024-10-07 18:22:21 +02:00
parent 8c4f392f93
commit 1af4bbe1ab
15 changed files with 647 additions and 21 deletions

View File

@ -7,7 +7,6 @@ set(m_deps_dir ${m_dir}/deps)
set(m_srcs
${m_lib_dir}/memory_mosq.c
${m_lib_dir}/util_mosq.c
${m_lib_dir}/net_mosq.c
${m_lib_dir}/will_mosq.c
${m_lib_dir}/alias_mosq.c
${m_lib_dir}/send_mosq.c
@ -46,7 +45,6 @@ set(m_srcs
${m_src_dir}/mux.c
${m_src_dir}/mux_epoll.c
${m_src_dir}/mux_poll.c
${m_src_dir}/net.c
${m_src_dir}/password_mosq.c
${m_src_dir}/persist_read.c
${m_src_dir}/persist_read_v234.c
@ -73,20 +71,26 @@ set(m_srcs
${m_src_dir}/xtreport.c)
idf_component_register(SRCS ${m_srcs}
port/callbacks.c port/config.c port/signals.c port/ifaddrs.c port/broker.c port/files.c
port/callbacks.c
port/config.c
port/signals.c
port/ifaddrs.c
port/broker.c
port/files.c
port/net__esp_tls.c
PRIV_INCLUDE_DIRS port/priv_include port/priv_include/sys ${m_dir} ${m_src_dir}
${m_incl_dir} ${m_lib_dir} ${m_deps_dir}
INCLUDE_DIRS ${m_incl_dir} port/include
PRIV_REQUIRES newlib
PRIV_REQUIRES newlib esp-tls
)
target_compile_definitions(${COMPONENT_LIB} PRIVATE "WITH_BROKER")
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")
# Some mosquittos source unconditionally define `_GNU_SOURCE` which collides with IDF build system
# Some mosquitto source unconditionally define `_GNU_SOURCE` which collides with IDF build system
# producing warning: "_GNU_SOURCE" redefined
# This workarounds this issue by undefining the macro for the selected files
set(sources_that_define_gnu_source ${m_lib_dir}/net_mosq.c ${m_src_dir}/loop.c ${m_src_dir}/mux_poll.c)
set(sources_that_define_gnu_source ${m_src_dir}/loop.c ${m_src_dir}/mux_poll.c)
foreach(offending_src ${sources_that_define_gnu_source})
set_source_files_properties(${offending_src} PROPERTIES COMPILE_OPTIONS "-U_GNU_SOURCE")
endforeach()

View File

@ -1,21 +1,21 @@
# ESP32 Mosquitto Port
This is a lightweight port of the Mosquitto broker designed to run on the ESP32. It currently supports a single listener and TCP transport only.
This is a lightweight port of the Mosquitto broker designed to run on the ESP32. It currently supports a single listener with TCP transport or TLS transport based on ESP-TLS library.
## Supported Options
The Espressif port supports a limited set of options (with plans to add more in future releases). These options can be configured through a structure passed to the `mosq_broker_start()` function. For detailed information on available configuration options, refer to the [API documentation](api.md).
The Espressif port supports a limited set of options (with plans to add more in future releases). These options can be configured through a structure passed to the `mosq_broker_run()` function. For detailed information on available configuration options, refer to the [API documentation](api.md).
## API
### Starting the Broker
To start the broker, call the `mosq_broker_start()` function with a properly configured settings structure. The broker operates in the context of the calling task and does not create a separate task.
To start the broker, call the `mosq_broker_run()` function with a properly configured settings structure. The broker operates in the context of the calling task and does not create a separate task.
It's recommended to analyze the stack size needed for the task, but in general, the broker requires at least 4 kB of stack size.
```c
mosq_broker_start(&config);
mosq_broker_run(&config);
```
## Memory Footprint Considerations

View File

@ -20,7 +20,8 @@
| Type | Name |
| ---: | :--- |
| int | [**mosq\_broker\_start**](#function-mosq_broker_start) (struct [**mosq\_broker\_config**](#struct-mosq_broker_config) \*config) <br>_Start mosquitto broker._ |
| int | [**mosq\_broker\_run**](#function-mosq_broker_run) (struct [**mosq\_broker\_config**](#struct-mosq_broker_config) \*config) <br>_Start mosquitto broker._ |
| void | [**mosq\_broker\_stop**](#function-mosq_broker_stop) (void) <br>_Stops running broker._ |
## Structures and Types Documentation
@ -37,14 +38,16 @@ Variables:
- int port <br>Port number of the broker to listen to
- esp\_tls\_cfg\_server\_t \* tls_cfg <br>ESP-TLS configuration (if TLS transport used) Please refer to the ESP-TLS official documentation for more details on configuring the TLS options. You can open the respective docs with this idf.py command: `idf.py docs -sp api-reference/protocols/esp_tls.html`
## Functions Documentation
### function `mosq_broker_start`
### function `mosq_broker_run`
_Start mosquitto broker._
```c
int mosq_broker_start (
int mosq_broker_run (
struct mosq_broker_config *config
)
```
@ -63,3 +66,16 @@ This API runs the broker in the calling thread and blocks until the mosquitto ex
**Returns:**
int Exit code (0 on success)
### function `mosq_broker_stop`
_Stops running broker._
```c
void mosq_broker_stop (
void
)
```
**Note:**
After calling this API, function mosq\_broker\_run() unblocks and returns.

View File

@ -2,7 +2,7 @@
## Overview
This example runs a TCP broker on a specified host and port.
This example runs a broker on TLS or TCP transport, specified host and port.
### How to use this example
@ -13,6 +13,19 @@ If you enabled also the mqtt client, this example will connect to the local brok
You can connect to the ESP32 mosquitto broker using some other client using the ESP32 IPv4 address and the port specified in the project configuration menu.
> [!IMPORTANT]
> The certificates and keys provided in this example are intended for testing purposes only. They are self-signed, single-use, and configured with a common name of "127.0.0.1". Do not reuse these credentials in any production or real-world applications, as they are not secure for such environments.
For more information on setting up TLS configuration (including certificates and keys), please refer to the ESP-TLS documentation:
```bash
idf.py docs -sp api-reference/protocols/esp_tls.html
```
Configuring the TLS option for the broker is quite similar to setting it up for an HTTPS server, as both involve server-side security configuration. Refer to the HTTPS server documentation for details:
```bash
idf.py docs -sp api-reference/protocols/esp_https_server.html
```
### Test version
This example is also used for testing on loopback interface only, disabling any actual connection, just using the local mqtt client to the loopback interface.

View File

@ -1,2 +1,3 @@
idf_component_register(SRCS "broker.c"
PRIV_REQUIRES newlib nvs_flash esp_netif esp_event mqtt)
idf_component_register(SRCS "example_broker.c"
PRIV_REQUIRES newlib nvs_flash esp_netif esp_event mqtt
EMBED_TXTFILES servercert.pem serverkey.pem cacert.pem)

View File

@ -19,4 +19,11 @@ menu "Example Configuration"
If enabled, it runs a local mqtt client connecting
to the same endpoint ans the broker listens to
config EXAMPLE_BROKER_WITH_TLS
bool "Use TLS"
default y
help
If enabled, the broker (and the client too, if enabled)
uses TLS transport layer
endmenu

View File

@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDIzCCAgugAwIBAgIUXichctvCn6/6xXr0+UOBaqwkBMMwDQYJKoZIhvcNAQEL
BQAwITELMAkGA1UEBhMCQ1oxEjAQBgNVBAMMCUVzcHJlc3NpZjAeFw0yNDA3MDgx
NDE5NDNaFw0yNTA3MDgxNDE5NDNaMCExCzAJBgNVBAYTAkNaMRIwEAYDVQQDDAlF
c3ByZXNzaWYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqAGUZALUS
AwWkslBH0RJcgyTTYZ6Q3xadG9rubTGN0DNt8INlguElN9eUhj7VzQZeGxRtAk3A
b4r5MpTWAAC8maDgZU97TOmAaxA04h0P2MHTGG4i1vSm2/jebhh5Ydh8nKs9DdAO
YJWfbtt3XukBe5VJcmp7OICz88LFc/fArrAnBFdmrVX+0Y2l/5KDW6ItvcXhorpz
sO5hOnPXIs4Hq5TYOJbUw6h9E8O6bxUG4AXcSWqqbLJ6PzEFSBMBnjwBQn4HCWvM
GV6w2+I1QbtOTe6yNzBa7O3yqzSYeTcdpjv/FFngo4oRN1RMiCYc1Ae3hJiIhDlN
SRB1CHPi4MblAgMBAAGjUzBRMB0GA1UdDgQWBBTdlh8T2ze2K81IrZCpUv9yhZq2
qjAfBgNVHSMEGDAWgBTdlh8T2ze2K81IrZCpUv9yhZq2qjAPBgNVHRMBAf8EBTAD
AQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBDTt9V3LnyBjHDi9pQa+Q8bjVYCMaOSBFE
LJj8GhkXxtfTzqO2u7vkvfz+2MaRDNpL2lePWDB0BwINT+mSYNWjD5bP2mdgJ2nK
BStzWT6MR4hiQ6u6hXy2Q8brqPN+dP4Pay8fXHe3JNadC/nSk4AC3EvVDpghCJJB
1W5az4YmJzK0F6S84AkKnXYdlYyb94RwWSevn7HYZM+xQjoJmBhQ+XnQ7o2uaEur
52igRRHQQ4xrF5JrbGAqfFVqfA8lJDYiAZCG/aNlV0VpgzyxpDxvPFvvlEYJoL63
/asgSIzYoBknZjNZnPSKcsYGa+0Bjjh7tS50bV++5sN+aW/WDRLd
-----END CERTIFICATE-----

View File

@ -14,6 +14,16 @@
const static char *TAG = "mqtt_broker";
#if CONFIG_EXAMPLE_BROKER_WITH_TLS
extern const unsigned char servercert_start[] asm("_binary_servercert_pem_start");
extern const unsigned char servercert_end[] asm("_binary_servercert_pem_end");
extern const unsigned char serverkey_start[] asm("_binary_serverkey_pem_start");
extern const unsigned char serverkey_end[] asm("_binary_serverkey_pem_end");
extern const char cacert_start[] asm("_binary_cacert_pem_start");
extern const char cacert_end[] asm("_binary_cacert_pem_end");
#endif
#if CONFIG_EXAMPLE_BROKER_RUN_LOCAL_MQTT_CLIENT
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
@ -63,7 +73,13 @@ static void mqtt_app_start(struct mosq_broker_config *config)
{
esp_mqtt_client_config_t mqtt_cfg = {
.broker.address.hostname = "127.0.0.1",
.broker.address.transport = MQTT_TRANSPORT_OVER_TCP, // we support only TCP transport now
#if CONFIG_EXAMPLE_BROKER_WITH_TLS
.broker.address.transport = MQTT_TRANSPORT_OVER_SSL,
.broker.verification.certificate = cacert_start,
.broker.verification.certificate_len = cacert_end - cacert_start,
#else
.broker.address.transport = MQTT_TRANSPORT_OVER_TCP,
#endif
.broker.address.port = config->port,
};
esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg);
@ -79,11 +95,22 @@ void app_main(void)
ESP_ERROR_CHECK(esp_event_loop_create_default());
ESP_ERROR_CHECK(example_connect());
struct mosq_broker_config config = { .host = CONFIG_EXAMPLE_BROKER_HOST, .port = CONFIG_EXAMPLE_BROKER_PORT };
struct mosq_broker_config config = { .host = CONFIG_EXAMPLE_BROKER_HOST, .port = CONFIG_EXAMPLE_BROKER_PORT, .tls_cfg = NULL };
#if CONFIG_EXAMPLE_BROKER_RUN_LOCAL_MQTT_CLIENT
mqtt_app_start(&config);
#endif
#if CONFIG_EXAMPLE_BROKER_WITH_TLS
esp_tls_cfg_server_t tls_cfg = {
.servercert_buf = servercert_start,
.servercert_bytes = servercert_end - servercert_start,
.serverkey_buf = serverkey_start,
.serverkey_bytes = serverkey_end - serverkey_start,
};
config.tls_cfg = &tls_cfg;
#endif
// broker continues to run in this task
mosq_broker_start(&config);
mosq_broker_run(&config);
}

View File

@ -0,0 +1,17 @@
-----BEGIN CERTIFICATE-----
MIICvDCCAaQCFAo5BZTT6BC7rKLiWZA9mwGrEQYeMA0GCSqGSIb3DQEBCwUAMCEx
CzAJBgNVBAYTAkNaMRIwEAYDVQQDDAlFc3ByZXNzaWYwHhcNMjQwNzA4MTQxOTQ0
WhcNMjUwNzA4MTQxOTQ0WjAUMRIwEAYDVQQDDAkxMjcuMC4wLjEwggEiMA0GCSqG
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCkCVA8MY/BMZ2e95s+oNMZ2cXEF7lbUL58
I9OT5Y+0Mxy3mxsezEye0kKXgV7TqzXdWbu8VhDQONkKkeO4n/lPKhULmKQ7gIja
1aiL5Wt07S/jHBaYbMymbssCJaVcFpl+gclZ0oJJKDtJN9wYoUAAXeZkEE47rRWm
W5CZP92bn8TYar+3rjexHvZTtPhSKucsN/YoAtdC5ywRcf9lbhmjV4sMzUSAlPG4
ZY8sG1mrul1AO2c7OI4lcm+iBo7WiIwtASmqvD/Ahhye5kY00jfAiQJvRl4Da65x
m6NVVUAk3pUsKjOHI/4FisnP0kIJrfMNaiiumroFApuMl3YxEN6tAgMBAAEwDQYJ
KoZIhvcNAQELBQADggEBAD+ML2Dp2GDomHoFxyTmu9msv+8YyZy4VhRGUWnG4k8f
XV+9cBoQkiV8VUDETwjcdp0lRVmyxy8w1x4ovJ/EO5udfXom8gxMS7lZVXw1Iv09
vPHIpr9kQg2hxTpqoHSRKLRJv796kfYoPK+I43hYlhvewQko7+E8EEns46qXc4I3
wqrwNOw1gjUzyj5DhW4RCJ9sBS/FaVyliCxICoDXRFhnSXWi0HaEjzq815muN6DG
lD5ENFExpPWpvjyVPQC6tNYRlRCAGKn5qbx/YetGNX3slHJHgHAO2dyPYdiXwkhG
GwQPqXJrvu0k83h5lTeW98wwgcqFEzVHQCJhREdW4uE=
-----END CERTIFICATE-----

View File

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCkCVA8MY/BMZ2e
95s+oNMZ2cXEF7lbUL58I9OT5Y+0Mxy3mxsezEye0kKXgV7TqzXdWbu8VhDQONkK
keO4n/lPKhULmKQ7gIja1aiL5Wt07S/jHBaYbMymbssCJaVcFpl+gclZ0oJJKDtJ
N9wYoUAAXeZkEE47rRWmW5CZP92bn8TYar+3rjexHvZTtPhSKucsN/YoAtdC5ywR
cf9lbhmjV4sMzUSAlPG4ZY8sG1mrul1AO2c7OI4lcm+iBo7WiIwtASmqvD/Ahhye
5kY00jfAiQJvRl4Da65xm6NVVUAk3pUsKjOHI/4FisnP0kIJrfMNaiiumroFApuM
l3YxEN6tAgMBAAECggEBAIAmxohAMA6+tGV9C8vh2QpZGCgaYLT2X9qcIq9oqNwf
ElBe3NEyyqlJmrzCVVMIbwx/DiwVEQ2bW/TmBQI3+I3gUpC8r5HM2R4dzY99rHWs
17yWNRDf7wIXjIIg5w8KmOA8hRGnZCHDTI3nFgwn7dhbg6KpGnWEw2U0I8OWIYty
BX/CvsaWCtkOc1o1bUfnt91YlZlFm7VSK+jdxhnt+A5FJ06CllTaur9nf3u/sLO9
4SKtd94S/pTlIWM4x+4565fNplNuKLsrnNJHV5eiPwiMb9OZzeFa48z26No2ebHc
zA5PlrzjtQRQaSiWausgZKfLp1+lA9kNNhi6gBPt4AECgYEA2mUZErdAmm0WS+Hl
/AEJWsXzoYLGmOvToVEyUqRNffsfkbTzSHajLGzntUd/ZnQQWh5NkvOPJ3BHTszb
vwVeGIQehpwqoCvF1Nz+XhI9yAGIoSzxq0FMMcmlW0XGdK8D30qbeygBEL2FJnK5
mixWe3dZx3kujIFJ4VI8S4g/CjcCgYEAwEgT7FCk4xw6NClI3kAQ6NqqJ+qKDwFo
LQQKUep3juA7i84KwqRCVSoKVjJfkVw7MBb520MgnjAwnekyRql6ALDrSUqgXNB9
R8Sa5b19mmmBDtvh+IrhD6gmAyzXVdGU9fxMnW3iW3sx5Anl/4lx8r7ITcrM9dgd
xY7pwzownDsCgYBkTuz+OKb2hsYn4kC0x3EZfTQSabN3x1EzlcysQoTJKU9tqBPZ
o4v8uqSOEaHFV+euzJ5KsY19ysclvVfs27VFQ2GV6CJ34MMDquE2KeCwfWvYw4DY
bKxnbbuCOYEWVNBNfcH+Bfi/TJzcdPMkidrK6J2WzeUAad2aHSBOfOyfbwKBgEkK
WD8ZdzkqXNW5pQt/7Kx3e9GD34PJtgf7k+wAFAB7H0OBNkcv3F67hIevxOvTzEv9
PlZTDo3ool8p2UZMVKL0kbwalAYN0Lk1bt28eHzyfOrnDdS69Llc12u3Wekontw+
ReA7gJPdnVsRg4PpcxaR8EbUtbzhppWILzZQ4WxHAoGALs0n0pDVQlkAuDtNhA/i
7/jIo1hd3fPpWbMfcKeP+TtlpXMu/BCsR5A/u+4iSfLMy9/Ggqad4jUsdd9+myvr
j/3BzbSx7OnD+gg8ao0K2FwO33ncM1iAw3G5QCKs1waHsVen43Oe3GtQxHxxi/G0
Y4EIG5wkDz4YQOEXacvTWMo=
-----END PRIVATE KEY-----

View File

@ -94,7 +94,14 @@ static void listeners__stop(void)
mosquitto__free(listensock);
}
int mosq_broker_start(struct mosq_broker_config *broker_config)
void net__set_tls_config(esp_tls_cfg_server_t *config);
void mosq_broker_stop(void)
{
run = 0;
}
int mosq_broker_run(struct mosq_broker_config *broker_config)
{
struct mosquitto__config config;
@ -115,6 +122,10 @@ int mosq_broker_start(struct mosq_broker_config *broker_config)
config__init(&config);
if (broker_config->tls_cfg) {
net__set_tls_config(broker_config->tls_cfg);
}
db.config = &config;
rc = db__open(&config);

View File

@ -5,6 +5,7 @@
*/
#pragma once
#include "mosquitto.h"
#include "esp_tls.h"
struct mosquitto__config;
@ -17,6 +18,12 @@ struct mosquitto__config;
struct mosq_broker_config {
char *host; /*!< Address on which the broker is listening for connections */
int port; /*!< Port number of the broker to listen to */
esp_tls_cfg_server_t *tls_cfg; /*!< ESP-TLS configuration (if TLS transport used)
* Please refer to the ESP-TLS official documentation
* for more details on configuring the TLS options.
* You can open the respective docs with this idf.py command:
* `idf.py docs -sp api-reference/protocols/esp_tls.html`
*/
};
/**
@ -28,4 +35,11 @@ struct mosq_broker_config {
* @param config Mosquitto configuration structure
* @return int Exit code (0 on success)
*/
int mosq_broker_start(struct mosq_broker_config *config);
int mosq_broker_run(struct mosq_broker_config *config);
/**
* @brief Stops running broker
*
* @note After calling this API, function mosq_broker_run() unblocks and returns.
*/
void mosq_broker_stop(void);

View File

@ -0,0 +1,467 @@
/*
* SPDX-FileCopyrightText: 2009-2020 Roger Light <roger@atchoo.org>
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*
* SPDX-FileContributor: 2024 Espressif Systems (Shanghai) CO LTD
*/
/*
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License 2.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
https://www.eclipse.org/legal/epl-2.0/
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
Contributors:
Roger Light - initial implementation and documentation.
Espressif Systems (Shanghai) CO LTD - added support for ESP-TLS based connections
*/
#include "config.h"
# include <arpa/inet.h>
# include <ifaddrs.h>
# include <netdb.h>
# include <netinet/tcp.h>
# include <strings.h>
# include <sys/socket.h>
# include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
#include "mosquitto_broker_internal.h"
#include "mqtt_protocol.h"
#include "memory_mosq.h"
#include "misc_mosq.h"
#include "net_mosq.h"
#include "util_mosq.h"
#include "esp_tls.h"
#include "sys_tree.h"
#define MAX_CONNECTIONS (64)
struct esp_tls_context {
int sock;
esp_tls_t *tls;
};
static struct esp_tls_context tls_ctx[MAX_CONNECTIONS];
static esp_tls_cfg_server_t *tls_cfg;
void net__set_tls_config(esp_tls_cfg_server_t *config)
{
if (config) {
tls_cfg = mosquitto__calloc(1, sizeof(esp_tls_cfg_server_t));
if (tls_cfg) {
memcpy(tls_cfg, config, sizeof(esp_tls_cfg_server_t));
} else {
log__printf(NULL, MOSQ_LOG_ERR, "Unable to allocate ESP-TLS configuration structure, continuing with plain TCP transport only");
}
}
}
void net__broker_init(void)
{
for (int i = 0; i < MAX_CONNECTIONS; ++i) {
tls_ctx[i].sock = INVALID_SOCKET;
tls_ctx[i].tls = NULL;
}
net__init();
}
void net__broker_cleanup(void)
{
net__cleanup();
mosquitto__free(tls_cfg);
tls_cfg = NULL;
}
static void net__print_error(unsigned int log, const char *format_str)
{
char *buf;
buf = strerror(errno);
log__printf(NULL, log, format_str, buf);
}
struct mosquitto *net__socket_accept(struct mosquitto__listener_sock *listensock)
{
mosq_sock_t new_sock = INVALID_SOCKET;
struct mosquitto *new_context;
new_sock = accept(listensock->sock, NULL, 0);
if (new_sock == INVALID_SOCKET) {
log__printf(NULL, MOSQ_LOG_ERR,
"Unable to accept new connection, system socket count has been exceeded. Try increasing \"ulimit -n\" or equivalent.");
return NULL;
}
if (tls_cfg) {
// Finds first free spot in the context array
int ctx;
for (ctx = 0; ctx < MAX_CONNECTIONS; ++ctx) {
if (tls_ctx[ctx].sock == INVALID_SOCKET) {
tls_ctx[ctx].sock = new_sock;
tls_ctx[ctx].tls = esp_tls_init();
if (!tls_ctx[ctx].tls) {
log__printf(NULL, MOSQ_LOG_ERR, "Faled to create a new ESP-TLS context");
return NULL;
}
break;
}
}
if (ctx >= MAX_CONNECTIONS) {
log__printf(NULL, MOSQ_LOG_ERR, "Unable to create new ESP-TLS connection. Try increasing \"MAX_CONNECTIONS\"");
return NULL;
}
int ret = esp_tls_server_session_create(tls_cfg, new_sock, tls_ctx[ctx].tls);
if (ret != 0) {
log__printf(NULL, MOSQ_LOG_ERR, "Unable to create new ESP-TLS session");
return NULL;
}
}
G_SOCKET_CONNECTIONS_INC();
if (net__socket_nonblock(&new_sock)) {
return NULL;
}
if (db.config->set_tcp_nodelay) {
int flag = 1;
if (setsockopt(new_sock, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(int)) != 0) {
log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Unable to set TCP_NODELAY.");
}
}
new_context = context__init(new_sock);
if (!new_context) {
COMPAT_CLOSE(new_sock);
return NULL;
}
new_context->listener = listensock->listener;
if (!new_context->listener) {
context__cleanup(new_context, true);
return NULL;
}
new_context->listener->client_count++;
if (new_context->listener->max_connections > 0 && new_context->listener->client_count > new_context->listener->max_connections) {
if (db.config->connection_messages == true) {
log__printf(NULL, MOSQ_LOG_NOTICE, "Client connection from %s denied: max_connections exceeded.", new_context->address);
}
context__cleanup(new_context, true);
return NULL;
}
if (db.config->connection_messages == true) {
log__printf(NULL, MOSQ_LOG_NOTICE, "New connection from %s:%d on port %d.",
new_context->address, new_context->remote_port, new_context->listener->port);
}
return new_context;
}
int net__load_certificates(struct mosquitto__listener *listener)
{
return MOSQ_ERR_SUCCESS;
}
int net__tls_load_verify(struct mosquitto__listener *listener)
{
return net__load_certificates(listener);
}
static int net__socket_listen_tcp(struct mosquitto__listener *listener)
{
mosq_sock_t sock = INVALID_SOCKET;
struct addrinfo hints;
struct addrinfo *ainfo, *rp;
char service[10];
int rc;
int ss_opt = 1;
if (!listener) {
return MOSQ_ERR_INVAL;
}
snprintf(service, 10, "%d", listener->port);
memset(&hints, 0, sizeof(struct addrinfo));
if (listener->socket_domain) {
hints.ai_family = listener->socket_domain;
} else {
hints.ai_family = AF_UNSPEC;
}
hints.ai_flags = AI_PASSIVE;
hints.ai_socktype = SOCK_STREAM;
rc = getaddrinfo(listener->host, service, &hints, &ainfo);
if (rc) {
log__printf(NULL, MOSQ_LOG_ERR, "Error creating listener: %s.", gai_strerror(rc));
return INVALID_SOCKET;
}
listener->sock_count = 0;
listener->socks = NULL;
for (rp = ainfo; rp; rp = rp->ai_next) {
if (rp->ai_family == AF_INET) {
log__printf(NULL, MOSQ_LOG_INFO, "Opening ipv4 listen socket on port %d.", ntohs(((struct sockaddr_in *)rp->ai_addr)->sin_port));
} else if (rp->ai_family == AF_INET6) {
log__printf(NULL, MOSQ_LOG_INFO, "Opening ipv6 listen socket on port %d.", ntohs(((struct sockaddr_in6 *)rp->ai_addr)->sin6_port));
} else {
continue;
}
sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (sock == INVALID_SOCKET) {
net__print_error(MOSQ_LOG_WARNING, "Warning: %s");
continue;
}
listener->sock_count++;
listener->socks = mosquitto__realloc(listener->socks, sizeof(mosq_sock_t) * (size_t)listener->sock_count);
if (!listener->socks) {
log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory.");
freeaddrinfo(ainfo);
COMPAT_CLOSE(sock);
return MOSQ_ERR_NOMEM;
}
listener->socks[listener->sock_count - 1] = sock;
#ifndef WIN32
ss_opt = 1;
/* Unimportant if this fails */
(void)setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &ss_opt, sizeof(ss_opt));
#endif
#ifdef IPV6_V6ONLY
ss_opt = 1;
(void)setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &ss_opt, sizeof(ss_opt));
#endif
if (net__socket_nonblock(&sock)) {
freeaddrinfo(ainfo);
mosquitto__free(listener->socks);
return 1;
}
if (listener->bind_interface) {
log__printf(NULL, MOSQ_LOG_ERR, "Error: listener->bind_interface is not supported");
return 1;
}
if (bind(sock, rp->ai_addr, rp->ai_addrlen) == -1) {
net__print_error(MOSQ_LOG_ERR, "Error: %s");
COMPAT_CLOSE(sock);
freeaddrinfo(ainfo);
mosquitto__free(listener->socks);
return 1;
}
if (listen(sock, 100) == -1) {
net__print_error(MOSQ_LOG_ERR, "Error: %s");
freeaddrinfo(ainfo);
COMPAT_CLOSE(sock);
mosquitto__free(listener->socks);
return 1;
}
}
freeaddrinfo(ainfo);
if (listener->bind_interface) {
mosquitto__free(listener->socks);
return 1;
}
return 0;
}
/* Creates a socket and listens on port 'port'.
* Returns 1 on failure
* Returns 0 on success.
*/
int net__socket_listen(struct mosquitto__listener *listener)
{
int rc;
if (!listener) {
return MOSQ_ERR_INVAL;
}
rc = net__socket_listen_tcp(listener);
if (rc) {
return rc;
}
/* We need to have at least one working socket. */
if (listener->sock_count > 0) {
return 0;
} else {
return 1;
}
}
int net__socket_get_address(mosq_sock_t sock, char *buf, size_t len, uint16_t *remote_port)
{
struct sockaddr_storage addr;
socklen_t addrlen;
memset(&addr, 0, sizeof(struct sockaddr_storage));
addrlen = sizeof(addr);
if (!getpeername(sock, (struct sockaddr *)&addr, &addrlen)) {
if (addr.ss_family == AF_INET) {
if (remote_port) {
*remote_port = ntohs(((struct sockaddr_in *)&addr)->sin_port);
}
if (inet_ntop(AF_INET, &((struct sockaddr_in *)&addr)->sin_addr.s_addr, buf, (socklen_t)len)) {
return 0;
}
} else if (addr.ss_family == AF_INET6) {
if (remote_port) {
*remote_port = ntohs(((struct sockaddr_in6 *)&addr)->sin6_port);
}
if (inet_ntop(AF_INET6, &((struct sockaddr_in6 *)&addr)->sin6_addr.s6_addr, buf, (socklen_t)len)) {
return 0;
}
}
}
return 1;
}
// -----------------------------------
ssize_t net__read(struct mosquitto *mosq, void *buf, size_t count)
{
assert(mosq);
errno = 0;
for (int i = 0; i < MAX_CONNECTIONS; ++i) {
if (tls_ctx[i].sock == mosq->sock) {
return esp_tls_conn_read(tls_ctx[i].tls, buf, count);
}
}
return read(mosq->sock, buf, count);
}
ssize_t net__write(struct mosquitto *mosq, const void *buf, size_t count)
{
assert(mosq);
errno = 0;
for (int i = 0; i < MAX_CONNECTIONS; ++i) {
if (tls_ctx[i].sock == mosq->sock) {
return esp_tls_conn_write(tls_ctx[i].tls, buf, count);
}
}
return send(mosq->sock, buf, count, MSG_NOSIGNAL);
}
int net__socket_nonblock(mosq_sock_t *sock)
{
int opt;
/* Set non-blocking */
opt = fcntl(*sock, F_GETFL, 0);
if (opt == -1) {
COMPAT_CLOSE(*sock);
*sock = INVALID_SOCKET;
return MOSQ_ERR_ERRNO;
}
if (fcntl(*sock, F_SETFL, opt | O_NONBLOCK) == -1) {
/* If either fcntl fails, don't want to allow this client to connect. */
COMPAT_CLOSE(*sock);
*sock = INVALID_SOCKET;
return MOSQ_ERR_ERRNO;
}
return MOSQ_ERR_SUCCESS;
}
/* Close a socket associated with a context and set it to -1.
* Returns 1 on failure (context is NULL)
* Returns 0 on success.
*/
int net__socket_close(struct mosquitto *mosq)
{
int rc = 0;
#ifdef WITH_BROKER
struct mosquitto *mosq_found;
#endif
assert(mosq);
#ifdef WITH_TLS
#ifdef WITH_WEBSOCKETS
if (!mosq->wsi)
#endif
{
if (mosq->ssl) {
if (!SSL_in_init(mosq->ssl)) {
SSL_shutdown(mosq->ssl);
}
SSL_free(mosq->ssl);
mosq->ssl = NULL;
}
}
#endif
#ifdef WITH_WEBSOCKETS
if (mosq->wsi) {
if (mosq->state != mosq_cs_disconnecting) {
mosquitto__set_state(mosq, mosq_cs_disconnect_ws);
}
lws_callback_on_writable(mosq->wsi);
} else
#endif
{
if (mosq->sock != INVALID_SOCKET) {
#ifdef WITH_BROKER
HASH_FIND(hh_sock, db.contexts_by_sock, &mosq->sock, sizeof(mosq->sock), mosq_found);
if (mosq_found) {
HASH_DELETE(hh_sock, db.contexts_by_sock, mosq_found);
}
#endif
rc = COMPAT_CLOSE(mosq->sock);
// Finds first free spot in the context array
for (int i = 0; i < MAX_CONNECTIONS; ++i) {
if (tls_ctx[i].sock == mosq->sock) {
tls_ctx[i].sock = INVALID_SOCKET;
esp_tls_server_session_delete(tls_ctx[i].tls);
break;
}
}
mosq->sock = INVALID_SOCKET;
}
}
#ifdef WITH_BROKER
if (mosq->listener) {
mosq->listener->client_count--;
mosq->listener = NULL;
}
#endif
return rc;
}
int net__init(void)
{
return MOSQ_ERR_SUCCESS;
}
void net__cleanup(void)
{
}