diff --git a/CMakeLists.txt b/CMakeLists.txt index ac65bd74..9e1c58d1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,7 @@ cmake_minimum_required(VERSION 3.18) project(mosquitto) -set (VERSION 2.0.21) +set (VERSION 2.0.22) list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/") diff --git a/ChangeLog.txt b/ChangeLog.txt index 38cb14e1..32866bb5 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -1,3 +1,46 @@ +2.0.22 - 2025-07-11 +=================== + +Broker: +- Windows: Fix broker crash on startup if using `log_dest stdout` +- Bridge: Fix idle_timeout never occurring for lazy bridges. +- Fix case where max_queued_messages = 0 was not treated as unlimited. + Closes #3244. +- Fix `--version` exit code and output. Closes #3267. +- Fix crash on receiving a $CONTROL message over a bridge, if + per_listener_settings is set true and the bridge is carrying out topic + remapping. Closes #3261. +- Fix incorrect reference clock being selected on startup on Linux. + Closes #3238. +- Fix reporting of client disconnections being incorrectly attributed to "out + of memory". Closes #3253. +- Fix compilation when using `WITH_OLD_KEEPALIVE`. Closes #3250. +- Add Windows linker file for the broker to the installer. Closes #3269. +- Fix Websockets PING not being sent on Windows. Closes #3272. +- Fix problems with secure websockets. Closes #1211. +- Fix crash on exit when using WITH_EPOLL=no. Closes #3302. +- Fix clients being incorrectly expired when they have keepalive == + max_keepalive. Closes #3226, #3286. + +Dynamic security plugin: +- Fix mismatch memory free when saving config which caused memory tracking to + be incorrect. + +Client library: +- Fix C++ symbols being removed when compiled with link time optimisation. + Closes #3259. +- TLS error handling was incorrectly setting a protocol error for non-TLS + errors. This would cause the mosquitto_loop_start() thread to exit if no + broker was available on the first connection attempt. This has been fixed. + Closes #3258. +- Fix linker errors on some architectures using cmake. Closes #3167. + + +Tests: +- Fix 08-ssl-connect-cert-auth-expired and 08-ssl-connect-cert-auth-revoked + tests when running on a single CPU system. Closes #3230. + + 2.0.21 - 2025-03-06 =================== diff --git a/apps/mosquitto_ctrl/CMakeLists.txt b/apps/mosquitto_ctrl/CMakeLists.txt index 26dbe8d6..734e6164 100644 --- a/apps/mosquitto_ctrl/CMakeLists.txt +++ b/apps/mosquitto_ctrl/CMakeLists.txt @@ -43,7 +43,7 @@ if (WITH_TLS AND CJSON_FOUND) endif (APPLE) endif (UNIX) - target_link_libraries(mosquitto_ctrl ${OPENSSL_LIBRARIES} ${CJSON_LIBRARIES}) + target_link_libraries(mosquitto_ctrl OpenSSL::SSL ${CJSON_LIBRARIES}) install(TARGETS mosquitto_ctrl RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") endif (WITH_TLS AND CJSON_FOUND) diff --git a/apps/mosquitto_passwd/CMakeLists.txt b/apps/mosquitto_passwd/CMakeLists.txt index 13a7d826..7ed17439 100644 --- a/apps/mosquitto_passwd/CMakeLists.txt +++ b/apps/mosquitto_passwd/CMakeLists.txt @@ -13,6 +13,6 @@ if (WITH_TLS) ) - target_link_libraries(mosquitto_passwd ${OPENSSL_LIBRARIES}) + target_link_libraries(mosquitto_passwd OpenSSL::SSL) install(TARGETS mosquitto_passwd RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") endif (WITH_TLS) diff --git a/config.mk b/config.mk index effc568b..34d5163f 100644 --- a/config.mk +++ b/config.mk @@ -134,7 +134,7 @@ WITH_ASAN=no # Also bump lib/mosquitto.h, CMakeLists.txt, # installer/mosquitto.nsi, installer/mosquitto64.nsi -VERSION=2.0.21 +VERSION=2.0.22 # Client library SO version. Bump if incompatible API/ABI changes are made. SOVERSION=1 diff --git a/include/mosquitto.h b/include/mosquitto.h index 447f31a8..7c80c8b0 100644 --- a/include/mosquitto.h +++ b/include/mosquitto.h @@ -61,12 +61,18 @@ extern "C" { # endif #endif +#ifndef _MSC_VER +# define MOSQ_USED __attribute__((used)) +#else +# define MOSQ_USED +#endif + #include #include #define LIBMOSQUITTO_MAJOR 2 #define LIBMOSQUITTO_MINOR 0 -#define LIBMOSQUITTO_REVISION 21 +#define LIBMOSQUITTO_REVISION 22 /* LIBMOSQUITTO_VERSION_NUMBER looks like 1002001 for e.g. version 1.2.1. */ #define LIBMOSQUITTO_VERSION_NUMBER (LIBMOSQUITTO_MAJOR*1000000+LIBMOSQUITTO_MINOR*1000+LIBMOSQUITTO_REVISION) diff --git a/installer/mosquitto.nsi b/installer/mosquitto.nsi index 55c4ac8b..4c4b12bc 100644 --- a/installer/mosquitto.nsi +++ b/installer/mosquitto.nsi @@ -9,7 +9,7 @@ !define env_hklm 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"' Name "Eclipse Mosquitto" -!define VERSION 2.0.21 +!define VERSION 2.0.22 OutFile "mosquitto-${VERSION}-install-windows-x86.exe" InstallDir "$PROGRAMFILES\mosquitto" @@ -73,8 +73,10 @@ Section "Files" SecInstall File "..\build\vcpkg_installed\x86-windows\bin\pthreadVC3.dll" File "..\build\vcpkg_installed\x86-windows\bin\uv.dll" File "..\build\vcpkg_installed\x86-windows\bin\websockets.dll" + File "..\build\vcpkg_installed\x86-windows\bin\zlib1.dll" SetOutPath "$INSTDIR\devel" + File /oname=mosquitto_broker.lib "..\build\src\Release\mosquitto.lib" File "..\build\lib\Release\mosquitto.lib" File "..\build\lib\cpp\Release\mosquittopp.lib" File "..\include\mosquitto.h" diff --git a/installer/mosquitto64.nsi b/installer/mosquitto64.nsi index 22fb5e22..0b0b7dc5 100644 --- a/installer/mosquitto64.nsi +++ b/installer/mosquitto64.nsi @@ -9,7 +9,7 @@ !define env_hklm 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"' Name "Eclipse Mosquitto" -!define VERSION 2.0.21 +!define VERSION 2.0.22 OutFile "mosquitto-${VERSION}-install-windows-x64.exe" !include "x64.nsh" @@ -74,8 +74,10 @@ Section "Files" SecInstall File "..\build64\vcpkg_installed\x64-windows-release\bin\pthreadVC3.dll" File "..\build64\vcpkg_installed\x64-windows-release\bin\uv.dll" File "..\build64\vcpkg_installed\x64-windows-release\bin\websockets.dll" + File "..\build64\vcpkg_installed\x64-windows-release\bin\zlib1.dll" SetOutPath "$INSTDIR\devel" + File /oname=mosquitto_broker.lib "..\build64\src\Release\mosquitto.lib" File "..\build64\lib\Release\mosquitto.lib" File "..\build64\lib\cpp\Release\mosquittopp.lib" File "..\include\mosquitto.h" diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 646d0ea9..21b61497 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -60,7 +60,7 @@ set(C_SRC util_mosq.c util_topic.c util_mosq.h will_mosq.c will_mosq.h) -set (LIBRARIES ${OPENSSL_LIBRARIES}) +set (LIBRARIES OpenSSL::SSL) if (UNIX AND NOT APPLE AND NOT ANDROID) find_library(LIBRT rt) @@ -100,7 +100,7 @@ if (WITH_THREADING) endif() endif() -target_link_libraries(libmosquitto PRIVATE ${LIBRARIES}) +target_link_libraries(libmosquitto ${LIBRARIES}) set_target_properties(libmosquitto PROPERTIES OUTPUT_NAME mosquitto diff --git a/lib/cpp/mosquittopp.h b/lib/cpp/mosquittopp.h index f47535a9..510abb0e 100644 --- a/lib/cpp/mosquittopp.h +++ b/lib/cpp/mosquittopp.h @@ -124,15 +124,15 @@ class mosqpp_EXPORT mosquittopp { int socks5_set(const char *host, int port=1080, const char *username=NULL, const char *password=NULL); // names in the functions commented to prevent unused parameter warning - virtual void on_connect(int /*rc*/) {return;} - virtual void on_connect_with_flags(int /*rc*/, int /*flags*/) {return;} - virtual void on_disconnect(int /*rc*/) {return;} - virtual void on_publish(int /*mid*/) {return;} - virtual void on_message(const struct mosquitto_message * /*message*/) {return;} - virtual void on_subscribe(int /*mid*/, int /*qos_count*/, const int * /*granted_qos*/) {return;} - virtual void on_unsubscribe(int /*mid*/) {return;} - virtual void on_log(int /*level*/, const char * /*str*/) {return;} - virtual void on_error() {return;} + virtual void MOSQ_USED on_connect(int /*rc*/) {return;} + virtual void MOSQ_USED on_connect_with_flags(int /*rc*/, int /*flags*/) {return;} + virtual void MOSQ_USED on_disconnect(int /*rc*/) {return;} + virtual void MOSQ_USED on_publish(int /*mid*/) {return;} + virtual void MOSQ_USED on_message(const struct mosquitto_message * /*message*/) {return;} + virtual void MOSQ_USED on_subscribe(int /*mid*/, int /*qos_count*/, const int * /*granted_qos*/) {return;} + virtual void MOSQ_USED on_unsubscribe(int /*mid*/) {return;} + virtual void MOSQ_USED on_log(int /*level*/, const char * /*str*/) {return;} + virtual void MOSQ_USED on_error() {return;} }; } diff --git a/lib/helpers.c b/lib/helpers.c index b418065a..c3d5a09e 100644 --- a/lib/helpers.c +++ b/lib/helpers.c @@ -114,7 +114,7 @@ libmosq_EXPORT int mosquitto_subscribe_simple( *messages = NULL; - userdata.messages = calloc(sizeof(struct mosquitto_message), (size_t)msg_count); + userdata.messages = calloc((size_t)msg_count, sizeof(struct mosquitto_message)); if(!userdata.messages){ return MOSQ_ERR_NOMEM; } diff --git a/lib/loop.c b/lib/loop.c index 4905fd99..215982cd 100644 --- a/lib/loop.c +++ b/lib/loop.c @@ -135,9 +135,7 @@ int mosquitto_loop(struct mosquitto *mosq, int timeout, int max_packets) fdcount = select(maxfd+1, &readfds, &writefds, NULL, &local_timeout); #endif if(fdcount == -1){ -#ifdef WIN32 - errno = WSAGetLastError(); -#endif + WINDOWS_SET_ERRNO(); if(errno == EINTR){ return MOSQ_ERR_SUCCESS; }else{ @@ -219,9 +217,7 @@ static int interruptible_sleep(struct mosquitto *mosq, time_t reconnect_delay) fdcount = select(maxfd+1, &readfds, NULL, NULL, &local_timeout); #endif if(fdcount == -1){ -#ifdef WIN32 - errno = WSAGetLastError(); -#endif + WINDOWS_SET_ERRNO(); if(errno == EINTR){ return MOSQ_ERR_SUCCESS; }else{ diff --git a/lib/mosquitto_internal.h b/lib/mosquitto_internal.h index 48c8c108..8021b3ae 100644 --- a/lib/mosquitto_internal.h +++ b/lib/mosquitto_internal.h @@ -68,6 +68,15 @@ typedef SOCKET mosq_sock_t; typedef int mosq_sock_t; #endif +#ifdef WIN32 +# define WINDOWS_SET_ERRNO() \ + if(errno != EAGAIN){ \ + errno = WSAGetLastError(); \ + } +#else +# define WINDOWS_SET_ERRNO() +#endif + #define SAFE_PRINT(A) (A)?(A):"null" enum mosquitto_msg_direction { @@ -356,6 +365,7 @@ struct mosquitto { # ifndef WITH_OLD_KEEPALIVE struct mosquitto *keepalive_next; struct mosquitto *keepalive_prev; + time_t keepalive_add_time; # endif #endif uint32_t events; diff --git a/lib/net_mosq.c b/lib/net_mosq.c index 5c86d953..d9eb81e0 100644 --- a/lib/net_mosq.c +++ b/lib/net_mosq.c @@ -361,9 +361,7 @@ int net__try_connect_step2(struct mosquitto *mosq, uint16_t port, mosq_sock_t *s } rc = connect(*sock, rp->ai_addr, rp->ai_addrlen); -#ifdef WIN32 - errno = WSAGetLastError(); -#endif + WINDOWS_SET_ERRNO(); if(rc == 0 || errno == EINPROGRESS || errno == COMPAT_EWOULDBLOCK){ if(rc < 0 && (errno == EINPROGRESS || errno == COMPAT_EWOULDBLOCK)){ rc = MOSQ_ERR_CONN_PENDING; @@ -461,9 +459,7 @@ static int net__try_connect_tcp(const char *host, uint16_t port, mosq_sock_t *so } rc = connect(*sock, rp->ai_addr, rp->ai_addrlen); -#ifdef WIN32 - errno = WSAGetLastError(); -#endif + WINDOWS_SET_ERRNO(); if(rc == 0 || errno == EINPROGRESS || errno == COMPAT_EWOULDBLOCK){ if(rc < 0 && (errno == EINPROGRESS || errno == COMPAT_EWOULDBLOCK)){ rc = MOSQ_ERR_CONN_PENDING; @@ -944,34 +940,30 @@ int net__socket_connect(struct mosquitto *mosq, const char *host, uint16_t port, #ifdef WITH_TLS -static int net__handle_ssl(struct mosquitto* mosq, int ret) +static void net__handle_ssl(struct mosquitto* mosq, int ret) { int err; err = SSL_get_error(mosq->ssl, ret); if (err == SSL_ERROR_WANT_READ) { - ret = -1; errno = EAGAIN; } else if (err == SSL_ERROR_WANT_WRITE) { - ret = -1; #ifdef WITH_BROKER mux__add_out(mosq); #else mosq->want_write = true; #endif errno = EAGAIN; - } - else { + }else if(err == SSL_ERROR_SSL){ net__print_ssl_error(mosq); errno = EPROTO; + /* else if SSL_ERROR_SYSCALL leave errno alone */ } ERR_clear_error(); #ifdef WIN32 WSASetLastError(errno); #endif - - return ret; } #endif @@ -987,7 +979,7 @@ ssize_t net__read(struct mosquitto *mosq, void *buf, size_t count) ERR_clear_error(); ret = SSL_read(mosq->ssl, buf, (int)count); if(ret <= 0){ - ret = net__handle_ssl(mosq, ret); + net__handle_ssl(mosq, ret); } return (ssize_t )ret; }else{ @@ -1020,7 +1012,7 @@ ssize_t net__write(struct mosquitto *mosq, const void *buf, size_t count) mosq->want_write = false; ret = SSL_write(mosq->ssl, buf, (int)count); if(ret < 0){ - ret = net__handle_ssl(mosq, ret); + net__handle_ssl(mosq, ret); } return (ssize_t )ret; }else{ @@ -1138,9 +1130,7 @@ int net__socketpair(mosq_sock_t *pairR, mosq_sock_t *pairW) continue; } if(connect(spR, (struct sockaddr *)&ss, ss_len) < 0){ -#ifdef WIN32 - errno = WSAGetLastError(); -#endif + WINDOWS_SET_ERRNO(); if(errno != EINPROGRESS && errno != COMPAT_EWOULDBLOCK){ COMPAT_CLOSE(spR); COMPAT_CLOSE(listensock); @@ -1149,9 +1139,7 @@ int net__socketpair(mosq_sock_t *pairR, mosq_sock_t *pairW) } spW = accept(listensock, NULL, 0); if(spW == -1){ -#ifdef WIN32 - errno = WSAGetLastError(); -#endif + WINDOWS_SET_ERRNO(); if(errno != EINPROGRESS && errno != COMPAT_EWOULDBLOCK){ COMPAT_CLOSE(spR); COMPAT_CLOSE(listensock); diff --git a/lib/packet_mosq.c b/lib/packet_mosq.c index 86b58f78..350cbf58 100644 --- a/lib/packet_mosq.c +++ b/lib/packet_mosq.c @@ -267,9 +267,8 @@ int packet__write(struct mosquitto *mosq) packet->to_process -= (uint32_t)write_length; packet->pos += (uint32_t)write_length; }else{ -#ifdef WIN32 - errno = WSAGetLastError(); -#endif + WINDOWS_SET_ERRNO(); + if(errno == EAGAIN || errno == COMPAT_EWOULDBLOCK #ifdef WIN32 || errno == WSAENOTCONN @@ -411,9 +410,7 @@ int packet__read(struct mosquitto *mosq) if(read_length == 0){ return MOSQ_ERR_CONN_LOST; /* EOF */ } -#ifdef WIN32 - errno = WSAGetLastError(); -#endif + WINDOWS_SET_ERRNO(); if(errno == EAGAIN || errno == COMPAT_EWOULDBLOCK){ return MOSQ_ERR_SUCCESS; }else{ @@ -456,9 +453,7 @@ int packet__read(struct mosquitto *mosq) if(read_length == 0){ return MOSQ_ERR_CONN_LOST; /* EOF */ } -#ifdef WIN32 - errno = WSAGetLastError(); -#endif + WINDOWS_SET_ERRNO(); if(errno == EAGAIN || errno == COMPAT_EWOULDBLOCK){ return MOSQ_ERR_SUCCESS; }else{ @@ -533,9 +528,7 @@ int packet__read(struct mosquitto *mosq) mosq->in_packet.to_process -= (uint32_t)read_length; mosq->in_packet.pos += (uint32_t)read_length; }else{ -#ifdef WIN32 - errno = WSAGetLastError(); -#endif + WINDOWS_SET_ERRNO(); if(errno == EAGAIN || errno == COMPAT_EWOULDBLOCK){ if(mosq->in_packet.to_process > 1000){ /* Update last_msg_in time if more than 1000 bytes left to diff --git a/lib/socks_mosq.c b/lib/socks_mosq.c index a7a0e035..6cb97ed0 100644 --- a/lib/socks_mosq.c +++ b/lib/socks_mosq.c @@ -281,9 +281,7 @@ int socks5__read(struct mosquitto *mosq) mosq->in_packet.pos += (uint32_t)len; mosq->in_packet.to_process -= (uint32_t)len; }else{ -#ifdef WIN32 - errno = WSAGetLastError(); -#endif + WINDOWS_SET_ERRNO(); if(errno == EAGAIN || errno == COMPAT_EWOULDBLOCK){ return MOSQ_ERR_SUCCESS; }else{ @@ -323,9 +321,7 @@ int socks5__read(struct mosquitto *mosq) mosq->in_packet.pos += (uint32_t)len; mosq->in_packet.to_process -= (uint32_t)len; }else{ -#ifdef WIN32 - errno = WSAGetLastError(); -#endif + WINDOWS_SET_ERRNO(); if(errno == EAGAIN || errno == COMPAT_EWOULDBLOCK){ return MOSQ_ERR_SUCCESS; }else{ @@ -379,9 +375,7 @@ int socks5__read(struct mosquitto *mosq) mosq->in_packet.pos += (uint32_t)len; mosq->in_packet.to_process -= (uint32_t)len; }else{ -#ifdef WIN32 - errno = WSAGetLastError(); -#endif + WINDOWS_SET_ERRNO(); if(errno == EAGAIN || errno == COMPAT_EWOULDBLOCK){ return MOSQ_ERR_SUCCESS; }else{ diff --git a/lib/util_mosq.c b/lib/util_mosq.c index 134e78fd..bed940b4 100644 --- a/lib/util_mosq.c +++ b/lib/util_mosq.c @@ -80,7 +80,7 @@ int mosquitto__check_keepalive(struct mosquitto *mosq) /* Check if a lazy bridge should be timed out due to idle. */ if(mosq->bridge && mosq->bridge->start_type == bst_lazy && mosq->sock != INVALID_SOCKET - && now - mosq->next_msg_out - mosq->keepalive >= mosq->bridge->idle_timeout){ + && now - mosq->last_msg_in >= mosq->bridge->idle_timeout){ log__printf(NULL, MOSQ_LOG_NOTICE, "Bridge connection %s has exceeded idle timeout, disconnecting.", mosq->id); net__socket_close(mosq); diff --git a/plugins/dynamic-security/CMakeLists.txt b/plugins/dynamic-security/CMakeLists.txt index 643de1de..5f7ada4f 100644 --- a/plugins/dynamic-security/CMakeLists.txt +++ b/plugins/dynamic-security/CMakeLists.txt @@ -32,7 +32,7 @@ if (CJSON_FOUND AND WITH_TLS) ) set_target_properties(mosquitto_dynamic_security PROPERTIES PREFIX "") - target_link_libraries(mosquitto_dynamic_security ${CJSON_LIBRARIES} ${OPENSSL_LIBRARIES}) + target_link_libraries(mosquitto_dynamic_security ${CJSON_LIBRARIES} OpenSSL::SSL) if(WIN32) target_link_libraries(mosquitto_dynamic_security mosquitto) install(TARGETS mosquitto_dynamic_security diff --git a/plugins/dynamic-security/plugin.c b/plugins/dynamic-security/plugin.c index 125052e6..ce391456 100644 --- a/plugins/dynamic-security/plugin.c +++ b/plugins/dynamic-security/plugin.c @@ -629,7 +629,7 @@ void dynsec__config_save(void) return; } fwrite(json_str, 1, json_str_len, fptr); - mosquitto_free(json_str); + cJSON_free(json_str); fclose(fptr); /* Everything is ok, so move new file over proper file */ diff --git a/set-version.sh b/set-version.sh index 467b4c4a..30d6361b 100755 --- a/set-version.sh +++ b/set-version.sh @@ -2,7 +2,7 @@ MAJOR=2 MINOR=0 -REVISION=21 +REVISION=22 sed -i "s/^VERSION=.*/VERSION=${MAJOR}.${MINOR}.${REVISION}/" config.mk diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 2a78b8fa..e4da5597 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -1,5 +1,5 @@ name: mosquitto -version: 2.0.21 +version: 2.0.22 summary: Eclipse Mosquitto MQTT broker description: This is a message broker that supports version 5.0, 3.1.1, and 3.1 of the MQTT protocol. MQTT provides a method of carrying out messaging using a publish/subscribe diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dce8313f..d4bae7cd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -113,6 +113,12 @@ if (WITH_PERSISTENCE) add_definitions("-DWITH_PERSISTENCE") endif (WITH_PERSISTENCE) +option(WITH_OLD_KEEPALIVE + "Use legacy keepalive check mechanism?" OFF) +if (WITH_OLD_KEEPALIVE) + add_definitions("-DWITH_OLD_KEEPALIVE") +endif (WITH_OLD_KEEPALIVE) + option(WITH_SYS_TREE "Include $SYS tree support?" ON) if (WITH_SYS_TREE) @@ -157,7 +163,7 @@ if (WITH_DLT) set (MOSQ_LIBS ${MOSQ_LIBS} ${DLT_LIBRARIES}) endif (WITH_DLT) -set (MOSQ_LIBS ${MOSQ_LIBS} ${OPENSSL_LIBRARIES}) +set (MOSQ_LIBS ${MOSQ_LIBS} OpenSSL::SSL) # Check for getaddrinfo_a include(CheckLibraryExists) check_library_exists(anl getaddrinfo_a "" HAVE_GETADDRINFO_A) diff --git a/src/conf.c b/src/conf.c index 59404f6e..a3eaef9e 100644 --- a/src/conf.c +++ b/src/conf.c @@ -356,6 +356,13 @@ void config__cleanup(struct mosquitto__config *config) } } +static void print_version(void) +{ + printf("mosquitto %s\n", VERSION); + printf("Copyright © 2025 Roger Light.\n"); + printf("License EPL-2.0 OR BSD-3-Clause.\n"); +} + static void print_usage(void) { printf("mosquitto version %s\n\n", VERSION); @@ -393,7 +400,10 @@ int config__parse_args(struct mosquitto__config *config, int argc, char *argv[]) config->daemon = true; }else if(!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")){ print_usage(); - return MOSQ_ERR_INVAL; + return MOSQ_ERR_UNKNOWN; + }else if(!strcmp(argv[i], "--version")){ + print_version(); + return MOSQ_ERR_UNKNOWN; }else if(!strcmp(argv[i], "-p") || !strcmp(argv[i], "--port")){ if(ilistener_count; i++){ bool cafile = !!config->listeners[i].cafile; @@ -2333,6 +2344,7 @@ static int config__check(struct mosquitto__config *config) return MOSQ_ERR_INVAL; } } +#endif return MOSQ_ERR_SUCCESS; } diff --git a/src/control.c b/src/control.c index 8a70a967..79785d74 100644 --- a/src/control.c +++ b/src/control.c @@ -37,10 +37,16 @@ int control__process(struct mosquitto *context, struct mosquitto_msg_store *stor int rc = MOSQ_ERR_SUCCESS; if(db.config->per_listener_settings){ + if(!context->listener){ + log__printf(NULL, MOSQ_LOG_WARNING, "Warning: $CONTROL command received from client with no listener, when per_listener_settings is true."); + log__printf(NULL, MOSQ_LOG_WARNING, " If this is a bridge, please be aware this does not work."); + return MOSQ_ERR_SUCCESS; + } opts = &context->listener->security_options; }else{ opts = &db.config->security_options; } + HASH_FIND(hh, opts->plugin_callbacks.control, stored->topic, strlen(stored->topic), cb_found); if(cb_found){ memset(&event_data, 0, sizeof(event_data)); diff --git a/src/handle_publish.c b/src/handle_publish.c index eb0cef3e..d3b88e68 100644 --- a/src/handle_publish.c +++ b/src/handle_publish.c @@ -328,16 +328,18 @@ int handle__publish(struct mosquitto *context) switch(stored->qos){ case 0: rc2 = sub__messages_queue(context->id, stored->topic, stored->qos, stored->retain, &stored); - if(rc2 > 0) rc = 1; + if(rc2 > 0) rc = rc2; break; case 1: util__decrement_receive_quota(context); rc2 = sub__messages_queue(context->id, stored->topic, stored->qos, stored->retain, &stored); /* stored may now be free, so don't refer to it */ if(rc2 == MOSQ_ERR_SUCCESS || context->protocol != mosq_p_mqtt5){ - if(send__puback(context, mid, 0, NULL)) rc = 1; + rc2 = send__puback(context, mid, 0, NULL); + if(rc2) rc = rc2; }else if(rc2 == MOSQ_ERR_NO_SUBSCRIBERS){ - if(send__puback(context, mid, MQTT_RC_NO_MATCHING_SUBSCRIBERS, NULL)) rc = 1; + rc2 = send__puback(context, mid, MQTT_RC_NO_MATCHING_SUBSCRIBERS, NULL); + if(rc2) rc = rc2; }else{ rc = rc2; } @@ -359,8 +361,8 @@ int handle__publish(struct mosquitto *context) }else{ return MOSQ_ERR_PROTOCOL; } - }else if(res == 1){ - rc = 1; + }else{ + rc = res; } break; } @@ -368,7 +370,7 @@ int handle__publish(struct mosquitto *context) db__message_write_queued_in(context); return rc; process_bad_message: - rc = 1; + rc = MOSQ_ERR_UNKNOWN; if(msg){ switch(msg->qos){ case 0: @@ -383,7 +385,7 @@ process_bad_message: } db__msg_store_free(msg); } - if(context->out_packet_count >= db.config->max_queued_messages){ + if(db.config->max_queued_messages > 0 && context->out_packet_count >= db.config->max_queued_messages){ rc = MQTT_RC_QUOTA_EXCEEDED; } return rc; diff --git a/src/keepalive.c b/src/keepalive.c index dda13f14..44602b13 100644 --- a/src/keepalive.c +++ b/src/keepalive.c @@ -75,7 +75,6 @@ int keepalive__init(void) #ifndef WITH_OLD_KEEPALIVE struct mosquitto *context, *ctxt_tmp; - last_keepalive_check = db.now_s; if(db.config->max_keepalive <= 0){ keepalive_list_max = (UINT16_MAX * 3)/2 + 1; }else{ @@ -95,12 +94,19 @@ int keepalive__init(void) } } #endif + last_keepalive_check = db.now_s; return MOSQ_ERR_SUCCESS; } void keepalive__cleanup(void) { #ifndef WITH_OLD_KEEPALIVE + for(int idx=0; idxkeepalive_add_time = db.now_s; #else UNUSED(context); #endif @@ -132,7 +139,11 @@ void keepalive__check(void) int idx = (int)(i % keepalive_list_max); if(keepalive_list[idx]){ DL_FOREACH_SAFE2(keepalive_list[idx], context, ctxt_tmp, keepalive_next){ - if(net__is_connected(context)){ + /* keepalive_add_time lets us account for the client adding itself to the keepalive + * list when its last_msg_in value is greater than the last_keepalive_check. + * Without this, the client would be expired if it has keepalive == max_keepalive. + */ + if(context->keepalive_add_time <= last_keepalive_check && net__is_connected(context)){ /* Client has exceeded keepalive*1.5 */ do_disconnect(context, MOSQ_ERR_KEEPALIVE); } @@ -146,15 +157,7 @@ void keepalive__check(void) void keepalive__check(void) { struct mosquitto *context, *ctxt_tmp; - time_t timeout; - if(db.contexts_by_sock){ - timeout = (last_keepalive_check + 5 - db.now_s); - if(timeout <= 0){ - timeout = 5; - } - loop__update_next_event(timeout*1000); - } if(last_keepalive_check + 5 <= db.now_s){ last_keepalive_check = db.now_s; diff --git a/src/logging.c b/src/logging.c index 0af2787f..b995eeeb 100644 --- a/src/logging.c +++ b/src/logging.c @@ -130,9 +130,11 @@ int log__init(struct mosquitto__config *config) log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to open log file %s for writing.", config->log_file); } } +#ifndef WIN32 if(log_destinations & MQTT3_LOG_STDOUT){ setvbuf(stdout, NULL, _IOLBF, 0); } +#endif #ifdef WITH_DLT if(log_destinations & MQTT3_LOG_DLT){ dlt_fifo_check(); diff --git a/src/loop.c b/src/loop.c index 084b2f3d..26bc44d7 100644 --- a/src/loop.c +++ b/src/loop.c @@ -272,8 +272,6 @@ int mosquitto_main_loop(struct mosquitto__listener_sock *listensock, int listens plugin__handle_tick(); } - mux__cleanup(); - return MOSQ_ERR_SUCCESS; } diff --git a/src/mosquitto.c b/src/mosquitto.c index de5d4c10..50d50d26 100644 --- a/src/mosquitto.c +++ b/src/mosquitto.c @@ -465,6 +465,7 @@ int main(int argc, char *argv[]) #endif struct mosquitto *ctxt, *ctxt_tmp; + mosquitto_time_init(); #if defined(WIN32) || defined(__CYGWIN__) if(argc == 2){ if(!strcmp(argv[1], "run")){ @@ -506,7 +507,11 @@ int main(int argc, char *argv[]) config__init(&config); rc = config__parse_args(&config, argc, argv); - if(rc != MOSQ_ERR_SUCCESS) return rc; + if(rc == MOSQ_ERR_UNKNOWN){ + return MOSQ_ERR_SUCCESS; + }else if(rc != MOSQ_ERR_SUCCESS){ + return rc; + } db.config = &config; rc = keepalive__init(); @@ -633,6 +638,8 @@ int main(int argc, char *argv[]) (void)remove(config.pid_file); } + mux__cleanup(); + log__close(&config); config__cleanup(db.config); net__broker_cleanup(); diff --git a/src/net.c b/src/net.c index 77ce7e41..0196918a 100644 --- a/src/net.c +++ b/src/net.c @@ -126,7 +126,7 @@ struct mosquitto *net__socket_accept(struct mosquitto__listener_sock *listensock new_sock = accept(listensock->sock, NULL, 0); if(new_sock == INVALID_SOCKET){ #ifdef WIN32 - errno = WSAGetLastError(); + WINDOWS_SET_ERRNO(); if(errno == WSAEMFILE){ #else if(errno == EMFILE || errno == ENFILE){ diff --git a/src/websockets.c b/src/websockets.c index 5051556c..688d3b87 100644 --- a/src/websockets.c +++ b/src/websockets.c @@ -711,6 +711,10 @@ void mosq_websockets_init(struct mosquitto__listener *listener, const struct mos info.ssl_cert_filepath = listener->certfile; info.ssl_private_key_filepath = listener->keyfile; info.ssl_cipher_list = listener->ciphers; + /* HTTP 1 only, due to HTTP 2 issues in Firefox: + https://github.com/eclipse-mosquitto/mosquitto/issues/1211 + */ + info.alpn = "h1"; #if defined(WITH_WEBSOCKETS) && LWS_LIBRARY_VERSION_NUMBER>=3001000 info.tls1_3_plus_cipher_list = listener->ciphers_tls13; #endif diff --git a/test/broker/08-ssl-connect-cert-auth-expired.py b/test/broker/08-ssl-connect-cert-auth-expired.py index f4226d56..a2a81801 100755 --- a/test/broker/08-ssl-connect-cert-auth-expired.py +++ b/test/broker/08-ssl-connect-cert-auth-expired.py @@ -24,8 +24,6 @@ conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port1, port2) rc = 1 -connect_packet = mosq_test.gen_connect("connect-success-test") - broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True) ssl_eof = False @@ -36,7 +34,7 @@ try: ssock = context.wrap_socket(sock, server_hostname="localhost", suppress_ragged_eofs=True) ssock.settimeout(None) try: - mosq_test.do_send_receive(ssock, connect_packet, "", "connack") + ssock.read(1) except ssl.SSLEOFError: # Under load, sometimes the broker closes the connection after the # handshake has failed, but before we have chance to send our diff --git a/test/broker/08-ssl-connect-cert-auth-revoked.py b/test/broker/08-ssl-connect-cert-auth-revoked.py index 5d189af2..062fc83d 100755 --- a/test/broker/08-ssl-connect-cert-auth-revoked.py +++ b/test/broker/08-ssl-connect-cert-auth-revoked.py @@ -23,9 +23,6 @@ conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port1, port2) rc = 1 -keepalive = 10 -connect_packet = mosq_test.gen_connect("connect-revoked-test", keepalive=keepalive) - broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True) ssl_eof = False @@ -38,7 +35,7 @@ try: try: ssock.connect(("localhost", port1)) try: - mosq_test.do_send_receive(ssock, connect_packet, "", "connack") + ssock.read(1) except ssl.SSLEOFError: # Under load, sometimes the broker closes the connection after the # handshake has failed, but before we have chance to send our diff --git a/vcpkg.json b/vcpkg.json index 285dfcbe..c772a462 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -1,6 +1,6 @@ { "name": "mosquitto", - "version-string": "2.0.21", + "version-string": "2.0.22", "dependencies": [ "cjson", "libwebsockets", diff --git a/www/files/favicon.ico b/www/files/favicon.ico index c4efbcc0..643ef390 100644 Binary files a/www/files/favicon.ico and b/www/files/favicon.ico differ diff --git a/www/files/favicon.svg b/www/files/favicon.svg new file mode 100644 index 00000000..95b573fc --- /dev/null +++ b/www/files/favicon.svg @@ -0,0 +1,3 @@ +image/svg+xml \ No newline at end of file diff --git a/www/pages/download.md b/www/pages/download.md index f51bd953..bfe5c76e 100644 --- a/www/pages/download.md +++ b/www/pages/download.md @@ -11,7 +11,7 @@ # Source -* [mosquitto-2.0.21.tar.gz](https://mosquitto.org/files/source/mosquitto-2.0.21.tar.gz) ([GPG signature](https://mosquitto.org/files/source/mosquitto-2.0.21.tar.gz.asc)) +* [mosquitto-2.0.22.tar.gz](https://mosquitto.org/files/source/mosquitto-2.0.22.tar.gz) ([GPG signature](https://mosquitto.org/files/source/mosquitto-2.0.22.tar.gz.asc)) * [Git source code repository](https://github.com/eclipse/mosquitto) (github.com) Older downloads are available at [https://mosquitto.org/files/](../files/) @@ -24,8 +24,8 @@ distributions. ## Windows -* [mosquitto-2.0.21-install-windows-x64.exe](https://mosquitto.org/files/binary/win64/mosquitto-2.0.21-install-windows-x64.exe) -* [mosquitto-2.0.21-install-windows-x86.exe](https://mosquitto.org/files/binary/win32/mosquitto-2.0.21-install-windows-x86.exe) +* [mosquitto-2.0.22-install-windows-x64.exe](https://mosquitto.org/files/binary/win64/mosquitto-2.0.22-install-windows-x64.exe) +* [mosquitto-2.0.22-install-windows-x86.exe](https://mosquitto.org/files/binary/win32/mosquitto-2.0.22-install-windows-x86.exe) Older installers can be found at [https://mosquitto.org/files/binary/](https://mosquitto.org/files/binary/). @@ -42,6 +42,7 @@ Mosquitto can be installed from the homebrew project. See ## Debian * Mosquitto is now in Debian proper. There will be a short delay between a new release and it appearing in Debian as part of the normal Debian procedures. + The tracker for the package is at . * There are also Debian repositories provided by the mosquitto project, as described at diff --git a/www/posts/2025/07/version-2-0-22-released.md b/www/posts/2025/07/version-2-0-22-released.md new file mode 100644 index 00000000..71c5a2be --- /dev/null +++ b/www/posts/2025/07/version-2-0-22-released.md @@ -0,0 +1,69 @@ + + +Version 2.0.22 of Mosquitto has been released. This is a bugfix release. + +# Broker + +- Windows: Fix broker crash on startup if using `log_dest stdout` +- Bridge: Fix `idle_timeout` never occurring for lazy bridges. +- Fix case where `max_queued_messages = 0` was not treated as unlimited. + Closes [#3244]. +- Fix `--version` exit code and output. Closes [#3267]. +- Fix crash on receiving a $CONTROL message over a bridge, if + `per_listener_settings` is set true and the bridge is carrying out topic + remapping. Closes [#3261]. +- Fix incorrect reference clock being selected on startup on Linux. + Closes [#3238]. +- Fix reporting of client disconnections being incorrectly attributed to "out + of memory". Closes [#3253]. +- Fix compilation when using `WITH_OLD_KEEPALIVE`. Closes [#3250]. +- Add Windows linker file for the broker to the installer. Closes [#3269]. +- Fix Websockets PING not being sent on Windows. Closes [#3272]. +- Fix problems with secure websockets. Closes [#1211]. +- Fix crash on exit when using `WITH_EPOLL=no`. Closes [#3302]. +- Fix clients being incorrectly expired when they have keepalive == + `max_keepalive`. Closes [#3226], [#3286]. + +# Dynamic security plugin +- Fix mismatch memory free when saving config which caused memory tracking to + be incorrect. + +# Client library +- Fix C++ symbols being removed when compiled with link time optimisation. + Closes [#3259]. +- TLS error handling was incorrectly setting a protocol error for non-TLS + errors. This would cause the `mosquitto_loop_start()` thread to exit if no + broker was available on the first connection attempt. This has been fixed. + Closes [#3258]. +- Fix linker errors on some architectures using cmake. Closes [#3167]. + + +Tests: +- Fix 08-ssl-connect-cert-auth-expired and 08-ssl-connect-cert-auth-revoked + tests when running on a single CPU system. Closes [#3230]. + +[#1211]: https://github.com/eclipse/mosquitto/issues/1211 +[#3167]: https://github.com/eclipse/mosquitto/issues/3167 +[#3226]: https://github.com/eclipse/mosquitto/issues/3226 +[#3230]: https://github.com/eclipse/mosquitto/issues/3230 +[#3238]: https://github.com/eclipse/mosquitto/issues/3238 +[#3244]: https://github.com/eclipse/mosquitto/issues/3244 +[#3250]: https://github.com/eclipse/mosquitto/issues/3250 +[#3253]: https://github.com/eclipse/mosquitto/issues/3253 +[#3258]: https://github.com/eclipse/mosquitto/issues/3258 +[#3259]: https://github.com/eclipse/mosquitto/issues/3259 +[#3261]: https://github.com/eclipse/mosquitto/issues/3261 +[#3267]: https://github.com/eclipse/mosquitto/issues/3267 +[#3269]: https://github.com/eclipse/mosquitto/issues/3269 +[#3272]: https://github.com/eclipse/mosquitto/issues/3272 +[#3286]: https://github.com/eclipse/mosquitto/issues/3286 +[#3302]: https://github.com/eclipse/mosquitto/issues/3302