diff --git a/CMakeLists.txt b/CMakeLists.txt index 6d5d7201..3a680614 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ project(mosquitto) cmake_minimum_required(VERSION 2.8) # Only for version 3 and up. cmake_policy(SET CMP0042 NEW) -set (VERSION 1.4.14) +set (VERSION 1.4.15) if (WIN32) execute_process(COMMAND cmd /c echo %DATE% %TIME% OUTPUT_VARIABLE TIMESTAMP diff --git a/ChangeLog.txt b/ChangeLog.txt index 9f471d92..e31cf4f1 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -1,9 +1,46 @@ +1.4.15 - 20180228 +================= + +Security: +- Fix CVE-2017-7652. If a SIGHUP is sent to the broker when there are no more + file descriptors, then opening the configuration file will fail and security + settings will be set back to their default values. +- Fix CVE-2017-7651. Unauthenticated clients can cause excessive memory use by + setting "remaining length" to be a large value. This is now mitigated by + limiting the size of remaining length to valid values. A "memory_limit" + configuration option has also been added to allow the overall memory used by + the broker to be limited. + Broker: - Use constant time memcmp for password comparisons. - Fix incorrect PSK key being used if it had leading zeroes. +- Fix memory leak if a client provided a username/password for a listener with + use_identity_as_username configured. +- Fix use_identity_as_username not working on websockets clients. +- Don't crash if an auth plugin returns MOSQ_ERR_AUTH for a username check on + a websockets client. Closes #490. +- Fix 08-ssl-bridge.py test when using async dns lookups. Closes #507. +- Lines in the config file are no longer limited to 1024 characters long. + Closes #652. +- Fix $SYS counters of messages and bytes sent when message is sent over + a Websockets. Closes #250. +- Fix upgrade_outgoing_qos for retained message. Closes #534. +- Fix CONNACK message not being sent for unauthorised connect on websockets. + Closes #8. Client library: - Fix incorrect PSK key being used if it had leading zeroes. +- Initialise "result" variable as soon as possible in + mosquitto_topic_matches_sub. Closes #654. +- No need to close socket again if setting non-blocking failed. Closes #649. +- Fix mosquitto_topic_matches_sub() not correctly matching foo/bar against + foo/+/#. Closes #670. + +Clients: +- Correctly handle empty files with "mosquitto_pub -l". Closes #676. + +Build: +- Don't run TLS-PSK tests if TLS-PSK disabled at compile time. Closes #636. 1.4.14 - 20170710 diff --git a/client/client_shared.c b/client/client_shared.c index fa3ea188..2d9f6744 100644 --- a/client/client_shared.c +++ b/client/client_shared.c @@ -1,5 +1,5 @@ /* -Copyright (c) 2014 Roger Light +Copyright (c) 2014-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 diff --git a/client/client_shared.h b/client/client_shared.h index 0a974b1e..2b557345 100644 --- a/client/client_shared.h +++ b/client/client_shared.h @@ -1,5 +1,5 @@ /* -Copyright (c) 2014 Roger Light +Copyright (c) 2014-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 diff --git a/client/pub_client.c b/client/pub_client.c index c8ca981a..50210bb2 100644 --- a/client/pub_client.c +++ b/client/pub_client.c @@ -1,5 +1,5 @@ /* -Copyright (c) 2009-2014 Roger Light +Copyright (c) 2009-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 @@ -34,6 +34,7 @@ Contributors: #define STATUS_CONNECTING 0 #define STATUS_CONNACK_RECVD 1 #define STATUS_WAITING 2 +#define STATUS_DISCONNECTING 3 /* Global variables for use in callbacks. See sub_client.c for an example of * using a struct to hold variables for use in callbacks. */ @@ -410,8 +411,15 @@ int main(int argc, char *argv[]) } } if(feof(stdin)){ - last_mid = mid_sent; - status = STATUS_WAITING; + if(last_mid == -1){ + /* Empty file */ + mosquitto_disconnect(mosq); + disconnect_sent = true; + status = STATUS_DISCONNECTING; + }else{ + last_mid = mid_sent; + status = STATUS_WAITING; + } } }else if(status == STATUS_WAITING){ if(last_mid_sent == last_mid && disconnect_sent == false){ diff --git a/client/sub_client.c b/client/sub_client.c index 3c6c2256..be4c15f1 100644 --- a/client/sub_client.c +++ b/client/sub_client.c @@ -1,5 +1,5 @@ /* -Copyright (c) 2009-2014 Roger Light +Copyright (c) 2009-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 diff --git a/config.mk b/config.mk index 71f17e54..bfaa2082 100644 --- a/config.mk +++ b/config.mk @@ -86,7 +86,7 @@ WITH_SOCKS:=yes # Also bump lib/mosquitto.h, CMakeLists.txt, # installer/mosquitto.nsi, installer/mosquitto-cygwin.nsi -VERSION=1.4.14 +VERSION=1.4.15 TIMESTAMP:=$(shell date "+%F %T%z") # Client library SO version. Bump if incompatible API/ABI changes are made. diff --git a/examples/mysql_log/mysql_log.c b/examples/mysql_log/mysql_log.c index 565d7e9d..9e7e8d71 100644 --- a/examples/mysql_log/mysql_log.c +++ b/examples/mysql_log/mysql_log.c @@ -43,8 +43,13 @@ void message_callback(struct mosquitto *mosq, void *obj, const struct mosquitto_ bind[0].buffer_type = MYSQL_TYPE_STRING; bind[0].buffer = message->topic; + bind[0].buffer_length = strlen(message->topic); + // Note: payload is normally a binary blob and could contains + // NULL byte. This sample does not handle it and assume payload is a + // string. bind[1].buffer_type = MYSQL_TYPE_STRING; bind[1].buffer = message->payload; + bind[1].buffer_length = message->payloadlen; mysql_stmt_bind_param(stmt, bind); mysql_stmt_execute(stmt); diff --git a/installer/mosquitto-cygwin.nsi b/installer/mosquitto-cygwin.nsi index 354cfcaa..875f78c2 100644 --- a/installer/mosquitto-cygwin.nsi +++ b/installer/mosquitto-cygwin.nsi @@ -7,7 +7,7 @@ !define env_hklm 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"' Name "mosquitto" -!define VERSION 1.4.14 +!define VERSION 1.4.15 OutFile "mosquitto-${VERSION}-install-cygwin.exe" InstallDir "$PROGRAMFILES\mosquitto" diff --git a/installer/mosquitto.nsi b/installer/mosquitto.nsi index 7bf3162b..a0710b33 100644 --- a/installer/mosquitto.nsi +++ b/installer/mosquitto.nsi @@ -9,7 +9,7 @@ !define env_hklm 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"' Name "mosquitto" -!define VERSION 1.4.14 +!define VERSION 1.4.15 OutFile "mosquitto-${VERSION}-install-win32.exe" InstallDir "$PROGRAMFILES\mosquitto" diff --git a/lib/cpp/mosquittopp.cpp b/lib/cpp/mosquittopp.cpp index 0a22b80f..cb631970 100644 --- a/lib/cpp/mosquittopp.cpp +++ b/lib/cpp/mosquittopp.cpp @@ -1,5 +1,5 @@ /* -Copyright (c) 2010-2014 Roger Light +Copyright (c) 2010-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 diff --git a/lib/cpp/mosquittopp.h b/lib/cpp/mosquittopp.h index 9b0cb694..1ae0ef21 100644 --- a/lib/cpp/mosquittopp.h +++ b/lib/cpp/mosquittopp.h @@ -1,5 +1,5 @@ /* -Copyright (c) 2010-2013 Roger Light +Copyright (c) 2010-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 diff --git a/lib/logging_mosq.c b/lib/logging_mosq.c index 3d94974c..de6182fc 100644 --- a/lib/logging_mosq.c +++ b/lib/logging_mosq.c @@ -1,5 +1,5 @@ /* -Copyright (c) 2009-2014 Roger Light +Copyright (c) 2009-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 diff --git a/lib/logging_mosq.h b/lib/logging_mosq.h index 3430fc25..d802ebd0 100644 --- a/lib/logging_mosq.h +++ b/lib/logging_mosq.h @@ -1,5 +1,5 @@ /* -Copyright (c) 2009-2014 Roger Light +Copyright (c) 2009-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 diff --git a/lib/memory_mosq.c b/lib/memory_mosq.c index dd3c50d0..e6b508d1 100644 --- a/lib/memory_mosq.c +++ b/lib/memory_mosq.c @@ -1,5 +1,5 @@ /* -Copyright (c) 2009-2014 Roger Light +Copyright (c) 2009-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 @@ -37,8 +37,32 @@ static unsigned long memcount = 0; static unsigned long max_memcount = 0; #endif +#ifdef WITH_BROKER +static size_t mem_limit = 0; +void memory__set_limit(size_t lim) +{ +#ifdef LINUX + struct rlimit r; + + r.rlim_cur = lim; + r.rlim_max = lim; + + setrlimit(RLIMIT_CPU, &r); + + mem_limit = 0; +#else + mem_limit = lim; +#endif +} +#endif + void *_mosquitto_calloc(size_t nmemb, size_t size) { +#ifdef REAL_WITH_MEMORY_TRACKING + if(mem_limit && memcount + size > mem_limit){ + return NULL; + } +#endif void *mem = calloc(nmemb, size); #ifdef REAL_WITH_MEMORY_TRACKING @@ -64,6 +88,11 @@ void _mosquitto_free(void *mem) void *_mosquitto_malloc(size_t size) { +#ifdef REAL_WITH_MEMORY_TRACKING + if(mem_limit && memcount + size > mem_limit){ + return NULL; + } +#endif void *mem = malloc(size); #ifdef REAL_WITH_MEMORY_TRACKING @@ -90,6 +119,11 @@ unsigned long _mosquitto_max_memory_used(void) void *_mosquitto_realloc(void *ptr, size_t size) { +#ifdef REAL_WITH_MEMORY_TRACKING + if(mem_limit && memcount + size > mem_limit){ + return NULL; + } +#endif void *mem; #ifdef REAL_WITH_MEMORY_TRACKING if(ptr){ @@ -110,6 +144,11 @@ void *_mosquitto_realloc(void *ptr, size_t size) char *_mosquitto_strdup(const char *s) { +#ifdef REAL_WITH_MEMORY_TRACKING + if(mem_limit && memcount + strlen(s) > mem_limit){ + return NULL; + } +#endif char *str = strdup(s); #ifdef REAL_WITH_MEMORY_TRACKING diff --git a/lib/memory_mosq.h b/lib/memory_mosq.h index 6e14d7f4..c68daf87 100644 --- a/lib/memory_mosq.h +++ b/lib/memory_mosq.h @@ -1,5 +1,5 @@ /* -Copyright (c) 2010-2014 Roger Light +Copyright (c) 2010-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 @@ -34,4 +34,8 @@ unsigned long _mosquitto_max_memory_used(void); void *_mosquitto_realloc(void *ptr, size_t size); char *_mosquitto_strdup(const char *s); +#ifdef WITH_BROKER +void memory__set_limit(size_t lim); +#endif + #endif diff --git a/lib/messages_mosq.c b/lib/messages_mosq.c index 2adaa867..46f9d9a3 100644 --- a/lib/messages_mosq.c +++ b/lib/messages_mosq.c @@ -1,5 +1,5 @@ /* -Copyright (c) 2010-2014 Roger Light +Copyright (c) 2010-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 diff --git a/lib/messages_mosq.h b/lib/messages_mosq.h index acae1b46..a9b92721 100644 --- a/lib/messages_mosq.h +++ b/lib/messages_mosq.h @@ -1,5 +1,5 @@ /* -Copyright (c) 2010-2014 Roger Light +Copyright (c) 2010-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 diff --git a/lib/mosquitto.c b/lib/mosquitto.c index 61ffdd87..f3533fc9 100644 --- a/lib/mosquitto.c +++ b/lib/mosquitto.c @@ -1,5 +1,5 @@ /* -Copyright (c) 2010-2014 Roger Light +Copyright (c) 2010-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 @@ -1202,15 +1202,20 @@ int mosquitto_loop_write(struct mosquitto *mosq, int max_packets) bool mosquitto_want_write(struct mosquitto *mosq) { + bool result = false; if(mosq->out_packet || mosq->current_out_packet){ - return true; + result = true; + } #ifdef WITH_TLS - }else if(mosq->ssl && mosq->want_write){ - return true; -#endif - }else{ - return false; + if(mosq->ssl){ + if (mosq->want_write) { + result = true; + }else if(mosq->want_connect){ + result = false; + } } +#endif + return result; } int mosquitto_opts_set(struct mosquitto *mosq, enum mosq_opt_t option, void *value) diff --git a/lib/mosquitto.h b/lib/mosquitto.h index b7cc749f..f304a063 100644 --- a/lib/mosquitto.h +++ b/lib/mosquitto.h @@ -1,5 +1,5 @@ /* -Copyright (c) 2010-2014 Roger Light +Copyright (c) 2010-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 @@ -45,7 +45,7 @@ extern "C" { #define LIBMOSQUITTO_MAJOR 1 #define LIBMOSQUITTO_MINOR 4 -#define LIBMOSQUITTO_REVISION 14 +#define LIBMOSQUITTO_REVISION 15 /* 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/lib/mosquitto_internal.h b/lib/mosquitto_internal.h index 8d3014dd..83eff081 100644 --- a/lib/mosquitto_internal.h +++ b/lib/mosquitto_internal.h @@ -1,5 +1,5 @@ /* -Copyright (c) 2010-2014 Roger Light +Copyright (c) 2010-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 diff --git a/lib/mqtt3_protocol.h b/lib/mqtt3_protocol.h index 0e8ac8d8..8cd6f58f 100644 --- a/lib/mqtt3_protocol.h +++ b/lib/mqtt3_protocol.h @@ -1,5 +1,5 @@ /* -Copyright (c) 2009-2014 Roger Light +Copyright (c) 2009-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 diff --git a/lib/net_mosq.c b/lib/net_mosq.c index 063c4a22..243c439c 100644 --- a/lib/net_mosq.c +++ b/lib/net_mosq.c @@ -1,5 +1,5 @@ /* -Copyright (c) 2009-2014 Roger Light +Copyright (c) 2009-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 @@ -214,33 +214,39 @@ int _mosquitto_socket_close(struct mosquitto *mosq) assert(mosq); #ifdef WITH_TLS - if(mosq->ssl){ - SSL_shutdown(mosq->ssl); - SSL_free(mosq->ssl); - mosq->ssl = NULL; - } - if(mosq->ssl_ctx){ - SSL_CTX_free(mosq->ssl_ctx); - mosq->ssl_ctx = NULL; +#ifdef WITH_WEBSOCKETS + if(!mosq->wsi) +#endif + { + if(mosq->ssl){ + SSL_shutdown(mosq->ssl); + SSL_free(mosq->ssl); + mosq->ssl = NULL; + } + if(mosq->ssl_ctx){ + SSL_CTX_free(mosq->ssl_ctx); + mosq->ssl_ctx = NULL; + } } #endif - if((int)mosq->sock >= 0){ -#ifdef WITH_BROKER - HASH_DELETE(hh_sock, db->contexts_by_sock, mosq); -#endif - rc = COMPAT_CLOSE(mosq->sock); - mosq->sock = INVALID_SOCKET; #ifdef WITH_WEBSOCKETS - }else if(mosq->sock == WEBSOCKET_CLIENT){ + if(mosq->wsi) + { if(mosq->state != mosq_cs_disconnecting){ mosq->state = mosq_cs_disconnect_ws; } - if(mosq->wsi){ - libwebsocket_callback_on_writable(mosq->ws_context, mosq->wsi); - } - mosq->sock = INVALID_SOCKET; + libwebsocket_callback_on_writable(mosq->ws_context, mosq->wsi); + }else +#endif + { + if((int)mosq->sock >= 0){ +#ifdef WITH_BROKER + HASH_DELETE(hh_sock, db->contexts_by_sock, mosq); #endif + rc = COMPAT_CLOSE(mosq->sock); + mosq->sock = INVALID_SOCKET; + } } #ifdef WITH_BROKER @@ -323,7 +329,6 @@ int _mosquitto_try_connect_step2(struct mosquitto *mosq, uint16_t port, mosq_soc /* Set non-blocking */ if(_mosquitto_socket_nonblock(*sock)){ - COMPAT_CLOSE(*sock); continue; } @@ -338,7 +343,6 @@ int _mosquitto_try_connect_step2(struct mosquitto *mosq, uint16_t port, mosq_soc /* Set non-blocking */ if(_mosquitto_socket_nonblock(*sock)){ - COMPAT_CLOSE(*sock); continue; } break; @@ -423,7 +427,6 @@ int _mosquitto_try_connect(struct mosquitto *mosq, const char *host, uint16_t po if(!blocking){ /* Set non-blocking */ if(_mosquitto_socket_nonblock(*sock)){ - COMPAT_CLOSE(*sock); continue; } } @@ -440,7 +443,6 @@ int _mosquitto_try_connect(struct mosquitto *mosq, const char *host, uint16_t po if(blocking){ /* Set non-blocking */ if(_mosquitto_socket_nonblock(*sock)){ - COMPAT_CLOSE(*sock); continue; } } @@ -869,7 +871,11 @@ int _mosquitto_packet_write(struct mosquitto *mosq) } pthread_mutex_unlock(&mosq->out_packet_mutex); +#if defined(WITH_TLS) && !defined(WITH_BROKER) + if((mosq->state == mosq_cs_connect_pending)||mosq->want_connect){ +#else if(mosq->state == mosq_cs_connect_pending){ +#endif pthread_mutex_unlock(&mosq->current_out_packet_mutex); return MOSQ_ERR_SUCCESS; } @@ -1084,6 +1090,36 @@ int _mosquitto_packet_read(struct mosquitto *mosq) * positive. */ mosq->in_packet.remaining_count *= -1; +#ifdef WITH_BROKER + /* Check packet sizes before allocating memory. + * Will need modifying for MQTT v5. */ + switch(mosq->in_packet.command & 0xF0){ + case CONNECT: + if(mosq->in_packet.remaining_length > 327699){ + return MOSQ_ERR_PROTOCOL; + } + break; + + case PUBACK: + case PUBREC: + case PUBREL: + case PUBCOMP: + case UNSUBACK: + if(mosq->in_packet.remaining_length != 2){ + return MOSQ_ERR_PROTOCOL; + } + break; + + case PINGREQ: + case PINGRESP: + case DISCONNECT: + if(mosq->in_packet.remaining_length != 0){ + return MOSQ_ERR_PROTOCOL; + } + break; + } +#endif + if(mosq->in_packet.remaining_length > 0){ mosq->in_packet.payload = _mosquitto_malloc(mosq->in_packet.remaining_length*sizeof(uint8_t)); if(!mosq->in_packet.payload) return MOSQ_ERR_NOMEM; @@ -1244,7 +1280,6 @@ int _mosquitto_socketpair(mosq_sock_t *pairR, mosq_sock_t *pairW) continue; } if(_mosquitto_socket_nonblock(spR)){ - COMPAT_CLOSE(spR); COMPAT_CLOSE(listensock); continue; } @@ -1272,7 +1307,6 @@ int _mosquitto_socketpair(mosq_sock_t *pairR, mosq_sock_t *pairW) if(_mosquitto_socket_nonblock(spW)){ COMPAT_CLOSE(spR); - COMPAT_CLOSE(spW); COMPAT_CLOSE(listensock); continue; } @@ -1290,13 +1324,11 @@ int _mosquitto_socketpair(mosq_sock_t *pairR, mosq_sock_t *pairW) return MOSQ_ERR_ERRNO; } if(_mosquitto_socket_nonblock(sv[0])){ - COMPAT_CLOSE(sv[0]); COMPAT_CLOSE(sv[1]); return MOSQ_ERR_ERRNO; } if(_mosquitto_socket_nonblock(sv[1])){ COMPAT_CLOSE(sv[0]); - COMPAT_CLOSE(sv[1]); return MOSQ_ERR_ERRNO; } *pairR = sv[0]; diff --git a/lib/net_mosq.h b/lib/net_mosq.h index b504ebc0..da597b2a 100644 --- a/lib/net_mosq.h +++ b/lib/net_mosq.h @@ -1,5 +1,5 @@ /* -Copyright (c) 2010-2014 Roger Light +Copyright (c) 2010-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 diff --git a/lib/read_handle.c b/lib/read_handle.c index d83294a7..722909c1 100644 --- a/lib/read_handle.c +++ b/lib/read_handle.c @@ -1,5 +1,5 @@ /* -Copyright (c) 2009-2014 Roger Light +Copyright (c) 2009-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 diff --git a/lib/read_handle.h b/lib/read_handle.h index 96e0467e..a66ff51a 100644 --- a/lib/read_handle.h +++ b/lib/read_handle.h @@ -1,5 +1,5 @@ /* -Copyright (c) 2010-2014 Roger Light +Copyright (c) 2010-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 diff --git a/lib/read_handle_client.c b/lib/read_handle_client.c index 71c32316..8e8de49d 100644 --- a/lib/read_handle_client.c +++ b/lib/read_handle_client.c @@ -1,5 +1,5 @@ /* -Copyright (c) 2009-2014 Roger Light +Copyright (c) 2009-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 diff --git a/lib/read_handle_shared.c b/lib/read_handle_shared.c index 6a135dfc..da086820 100644 --- a/lib/read_handle_shared.c +++ b/lib/read_handle_shared.c @@ -1,5 +1,5 @@ /* -Copyright (c) 2009-2014 Roger Light +Copyright (c) 2009-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 diff --git a/lib/send_client_mosq.c b/lib/send_client_mosq.c index a930bc99..1a45b160 100644 --- a/lib/send_client_mosq.c +++ b/lib/send_client_mosq.c @@ -1,5 +1,5 @@ /* -Copyright (c) 2009-2014 Roger Light +Copyright (c) 2009-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 diff --git a/lib/send_mosq.c b/lib/send_mosq.c index a4aae723..9c069d25 100644 --- a/lib/send_mosq.c +++ b/lib/send_mosq.c @@ -1,5 +1,5 @@ /* -Copyright (c) 2009-2014 Roger Light +Copyright (c) 2009-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 diff --git a/lib/send_mosq.h b/lib/send_mosq.h index ff2c6b9d..1f5def94 100644 --- a/lib/send_mosq.h +++ b/lib/send_mosq.h @@ -1,5 +1,5 @@ /* -Copyright (c) 2010-2014 Roger Light +Copyright (c) 2010-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 diff --git a/lib/socks_mosq.c b/lib/socks_mosq.c index 36f5ed68..4da44972 100644 --- a/lib/socks_mosq.c +++ b/lib/socks_mosq.c @@ -1,5 +1,5 @@ /* -Copyright (c) 2014 Roger Light +Copyright (c) 2014-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 diff --git a/lib/socks_mosq.h b/lib/socks_mosq.h index f3f0683f..daa6494a 100644 --- a/lib/socks_mosq.h +++ b/lib/socks_mosq.h @@ -1,5 +1,5 @@ /* -Copyright (c) 2014 Roger Light +Copyright (c) 2014-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 diff --git a/lib/srv_mosq.c b/lib/srv_mosq.c index b6f2e46d..c638919e 100644 --- a/lib/srv_mosq.c +++ b/lib/srv_mosq.c @@ -1,5 +1,5 @@ /* -Copyright (c) 2013,2014 Roger Light +Copyright (c) 2013-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 diff --git a/lib/thread_mosq.c b/lib/thread_mosq.c index 93c5b635..0dbd9730 100644 --- a/lib/thread_mosq.c +++ b/lib/thread_mosq.c @@ -1,5 +1,5 @@ /* -Copyright (c) 2011-2014 Roger Light +Copyright (c) 2011-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 diff --git a/lib/time_mosq.c b/lib/time_mosq.c index 0f85bbc4..ea88c0c1 100644 --- a/lib/time_mosq.c +++ b/lib/time_mosq.c @@ -1,5 +1,5 @@ /* -Copyright (c) 2013,2014 Roger Light +Copyright (c) 2013-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 diff --git a/lib/time_mosq.h b/lib/time_mosq.h index 106279c3..9f619e03 100644 --- a/lib/time_mosq.h +++ b/lib/time_mosq.h @@ -1,5 +1,5 @@ /* -Copyright (c) 2013,2014 Roger Light +Copyright (c) 2013-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 diff --git a/lib/tls_mosq.c b/lib/tls_mosq.c index 20f0d3ae..60e1d4be 100644 --- a/lib/tls_mosq.c +++ b/lib/tls_mosq.c @@ -1,5 +1,5 @@ /* -Copyright (c) 2013,2014 Roger Light +Copyright (c) 2013-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 diff --git a/lib/tls_mosq.h b/lib/tls_mosq.h index c1d38037..f4948e09 100644 --- a/lib/tls_mosq.h +++ b/lib/tls_mosq.h @@ -1,5 +1,5 @@ /* -Copyright (c) 2013,2014 Roger Light +Copyright (c) 2013-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 diff --git a/lib/util_mosq.c b/lib/util_mosq.c index 55e65e9e..27af3bcf 100644 --- a/lib/util_mosq.c +++ b/lib/util_mosq.c @@ -1,5 +1,5 @@ /* -Copyright (c) 2009-2014 Roger Light +Copyright (c) 2009-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 @@ -228,13 +228,17 @@ int mosquitto_topic_matches_sub(const char *sub, const char *topic, bool *result int spos, tpos; bool multilevel_wildcard = false; - if(!sub || !topic || !result) return MOSQ_ERR_INVAL; + if(!result) return MOSQ_ERR_INVAL; + *result = false; + + if(!sub || !topic){ + return MOSQ_ERR_INVAL; + } slen = strlen(sub); tlen = strlen(topic); if(!slen || !tlen){ - *result = false; return MOSQ_ERR_INVAL; } @@ -242,7 +246,6 @@ int mosquitto_topic_matches_sub(const char *sub, const char *topic, bool *result if((sub[0] == '$' && topic[0] != '$') || (topic[0] == '$' && sub[0] != '$')){ - *result = false; return MOSQ_ERR_SUCCESS; } } @@ -269,7 +272,6 @@ int mosquitto_topic_matches_sub(const char *sub, const char *topic, bool *result return MOSQ_ERR_SUCCESS; }else if(tpos == tlen && spos == slen-1 && sub[spos] == '+'){ if(spos > 0 && sub[spos-1] != '/'){ - *result = false; return MOSQ_ERR_INVAL; } spos++; @@ -280,12 +282,10 @@ int mosquitto_topic_matches_sub(const char *sub, const char *topic, bool *result if(sub[spos] == '+'){ /* Check for bad "+foo" or "a/+foo" subscription */ if(spos > 0 && sub[spos-1] != '/'){ - *result = false; return MOSQ_ERR_INVAL; } /* Check for bad "foo+" or "foo+/a" subscription */ if(spos < slen-1 && sub[spos+1] != '/'){ - *result = false; return MOSQ_ERR_INVAL; } spos++; @@ -298,19 +298,28 @@ int mosquitto_topic_matches_sub(const char *sub, const char *topic, bool *result } }else if(sub[spos] == '#'){ if(spos > 0 && sub[spos-1] != '/'){ - *result = false; return MOSQ_ERR_INVAL; } multilevel_wildcard = true; if(spos+1 != slen){ - *result = false; return MOSQ_ERR_INVAL; }else{ *result = true; return MOSQ_ERR_SUCCESS; } }else{ - *result = false; + /* Check for e.g. foo/bar matching foo/+/# */ + if(spos > 0 + && spos+2 == slen + && tpos == tlen + && sub[spos-1] == '+' + && sub[spos] == '/' + && sub[spos+1] == '#') + { + *result = true; + multilevel_wildcard = true; + return MOSQ_ERR_SUCCESS; + } return MOSQ_ERR_SUCCESS; } } diff --git a/lib/util_mosq.h b/lib/util_mosq.h index 8a8b7b53..cbd07779 100644 --- a/lib/util_mosq.h +++ b/lib/util_mosq.h @@ -1,5 +1,5 @@ /* -Copyright (c) 2009-2014 Roger Light +Copyright (c) 2009-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 diff --git a/lib/will_mosq.c b/lib/will_mosq.c index 2155a279..f18221fe 100644 --- a/lib/will_mosq.c +++ b/lib/will_mosq.c @@ -1,5 +1,5 @@ /* -Copyright (c) 2010-2014 Roger Light +Copyright (c) 2010-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 diff --git a/lib/will_mosq.h b/lib/will_mosq.h index 66ac9af1..5dd2ecd9 100644 --- a/lib/will_mosq.h +++ b/lib/will_mosq.h @@ -1,5 +1,5 @@ /* -Copyright (c) 2010-2014 Roger Light +Copyright (c) 2010-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 diff --git a/man/mosquitto-tls.7.xml b/man/mosquitto-tls.7.xml index 1fa40f7f..ab0124f5 100644 --- a/man/mosquitto-tls.7.xml +++ b/man/mosquitto-tls.7.xml @@ -50,6 +50,7 @@ openssl req -out server.csr -key server.key -new + When prompted for the CN (Common Name), please enter either your server (or broker) hostname or domain name. Send the CSR to the CA, or sign it with your CA key: diff --git a/man/mosquitto.conf.5.xml b/man/mosquitto.conf.5.xml index 37614dd4..bb51589f 100644 --- a/man/mosquitto.conf.5.xml +++ b/man/mosquitto.conf.5.xml @@ -116,7 +116,7 @@ user <username> The username referred to here is the same as in - e. It is not the + . It is not the clientid. It is also possible to define ACLs based on pattern @@ -1031,9 +1031,9 @@ Set the clientid to use on the local broker. If not defined, this defaults to - . If you are + . If you are bridging a broker to itself, it is important that - local_clientid and clientid do not match. + local_clientid and remote_clientid do not match. @@ -1059,7 +1059,7 @@ notification messages to the local and remote brokers giving information about the state of the bridge connection. Retained messages are published to the - topic $SYS/broker/connection/<clientid>/state + topic $SYS/broker/connection/<remote_clientid>/state unless otherwise set with s. If the message is 1 then the connection is active, or 0 if the @@ -1073,7 +1073,7 @@ Choose the topic on which notifications will be published for this bridge. If not set the messages will be sent on the topic - $SYS/broker/connection/<clientid>/state. + $SYS/broker/connection/<remote_clientid>/state. @@ -1295,21 +1295,6 @@ topic clients/total in 0 test/mosquitto/org $SYS/broker/ The following options are available for all bridges to configure SSL/TLS support. - - [ true | false ] - - If a bridge has topics that have "out" direction, - the default behaviour is to send an unsubscribe - request to the remote broker on that topic. This - means that changing a topic direction from "in" to - "out" will not keep receiving incoming messages. - Sending these unsubscribe requests is not always - desirable, setting - to - false will disable - sending the unsubscribe request. - - file path diff --git a/man/mosquitto_passwd.1.xml b/man/mosquitto_passwd.1.xml index 95b3c130..5f144bf9 100644 --- a/man/mosquitto_passwd.1.xml +++ b/man/mosquitto_passwd.1.xml @@ -41,7 +41,7 @@ Description mosquitto_passwd is a tool for managing - password files the mosquitto MQTT broker. + password files for the mosquitto MQTT broker. Usernames must not contain ":". Passwords are stored in a similar format to crypt3. diff --git a/mosquitto.conf b/mosquitto.conf index 99af968c..8d7dd2b9 100644 --- a/mosquitto.conf +++ b/mosquitto.conf @@ -220,7 +220,7 @@ #crlfile # If you wish to control which encryption ciphers are used, use the ciphers -# option. The list of available ciphers can be optained using the "openssl +# option. The list of available ciphers can be obtained using the "openssl # ciphers" command and should be provided in the same format as the output of # that command. # If unset defaults to DEFAULT:!aNULL:!eNULL:!LOW:!EXPORT:!SSLv2:@STRENGTH @@ -253,7 +253,7 @@ # When using PSK, the encryption ciphers used will be chosen from the list of # available PSK ciphers. If you want to control which ciphers are available, -# use the "ciphers" option. The list of available ciphers can be optained +# use the "ciphers" option. The list of available ciphers can be obtained # using the "openssl ciphers" command and should be provided in the same format # as the output of that command. #ciphers diff --git a/readme.md b/readme.md index 0ae7587f..32726894 100644 --- a/readme.md +++ b/readme.md @@ -69,6 +69,7 @@ already be built. Use `make binary` to skip building the man pages, or install * libuuid (uuid-dev) - disable with `make WITH_UUID=no` * libwebsockets (libwebsockets-dev) - enable with `make WITH_WEBSOCKETS=yes` * openssl (libssl-dev on Debian based systems) - disable with `make WITH_TLS=no` +* xsltproc (xsltproc and docbook-xsl on Debian based systems) - only needed when building from git sources - disable with `make WITH_DOCS=no` ## Credits diff --git a/set-version.sh b/set-version.sh index 31a9a576..652877f3 100755 --- a/set-version.sh +++ b/set-version.sh @@ -2,7 +2,7 @@ MAJOR=1 MINOR=4 -REVISION=14 +REVISION=15 sed -i "s/^VERSION=.*/VERSION=${MAJOR}.${MINOR}.${REVISION}/" config.mk diff --git a/src/bridge.c b/src/bridge.c index dc5f9c44..b0f5b941 100644 --- a/src/bridge.c +++ b/src/bridge.c @@ -1,5 +1,5 @@ /* -Copyright (c) 2009-2014 Roger Light +Copyright (c) 2009-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 diff --git a/src/conf.c b/src/conf.c index a3e233de..eef681f4 100644 --- a/src/conf.c +++ b/src/conf.c @@ -1,5 +1,5 @@ /* -Copyright (c) 2009-2014 Roger Light +Copyright (c) 2009-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 @@ -64,6 +64,35 @@ static int _conf_parse_int(char **token, const char *name, int *value, char *sav static int _conf_parse_string(char **token, const char *name, char **value, char *saveptr); static int _config_read_file(struct mqtt3_config *config, bool reload, const char *file, struct config_recurse *config_tmp, int level, int *lineno); +static char *fgets_extending(char **buf, int *buflen, FILE *stream) +{ + char *rc; + char endchar; + int offset = 0; + char *newbuf; + + do{ + rc = fgets(&((*buf)[offset]), *buflen-offset, stream); + if(feof(stream)){ + return rc; + } + + endchar = (*buf)[strlen(*buf)-1]; + if(endchar == '\n'){ + return rc; + } + /* No EOL char found, so extend buffer */ + offset = *buflen-1; + *buflen += 1000; + newbuf = realloc(*buf, *buflen); + if(!newbuf){ + return NULL; + } + *buf = newbuf; + }while(1); +} + + static int _conf_attempt_resolve(const char *host, const char *text, int log, const char *msg) { struct addrinfo gai_hints; @@ -100,7 +129,7 @@ static int _conf_attempt_resolve(const char *host, const char *text, int log, co return MOSQ_ERR_SUCCESS; } -static void _config_init_reload(struct mqtt3_config *config) +static void _config_init_reload(struct mosquitto_db *db, struct mqtt3_config *config) { int i; /* Set defaults */ @@ -136,7 +165,7 @@ static void _config_init_reload(struct mqtt3_config *config) #else config->log_facility = LOG_DAEMON; config->log_dest = MQTT3_LOG_STDERR; - if(config->verbose){ + if(db->verbose){ config->log_type = INT_MAX; }else{ config->log_type = MOSQ_LOG_ERR | MOSQ_LOG_WARNING | MOSQ_LOG_NOTICE | MOSQ_LOG_INFO; @@ -168,11 +197,10 @@ static void _config_init_reload(struct mqtt3_config *config) } } -void mqtt3_config_init(struct mqtt3_config *config) +void mqtt3_config_init(struct mosquitto_db *db, struct mqtt3_config *config) { memset(config, 0, sizeof(struct mqtt3_config)); - _config_init_reload(config); - config->config_file = NULL; + _config_init_reload(db, config); config->daemon = false; config->default_listener.host = NULL; config->default_listener.port = 0; @@ -205,7 +233,6 @@ void mqtt3_config_init(struct mqtt3_config *config) #endif config->auth_plugin = NULL; config->auth_plugin_deny_special_chars = true; - config->verbose = false; config->message_size_limit = 0; } @@ -219,7 +246,6 @@ void mqtt3_config_cleanup(struct mqtt3_config *config) if(config->acl_file) _mosquitto_free(config->acl_file); if(config->auto_id_prefix) _mosquitto_free(config->auto_id_prefix); if(config->clientid_prefixes) _mosquitto_free(config->clientid_prefixes); - if(config->config_file) _mosquitto_free(config->config_file); if(config->password_file) _mosquitto_free(config->password_file); if(config->persistence_location) _mosquitto_free(config->persistence_location); if(config->persistence_file) _mosquitto_free(config->persistence_file); @@ -240,10 +266,13 @@ void mqtt3_config_cleanup(struct mqtt3_config *config) if(config->listeners[i].psk_hint) _mosquitto_free(config->listeners[i].psk_hint); if(config->listeners[i].crlfile) _mosquitto_free(config->listeners[i].crlfile); if(config->listeners[i].tls_version) _mosquitto_free(config->listeners[i].tls_version); - if(config->listeners[i].ssl_ctx) SSL_CTX_free(config->listeners[i].ssl_ctx); -#endif #ifdef WITH_WEBSOCKETS if(config->listeners[i].http_dir) _mosquitto_free(config->listeners[i].http_dir); + if(!config->listeners[i].ws_context) /* libwebsockets frees its own SSL_CTX */ +#endif + { + SSL_CTX_free(config->listeners[i].ssl_ctx); + } #endif } _mosquitto_free(config->listeners); @@ -322,7 +351,7 @@ static void print_usage(void) printf("\nSee http://mosquitto.org/ for more information.\n\n"); } -int mqtt3_config_parse_args(struct mqtt3_config *config, int argc, char *argv[]) +int mqtt3_config_parse_args(struct mosquitto_db *db, struct mqtt3_config *config, int argc, char *argv[]) { int i; int port_tmp; @@ -330,13 +359,9 @@ int mqtt3_config_parse_args(struct mqtt3_config *config, int argc, char *argv[]) for(i=1; iconfig_file = _mosquitto_strdup(argv[i+1]); - if(!config->config_file){ - _mosquitto_log_printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); - return MOSQ_ERR_NOMEM; - } + db->config_file = argv[i+1]; - if(mqtt3_config_read(config, false)){ + if(mqtt3_config_read(db, config, false)){ _mosquitto_log_printf(NULL, MOSQ_LOG_ERR, "Error: Unable to open configuration file."); return MOSQ_ERR_INVAL; } @@ -368,7 +393,7 @@ int mqtt3_config_parse_args(struct mqtt3_config *config, int argc, char *argv[]) } i++; }else if(!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose")){ - config->verbose = true; + db->verbose = true; }else{ fprintf(stderr, "Error: Unknown option '%s'.\n",argv[i]); print_usage(); @@ -443,22 +468,87 @@ int mqtt3_config_parse_args(struct mqtt3_config *config, int argc, char *argv[]) if(!config->user){ config->user = "mosquitto"; } - if(config->verbose){ + if(db->verbose){ config->log_type = INT_MAX; } return MOSQ_ERR_SUCCESS; } -int mqtt3_config_read(struct mqtt3_config *config, bool reload) +/* Copy reloaded config into existing config struct */ +void config__copy(struct mqtt3_config *src, struct mqtt3_config *dest) +{ + _mosquitto_free(dest->acl_file); + dest->acl_file = src->acl_file; + + dest->allow_anonymous = src->allow_anonymous; + dest->allow_duplicate_messages = src->allow_duplicate_messages; + dest->allow_zero_length_clientid = src->allow_zero_length_clientid; + + _mosquitto_free(dest->auto_id_prefix); + dest->auto_id_prefix = src->auto_id_prefix; + dest->auto_id_prefix_len = src->auto_id_prefix_len; + + dest->autosave_interval = src->autosave_interval; + dest->autosave_on_changes = src->autosave_on_changes; + + _mosquitto_free(dest->clientid_prefixes); + dest->clientid_prefixes = src->clientid_prefixes; + + dest->connection_messages = src->connection_messages; + dest->log_dest = src->log_dest; + dest->log_facility = src->log_facility; + dest->log_type = src->log_type; + dest->log_timestamp = src->log_timestamp; + + _mosquitto_free(dest->log_file); + dest->log_file = src->log_file; + + dest->message_size_limit = src->message_size_limit; + + _mosquitto_free(dest->password_file); + dest->password_file = src->password_file; + + dest->persistence = src->persistence; + + _mosquitto_free(dest->persistence_location); + dest->persistence_location = src->persistence_location; + + _mosquitto_free(dest->persistence_file); + dest->persistence_file = src->persistence_file; + + _mosquitto_free(dest->persistence_filepath); + dest->persistence_filepath = src->persistence_filepath; + + dest->persistent_client_expiration = src->persistent_client_expiration; + + _mosquitto_free(dest->psk_file); + dest->psk_file = src->psk_file; + + dest->queue_qos0_messages = src->queue_qos0_messages; + dest->retry_interval = src->retry_interval; + dest->sys_interval = src->sys_interval; + dest->upgrade_outgoing_qos = src->upgrade_outgoing_qos; + +#ifdef WITH_WEBSOCKETS + dest->websockets_log_level = src->websockets_log_level; +#endif +} + +int mqtt3_config_read(struct mosquitto_db *db, struct mqtt3_config *config, bool reload) { int rc = MOSQ_ERR_SUCCESS; struct config_recurse cr; int lineno; int len; + struct mqtt3_config config_reload; #ifdef WITH_BRIDGE int i; #endif + if(reload){ + memset(&config_reload, 0, sizeof(struct mqtt3_config)); + } + cr.log_dest = MQTT3_LOG_NONE; cr.log_dest_set = 0; cr.log_type = MOSQ_LOG_NONE; @@ -466,18 +556,24 @@ int mqtt3_config_read(struct mqtt3_config *config, bool reload) cr.max_inflight_messages = 20; cr.max_queued_messages = 100; - if(!config->config_file) return 0; + if(!db->config_file) return 0; if(reload){ /* Re-initialise appropriate config vars to default for reload. */ - _config_init_reload(config); + _config_init_reload(db, &config_reload); + rc = _config_read_file(&config_reload, reload, db->config_file, &cr, 0, &lineno); + }else{ + rc = _config_read_file(config, reload, db->config_file, &cr, 0, &lineno); } - rc = _config_read_file(config, reload, config->config_file, &cr, 0, &lineno); if(rc){ - _mosquitto_log_printf(NULL, MOSQ_LOG_ERR, "Error found at %s:%d.", config->config_file, lineno); + _mosquitto_log_printf(NULL, MOSQ_LOG_ERR, "Error found at %s:%d.", db->config_file, lineno); return rc; } + if(reload){ + config__copy(&config_reload, config); + } + #ifdef WITH_PERSISTENCE if(config->persistence){ if(!config->persistence_file){ @@ -529,7 +625,7 @@ int mqtt3_config_read(struct mqtt3_config *config, bool reload) if(cr.log_dest_set){ config->log_dest = cr.log_dest; } - if(config->verbose){ + if(db->verbose){ config->log_type = INT_MAX; }else if(cr.log_type_set){ config->log_type = cr.log_type; @@ -537,10 +633,9 @@ int mqtt3_config_read(struct mqtt3_config *config, bool reload) return MOSQ_ERR_SUCCESS; } -int _config_read_file_core(struct mqtt3_config *config, bool reload, const char *file, struct config_recurse *cr, int level, int *lineno, FILE *fptr) +int _config_read_file_core(struct mqtt3_config *config, bool reload, const char *file, struct config_recurse *cr, int level, int *lineno, FILE *fptr, char **buf, int *buflen) { int rc; - char buf[1024]; char *token; int tmp_int; char *saveptr = NULL; @@ -569,13 +664,13 @@ int _config_read_file_core(struct mqtt3_config *config, bool reload, const char *lineno = 0; - while(fgets(buf, 1024, fptr)){ + while(fgets_extending(buf, buflen, fptr)){ (*lineno)++; - if(buf[0] != '#' && buf[0] != 10 && buf[0] != 13){ - while(buf[strlen(buf)-1] == 10 || buf[strlen(buf)-1] == 13){ - buf[strlen(buf)-1] = 0; + if((*buf)[0] != '#' && (*buf)[0] != 10 && (*buf)[0] != 13){ + while((*buf)[strlen((*buf))-1] == 10 || (*buf)[strlen((*buf))-1] == 13){ + (*buf)[strlen((*buf))-1] = 0; } - token = strtok_r(buf, " ", &saveptr); + token = strtok_r((*buf), " ", &saveptr); if(token){ if(!strcmp(token, "acl_file")){ if(reload){ @@ -1013,7 +1108,7 @@ int _config_read_file_core(struct mqtt3_config *config, bool reload, const char snprintf(conf_file, len, "%s\\%s", token, find_data.cFileName); conf_file[len] = '\0'; - rc = _config_read_file(config, reload, conf_file, cr, level+1, &lineno_ext); + rc = _config_read_file(config, reload, conf_file, cr, level+1, &lineno_ext, buf, buflen); if(rc){ FindClose(fh); _mosquitto_log_printf(NULL, MOSQ_LOG_ERR, "Error found at %s:%d.", conf_file, lineno_ext); @@ -1286,6 +1381,14 @@ int _config_read_file_core(struct mqtt3_config *config, bool reload, const char }else{ _mosquitto_log_printf(NULL, MOSQ_LOG_ERR, "Error: Empty max_queued_messages value in configuration."); } + }else if(!strcmp(token, "memory_limit")){ + size_t lim; + if(_conf_parse_int(&token, "memory_limit", (int *)&lim, saveptr)) return MOSQ_ERR_INVAL; + if(lim < 0){ + _mosquitto_log_printf(NULL, MOSQ_LOG_ERR, "Error: Invalid memory_limit value (%lu).", lim); + return MOSQ_ERR_INVAL; + } + memory__set_limit(lim); }else if(!strcmp(token, "message_size_limit")){ if(_conf_parse_int(&token, "message_size_limit", (int *)&config->message_size_limit, saveptr)) return MOSQ_ERR_INVAL; if(config->message_size_limit > MQTT_MAX_PAYLOAD){ @@ -1765,6 +1868,8 @@ int _config_read_file(struct mqtt3_config *config, bool reload, const char *file { int rc; FILE *fptr = NULL; + char *buf; + int buflen; fptr = _mosquitto_fopen(file, "rt", false); if(!fptr){ @@ -1772,7 +1877,15 @@ int _config_read_file(struct mqtt3_config *config, bool reload, const char *file return 1; } - rc = _config_read_file_core(config, reload, file, cr, level, lineno, fptr); + buflen = 1000; + buf = _mosquitto_malloc(buflen); + if(!buf){ + _mosquitto_log_printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); + return MOSQ_ERR_NOMEM; + } + + rc = _config_read_file_core(config, reload, file, cr, level, lineno, fptr, &buf, &buflen); + _mosquitto_free(buf); fclose(fptr); return rc; diff --git a/src/context.c b/src/context.c index 561ff4aa..8819268b 100644 --- a/src/context.c +++ b/src/context.c @@ -1,5 +1,5 @@ /* -Copyright (c) 2009-2014 Roger Light +Copyright (c) 2009-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 diff --git a/src/database.c b/src/database.c index 1ea994a0..4848871d 100644 --- a/src/database.c +++ b/src/database.c @@ -1,5 +1,5 @@ /* -Copyright (c) 2009-2014 Roger Light +Copyright (c) 2009-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 diff --git a/src/lib_load.h b/src/lib_load.h index 76fb8aad..53f193ba 100644 --- a/src/lib_load.h +++ b/src/lib_load.h @@ -1,5 +1,5 @@ /* -Copyright (c) 2012-2014 Roger Light +Copyright (c) 2012-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 diff --git a/src/logging.c b/src/logging.c index 18d8d7db..4a58d4d2 100644 --- a/src/logging.c +++ b/src/logging.c @@ -1,5 +1,5 @@ /* -Copyright (c) 2009-2014 Roger Light +Copyright (c) 2009-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 diff --git a/src/loop.c b/src/loop.c index bf4876bc..d4b63daf 100644 --- a/src/loop.c +++ b/src/loop.c @@ -1,5 +1,5 @@ /* -Copyright (c) 2009-2014 Roger Light +Copyright (c) 2009-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 @@ -390,7 +390,7 @@ int mosquitto_main_loop(struct mosquitto_db *db, mosq_sock_t *listensock, int li #endif if(flag_reload){ _mosquitto_log_printf(NULL, MOSQ_LOG_INFO, "Reloading config."); - mqtt3_config_read(db->config, true); + mqtt3_config_read(db, db->config, true); mosquitto_security_cleanup(db, true); mosquitto_security_init(db, true); mosquitto_security_apply(db); diff --git a/src/mosquitto.c b/src/mosquitto.c index b28150ce..22b6372f 100644 --- a/src/mosquitto.c +++ b/src/mosquitto.c @@ -1,5 +1,5 @@ /* -Copyright (c) 2009-2014 Roger Light +Copyright (c) 2009-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 @@ -259,8 +259,8 @@ int main(int argc, char *argv[]) _mosquitto_net_init(); - mqtt3_config_init(&config); - rc = mqtt3_config_parse_args(&config, argc, argv); + mqtt3_config_init(&int_db, &config); + rc = mqtt3_config_parse_args(&int_db, &config, argc, argv); if(rc != MOSQ_ERR_SUCCESS) return rc; int_db.config = &config; @@ -292,8 +292,8 @@ int main(int argc, char *argv[]) return rc; } _mosquitto_log_printf(NULL, MOSQ_LOG_INFO, "mosquitto version %s (build date %s) starting", VERSION, TIMESTAMP); - if(config.config_file){ - _mosquitto_log_printf(NULL, MOSQ_LOG_INFO, "Config loaded from %s.", config.config_file); + if(int_db.config_file){ + _mosquitto_log_printf(NULL, MOSQ_LOG_INFO, "Config loaded from %s.", int_db.config_file); }else{ _mosquitto_log_printf(NULL, MOSQ_LOG_INFO, "Using default config."); } diff --git a/src/mosquitto_broker.h b/src/mosquitto_broker.h index f33007c8..6b170ecf 100644 --- a/src/mosquitto_broker.h +++ b/src/mosquitto_broker.h @@ -1,5 +1,5 @@ /* -Copyright (c) 2009-2014 Roger Light +Copyright (c) 2009-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 @@ -101,7 +101,6 @@ struct _mqtt3_listener { }; struct mqtt3_config { - char *config_file; char *acl_file; bool allow_anonymous; bool allow_duplicate_messages; @@ -137,7 +136,6 @@ struct mqtt3_config { int sys_interval; bool upgrade_outgoing_qos; char *user; - bool verbose; #ifdef WITH_WEBSOCKETS int websockets_log_level; bool have_websockets_listener; @@ -260,9 +258,11 @@ struct mosquitto_db{ int bridge_count; #endif int msg_store_count; + char *config_file; struct mqtt3_config *config; int persistence_changes; struct _mosquitto_auth_plugin auth_plugin; + bool verbose; #ifdef WITH_SYS_TREE int subscription_count; int retained_count; @@ -365,14 +365,14 @@ struct mosquitto_db *_mosquitto_get_db(void); * Config functions * ============================================================ */ /* Initialise config struct to default values. */ -void mqtt3_config_init(struct mqtt3_config *config); +void mqtt3_config_init(struct mosquitto_db *db, struct mqtt3_config *config); /* Parse command line options into config. */ -int mqtt3_config_parse_args(struct mqtt3_config *config, int argc, char *argv[]); +int mqtt3_config_parse_args(struct mosquitto_db *db, struct mqtt3_config *config, int argc, char *argv[]); /* Read configuration data from config->config_file into config. * If reload is true, don't process config options that shouldn't be reloaded (listeners etc) * Returns 0 on success, 1 if there is a configuration error or if a file cannot be opened. */ -int mqtt3_config_read(struct mqtt3_config *config, bool reload); +int mqtt3_config_read(struct mosquitto_db *db, struct mqtt3_config *config, bool reload); /* Free all config data. */ void mqtt3_config_cleanup(struct mqtt3_config *config); diff --git a/src/mosquitto_passwd.c b/src/mosquitto_passwd.c index c3dd05ab..55780a65 100644 --- a/src/mosquitto_passwd.c +++ b/src/mosquitto_passwd.c @@ -1,5 +1,5 @@ /* -Copyright (c) 2012-2014 Roger Light +Copyright (c) 2012-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 diff --git a/src/mosquitto_plugin.h b/src/mosquitto_plugin.h index 5b6449c0..94c7ad7a 100644 --- a/src/mosquitto_plugin.h +++ b/src/mosquitto_plugin.h @@ -1,5 +1,5 @@ /* -Copyright (c) 2012-2014 Roger Light +Copyright (c) 2012-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 diff --git a/src/net.c b/src/net.c index b8a03dc0..cbf80310 100644 --- a/src/net.c +++ b/src/net.c @@ -1,5 +1,5 @@ /* -Copyright (c) 2009-2014 Roger Light +Copyright (c) 2009-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 diff --git a/src/persist.c b/src/persist.c index 16323588..fdfd3af1 100644 --- a/src/persist.c +++ b/src/persist.c @@ -1,5 +1,5 @@ /* -Copyright (c) 2010-2014 Roger Light +Copyright (c) 2010-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 diff --git a/src/persist.h b/src/persist.h index 808b05fd..63a1a0cd 100644 --- a/src/persist.h +++ b/src/persist.h @@ -1,5 +1,5 @@ /* -Copyright (c) 2010-2014 Roger Light +Copyright (c) 2010-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 diff --git a/src/read_handle.c b/src/read_handle.c index ddc16ce4..43640709 100644 --- a/src/read_handle.c +++ b/src/read_handle.c @@ -1,5 +1,5 @@ /* -Copyright (c) 2009-2014 Roger Light +Copyright (c) 2009-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 diff --git a/src/read_handle_client.c b/src/read_handle_client.c index b1e1d1f1..a8b8ce52 100644 --- a/src/read_handle_client.c +++ b/src/read_handle_client.c @@ -1,5 +1,5 @@ /* -Copyright (c) 2009-2014 Roger Light +Copyright (c) 2009-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 diff --git a/src/read_handle_server.c b/src/read_handle_server.c index 6be6a250..a3078683 100644 --- a/src/read_handle_server.c +++ b/src/read_handle_server.c @@ -1,5 +1,5 @@ /* -Copyright (c) 2009-2014 Roger Light +Copyright (c) 2009-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 @@ -336,6 +336,12 @@ int mqtt3_handle_connect(struct mosquitto_db *db, struct mosquitto *context) #ifdef WITH_TLS if(context->listener && context->listener->ssl_ctx && context->listener->use_identity_as_username){ + /* Don't need the username or password if provided */ + _mosquitto_free(username); + username = NULL; + _mosquitto_free(password); + password = NULL; + if(!context->ssl){ _mosquitto_send_connack(context, 0, CONNACK_REFUSED_BAD_USERNAME_PASSWORD); rc = 1; diff --git a/src/security.c b/src/security.c index 67a4f791..852682b4 100644 --- a/src/security.c +++ b/src/security.c @@ -1,5 +1,5 @@ /* -Copyright (c) 2011-2014 Roger Light +Copyright (c) 2011-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 diff --git a/src/security_default.c b/src/security_default.c index c4085828..0e1b82c9 100644 --- a/src/security_default.c +++ b/src/security_default.c @@ -1,5 +1,5 @@ /* -Copyright (c) 2011-2014 Roger Light +Copyright (c) 2011-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 diff --git a/src/send_server.c b/src/send_server.c index 3d64bce0..f2a0189c 100644 --- a/src/send_server.c +++ b/src/send_server.c @@ -1,5 +1,5 @@ /* -Copyright (c) 2009-2014 Roger Light +Copyright (c) 2009-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 diff --git a/src/service.c b/src/service.c index ffe9ffe6..0d743700 100644 --- a/src/service.c +++ b/src/service.c @@ -1,5 +1,5 @@ /* -Copyright (c) 2011-2014 Roger Light +Copyright (c) 2011-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 diff --git a/src/subs.c b/src/subs.c index dcf1b3af..7c1029fa 100644 --- a/src/subs.c +++ b/src/subs.c @@ -1,5 +1,5 @@ /* -Copyright (c) 2010-2014 Roger Light +Copyright (c) 2010-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 @@ -681,9 +681,12 @@ static int _retain_process(struct mosquitto_db *db, struct mosquitto_msg_store * return rc; } - qos = retained->qos; - - if(qos > sub_qos) qos = sub_qos; + if (db->config->upgrade_outgoing_qos){ + qos = sub_qos; + } else { + qos = retained->qos; + if(qos > sub_qos) qos = sub_qos; + } if(qos > 0){ mid = _mosquitto_mid_generate(context); }else{ diff --git a/src/sys_tree.c b/src/sys_tree.c index be421b16..ebf66f2b 100644 --- a/src/sys_tree.c +++ b/src/sys_tree.c @@ -1,5 +1,5 @@ /* -Copyright (c) 2009-2014 Roger Light +Copyright (c) 2009-2018 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 diff --git a/src/websockets.c b/src/websockets.c index dde0c9cc..d4d79610 100644 --- a/src/websockets.c +++ b/src/websockets.c @@ -1,5 +1,5 @@ /* -Copyright (c) 2014 Roger Light +Copyright (c) 2014-2018 Roger Light All rights reserved. Redistribution and use in source and binary forms, with or without @@ -201,6 +201,12 @@ static int callback_mqtt(struct libwebsocket_context *context, mosq->ws_context = context; #endif mosq->wsi = wsi; + if(in){ + mosq->ssl = (SSL *)in; + if(!mosq->listener->ssl_ctx){ + mosq->listener->ssl_ctx = SSL_get_SSL_CTX(mosq->ssl); + } + } u->mosq = mosq; }else{ return -1; @@ -234,6 +240,7 @@ static int callback_mqtt(struct libwebsocket_context *context, mosq->pollfd_index = -1; } mosq->wsi = NULL; + mosq->ssl = NULL; do_disconnect(db, mosq); } break; @@ -243,7 +250,7 @@ static int callback_mqtt(struct libwebsocket_context *context, return -1; } mosq = u->mosq; - if(!mosq || mosq->state == mosq_cs_disconnect_ws || mosq->state == mosq_cs_disconnecting){ + if(!mosq){ return -1; } @@ -276,14 +283,30 @@ static int callback_mqtt(struct libwebsocket_context *context, count = packet->to_process; #endif if(count < 0){ + if (mosq->state == mosq_cs_disconnect_ws || mosq->state == mosq_cs_disconnecting){ + return -1; + } return 0; } +#ifdef WITH_SYS_TREE + g_bytes_sent += count; +#endif packet->to_process -= count; packet->pos += count; if(packet->to_process > 0){ + if (mosq->state == mosq_cs_disconnect_ws || mosq->state == mosq_cs_disconnecting){ + return -1; + } break; } +#ifdef WITH_SYS_TREE + g_msgs_sent++; + if(((packet->command)&0xF6) == PUBLISH){ + g_pub_msgs_sent++; + } +#endif + /* Free data and reset values */ mosq->current_out_packet = mosq->out_packet; if(mosq->out_packet){ @@ -298,6 +321,9 @@ static int callback_mqtt(struct libwebsocket_context *context, mosq->next_msg_out = mosquitto_time() + mosq->keepalive; } + if (mosq->state == mosq_cs_disconnect_ws || mosq->state == mosq_cs_disconnecting){ + return -1; + } if(mosq->current_out_packet){ libwebsocket_callback_on_writable(mosq->ws_context, mosq->wsi); } @@ -379,7 +405,12 @@ static int callback_mqtt(struct libwebsocket_context *context, mosq->last_msg_in = mosquitto_time(); - if(rc){ + if(rc && (mosq->out_packet || mosq->current_out_packet)) { + if(mosq->state != mosq_cs_disconnecting){ + mosq->state = mosq_cs_disconnect_ws; + } + libwebsocket_callback_on_writable(mosq->ws_context, mosq->wsi); + } else if (rc) { do_disconnect(db, mosq); return -1; } diff --git a/test/broker/04-retain-upgrade-outgoing-qos.conf b/test/broker/04-retain-upgrade-outgoing-qos.conf new file mode 100644 index 00000000..92f0ad4f --- /dev/null +++ b/test/broker/04-retain-upgrade-outgoing-qos.conf @@ -0,0 +1,2 @@ +port 1888 +upgrade_outgoing_qos true diff --git a/test/broker/04-retain-upgrade-outgoing-qos.py b/test/broker/04-retain-upgrade-outgoing-qos.py new file mode 100755 index 00000000..3ca5a74e --- /dev/null +++ b/test/broker/04-retain-upgrade-outgoing-qos.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python + +# Test whether a retained PUBLISH to a topic with QoS 0 is sent with subscriber QoS +# when upgrade_outgoing_qos is true + +import inspect, os, sys +# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder +cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) +if cmd_subfolder not in sys.path: + sys.path.insert(0, cmd_subfolder) + +import mosq_test + +rc = 1 +keepalive = 60 +mid = 16 +connect_packet = mosq_test.gen_connect("retain-qos0-test", keepalive=keepalive) +connack_packet = mosq_test.gen_connack(rc=0) + +publish_packet = mosq_test.gen_publish("retain/qos0/test", qos=0, payload="retained message", retain=True) +subscribe_packet = mosq_test.gen_subscribe(mid, "retain/qos0/test", 1) +suback_packet = mosq_test.gen_suback(mid, 1) + +publish_packet2 = mosq_test.gen_publish("retain/qos0/test", mid=1, qos=1, payload="retained message", retain=True) + +broker = mosq_test.start_broker(filename=os.path.basename(__file__)) + +try: + sock = mosq_test.do_client_connect(connect_packet, connack_packet) + sock.send(publish_packet) + + #sock.close() + #sock = mosq_test.do_client_connect(connect_packet, connack_packet) + sock.send(subscribe_packet) + + if mosq_test.expect_packet(sock, "suback", suback_packet): + if mosq_test.expect_packet(sock, "publish", publish_packet2): + rc = 0 + sock.close() +finally: + broker.terminate() + broker.wait() + if rc: + (stdo, stde) = broker.communicate() + print(stde) + +exit(rc) + diff --git a/test/broker/06-bridge-b2br-remapping.conf b/test/broker/06-bridge-b2br-remapping.conf new file mode 100644 index 00000000..e9296279 --- /dev/null +++ b/test/broker/06-bridge-b2br-remapping.conf @@ -0,0 +1,14 @@ +port 1889 + +retry_interval 10 + +connection bridge_sample +address 127.0.0.1:1888 +bridge_attempt_unsubscribe false +topic # in 0 local/topic/ remote/topic/ +topic prefix/# in 0 local2/topic/ remote2/topic/ +topic +/value in 0 local3/topic/ remote3/topic/ +topic ic/+ in 0 local4/top remote4/tip +topic clients/total in 0 test/mosquitto/org $SYS/broker/ +notifications false +restart_timeout 5 diff --git a/test/broker/06-bridge-b2br-remapping.py b/test/broker/06-bridge-b2br-remapping.py new file mode 100755 index 00000000..bbcd6e80 --- /dev/null +++ b/test/broker/06-bridge-b2br-remapping.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python + +# Test remapping of topic name for incoming message + +import socket + +import inspect, os, sys +# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder +cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) +if cmd_subfolder not in sys.path: + sys.path.insert(0, cmd_subfolder) + +import mosq_test + +rc = 1 +keepalive = 60 +client_id = socket.gethostname()+".bridge_sample" +connect_packet = mosq_test.gen_connect(client_id, keepalive=keepalive, clean_session=False, proto_ver=128+3) +connack_packet = mosq_test.gen_connack(rc=0) + +client_connect_packet = mosq_test.gen_connect("pub-test", keepalive=keepalive) +client_connack_packet = mosq_test.gen_connack(rc=0) + +ssock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +ssock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) +ssock.settimeout(4) +ssock.bind(('', 1888)) +ssock.listen(5) + +broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=1889) + + +def test(bridge, sock): + if not mosq_test.expect_packet(bridge, "connect", connect_packet): + return 1 + bridge.send(connack_packet) + + mid = 0 + patterns = [ + "remote/topic/#", + "remote2/topic/prefix/#", + "remote3/topic/+/value", + "remote4/tipic/+", + "$SYS/broker/clients/total", + ] + for pattern in ("remote/topic/#", "remote2/topic/prefix/#", "remote3/topic/+/value"): + mid += 1 + subscribe_packet = mosq_test.gen_subscribe(mid, pattern, 0) + suback_packet = mosq_test.gen_suback(mid, 0) + if not mosq_test.expect_packet(bridge, "subscribe", subscribe_packet): + return 1 + bridge.send(suback_packet) + + mid += 1 + subscribe_packet = mosq_test.gen_subscribe(mid, "#", 0) + suback_packet = mosq_test.gen_suback(mid, 0) + sock.send(subscribe_packet) + if not mosq_test.expect_packet(sock, "suback", suback_packet): + return 1 + + cases = [ + ('local/topic/something', 'remote/topic/something'), + ('local/topic/some/t/h/i/n/g', 'remote/topic/some/t/h/i/n/g'), + ('local/topic/value', 'remote/topic/value'), + # Don't work, #40 must be fixed before + # ('local/topic', 'remote/topic'), + ('local2/topic/prefix/something', 'remote2/topic/prefix/something'), + ('local3/topic/something/value', 'remote3/topic/something/value'), + ('local4/topic/something', 'remote4/tipic/something'), + ('test/mosquitto/orgclients/total', '$SYS/broker/clients/total'), + ] + + for (local_topic, remote_topic) in cases: + mid += 1 + remote_publish_packet = mosq_test.gen_publish( + remote_topic, qos=0, mid=mid, payload='' + ) + local_publish_packet = mosq_test.gen_publish( + local_topic, qos=0, mid=mid, payload='' + ) + + bridge.send(remote_publish_packet) + match = mosq_test.expect_packet(sock, "publish", local_publish_packet) + if not match: + print("Fail on cases local_topic=%r, remote_topic=%r" % ( + local_topic, remote_topic, + )) + return 1 + return 0 + +try: + (bridge, address) = ssock.accept() + bridge.settimeout(2) + + sock = mosq_test.do_client_connect( + client_connect_packet, client_connack_packet, + port=1889, + ) + + rc = test(bridge, sock) + + sock.close() + bridge.close() +finally: + try: + bridge.close() + except NameError: + pass + + broker.terminate() + broker.wait() + if rc: + (stdo, stde) = broker.communicate() + print(stde) + ssock.close() + +exit(rc) diff --git a/test/broker/06-bridge-br2b-remapping.conf b/test/broker/06-bridge-br2b-remapping.conf new file mode 100644 index 00000000..49145362 --- /dev/null +++ b/test/broker/06-bridge-br2b-remapping.conf @@ -0,0 +1,15 @@ +port 1889 + +retry_interval 10 + +connection bridge_sample +address 127.0.0.1:1888 +bridge_attempt_unsubscribe false +topic # out 0 local/topic/ remote/topic/ +topic prefix/# out 0 local2/topic/ remote2/topic/ +topic +/value out 0 local3/topic/ remote3/topic/ +topic ic/+ out 0 local4/top remote4/tip +# this one is invalid +topic +/value out 0 local5/top remote5/tip +notifications false +restart_timeout 5 diff --git a/test/broker/06-bridge-br2b-remapping.py b/test/broker/06-bridge-br2b-remapping.py new file mode 100755 index 00000000..8efee5d5 --- /dev/null +++ b/test/broker/06-bridge-br2b-remapping.py @@ -0,0 +1,109 @@ +#!/usr/bin/env python + +# Test remapping of topic name for outgoing message + +import socket + +import inspect, os, sys +# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder +cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) +if cmd_subfolder not in sys.path: + sys.path.insert(0, cmd_subfolder) + +import mosq_test + +rc = 1 +keepalive = 60 +client_id = socket.gethostname()+".bridge_sample" +connect_packet = mosq_test.gen_connect(client_id, keepalive=keepalive, clean_session=False, proto_ver=128+3) +connack_packet = mosq_test.gen_connack(rc=0) + +client_connect_packet = mosq_test.gen_connect("pub-test", keepalive=keepalive) +client_connack_packet = mosq_test.gen_connack(rc=0) + +ssock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +ssock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) +ssock.settimeout(4) +ssock.bind(('', 1888)) +ssock.listen(5) + +broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=1889) + + +def test(bridge, sock): + if not mosq_test.expect_packet(bridge, "connect", connect_packet): + return 1 + + bridge.send(connack_packet) + + cases = [ + ('local/topic/something', 'remote/topic/something'), + ('local/topic/some/t/h/i/n/g', 'remote/topic/some/t/h/i/n/g'), + ('local/topic/value', 'remote/topic/value'), + # Don't work, #40 must be fixed before + # ('local/topic', 'remote/topic'), + ('local2/topic/something', None), # don't match topic pattern + ('local2/topic/prefix/something', 'remote2/topic/prefix/something'), + ('local3/topic/something/value', 'remote3/topic/something/value'), + ('local4/topic/something', 'remote4/tipic/something'), + ('local5/topic/something', None), + ] + + mid = 3 + for (local_topic, remote_topic) in cases: + mid += 1 + local_publish_packet = mosq_test.gen_publish( + local_topic, qos=0, mid=mid, payload='' + ) + sock.send(local_publish_packet) + if remote_topic: + remote_publish_packet = mosq_test.gen_publish( + remote_topic, qos=0, mid=mid, payload='' + ) + match = mosq_test.expect_packet(bridge, "publish", remote_publish_packet) + if not match: + print("Fail on cases local_topic=%r, remote_topic=%r" % ( + local_topic, remote_topic, + )) + return 1 + else: + bridge.settimeout(3) + try: + bridge.recv(1) + print("FAIL: Received data when nothing is expected") + print("Fail on cases local_topic=%r, remote_topic=%r" % ( + local_topic, remote_topic, + )) + return 1 + except socket.timeout: + pass + bridge.settimeout(20) + return 0 + +try: + (bridge, address) = ssock.accept() + bridge.settimeout(2) + + sock = mosq_test.do_client_connect( + client_connect_packet, client_connack_packet, + port=1889, + ) + + rc = test(bridge, sock) + + sock.close() + bridge.close() +finally: + try: + bridge.close() + except NameError: + pass + + broker.terminate() + broker.wait() + if rc: + (stdo, stde) = broker.communicate() + print(stde) + ssock.close() + +exit(rc) diff --git a/test/broker/08-ssl-bridge.conf b/test/broker/08-ssl-bridge.conf index fc2c17b5..763546e1 100644 --- a/test/broker/08-ssl-bridge.conf +++ b/test/broker/08-ssl-bridge.conf @@ -1,9 +1,10 @@ port 1889 connection bridge_test -address localhost:1888 +address 127.0.0.1:1888 topic bridge/# both 0 notifications false +restart_timeout 2 #bridge_cafile ../ssl/test-root-ca.crt bridge_cafile ../ssl/all-ca.crt diff --git a/test/broker/Makefile b/test/broker/Makefile index 45f08b2e..808de106 100644 --- a/test/broker/Makefile +++ b/test/broker/Makefile @@ -59,6 +59,7 @@ endif ./04-retain-qos0-repeated.py ./04-retain-qos1-qos0.py ./04-retain-qos0-clear.py + ./04-retain-upgrade-outgoing-qos.py 05 : ./05-clean-session-qos1.py @@ -71,6 +72,8 @@ endif ./06-bridge-b2br-disconnect-qos2.py ./06-bridge-fail-persist-resend-qos1.py ./06-bridge-fail-persist-resend-qos2.py + ./06-bridge-b2br-remapping.py + ./06-bridge-br2b-remapping.py 07 : ./07-will-qos0.py @@ -89,9 +92,11 @@ ifeq ($(WITH_TLS),yes) ./08-ssl-connect-identity.py ./08-ssl-connect-no-identity.py ./08-ssl-bridge.py +ifeq ($(WITH_TLS_PSK),yes) ./08-tls-psk-pub.py ./08-tls-psk-bridge.py endif +endif 09 : ./09-plugin-auth-unpwd-success.py diff --git a/test/lib/c/09-util-topic-matching.c b/test/lib/c/09-util-topic-matching.c index 8b59a633..12c512fd 100644 --- a/test/lib/c/09-util-topic-matching.c +++ b/test/lib/c/09-util-topic-matching.c @@ -2,6 +2,9 @@ #include #include +#define EXPECT_MATCH(A, B) do_check((A), (B), false) +#define EXPECT_NOMATCH(A, B) do_check((A), (B), true) + void do_check(const char *sub, const char *topic, bool bad_res) { bool match; @@ -16,42 +19,44 @@ void do_check(const char *sub, const char *topic, bool bad_res) int main(int argc, char *argv[]) { - do_check("foo/#", "foo/", false); - do_check("foo#", "foo", true); - do_check("fo#o/", "foo", true); - do_check("foo#", "fooa", true); - do_check("foo+", "foo", true); - do_check("foo+", "fooa", true); + EXPECT_MATCH("foo/#", "foo/"); + EXPECT_NOMATCH("foo#", "foo"); + EXPECT_NOMATCH("fo#o/", "foo"); + EXPECT_NOMATCH("foo#", "fooa"); + EXPECT_NOMATCH("foo+", "foo"); + EXPECT_NOMATCH("foo+", "fooa"); - do_check("test/6/#", "test/3", true); - do_check("foo/bar", "foo/bar", false); - do_check("foo/+", "foo/bar", false); - do_check("foo/+/baz", "foo/bar/baz", false); + EXPECT_NOMATCH("test/6/#", "test/3"); + EXPECT_MATCH("foo/bar", "foo/bar"); + EXPECT_MATCH("foo/+", "foo/bar"); + EXPECT_MATCH("foo/+/baz", "foo/bar/baz"); - do_check("A/B/+/#", "A/B/B/C", false); + EXPECT_MATCH("A/B/+/#", "A/B/B/C"); - do_check("foo/+/#", "foo/bar/baz", false); - do_check("#", "foo/bar/baz", false); + EXPECT_MATCH("foo/+/#", "foo/bar/baz"); + EXPECT_MATCH("foo/+/#", "foo/bar"); + EXPECT_MATCH("#", "foo/bar/baz"); + EXPECT_MATCH("#", "foo/bar/baz"); - do_check("foo/bar", "foo", true); - do_check("foo/+", "foo/bar/baz", true); - do_check("foo/+/baz", "foo/bar/bar", true); + EXPECT_NOMATCH("foo/bar", "foo"); + EXPECT_NOMATCH("foo/+", "foo/bar/baz"); + EXPECT_NOMATCH("foo/+/baz", "foo/bar/bar"); - do_check("foo/+/#", "fo2/bar/baz", true); + EXPECT_NOMATCH("foo/+/#", "fo2/bar/baz"); - do_check("#", "/foo/bar", false); - do_check("/#", "/foo/bar", false); - do_check("/#", "foo/bar", true); + EXPECT_MATCH("#", "/foo/bar"); + EXPECT_MATCH("/#", "/foo/bar"); + EXPECT_NOMATCH("/#", "foo/bar"); - do_check("foo//bar", "foo//bar", false); - do_check("foo//+", "foo//bar", false); - do_check("foo/+/+/baz", "foo///baz", false); - do_check("foo/bar/+", "foo/bar/", false); + EXPECT_MATCH("foo//bar", "foo//bar"); + EXPECT_MATCH("foo//+", "foo//bar"); + EXPECT_MATCH("foo/+/+/baz", "foo///baz"); + EXPECT_MATCH("foo/bar/+", "foo/bar/"); - do_check("$SYS/bar", "$SYS/bar", false); - do_check("#", "$SYS/bar", true); - do_check("$BOB/bar", "$SYS/bar", true); + EXPECT_MATCH("$SYS/bar", "$SYS/bar"); + EXPECT_NOMATCH("#", "$SYS/bar"); + EXPECT_NOMATCH("$BOB/bar", "$SYS/bar"); return 0; }