diff --git a/ci/check_copyright_config.yaml b/ci/check_copyright_config.yaml
index 43f2ad7018b..ce5eadf0794 100644
--- a/ci/check_copyright_config.yaml
+++ b/ci/check_copyright_config.yaml
@@ -53,6 +53,7 @@ mosquitto_component:
allowed_licenses:
- EPL-2.0
- Apache-2.0
+ - BSD-3-Clause
slim_modem_examples:
include:
diff --git a/components/mosquitto/CMakeLists.txt b/components/mosquitto/CMakeLists.txt
index ef43060db1e..1db843ec19c 100644
--- a/components/mosquitto/CMakeLists.txt
+++ b/components/mosquitto/CMakeLists.txt
@@ -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()
diff --git a/components/mosquitto/README.md b/components/mosquitto/README.md
index 9199917ed43..a6408db29ad 100644
--- a/components/mosquitto/README.md
+++ b/components/mosquitto/README.md
@@ -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
diff --git a/components/mosquitto/api.md b/components/mosquitto/api.md
index 39668bde340..00c9ba28da1 100644
--- a/components/mosquitto/api.md
+++ b/components/mosquitto/api.md
@@ -20,7 +20,8 @@
| Type | Name |
| ---: | :--- |
-| int | [**mosq\_broker\_start**](#function-mosq_broker_start) (struct [**mosq\_broker\_config**](#struct-mosq_broker_config) \*config)
_Start mosquitto broker._ |
+| int | [**mosq\_broker\_run**](#function-mosq_broker_run) (struct [**mosq\_broker\_config**](#struct-mosq_broker_config) \*config)
_Start mosquitto broker._ |
+| void | [**mosq\_broker\_stop**](#function-mosq_broker_stop) (void)
_Stops running broker._ |
## Structures and Types Documentation
@@ -37,14 +38,16 @@ Variables:
- int port
Port number of the broker to listen to
+- esp\_tls\_cfg\_server\_t \* tls_cfg
ESP-TLS configuration (if TLS transport used)
+
## 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 run\_broker() unblocks and returns.
diff --git a/components/mosquitto/examples/broker/README.md b/components/mosquitto/examples/broker/README.md
index 6d8545505c1..b9d41d29035 100644
--- a/components/mosquitto/examples/broker/README.md
+++ b/components/mosquitto/examples/broker/README.md
@@ -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,8 @@ 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.
+Please do not reuse the test certificates and keys used in this example. Note that these are single purpose, self-signed with common name set to `"127.0.0.1"`.
+
### 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.
diff --git a/components/mosquitto/examples/broker/main/CMakeLists.txt b/components/mosquitto/examples/broker/main/CMakeLists.txt
index b76a5b5306d..9c38477a64d 100644
--- a/components/mosquitto/examples/broker/main/CMakeLists.txt
+++ b/components/mosquitto/examples/broker/main/CMakeLists.txt
@@ -1,2 +1,3 @@
idf_component_register(SRCS "broker.c"
- PRIV_REQUIRES newlib nvs_flash esp_netif esp_event mqtt)
+ PRIV_REQUIRES newlib nvs_flash esp_netif esp_event mqtt
+ EMBED_TXTFILES servercert.pem serverkey.pem cacert.pem)
diff --git a/components/mosquitto/examples/broker/main/Kconfig.projbuild b/components/mosquitto/examples/broker/main/Kconfig.projbuild
index 2e5efc912a2..44c9e8f2d06 100644
--- a/components/mosquitto/examples/broker/main/Kconfig.projbuild
+++ b/components/mosquitto/examples/broker/main/Kconfig.projbuild
@@ -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
diff --git a/components/mosquitto/examples/broker/main/broker.c b/components/mosquitto/examples/broker/main/broker.c
index 3e1d09465c1..02632bb5f6e 100644
--- a/components/mosquitto/examples/broker/main/broker.c
+++ b/components/mosquitto/examples/broker/main/broker.c
@@ -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);
}
diff --git a/components/mosquitto/examples/broker/main/cacert.pem b/components/mosquitto/examples/broker/main/cacert.pem
new file mode 100644
index 00000000000..46e0eda8c0f
--- /dev/null
+++ b/components/mosquitto/examples/broker/main/cacert.pem
@@ -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-----
diff --git a/components/mosquitto/examples/broker/main/servercert.pem b/components/mosquitto/examples/broker/main/servercert.pem
new file mode 100644
index 00000000000..8582b46e0d4
--- /dev/null
+++ b/components/mosquitto/examples/broker/main/servercert.pem
@@ -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-----
diff --git a/components/mosquitto/examples/broker/main/serverkey.pem b/components/mosquitto/examples/broker/main/serverkey.pem
new file mode 100644
index 00000000000..02c08a16821
--- /dev/null
+++ b/components/mosquitto/examples/broker/main/serverkey.pem
@@ -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-----
diff --git a/components/mosquitto/port/broker.c b/components/mosquitto/port/broker.c
index 7742b559808..62cfb29bcef 100644
--- a/components/mosquitto/port/broker.c
+++ b/components/mosquitto/port/broker.c
@@ -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);
diff --git a/components/mosquitto/port/include/mosq_broker.h b/components/mosquitto/port/include/mosq_broker.h
index 88e2bc6975f..c615724720e 100644
--- a/components/mosquitto/port/include/mosq_broker.h
+++ b/components/mosquitto/port/include/mosq_broker.h
@@ -5,6 +5,7 @@
*/
#pragma once
#include "mosquitto.h"
+#include "esp_tls.h"
struct mosquitto__config;
@@ -17,6 +18,7 @@ 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) */
};
/**
@@ -28,4 +30,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 run_broker() unblocks and returns.
+ */
+void mosq_broker_stop(void);
diff --git a/components/mosquitto/port/net__esp_tls.c b/components/mosquitto/port/net__esp_tls.c
new file mode 100644
index 00000000000..078a47f9d90
--- /dev/null
+++ b/components/mosquitto/port/net__esp_tls.c
@@ -0,0 +1,467 @@
+/*
+ * SPDX-FileCopyrightText: 2009-2020 Roger Light
+ *
+ * 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
+# include
+# include
+# include
+# include
+# include
+# include
+
+#include
+#include
+#include
+#include
+#include
+
+#ifdef HAVE_NETINET_IN_H
+# include
+#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)
+{
+}