mirror of
https://github.com/espressif/esp-protocols.git
synced 2025-07-29 10:17:30 +02:00
feat(mosq): Added support for TLS transport using ESP-TLS
This commit is contained in:
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
19
components/mosquitto/examples/broker/main/cacert.pem
Normal file
19
components/mosquitto/examples/broker/main/cacert.pem
Normal 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-----
|
@ -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);
|
||||
}
|
17
components/mosquitto/examples/broker/main/servercert.pem
Normal file
17
components/mosquitto/examples/broker/main/servercert.pem
Normal 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-----
|
28
components/mosquitto/examples/broker/main/serverkey.pem
Normal file
28
components/mosquitto/examples/broker/main/serverkey.pem
Normal 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-----
|
@ -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);
|
||||
|
@ -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);
|
||||
|
467
components/mosquitto/port/net__esp_tls.c
Normal file
467
components/mosquitto/port/net__esp_tls.c
Normal 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)
|
||||
{
|
||||
}
|
Reference in New Issue
Block a user