diff --git a/CMakeLists.txt b/CMakeLists.txt index 7b53330906..6fe8a0018a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,18 +37,18 @@ if (WIN32) else (WIN32) set (BINDIR bin) set (SBINDIR sbin) - if (${CMAKE_INSTALL_PREFIX} STREQUAL /usr) + if ("${CMAKE_INSTALL_PREFIX}" STREQUAL /usr) set (SYSCONFDIR /etc/mosquitto) - else (${CMAKE_INSTALL_PREFIX} STREQUAL /usr) + else ("${CMAKE_INSTALL_PREFIX}" STREQUAL /usr) set (SYSCONFDIR etc/mosquitto) - endif (${CMAKE_INSTALL_PREFIX} STREQUAL /usr) + endif ("${CMAKE_INSTALL_PREFIX}" STREQUAL /usr) set (LIBDIR lib${LIB_SUFFIX}) set (CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${LIBDIR}") set (INCLUDEDIR include) set (DATAROOTDIR share) - set (MANDIR ${DATAROOTDIR}/man) - set (SHAREDIR ${DATAROOTDIR}/mosquitto) + set (MANDIR "${DATAROOTDIR}/man") + set (SHAREDIR "${DATAROOTDIR}/mosquitto") endif (WIN32) option(WITH_TLS @@ -96,7 +96,7 @@ endif (${DOCUMENTATION} STREQUAL ON) # Install config file # ======================================== -install(FILES mosquitto.conf aclfile.example pskfile.example pwfile.example DESTINATION ${SYSCONFDIR}) +install(FILES mosquitto.conf aclfile.example pskfile.example pwfile.example DESTINATION "${SYSCONFDIR}") # ======================================== # Testing diff --git a/ChangeLog.txt b/ChangeLog.txt index 5628e20a9c..9313ac941e 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -58,7 +58,73 @@ Build: - Add WITH_STRIP option (defaulting to "no") that when set to "yes" will strip executables and shared libraries when installing. - Add WITH_STATIC_LIBRARIES (defaulting to "no") that when set to "yes" will - buld and install static versions of the client libraries. + build and install static versions of the client libraries. + + +1.4.9 - 20160603 +================ + +Broker: +- Ensure websockets clients that previously connected with clean session set + to false have their queued messages delivered immediately on reconnecting. + Closes #476314. +- Reconnecting client with clean session set to false doesn't start with mid=1 + again. +- Will topic isn't truncated by one byte when using a mount_point any more. +- Network errors are printed correctly on Windows. +- Fix incorrect $SYS heap memory reporting when using ACLs. +- Bridge config parameters couldn't contain a space, this has been fixed. + Closes #150. +- Fix saving of persistence messages that start with a '/'. Closes #151. +- Fix reconnecting for bridges that use TLS on Windows. Closes #154. +- Broker and bridges can now cope with unknown incoming PUBACK, PUBREC, + PUBREL, PUBCOMP without disconnecting. Closes #57. +- Fix websockets listeners not being able to bind to an IP address. Closes + #170. +- mosquitto_passwd utility now correctly deals with unknown command line + arguments in all cases. Closes #169. +- Fix publishing of $SYS/broker/clients/maximum +- Fix order of #includes in lib/send_mosq.c to ensure struct mosquitto doesn't + differ between source files when websockets is being used. Closes #180. +- Fix possible rare crash when writing out persistence file and a client has + incomplete messages inflight that it has been denied the right to publish. + +Client library: +- Fix the case where a message received just before the keepalive timer + expired would cause the client to miss the keepalive timer. +- Return value of pthread_create is now checked. +- _mosquitto_destroy should not cancel threads that weren't created by + libmosquitto. Closes #166. +- Clients can now cope with unknown incoming PUBACK, PUBREC, PUBREL, PUBCOMP + without disconnecting. Closes #57. +- Fix mosquitto_topic_matches_sub() reporting matches on some invalid + subscriptions. + +Clients: +- Handle some unchecked malloc() calls. Closes #1. + +Build: +- Fix string quoting in CMakeLists.txt. Closes #4. +- Fix building on Visual Studio 2015. Closes #136. + + +1.4.8 - 20160214 +================ + +Broker: +- Wills published by clients connected to a listener with mount_point defined + now correctly obey the mount point. This was a potential security risk + because it allowed clients to publish messages outside of their restricted + mount point. This is only affects brokers where the mount_point option is in + use. Closes #487178. +- Fix detection of broken connections on Windows. Closes #485143. +- Close stdin etc. when daemonised. Closes #485589. +- Fix incorrect detection of FreeBSD and OpenBSD. Closes #485131. + +Client library: +- mosq->want_write should be cleared immediately before a call to SSL_write, + to allow clients using mosquitto_want_write() to get accurate results. + 1.4.7 - 20151221 ================ diff --git a/Mosquitto.podspec b/Mosquitto.podspec new file mode 100644 index 0000000000..64efb7978e --- /dev/null +++ b/Mosquitto.podspec @@ -0,0 +1,28 @@ +Pod::Spec.new do |s| + s.name = "Mosquitto" + s.version = "1.4.8" + s.summary = "Eclipse Mosquitto is an open source implementation of a server for version 3.1 and 3.1.1 of the MQTT protocol." + s.description = "Eclipse Mosquitto is an open source implementation of a server for version 3.1 and 3.1.1 of the MQTT protocol." + s.homepage = "https://github.com/eclipse/mosquitto" + s.license = 'This project is dual licensed under the Eclipse Public License 1.0 and the Eclipse Distribution License 1.0 as described in the epl-v10 and edl-v10 files.' + s.author = { "Eclipse Foundation" => "emo@eclipse.org" } + s.source = { :git => "https://github.com/eclipse/mosquitto.git", :tag => "v1.4.8" } + + s.ios.deployment_target = '6.0' + s.xcconfig = { 'HEADER_SEARCH_PATHS' => '${PODS_ROOT}/**', + 'CLANG_ALLOW_NON_MODULAR_INCLUDES_IN_FRAMEWORK_MODULES' => 'YES', + 'GCC_PREPROCESSOR_DEFINITIONS' => 'WITH_THREADING=1' + } + s.source_files = ['lib/*.{c,h}', '*.h'] + + s.subspec 'WithoutTLS' do |sp| + sp.source_files = ['lib/*.{c,h}', '*.h'] + end + + s.subspec 'TLS' do |sp| + sp.xcconfig = { 'GCC_PREPROCESSOR_DEFINITIONS' => 'WITH_TLS=1' } + sp.source_files = ['lib/*.{c,h}', '*.h'] + sp.dependency 'OpenSSL-Universal', '~> 1.0.1.19' + end + +end diff --git a/appveyor.yml b/appveyor.yml index 285a24960a..9e2dffa0f6 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,3 +1,5 @@ +os: Visual Studio 2013 + environment: CMAKE_ARGS: -DCMAKE_BUILD_TYPE=Release NSIS_ROOT: C:\nsis diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index a743fccf73..0b6f42027e 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -14,5 +14,5 @@ add_executable(mosquitto_sub sub_client.c ${shared_src}) target_link_libraries(mosquitto_pub libmosquitto) target_link_libraries(mosquitto_sub libmosquitto) -install(TARGETS mosquitto_pub RUNTIME DESTINATION ${BINDIR} LIBRARY DESTINATION ${LIBDIR}) -install(TARGETS mosquitto_sub RUNTIME DESTINATION ${BINDIR} LIBRARY DESTINATION ${LIBDIR}) +install(TARGETS mosquitto_pub RUNTIME DESTINATION "${BINDIR}" LIBRARY DESTINATION "${LIBDIR}") +install(TARGETS mosquitto_sub RUNTIME DESTINATION "${BINDIR}" LIBRARY DESTINATION "${LIBDIR}") diff --git a/client/client_shared.c b/client/client_shared.c index 10a53bfd79..68ea3895e6 100644 --- a/client/client_shared.c +++ b/client/client_shared.c @@ -121,6 +121,10 @@ int client_config_load(struct mosq_config *cfg, int pub_or_sub, int argc, char * if(env){ len = strlen(env) + strlen("/mosquitto_pub") + 1; loc = malloc(len); + if(!loc){ + fprintf(stderr, "Error: Out of memory.\n"); + return 1; + } if(pub_or_sub == CLIENT_PUB){ snprintf(loc, len, "%s/mosquitto_pub", env); }else{ @@ -132,6 +136,10 @@ int client_config_load(struct mosq_config *cfg, int pub_or_sub, int argc, char * if(env){ len = strlen(env) + strlen("/.config/mosquitto_pub") + 1; loc = malloc(len); + if(!loc){ + fprintf(stderr, "Error: Out of memory.\n"); + return 1; + } if(pub_or_sub == CLIENT_PUB){ snprintf(loc, len, "%s/.config/mosquitto_pub", env); }else{ @@ -148,6 +156,10 @@ int client_config_load(struct mosq_config *cfg, int pub_or_sub, int argc, char * if(rc > 0 && rc < 1024){ len = strlen(env) + strlen("\\mosquitto_pub.conf") + 1; loc = malloc(len); + if(!loc){ + fprintf(stderr, "Error: Out of memory.\n"); + return 1; + } if(pub_or_sub == CLIENT_PUB){ snprintf(loc, len, "%s\\mosquitto_pub.conf", env); }else{ diff --git a/config.h b/config.h index 6152c46842..6bdcb40dbd 100644 --- a/config.h +++ b/config.h @@ -11,8 +11,11 @@ * * Generally for Windows native support. * ============================================================ */ +#if defined(_MSC_VER) && _MSC_VER < 1900 +# define snprintf sprintf_s +#endif + #ifdef WIN32 -#define snprintf sprintf_s # ifndef strcasecmp # define strcasecmp strcmpi # endif diff --git a/config.mk b/config.mk index 88483a9e43..680c8b81f1 100644 --- a/config.mk +++ b/config.mk @@ -125,7 +125,7 @@ LIB_LDFLAGS:=${LDFLAGS} BROKER_CFLAGS:=${LIB_CFLAGS} ${CPPFLAGS} -DVERSION="\"${VERSION}\"" -DTIMESTAMP="\"${TIMESTAMP}\"" -DWITH_BROKER CLIENT_CFLAGS:=${CFLAGS} ${CPPFLAGS} -I../lib -DVERSION="\"${VERSION}\"" -ifneq ($(or $(find $(UNAME),FreeBSD), $(find $(UNAME),OpenBSD)),) +ifneq ($(or $(findstring $(UNAME),FreeBSD), $(findstring $(UNAME),OpenBSD)),) BROKER_LIBS:=-lm else BROKER_LIBS:=-ldl -lm diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 8ad9325e43..b9c8676a97 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -97,7 +97,7 @@ set_target_properties(libmosquitto PROPERTIES SOVERSION 1 ) -install(TARGETS libmosquitto RUNTIME DESTINATION ${BINDIR} LIBRARY DESTINATION ${LIBDIR}) +install(TARGETS libmosquitto RUNTIME DESTINATION "${BINDIR}" LIBRARY DESTINATION "${LIBDIR}") if (${WITH_STATIC_LIBRARIES} STREQUAL ON) if (${WITH_PIC} STREQUAL OFF) @@ -114,10 +114,10 @@ if (${WITH_STATIC_LIBRARIES} STREQUAL ON) ) target_compile_definitions(libmosquitto_static PUBLIC "LIBMOSQUITTO_STATIC") - install(TARGETS libmosquitto_static RUNTIME DESTINATION ${BINDIR} ARCHIVE DESTINATION ${LIBDIR}) + install(TARGETS libmosquitto_static RUNTIME DESTINATION "${BINDIR}" ARCHIVE DESTINATION "${LIBDIR}") endif (${WITH_STATIC_LIBRARIES} STREQUAL ON) -install(FILES mosquitto.h DESTINATION ${INCLUDEDIR}) +install(FILES mosquitto.h DESTINATION "${INCLUDEDIR}") if (UNIX AND NOT APPLE) install(CODE "EXEC_PROGRAM(/sbin/ldconfig)") diff --git a/lib/cpp/CMakeLists.txt b/lib/cpp/CMakeLists.txt index 91adc3fa0b..8eb19bfc4b 100644 --- a/lib/cpp/CMakeLists.txt +++ b/lib/cpp/CMakeLists.txt @@ -16,7 +16,7 @@ set_target_properties(mosquittopp PROPERTIES VERSION ${VERSION} SOVERSION 1 ) -install(TARGETS mosquittopp RUNTIME DESTINATION ${BINDIR} LIBRARY DESTINATION ${LIBDIR}) +install(TARGETS mosquittopp RUNTIME DESTINATION "${BINDIR}" LIBRARY DESTINATION "${LIBDIR}") if (${WITH_STATIC_LIBRARIES} STREQUAL ON) if (${WITH_PIC} STREQUAL OFF) @@ -39,10 +39,10 @@ if (${WITH_STATIC_LIBRARIES} STREQUAL ON) ) target_compile_definitions(mosquittopp_static PUBLIC "LIBMOSQUITTO_STATIC") - install(TARGETS mosquittopp_static RUNTIME DESTINATION ${BINDIR} ARCHIVE DESTINATION ${LIBDIR}) + install(TARGETS mosquittopp_static RUNTIME DESTINATION "${BINDIR}" ARCHIVE DESTINATION "${LIBDIR}") endif (${WITH_STATIC_LIBRARIES} STREQUAL ON) -install(FILES mosquittopp.h DESTINATION ${INCLUDEDIR}) +install(FILES mosquittopp.h DESTINATION "${INCLUDEDIR}") if (UNIX AND NOT APPLE) install(CODE "EXEC_PROGRAM(/sbin/ldconfig)") diff --git a/lib/handle_pubackcomp.c b/lib/handle_pubackcomp.c index a79d52a0e1..65ff66d51d 100644 --- a/lib/handle_pubackcomp.c +++ b/lib/handle_pubackcomp.c @@ -51,7 +51,12 @@ int handle__pubackcomp(struct mosquitto *mosq, const char *type) if(mid){ rc = db__message_delete(db, mosq, mid, mosq_md_out); - if(rc) return rc; + if(rc == MOSQ_ERR_NOT_FOUND){ + log__printf(mosq, MOSQ_LOG_WARNING, "Warning: Received %s from %s for an unknown packet identifier %d.", type, mosq->id, mid); + return MOSQ_ERR_SUCCESS; + }else{ + return rc; + } } #else log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received %s (Mid: %d)", mosq->id, type, mid); diff --git a/lib/handle_pubrec.c b/lib/handle_pubrec.c index f044e02d8d..c7c9394769 100644 --- a/lib/handle_pubrec.c +++ b/lib/handle_pubrec.c @@ -50,7 +50,11 @@ int handle__pubrec(struct mosquitto *mosq) rc = message__out_update(mosq, mid, mosq_ms_wait_for_pubcomp); #endif - if(rc) return rc; + if(rc == MOSQ_ERR_NOT_FOUND){ + log__printf(mosq, MOSQ_LOG_WARNING, "Warning: Received PUBREC from %s for an unknown packet identifier %d.", mosq->id, mid); + }else if(rc != MOSQ_ERR_SUCCESS){ + return rc; + } rc = send__pubrel(mosq, mid); if(rc) return rc; diff --git a/lib/handle_pubrel.c b/lib/handle_pubrel.c index c70b105fb1..a33a64a542 100644 --- a/lib/handle_pubrel.c +++ b/lib/handle_pubrel.c @@ -56,6 +56,7 @@ int handle__pubrel(struct mosquitto_db *db, struct mosquitto *mosq) if(db__message_release(db, mosq, mid, mosq_md_in)){ /* Message not found. Still send a PUBCOMP anyway because this could be * due to a repeated PUBREL after a client has reconnected. */ + log__printf(mosq, MOSQ_LOG_WARNING, "Warning: Received PUBREL from %s for an unknown packet identifier %d.", mosq->id, mid); } #else log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received PUBREL (Mid: %d)", mosq->id, mid); diff --git a/lib/mosquitto.c b/lib/mosquitto.c index d6501f16fe..c61d41c396 100644 --- a/lib/mosquitto.c +++ b/lib/mosquitto.c @@ -171,7 +171,7 @@ int mosquitto_reinitialise(struct mosquitto *mosq, const char *id, bool clean_se mosq->out_packet = NULL; mosq->current_out_packet = NULL; mosq->last_msg_in = mosquitto_time(); - mosq->last_msg_out = mosquitto_time(); + mosq->next_msg_out = mosquitto_time() + mosq->keepalive; mosq->ping_t = 0; mosq->last_mid = 0; mosq->state = mosq_cs_new; @@ -194,7 +194,7 @@ int mosquitto_reinitialise(struct mosquitto *mosq, const char *id, bool clean_se mosq->reconnect_delay = 1; mosq->reconnect_delay_max = 1; mosq->reconnect_exponential_backoff = false; - mosq->threaded = false; + mosq->threaded = mosq_ts_none; #ifdef WITH_TLS mosq->ssl = NULL; mosq->tls_cert_reqs = SSL_VERIFY_PEER; @@ -272,10 +272,10 @@ void mosquitto__destroy(struct mosquitto *mosq) if(!mosq) return; #ifdef WITH_THREADING - if(mosq->threaded && !pthread_equal(mosq->thread_id, pthread_self())){ + if(mosq->threaded == mosq_ts_self && !pthread_equal(mosq->thread_id, pthread_self())){ pthread_cancel(mosq->thread_id); pthread_join(mosq->thread_id, NULL); - mosq->threaded = false; + mosq->threaded = mosq_ts_none; } if(mosq->id){ @@ -476,7 +476,7 @@ static int mosquitto__reconnect(struct mosquitto *mosq, bool blocking) pthread_mutex_lock(&mosq->msgtime_mutex); mosq->last_msg_in = mosquitto_time(); - mosq->last_msg_out = mosquitto_time(); + mosq->next_msg_out = mosq->last_msg_in + mosq->keepalive; pthread_mutex_unlock(&mosq->msgtime_mutex); mosq->ping_t = 0; @@ -549,7 +549,7 @@ int mosquitto_publish(struct mosquitto *mosq, int *mid, const char *topic, int p if(!mosq || !topic || qos<0 || qos>2) return MOSQ_ERR_INVAL; if(STREMPTY(topic)) return MOSQ_ERR_INVAL; - if(!mosquitto_validate_utf8(topic, strlen(topic))) return MOSQ_ERR_MALFORMED_UTF8; + if(mosquitto_validate_utf8(topic, strlen(topic))) return MOSQ_ERR_MALFORMED_UTF8; if(payloadlen < 0 || payloadlen > MQTT_MAX_PAYLOAD) return MOSQ_ERR_PAYLOAD_SIZE; if(mosquitto_pub_topic_check(topic) != MOSQ_ERR_SUCCESS){ @@ -820,6 +820,7 @@ int mosquitto_loop(struct mosquitto *mosq, int timeout, int max_packets) int rc; char pairbuf; int maxfd = 0; + time_t now; if(!mosq || max_packets < 1) return MOSQ_ERR_INVAL; #ifndef WIN32 @@ -842,7 +843,6 @@ int mosquitto_loop(struct mosquitto *mosq, int timeout, int max_packets) if(mosq->ssl){ if(mosq->want_write){ FD_SET(mosq->sock, &writefds); - mosq->want_write = false; }else if(mosq->want_connect){ /* Remove possible FD_SET from above, we don't want to check * for writing if we are still connecting, unless want_write is @@ -882,21 +882,21 @@ int mosquitto_loop(struct mosquitto *mosq, int timeout, int max_packets) } } - if(timeout >= 0){ - local_timeout.tv_sec = timeout/1000; -#ifdef HAVE_PSELECT - local_timeout.tv_nsec = (timeout-local_timeout.tv_sec*1000)*1e6; -#else - local_timeout.tv_usec = (timeout-local_timeout.tv_sec*1000)*1000; -#endif - }else{ - local_timeout.tv_sec = 1; + if(timeout < 0){ + timeout = 1000; + } + + now = mosquitto_time(); + if(mosq->next_msg_out && now + timeout/1000 > mosq->next_msg_out){ + timeout = (mosq->next_msg_out - now)*1000; + } + + local_timeout.tv_sec = timeout/1000; #ifdef HAVE_PSELECT - local_timeout.tv_nsec = 0; + local_timeout.tv_nsec = (timeout-local_timeout.tv_sec*1000)*1e6; #else - local_timeout.tv_usec = 0; + local_timeout.tv_usec = (timeout-local_timeout.tv_sec*1000)*1000; #endif - } #ifdef HAVE_PSELECT fdcount = pselect(maxfd+1, &readfds, &writefds, NULL, &local_timeout, NULL); diff --git a/lib/mosquitto_internal.h b/lib/mosquitto_internal.h index 39b0431cc1..5ba4f7e85a 100644 --- a/lib/mosquitto_internal.h +++ b/lib/mosquitto_internal.h @@ -112,6 +112,12 @@ enum mosquitto__protocol { mosq_p_mqtts = 3 }; +enum mosquitto__threaded_state { + mosq_ts_none, /* No threads in use */ + mosq_ts_self, /* Threads started by libmosquitto */ + mosq_ts_external /* Threads started by external code */ +}; + enum mosquitto__transport { mosq_t_invalid = 0, mosq_t_tcp = 1, @@ -155,7 +161,7 @@ struct mosquitto { uint16_t last_mid; enum mosquitto_client_state state; time_t last_msg_in; - time_t last_msg_out; + time_t next_msg_out; time_t ping_t; struct mosquitto__packet in_packet; struct mosquitto__packet *current_out_packet; @@ -245,7 +251,7 @@ struct mosquitto { unsigned int reconnect_delay; unsigned int reconnect_delay_max; bool reconnect_exponential_backoff; - bool threaded; + char threaded; struct mosquitto__packet *out_packet_last; int inflight_messages; int max_inflight_messages; diff --git a/lib/net_mosq.c b/lib/net_mosq.c index f957aa8de0..d0a8e62054 100644 --- a/lib/net_mosq.c +++ b/lib/net_mosq.c @@ -320,15 +320,20 @@ void net__print_ssl_error(struct mosquitto *mosq) int net__socket_connect_tls(struct mosquitto *mosq) { - int ret; - + int ret, err; ret = SSL_connect(mosq->ssl); - if(ret != 1){ - ret = SSL_get_error(mosq->ssl, ret); - if(ret == SSL_ERROR_WANT_READ){ + if(ret != 1) { + err = SSL_get_error(mosq->ssl, ret); +#ifdef WIN32 + if (err == SSL_ERROR_SYSCALL) { + mosq->want_connect = true; + return MOSQ_ERR_SUCCESS; + } +#endif + if(err == SSL_ERROR_WANT_READ){ mosq->want_connect = true; /* We always try to read anyway */ - }else if(ret == SSL_ERROR_WANT_WRITE){ + }else if(err == SSL_ERROR_WANT_WRITE){ mosq->want_write = true; mosq->want_connect = true; }else{ @@ -569,6 +574,7 @@ ssize_t net__write(struct mosquitto *mosq, void *buf, size_t count) errno = 0; #ifdef WITH_TLS if(mosq->ssl){ + mosq->want_write = false; ret = SSL_write(mosq->ssl, buf, count); if(ret < 0){ err = SSL_get_error(mosq->ssl, ret); diff --git a/lib/packet_mosq.c b/lib/packet_mosq.c index 9dbbb20322..febe82ec56 100644 --- a/lib/packet_mosq.c +++ b/lib/packet_mosq.c @@ -334,7 +334,7 @@ int packet__write(struct mosquitto *mosq) mosquitto__free(packet); pthread_mutex_lock(&mosq->msgtime_mutex); - mosq->last_msg_out = mosquitto_time(); + mosq->next_msg_out = mosquitto_time() + mosq->keepalive; pthread_mutex_unlock(&mosq->msgtime_mutex); /* End of duplicate, possibly unnecessary code */ @@ -365,7 +365,7 @@ int packet__write(struct mosquitto *mosq) mosquitto__free(packet); pthread_mutex_lock(&mosq->msgtime_mutex); - mosq->last_msg_out = mosquitto_time(); + mosq->next_msg_out = mosquitto_time() + mosq->keepalive; pthread_mutex_unlock(&mosq->msgtime_mutex); } pthread_mutex_unlock(&mosq->current_out_packet_mutex); diff --git a/lib/read_handle_shared.c b/lib/read_handle_shared.c new file mode 100644 index 0000000000..6a135dfc4c --- /dev/null +++ b/lib/read_handle_shared.c @@ -0,0 +1,242 @@ +/* +Copyright (c) 2009-2014 Roger Light + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef WITH_BROKER +#include +#endif + +int _mosquitto_handle_pingreq(struct mosquitto *mosq) +{ + assert(mosq); +#ifdef WITH_BROKER + _mosquitto_log_printf(NULL, MOSQ_LOG_DEBUG, "Received PINGREQ from %s", mosq->id); +#else + _mosquitto_log_printf(mosq, MOSQ_LOG_DEBUG, "Client %s received PINGREQ", mosq->id); +#endif + return _mosquitto_send_pingresp(mosq); +} + +int _mosquitto_handle_pingresp(struct mosquitto *mosq) +{ + assert(mosq); + mosq->ping_t = 0; /* No longer waiting for a PINGRESP. */ +#ifdef WITH_BROKER + _mosquitto_log_printf(NULL, MOSQ_LOG_DEBUG, "Received PINGRESP from %s", mosq->id); +#else + _mosquitto_log_printf(mosq, MOSQ_LOG_DEBUG, "Client %s received PINGRESP", mosq->id); +#endif + return MOSQ_ERR_SUCCESS; +} + +#ifdef WITH_BROKER +int _mosquitto_handle_pubackcomp(struct mosquitto_db *db, struct mosquitto *mosq, const char *type) +#else +int _mosquitto_handle_pubackcomp(struct mosquitto *mosq, const char *type) +#endif +{ + uint16_t mid; + int rc; + + assert(mosq); + rc = _mosquitto_read_uint16(&mosq->in_packet, &mid); + if(rc) return rc; +#ifdef WITH_BROKER + _mosquitto_log_printf(NULL, MOSQ_LOG_DEBUG, "Received %s from %s (Mid: %d)", type, mosq->id, mid); + + if(mid){ + rc = mqtt3_db_message_delete(db, mosq, mid, mosq_md_out); + if(rc == MOSQ_ERR_NOT_FOUND){ + _mosquitto_log_printf(mosq, MOSQ_LOG_WARNING, "Warning: Received %s from %s for an unknown packet identifier %d.", type, mosq->id, mid); + return MOSQ_ERR_SUCCESS; + }else{ + return rc; + } + } +#else + _mosquitto_log_printf(mosq, MOSQ_LOG_DEBUG, "Client %s received %s (Mid: %d)", mosq->id, type, mid); + + if(!_mosquitto_message_delete(mosq, mid, mosq_md_out)){ + /* Only inform the client the message has been sent once. */ + pthread_mutex_lock(&mosq->callback_mutex); + if(mosq->on_publish){ + mosq->in_callback = true; + mosq->on_publish(mosq, mosq->userdata, mid); + mosq->in_callback = false; + } + pthread_mutex_unlock(&mosq->callback_mutex); + } +#endif + + return MOSQ_ERR_SUCCESS; +} + +int _mosquitto_handle_pubrec(struct mosquitto *mosq) +{ + uint16_t mid; + int rc; + + assert(mosq); + rc = _mosquitto_read_uint16(&mosq->in_packet, &mid); + if(rc) return rc; +#ifdef WITH_BROKER + _mosquitto_log_printf(NULL, MOSQ_LOG_DEBUG, "Received PUBREC from %s (Mid: %d)", mosq->id, mid); + + rc = mqtt3_db_message_update(mosq, mid, mosq_md_out, mosq_ms_wait_for_pubcomp); +#else + _mosquitto_log_printf(mosq, MOSQ_LOG_DEBUG, "Client %s received PUBREC (Mid: %d)", mosq->id, mid); + + rc = _mosquitto_message_out_update(mosq, mid, mosq_ms_wait_for_pubcomp); +#endif + if(rc == MOSQ_ERR_NOT_FOUND){ + _mosquitto_log_printf(mosq, MOSQ_LOG_WARNING, "Warning: Received PUBREC from %s for an unknown packet identifier %d.", mosq->id, mid); + }else if(rc != MOSQ_ERR_SUCCESS){ + return rc; + } + rc = _mosquitto_send_pubrel(mosq, mid); + if(rc) return rc; + + return MOSQ_ERR_SUCCESS; +} + +int _mosquitto_handle_pubrel(struct mosquitto_db *db, struct mosquitto *mosq) +{ + uint16_t mid; +#ifndef WITH_BROKER + struct mosquitto_message_all *message = NULL; +#endif + int rc; + + assert(mosq); + if(mosq->protocol == mosq_p_mqtt311){ + if((mosq->in_packet.command&0x0F) != 0x02){ + return MOSQ_ERR_PROTOCOL; + } + } + rc = _mosquitto_read_uint16(&mosq->in_packet, &mid); + if(rc) return rc; +#ifdef WITH_BROKER + _mosquitto_log_printf(NULL, MOSQ_LOG_DEBUG, "Received PUBREL from %s (Mid: %d)", mosq->id, mid); + + if(mqtt3_db_message_release(db, mosq, mid, mosq_md_in)){ + /* Message not found. Still send a PUBCOMP anyway because this could be + * due to a repeated PUBREL after a client has reconnected. */ + _mosquitto_log_printf(mosq, MOSQ_LOG_WARNING, "Warning: Received PUBREL from %s for an unknown packet identifier %d.", mosq->id, mid); + } +#else + _mosquitto_log_printf(mosq, MOSQ_LOG_DEBUG, "Client %s received PUBREL (Mid: %d)", mosq->id, mid); + + if(!_mosquitto_message_remove(mosq, mid, mosq_md_in, &message)){ + /* Only pass the message on if we have removed it from the queue - this + * prevents multiple callbacks for the same message. */ + pthread_mutex_lock(&mosq->callback_mutex); + if(mosq->on_message){ + mosq->in_callback = true; + mosq->on_message(mosq, mosq->userdata, &message->msg); + mosq->in_callback = false; + } + pthread_mutex_unlock(&mosq->callback_mutex); + _mosquitto_message_cleanup(&message); + } +#endif + rc = _mosquitto_send_pubcomp(mosq, mid); + if(rc) return rc; + + return MOSQ_ERR_SUCCESS; +} + +int _mosquitto_handle_suback(struct mosquitto *mosq) +{ + uint16_t mid; + uint8_t qos; + int *granted_qos; + int qos_count; + int i = 0; + int rc; + + assert(mosq); +#ifdef WITH_BROKER + _mosquitto_log_printf(NULL, MOSQ_LOG_DEBUG, "Received SUBACK from %s", mosq->id); +#else + _mosquitto_log_printf(mosq, MOSQ_LOG_DEBUG, "Client %s received SUBACK", mosq->id); +#endif + rc = _mosquitto_read_uint16(&mosq->in_packet, &mid); + if(rc) return rc; + + qos_count = mosq->in_packet.remaining_length - mosq->in_packet.pos; + granted_qos = _mosquitto_malloc(qos_count*sizeof(int)); + if(!granted_qos) return MOSQ_ERR_NOMEM; + while(mosq->in_packet.pos < mosq->in_packet.remaining_length){ + rc = _mosquitto_read_byte(&mosq->in_packet, &qos); + if(rc){ + _mosquitto_free(granted_qos); + return rc; + } + granted_qos[i] = (int)qos; + i++; + } +#ifndef WITH_BROKER + pthread_mutex_lock(&mosq->callback_mutex); + if(mosq->on_subscribe){ + mosq->in_callback = true; + mosq->on_subscribe(mosq, mosq->userdata, mid, qos_count, granted_qos); + mosq->in_callback = false; + } + pthread_mutex_unlock(&mosq->callback_mutex); +#endif + _mosquitto_free(granted_qos); + + return MOSQ_ERR_SUCCESS; +} + +int _mosquitto_handle_unsuback(struct mosquitto *mosq) +{ + uint16_t mid; + int rc; + + assert(mosq); +#ifdef WITH_BROKER + _mosquitto_log_printf(NULL, MOSQ_LOG_DEBUG, "Received UNSUBACK from %s", mosq->id); +#else + _mosquitto_log_printf(mosq, MOSQ_LOG_DEBUG, "Client %s received UNSUBACK", mosq->id); +#endif + rc = _mosquitto_read_uint16(&mosq->in_packet, &mid); + if(rc) return rc; +#ifndef WITH_BROKER + pthread_mutex_lock(&mosq->callback_mutex); + if(mosq->on_unsubscribe){ + mosq->in_callback = true; + mosq->on_unsubscribe(mosq, mosq->userdata, mid); + mosq->in_callback = false; + } + pthread_mutex_unlock(&mosq->callback_mutex); +#endif + + return MOSQ_ERR_SUCCESS; +} + diff --git a/lib/thread_mosq.c b/lib/thread_mosq.c index 6a850f410d..6248267666 100644 --- a/lib/thread_mosq.c +++ b/lib/thread_mosq.c @@ -28,11 +28,14 @@ void *mosquitto__thread_main(void *obj); int mosquitto_loop_start(struct mosquitto *mosq) { #ifdef WITH_THREADING - if(!mosq || mosq->threaded) return MOSQ_ERR_INVAL; + if(!mosq || mosq->threaded != mosq_ts_none) return MOSQ_ERR_INVAL; - mosq->threaded = true; - pthread_create(&mosq->thread_id, NULL, mosquitto__thread_main, mosq); - return MOSQ_ERR_SUCCESS; + mosq->threaded = mosq_ts_self; + if(!pthread_create(&mosq->thread_id, NULL, mosquitto__thread_main, mosq)){ + return MOSQ_ERR_SUCCESS; + }else{ + return MOSQ_ERR_ERRNO; + } #else return MOSQ_ERR_NOT_SUPPORTED; #endif @@ -45,7 +48,7 @@ int mosquitto_loop_stop(struct mosquitto *mosq, bool force) char sockpair_data = 0; # endif - if(!mosq || !mosq->threaded) return MOSQ_ERR_INVAL; + if(!mosq || mosq->threaded != mosq_ts_self) return MOSQ_ERR_INVAL; /* Write a single byte to sockpairW (connected to sockpairR) to break out @@ -64,7 +67,7 @@ int mosquitto_loop_stop(struct mosquitto *mosq, bool force) } pthread_join(mosq->thread_id, NULL); mosq->thread_id = pthread_self(); - mosq->threaded = false; + mosq->threaded = mosq_ts_none; return MOSQ_ERR_SUCCESS; #else @@ -103,7 +106,11 @@ int mosquitto_threaded_set(struct mosquitto *mosq, bool threaded) { if(!mosq) return MOSQ_ERR_INVAL; - mosq->threaded = threaded; + if(threaded){ + mosq->threaded = mosq_ts_external; + }else{ + mosq->threaded = mosq_ts_none; + } return MOSQ_ERR_SUCCESS; } diff --git a/lib/util_mosq.c b/lib/util_mosq.c index 363950c26d..3458c40ea5 100644 --- a/lib/util_mosq.c +++ b/lib/util_mosq.c @@ -44,7 +44,7 @@ void mosquitto__check_keepalive(struct mosquitto_db *db, struct mosquitto *mosq) void mosquitto__check_keepalive(struct mosquitto *mosq) #endif { - time_t last_msg_out; + time_t next_msg_out; time_t last_msg_in; time_t now = mosquitto_time(); #ifndef WITH_BROKER @@ -56,7 +56,7 @@ void 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->last_msg_out >= mosq->bridge->idle_timeout){ + && now - mosq->next_msg_out - mosq->keepalive >= mosq->bridge->idle_timeout){ log__printf(NULL, MOSQ_LOG_NOTICE, "Bridge connection %s has exceeded idle timeout, disconnecting.", mosq->id); net__socket_close(db, mosq); @@ -64,18 +64,18 @@ void mosquitto__check_keepalive(struct mosquitto *mosq) } #endif pthread_mutex_lock(&mosq->msgtime_mutex); - last_msg_out = mosq->last_msg_out; + next_msg_out = mosq->next_msg_out; last_msg_in = mosq->last_msg_in; pthread_mutex_unlock(&mosq->msgtime_mutex); if(mosq->keepalive && mosq->sock != INVALID_SOCKET && - (now - last_msg_out >= mosq->keepalive || now - last_msg_in >= mosq->keepalive)){ + (now >= next_msg_out || now - last_msg_in >= mosq->keepalive)){ if(mosq->state == mosq_cs_connected && mosq->ping_t == 0){ send__pingreq(mosq); /* Reset last msg times to give the server time to send a pingresp */ pthread_mutex_lock(&mosq->msgtime_mutex); mosq->last_msg_in = now; - mosq->last_msg_out = now; + mosq->next_msg_out = now + mosq->keepalive; pthread_mutex_unlock(&mosq->msgtime_mutex); }else{ #ifdef WITH_BROKER @@ -189,6 +189,11 @@ int mosquitto_topic_matches_sub(const char *sub, const char *topic, bool *result slen = strlen(sub); tlen = strlen(topic); + if(!slen || !tlen){ + *result = false; + return MOSQ_ERR_INVAL; + } + if(slen && tlen){ if((sub[0] == '$' && topic[0] != '$') || (topic[0] == '$' && sub[0] != '$')){ @@ -201,7 +206,7 @@ int mosquitto_topic_matches_sub(const char *sub, const char *topic, bool *result spos = 0; tpos = 0; - while(spos < slen && tpos < tlen){ + while(spos < slen && tpos <= tlen){ if(sub[spos] == topic[tpos]){ if(tpos == tlen-1){ /* Check for e.g. foo matching foo/# */ @@ -219,12 +224,26 @@ int mosquitto_topic_matches_sub(const char *sub, const char *topic, bool *result *result = true; 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++; *result = true; return MOSQ_ERR_SUCCESS; } }else{ 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++; while(tpos < tlen && topic[tpos] != '/'){ tpos++; @@ -234,10 +253,14 @@ int mosquitto_topic_matches_sub(const char *sub, const char *topic, bool *result return MOSQ_ERR_SUCCESS; } }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_SUCCESS; + return MOSQ_ERR_INVAL; }else{ *result = true; return MOSQ_ERR_SUCCESS; diff --git a/logo/mosquitto-14x14.png b/logo/legacy/mosquitto-14x14.png similarity index 100% rename from logo/mosquitto-14x14.png rename to logo/legacy/mosquitto-14x14.png diff --git a/logo/mosquitto-16x16.png b/logo/legacy/mosquitto-16x16.png similarity index 100% rename from logo/mosquitto-16x16.png rename to logo/legacy/mosquitto-16x16.png diff --git a/logo/mosquitto.svg b/logo/legacy/mosquitto.svg similarity index 100% rename from logo/mosquitto.svg rename to logo/legacy/mosquitto.svg diff --git a/logo/mosquitto-logo-only.svg b/logo/mosquitto-logo-only.svg new file mode 100644 index 0000000000..9030438218 --- /dev/null +++ b/logo/mosquitto-logo-only.svg @@ -0,0 +1,58 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/logo/mosquitto-text-below.svg b/logo/mosquitto-text-below.svg new file mode 100644 index 0000000000..ef8c3fc665 --- /dev/null +++ b/logo/mosquitto-text-below.svg @@ -0,0 +1,66 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/logo/mosquitto-text-side.svg b/logo/mosquitto-text-side.svg new file mode 100644 index 0000000000..7f50724c42 --- /dev/null +++ b/logo/mosquitto-text-side.svg @@ -0,0 +1,66 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/logo/mosquitto.ico b/logo/mosquitto.ico new file mode 100644 index 0000000000..88c91589f7 Binary files /dev/null and b/logo/mosquitto.ico differ diff --git a/man/mosquitto.conf.5.xml b/man/mosquitto.conf.5.xml index aa6ace009b..8d8bad7230 100644 --- a/man/mosquitto.conf.5.xml +++ b/man/mosquitto.conf.5.xml @@ -629,17 +629,23 @@ - port + port bind address/host Listen for incoming network connection on the specified port. A second optional argument allows the listener to be bound to a specific ip address/hostname. If this variable is used and - neither nor - are used then the default - listener will not be started. This option may be - specified multiple times. See also the - option. + neither the global + nor options are used then the + default listener will not be started. + The option + allows this listener to be bound to a specific IP + address by passing an IP address or hostname. For + websockets listeners, it is only possible to pass + an IP address here. + This option may be specified multiple times. See + also the + option. Not reloaded on reload signal. diff --git a/misc/currentcost/gnome-panel/CurrentCostMQTT.py b/misc/currentcost/gnome-panel/CurrentCostMQTT.py index dcc59958dc..1091f19499 100755 --- a/misc/currentcost/gnome-panel/CurrentCostMQTT.py +++ b/misc/currentcost/gnome-panel/CurrentCostMQTT.py @@ -1,12 +1,8 @@ #!/usr/bin/env python import gnomeapplet -import gobject import gtk import mosquitto -import os -import platform -import pygtk import sys class CurrentCostMQTT(gnomeapplet.Applet): @@ -30,7 +26,7 @@ def on_change_background(self, applet, type, color, pixmap): def show_menu(self, widget, event): print "menu" - + def __init__(self, applet, iid): self.applet = applet self.label = gtk.Label("0W") @@ -63,7 +59,7 @@ def CurrentCostMQTT_factory(applet, iid): main_window.show_all() gtk.main() sys.exit() - + if __name__ == '__main__': gnomeapplet.bonobo_factory("OAFIID:CurrentCostMQTT_Factory", gnomeapplet.Applet.__gtype__, "MQTT", "0", CurrentCostMQTT_factory) diff --git a/mosquitto.conf b/mosquitto.conf index 4d9cdef215..d06a7ccb0d 100644 --- a/mosquitto.conf +++ b/mosquitto.conf @@ -275,6 +275,8 @@ # this case, mosquitto will attempt to bind the listener to that # address and so restrict access to the associated network and # interface. By default, mosquitto will listen on all interfaces. +# Note that for a websockets listener it is not possible to bind to a host +# name. # listener port-number [ip address/host name] #listener diff --git a/readme.md b/readme.md index 20e20d0d79..34874fe9f1 100644 --- a/readme.md +++ b/readme.md @@ -1,5 +1,5 @@ -Mosquitto -========= +Eclipse Mosquitto +================= Mosquitto is an open source implementation of a server for version 3.1 and 3.1.1 of the MQTT protocol. @@ -12,10 +12,13 @@ See the following links for more information on MQTT: Mosquitto project information is available at the following locations: - Main homepage: -- Find existing bugs: -- Submit a bug: -- Source code repository: +- Find existing bugs or submit a new bug: +- Source code repository: There is also a public test server available at Mosquitto was written by Roger Light + +Master: [![Travis Build Status (master)](https://travis-ci.org/eclipse/mosquitto.svg?branch=master)](https://travis-ci.org/eclipse/mosquitto) +Develop: [![Travis Build Status (develop)](https://travis-ci.org/eclipse/mosquitto.svg?branch=develop)](https://travis-ci.org/eclipse/mosquitto) +Fixes: [![Travis Build Status (fixes)](https://travis-ci.org/eclipse/mosquitto.svg?branch=fixes)](https://travis-ci.org/eclipse/mosquitto) diff --git a/set-version.sh b/set-version.sh new file mode 100755 index 0000000000..71631010cd --- /dev/null +++ b/set-version.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +MAJOR=1 +MINOR=4 +REVISION=9 + +sed -i "s/^VERSION=.*/VERSION=${MAJOR}.${MINOR}.${REVISION}/" config.mk + +sed -i "s/^#define LIBMOSQUITTO_MAJOR .*/#define LIBMOSQUITTO_MAJOR ${MAJOR}/" lib/mosquitto.h +sed -i "s/^#define LIBMOSQUITTO_MINOR .*/#define LIBMOSQUITTO_MINOR ${MINOR}/" lib/mosquitto.h +sed -i "s/^#define LIBMOSQUITTO_REVISION .*/#define LIBMOSQUITTO_REVISION ${REVISION}/" lib/mosquitto.h + +sed -i "s/^set (VERSION .*)/set (VERSION ${MAJOR}.${MINOR}.${REVISION})/" CMakeLists.txt + +sed -i "s/^!define VERSION .*/!define VERSION ${MAJOR}.${MINOR}.${REVISION}/" installer/*.nsi + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 876ae1f280..1b447cea85 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -146,13 +146,13 @@ if (UNIX) endif (APPLE) endif (UNIX) -install(TARGETS mosquitto RUNTIME DESTINATION ${SBINDIR} LIBRARY DESTINATION ${LIBDIR}) -install(FILES mosquitto_plugin.h DESTINATION ${INCLUDEDIR}) +install(TARGETS mosquitto RUNTIME DESTINATION "${SBINDIR}" LIBRARY DESTINATION "${LIBDIR}") +install(FILES mosquitto_plugin.h DESTINATION "${INCLUDEDIR}") if (${WITH_TLS} STREQUAL ON) add_executable(mosquitto_passwd mosquitto_passwd.c) - target_link_libraries(mosquitto_passwd ${OPENSSL_LIBRARIES}) - install(TARGETS mosquitto_passwd RUNTIME DESTINATION ${BINDIR} LIBRARY DESTINATION ${LIBDIR}) + target_link_libraries(mosquitto_passwd "${OPENSSL_LIBRARIES}") + install(TARGETS mosquitto_passwd RUNTIME DESTINATION "${BINDIR}" LIBRARY DESTINATION "${LIBDIR}") endif (${WITH_TLS} STREQUAL ON) if (UNIX AND NOT APPLE) diff --git a/src/bridge.c b/src/bridge.c index 4cb92ce5fa..abaa2f98b6 100644 --- a/src/bridge.c +++ b/src/bridge.c @@ -116,7 +116,7 @@ int bridge__connect(struct mosquitto_db *db, struct mosquitto *context) context->state = mosq_cs_new; context->sock = INVALID_SOCKET; context->last_msg_in = mosquitto_time(); - context->last_msg_out = mosquitto_time(); + context->next_msg_out = mosquitto_time() + context->bridge->keepalive; context->keepalive = context->bridge->keepalive; context->clean_session = context->bridge->clean_session; context->in_packet.payload = NULL; diff --git a/src/conf.c b/src/conf.c index 33fc3dcc0e..a31f904b25 100644 --- a/src/conf.c +++ b/src/conf.c @@ -236,6 +236,7 @@ void config__cleanup(struct mosquitto__config *config) mosquitto__free(config->persistence_file); mosquitto__free(config->persistence_filepath); mosquitto__free(config->psk_file); + mosquitto__free(config->pid_file); if(config->listeners){ for(i=0; ilistener_count; i++){ mosquitto__free(config->listeners[i].host); @@ -745,21 +746,7 @@ int config__read_file_core(struct mosquitto__config *config, bool reload, const return MOSQ_ERR_INVAL; } #endif - token = strtok_r(NULL, " ", &saveptr); - if(token){ - if(cur_bridge->tls_cafile){ - log__printf(NULL, MOSQ_LOG_ERR, "Error: Duplicate bridge_cafile value in bridge configuration."); - return MOSQ_ERR_INVAL; - } - cur_bridge->tls_cafile = mosquitto__strdup(token); - if(!cur_bridge->tls_cafile){ - log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); - return MOSQ_ERR_NOMEM; - } - }else{ - log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty bridge_cafile value in configuration."); - return MOSQ_ERR_INVAL; - } + if(conf__parse_string(&token, "bridge_cafile", &cur_bridge->tls_cafile, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge and/or TLS support not available."); #endif @@ -776,21 +763,7 @@ int config__read_file_core(struct mosquitto__config *config, bool reload, const return MOSQ_ERR_INVAL; } #endif - token = strtok_r(NULL, " ", &saveptr); - if(token){ - if(cur_bridge->tls_capath){ - log__printf(NULL, MOSQ_LOG_ERR, "Error: Duplicate bridge_capath value in bridge configuration."); - return MOSQ_ERR_INVAL; - } - cur_bridge->tls_capath = mosquitto__strdup(token); - if(!cur_bridge->tls_capath){ - log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); - return MOSQ_ERR_NOMEM; - } - }else{ - log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty bridge_capath value in configuration."); - return MOSQ_ERR_INVAL; - } + if(conf__parse_string(&token, "bridge_capath", &cur_bridge->tls_capath, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge and/or TLS support not available."); #endif @@ -807,21 +780,7 @@ int config__read_file_core(struct mosquitto__config *config, bool reload, const return MOSQ_ERR_INVAL; } #endif - token = strtok_r(NULL, " ", &saveptr); - if(token){ - if(cur_bridge->tls_certfile){ - log__printf(NULL, MOSQ_LOG_ERR, "Error: Duplicate bridge_certfile value in bridge configuration."); - return MOSQ_ERR_INVAL; - } - cur_bridge->tls_certfile = mosquitto__strdup(token); - if(!cur_bridge->tls_certfile){ - log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); - return MOSQ_ERR_NOMEM; - } - }else{ - log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty bridge_certfile value in configuration."); - return MOSQ_ERR_INVAL; - } + if(conf__parse_string(&token, "bridge_certfile", &cur_bridge->tls_certfile, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge and/or TLS support not available."); #endif @@ -836,21 +795,7 @@ int config__read_file_core(struct mosquitto__config *config, bool reload, const log__printf(NULL, MOSQ_LOG_ERR, "Error: Cannot use both certificate and identity encryption in a single bridge."); return MOSQ_ERR_INVAL; } - token = strtok_r(NULL, " ", &saveptr); - if(token){ - if(cur_bridge->tls_psk_identity){ - log__printf(NULL, MOSQ_LOG_ERR, "Error: Duplicate bridge_identity value in bridge configuration."); - return MOSQ_ERR_INVAL; - } - cur_bridge->tls_psk_identity = mosquitto__strdup(token); - if(!cur_bridge->tls_psk_identity){ - log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); - return MOSQ_ERR_NOMEM; - } - }else{ - log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty bridge_identity value in configuration."); - return MOSQ_ERR_INVAL; - } + if(conf__parse_string(&token, "bridge_identity", &cur_bridge->tls_psk_identity, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge and/or TLS-PSK support not available."); #endif @@ -881,21 +826,7 @@ int config__read_file_core(struct mosquitto__config *config, bool reload, const return MOSQ_ERR_INVAL; } #endif - token = strtok_r(NULL, " ", &saveptr); - if(token){ - if(cur_bridge->tls_keyfile){ - log__printf(NULL, MOSQ_LOG_ERR, "Error: Duplicate bridge_keyfile value in bridge configuration."); - return MOSQ_ERR_INVAL; - } - cur_bridge->tls_keyfile = mosquitto__strdup(token); - if(!cur_bridge->tls_keyfile){ - log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); - return MOSQ_ERR_NOMEM; - } - }else{ - log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty bridge_keyfile value in configuration."); - return MOSQ_ERR_INVAL; - } + if(conf__parse_string(&token, "bridge_keyfile", &cur_bridge->tls_keyfile, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge and/or TLS support not available."); #endif @@ -906,7 +837,7 @@ int config__read_file_core(struct mosquitto__config *config, bool reload, const log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); return MOSQ_ERR_INVAL; } - token = strtok_r(NULL, " ", &saveptr); + token = strtok_r(NULL, "", &saveptr); if(token){ if(!strcmp(token, "mqttv31")){ cur_bridge->protocol_version = mosq_p_mqtt31; @@ -934,21 +865,7 @@ int config__read_file_core(struct mosquitto__config *config, bool reload, const log__printf(NULL, MOSQ_LOG_ERR, "Error: Cannot use both certificate and psk encryption in a single bridge."); return MOSQ_ERR_INVAL; } - token = strtok_r(NULL, " ", &saveptr); - if(token){ - if(cur_bridge->tls_psk){ - log__printf(NULL, MOSQ_LOG_ERR, "Error: Duplicate bridge_psk value in bridge configuration."); - return MOSQ_ERR_INVAL; - } - cur_bridge->tls_psk = mosquitto__strdup(token); - if(!cur_bridge->tls_psk){ - log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); - return MOSQ_ERR_NOMEM; - } - }else{ - log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty bridge_psk value in configuration."); - return MOSQ_ERR_INVAL; - } + if(conf__parse_string(&token, "bridge_psk", &cur_bridge->tls_psk, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge and/or TLS-PSK support not available."); #endif @@ -959,21 +876,7 @@ int config__read_file_core(struct mosquitto__config *config, bool reload, const log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); return MOSQ_ERR_INVAL; } - token = strtok_r(NULL, " ", &saveptr); - if(token){ - if(cur_bridge->tls_version){ - log__printf(NULL, MOSQ_LOG_ERR, "Error: Duplicate bridge_tls_version value in bridge configuration."); - return MOSQ_ERR_INVAL; - } - cur_bridge->tls_version = mosquitto__strdup(token); - if(!cur_bridge->tls_version){ - log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); - return MOSQ_ERR_NOMEM; - } - }else{ - log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty bridge_tls_version value in configuration."); - return MOSQ_ERR_INVAL; - } + if(conf__parse_string(&token, "bridge_tls_version", &cur_bridge->tls_version, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge and/or TLS support not available."); #endif @@ -1020,21 +923,7 @@ int config__read_file_core(struct mosquitto__config *config, bool reload, const log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); return MOSQ_ERR_INVAL; } - token = strtok_r(NULL, " ", &saveptr); - if(token){ - if(cur_bridge->remote_clientid){ - log__printf(NULL, MOSQ_LOG_ERR, "Error: Duplicate clientid value in bridge configuration."); - return MOSQ_ERR_INVAL; - } - cur_bridge->remote_clientid = mosquitto__strdup(token); - if(!cur_bridge->remote_clientid){ - log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); - return MOSQ_ERR_NOMEM; - } - }else{ - log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty clientid value in configuration."); - return MOSQ_ERR_INVAL; - } + if(conf__parse_string(&token, "bridge remote clientid", &cur_bridge->remote_clientid, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge support not available."); #endif @@ -1123,7 +1012,7 @@ int config__read_file_core(struct mosquitto__config *config, bool reload, const }else if(!strcmp(token, "include_dir")){ if(level == 0){ /* Only process include_dir from the main config file. */ - token = strtok_r(NULL, " ", &saveptr); + token = strtok_r(NULL, "", &saveptr); if(!token){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty include_dir value in configuration."); return 1; @@ -1230,7 +1119,7 @@ int config__read_file_core(struct mosquitto__config *config, bool reload, const memset(cur_listener, 0, sizeof(struct mosquitto__listener)); cur_listener->protocol = mp_mqtt; cur_listener->port = tmp_int; - token = strtok_r(NULL, " ", &saveptr); + token = strtok_r(NULL, "", &saveptr); if(token){ cur_listener->host = mosquitto__strdup(token); }else{ @@ -1247,20 +1136,7 @@ int config__read_file_core(struct mosquitto__config *config, bool reload, const log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); return MOSQ_ERR_INVAL; } - token = strtok_r(NULL, " ", &saveptr); - if(token){ - if(cur_bridge->local_clientid){ - log__printf(NULL, MOSQ_LOG_ERR, "Error: Duplicate local_clientid value in bridge configuration."); - return MOSQ_ERR_INVAL; - } - cur_bridge->local_clientid = mosquitto__strdup(token); - if(!cur_bridge->local_clientid){ - log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); - return MOSQ_ERR_NOMEM; - } - }else{ - cur_bridge->local_clientid = NULL; - } + if(conf__parse_string(&token, "bridge local clientd", &cur_bridge->local_clientid, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge support not available."); #endif @@ -1271,20 +1147,7 @@ int config__read_file_core(struct mosquitto__config *config, bool reload, const log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); return MOSQ_ERR_INVAL; } - token = strtok_r(NULL, " ", &saveptr); - if(token){ - if(cur_bridge->local_password){ - log__printf(NULL, MOSQ_LOG_ERR, "Error: Duplicate local_password value in bridge configuration."); - return MOSQ_ERR_INVAL; - } - cur_bridge->local_password = mosquitto__strdup(token); - if(!cur_bridge->local_password){ - log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); - return MOSQ_ERR_NOMEM; - } - }else{ - cur_bridge->local_password = NULL; - } + if(conf__parse_string(&token, "bridge local_password", &cur_bridge->local_password, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge support not available."); #endif @@ -1295,20 +1158,7 @@ int config__read_file_core(struct mosquitto__config *config, bool reload, const log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); return MOSQ_ERR_INVAL; } - token = strtok_r(NULL, " ", &saveptr); - if(token){ - if(cur_bridge->local_username){ - log__printf(NULL, MOSQ_LOG_ERR, "Error: Duplicate local_username value in bridge configuration."); - return MOSQ_ERR_INVAL; - } - cur_bridge->local_username = mosquitto__strdup(token); - if(!cur_bridge->local_username){ - log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); - return MOSQ_ERR_NOMEM; - } - }else{ - cur_bridge->local_username = NULL; - } + if(conf__parse_string(&token, "bridge local_username", &cur_bridge->local_username, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge support not available."); #endif @@ -1506,21 +1356,7 @@ int config__read_file_core(struct mosquitto__config *config, bool reload, const log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); return MOSQ_ERR_INVAL; } - token = strtok_r(NULL, " ", &saveptr); - if(token){ - if(cur_bridge->remote_password){ - log__printf(NULL, MOSQ_LOG_ERR, "Error: Duplicate password value in bridge configuration."); - return MOSQ_ERR_INVAL; - } - cur_bridge->remote_password = mosquitto__strdup(token); - if(!cur_bridge->remote_password){ - log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); - return MOSQ_ERR_NOMEM; - } - }else{ - log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty password value in configuration."); - return MOSQ_ERR_INVAL; - } + if(conf__parse_string(&token, "bridge remote_password", &cur_bridge->remote_password, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge support not available."); #endif diff --git a/src/context.c b/src/context.c index 82afa9176f..e4f6e6b742 100644 --- a/src/context.c +++ b/src/context.c @@ -37,7 +37,7 @@ struct mosquitto *context__init(struct mosquitto_db *db, mosq_sock_t sock) context->state = mosq_cs_new; context->sock = sock; context->last_msg_in = mosquitto_time(); - context->last_msg_out = mosquitto_time(); + context->next_msg_out = mosquitto_time() + 60; context->keepalive = 60; /* Default to 60s */ context->clean_session = true; context->disconnect_t = 0; diff --git a/src/database.c b/src/database.c index 981ea93bc2..71952030a7 100644 --- a/src/database.c +++ b/src/database.c @@ -490,7 +490,7 @@ int db__message_update(struct mosquitto *context, uint16_t mid, enum mosquitto_m } tail = tail->next; } - return 1; + return MOSQ_ERR_NOT_FOUND; } int db__messages_delete(struct mosquitto_db *db, struct mosquitto *context) diff --git a/src/handle_connect.c b/src/handle_connect.c index 43d675e712..b27bfc3edb 100644 --- a/src/handle_connect.c +++ b/src/handle_connect.c @@ -113,6 +113,7 @@ int handle__connect(struct mosquitto_db *db, struct mosquitto *context) uint8_t connect_ack = 0; char *client_id = NULL; char *will_payload = NULL, *will_topic = NULL; + char *will_topic_mount; uint16_t will_payloadlen; struct mosquitto_message *will_struct = NULL; uint8_t will, will_retain, will_qos, clean_session; @@ -265,6 +266,21 @@ int handle__connect(struct mosquitto_db *db, struct mosquitto *context) rc = 1; goto handle_connect_error; } + + if(context->listener && context->listener->mount_point){ + slen = strlen(context->listener->mount_point) + strlen(will_topic) + 1; + will_topic_mount = mosquitto__malloc(slen+1); + if(!will_topic_mount){ + rc = MOSQ_ERR_NOMEM; + goto handle_connect_error; + } + snprintf(will_topic_mount, slen, "%s%s", context->listener->mount_point, will_topic); + will_topic_mount[slen] = '\0'; + + mosquitto__free(will_topic); + will_topic = will_topic_mount; + } + if(mosquitto_pub_topic_check(will_topic)){ rc = 1; goto handle_connect_error; @@ -485,6 +501,7 @@ int handle__connect(struct mosquitto_db *db, struct mosquitto *context) found_context->subs = NULL; context->sub_count = found_context->sub_count; found_context->sub_count = 0; + context->last_mid = found_context->last_mid; for(i=0; isub_count; i++){ if(context->subs[i]){ diff --git a/src/loop.c b/src/loop.c index 72150982b7..e4634e2d4f 100644 --- a/src/loop.c +++ b/src/loop.c @@ -55,7 +55,6 @@ extern bool flag_db_backup; extern bool flag_tree_print; extern int run; -static void loop_handle_errors(struct mosquitto_db *db, struct pollfd *pollfds); static void loop_handle_reads_writes(struct mosquitto_db *db, struct pollfd *pollfds); #ifdef WITH_WEBSOCKETS @@ -119,6 +118,7 @@ int mosquitto_main_loop(struct mosquitto_db *db, mosq_sock_t *listensock, int li #ifndef WIN32 sigemptyset(&sigblock); sigaddset(&sigblock, SIGINT); + sigaddset(&sigblock, SIGTERM); #endif if(db->config->persistent_client_expiration > 0){ @@ -312,7 +312,7 @@ int mosquitto_main_loop(struct mosquitto_db *db, mosq_sock_t *listensock, int li fdcount = WSAPoll(pollfds, pollfd_index, 100); #endif if(fdcount == -1){ - loop_handle_errors(db, pollfds); + log__printf(NULL, MOSQ_LOG_ERR, "Error in poll: %s.", strerror(errno)); }else{ loop_handle_reads_writes(db, pollfds); @@ -427,23 +427,6 @@ void do_disconnect(struct mosquitto_db *db, struct mosquitto *context) } } -/* Error ocurred, probably an fd has been closed. - * Loop through and check them all. - */ -static void loop_handle_errors(struct mosquitto_db *db, struct pollfd *pollfds) -{ - struct mosquitto *context, *ctxt_tmp; - - HASH_ITER(hh_sock, db->contexts_by_sock, context, ctxt_tmp){ - if(context->pollfd_index < 0){ - continue; - } - - if(pollfds[context->pollfd_index].revents & (POLLERR | POLLNVAL)){ - do_disconnect(db, context); - } - } -} static void loop_handle_reads_writes(struct mosquitto_db *db, struct pollfd *pollfds) { @@ -457,6 +440,10 @@ static void loop_handle_reads_writes(struct mosquitto_db *db, struct pollfd *pol } assert(pollfds[context->pollfd_index].fd == context->sock); + if(pollfds[context->pollfd_index].revents & (POLLERR | POLLNVAL | POLLHUP)){ + do_disconnect(db, context); + continue; + } #ifdef WITH_TLS if(pollfds[context->pollfd_index].revents & POLLOUT || context->want_write || diff --git a/src/mosquitto.c b/src/mosquitto.c index 17d49d1a99..a324e4ff64 100644 --- a/src/mosquitto.c +++ b/src/mosquitto.c @@ -21,6 +21,7 @@ and the Eclipse Distribution License is available at # define _BSD_SOURCE # include # include +# include #endif #ifndef WIN32 @@ -178,6 +179,35 @@ void handle_sigusr1(int signal) #endif } +void mosquitto__daemonise(void) +{ +#ifndef WIN32 + char err[256]; + pid_t pid; + + pid = fork(); + if(pid < 0){ + strerror_r(errno, err, 256); + log__printf(NULL, MOSQ_LOG_ERR, "Error in fork: %s", err); + exit(1); + } + if(pid > 0){ + exit(0); + } + if(setsid() < 0){ + strerror_r(errno, err, 256); + log__printf(NULL, MOSQ_LOG_ERR, "Error in setsid: %s", err); + exit(1); + } + + assert(freopen("/dev/null", "r", stdin)); + assert(freopen("/dev/null", "w", stdout)); + assert(freopen("/dev/null", "w", stderr)); +#else + log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Can't start in daemon mode in Windows."); +#endif +} + /* Signal handler for SIGUSR2 - vacuum the db. */ void handle_sigusr2(int signal) { @@ -200,7 +230,6 @@ int main(int argc, char *argv[]) #ifdef WIN32 SYSTEMTIME st; #else - char err[256]; struct timeval tv; #endif struct mosquitto *ctxt, *ctxt_tmp; @@ -239,20 +268,7 @@ int main(int argc, char *argv[]) int_db.config = &config; if(config.daemon){ -#ifndef WIN32 - switch(fork()){ - case 0: - break; - case -1: - strerror_r(errno, err, 256); - log__printf(NULL, MOSQ_LOG_ERR, "Error in fork: %s", err); - return 1; - default: - return MOSQ_ERR_SUCCESS; - } -#else - log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Can't start in daemon mode in Windows."); -#endif + mosquitto__daemonise(); } if(config.daemon && config.pid_file){ diff --git a/src/mosquitto_passwd.c b/src/mosquitto_passwd.c index e504f1fe2c..4becd6f1ba 100644 --- a/src/mosquitto_passwd.c +++ b/src/mosquitto_passwd.c @@ -369,6 +369,7 @@ int main(int argc, char *argv[]) batch_mode = true; }else{ fprintf(stderr, "Error: Unknown option '%s'\n", argv[1]); + return 1; } password_file_tmp = argv[2]; username = argv[3]; diff --git a/src/net.c b/src/net.c index 3c8feac6ec..a9a813987e 100644 --- a/src/net.c +++ b/src/net.c @@ -61,6 +61,26 @@ static int tls_ex_index_listener = -1; #include "sys_tree.h" + +static void net__print_error(int log, const char *format_str) +{ +#ifdef WIN32 + char *buf; + + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, WSAGetLastError(), LANG_NEUTRAL, &buf, 0, NULL); + + log__printf(NULL, log, format_str, buf); + LocalFree(buf); +#else + char buf[256]; + + strerror_r(errno, buf, 256); + log__printf(NULL, log, format_str, buf); +#endif +} + + int net__socket_accept(struct mosquitto_db *db, mosq_sock_t listensock) { int i; @@ -331,7 +351,6 @@ int net__socket_listen(struct mosquitto__listener *listener) X509_STORE *store; X509_LOOKUP *lookup; #endif - char buf[256]; if(!listener) return MOSQ_ERR_INVAL; @@ -357,8 +376,7 @@ int net__socket_listen(struct mosquitto__listener *listener) sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if(sock == INVALID_SOCKET){ - strerror_r(errno, buf, 256); - log__printf(NULL, MOSQ_LOG_WARNING, "Warning: %s", buf); + net__print_error(MOSQ_LOG_WARNING, "Warning: %s"); continue; } listener->sock_count++; @@ -381,21 +399,13 @@ int net__socket_listen(struct mosquitto__listener *listener) } if(bind(sock, rp->ai_addr, rp->ai_addrlen) == -1){ -#ifdef WIN32 - errno = WSAGetLastError(); -#endif - strerror_r(errno, buf, 256); - log__printf(NULL, MOSQ_LOG_ERR, "Error: %s", buf); + net__print_error(MOSQ_LOG_ERR, "Error: %s"); COMPAT_CLOSE(sock); return 1; } if(listen(sock, 100) == -1){ -#ifdef WIN32 - errno = WSAGetLastError(); -#endif - strerror_r(errno, buf, 256); - log__printf(NULL, MOSQ_LOG_ERR, "Error: %s", buf); + net__print_error(MOSQ_LOG_ERR, "Error: %s"); COMPAT_CLOSE(sock); return 1; } diff --git a/src/persist.c b/src/persist.c index 77b00236f8..6a2460b476 100644 --- a/src/persist.c +++ b/src/persist.c @@ -129,7 +129,7 @@ static int persist__message_store_write(struct mosquitto_db *db, FILE *db_fptr) uint32_t length; dbid_t i64temp; uint32_t i32temp; - uint16_t i16temp, slen; + uint16_t i16temp, slen, tlen; uint8_t i8temp; struct mosquitto_msg_store *stored; bool force_no_retain; @@ -139,7 +139,12 @@ static int persist__message_store_write(struct mosquitto_db *db, FILE *db_fptr) stored = db->msg_store; while(stored){ - if(!strncmp(stored->topic, "$SYS", 4)){ + if(stored->topic && !strncmp(stored->topic, "$SYS", 4)){ + if(stored->ref_count == 1 && stored->dest_id_count == 0){ + /* $SYS messages that are only retained shouldn't be persisted. */ + stored = stored->next; + continue; + } /* Don't save $SYS messages as retained otherwise they can give * misleading information when reloaded. They should still be saved * because a disconnected durable client may have them in their @@ -148,9 +153,14 @@ static int persist__message_store_write(struct mosquitto_db *db, FILE *db_fptr) }else{ force_no_retain = false; } + if(stored->topic){ + tlen = strlen(stored->topic); + }else{ + tlen = 0; + } length = htonl(sizeof(dbid_t) + 2+strlen(stored->source_id) + sizeof(uint16_t) + sizeof(uint16_t) + - 2+strlen(stored->topic) + sizeof(uint32_t) + + 2+tlen + sizeof(uint32_t) + stored->payloadlen + sizeof(uint8_t) + sizeof(uint8_t)); i16temp = htons(DB_CHUNK_MSG_STORE); @@ -173,10 +183,11 @@ static int persist__message_store_write(struct mosquitto_db *db, FILE *db_fptr) i16temp = htons(stored->mid); write_e(db_fptr, &i16temp, sizeof(uint16_t)); - slen = strlen(stored->topic); - i16temp = htons(slen); + i16temp = htons(tlen); write_e(db_fptr, &i16temp, sizeof(uint16_t)); - write_e(db_fptr, stored->topic, slen); + if(tlen){ + write_e(db_fptr, stored->topic, tlen); + } i8temp = (uint8_t )stored->qos; write_e(db_fptr, &i8temp, sizeof(uint8_t)); @@ -244,7 +255,7 @@ static int persist__client_write(struct mosquitto_db *db, FILE *db_fptr) return 1; } -static int persist__subs_retain_write(struct mosquitto_db *db, FILE *db_fptr, struct mosquitto__subhier *node, const char *topic) +static int persist__subs_retain_write(struct mosquitto_db *db, FILE *db_fptr, struct mosquitto__subhier *node, const char *topic, int level) { struct mosquitto__subhier *subhier; struct mosquitto__subleaf *sub; @@ -257,7 +268,7 @@ static int persist__subs_retain_write(struct mosquitto_db *db, FILE *db_fptr, st slen = strlen(topic) + node->topic_len + 2; thistopic = mosquitto__malloc(sizeof(char)*slen); if(!thistopic) return MOSQ_ERR_NOMEM; - if(strlen(topic)){ + if(level > 1 || strlen(topic)){ snprintf(thistopic, slen, "%s/%s", topic, UHPA_ACCESS_TOPIC(node)); }else{ snprintf(thistopic, slen, "%s", UHPA_ACCESS_TOPIC(node)); @@ -302,7 +313,7 @@ static int persist__subs_retain_write(struct mosquitto_db *db, FILE *db_fptr, st subhier = node->children; while(subhier){ - persist__subs_retain_write(db, db_fptr, subhier, thistopic); + persist__subs_retain_write(db, db_fptr, subhier, thistopic, level+1); subhier = subhier->next; } mosquitto__free(thistopic); @@ -318,7 +329,9 @@ static int persist__subs_retain_write_all(struct mosquitto_db *db, FILE *db_fptr subhier = db->subs.children; while(subhier){ - persist__subs_retain_write(db, db_fptr, subhier, ""); + if(subhier->children){ + persist__subs_retain_write(db, db_fptr, subhier->children, "", 0); + } subhier = subhier->next; } @@ -620,11 +633,7 @@ static int persist__msg_store_chunk_restore(struct mosquitto_db *db, FILE *db_fp read_e(db_fptr, topic, slen); topic[slen] = '\0'; }else{ - mosquitto__free(load); - log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid msg_store chunk when restoring persistent database."); - fclose(db_fptr); - mosquitto__free(source_id); - return 1; + topic = NULL; } read_e(db_fptr, &qos, sizeof(uint8_t)); read_e(db_fptr, &retain, sizeof(uint8_t)); diff --git a/src/subs.c b/src/subs.c index 37192d9634..dc9fbdc685 100644 --- a/src/subs.c +++ b/src/subs.c @@ -468,12 +468,12 @@ int sub__add(struct mosquitto_db *db, struct mosquitto *context, const char *sub child->subs = NULL; child->children = NULL; child->retained = NULL; - if(db->subs.children){ - child->next = db->subs.children; + if(root->children){ + child->next = root->children; }else{ child->next = NULL; } - db->subs.children = child; + root->children = child; rc = sub__add_recurse(db, context, qos, child, tokens); } diff --git a/src/sys_tree.c b/src/sys_tree.c index 31a0b87bdb..b9d3946fe9 100644 --- a/src/sys_tree.c +++ b/src/sys_tree.c @@ -44,7 +44,7 @@ static void sys__update_clients(struct mosquitto_db *db, char *buf) { static unsigned int client_count = -1; static int clients_expired = -1; - static unsigned int client_max = -1; + static unsigned int client_max = 0; static unsigned int disconnected_count = -1; static unsigned int connected_count = -1; diff --git a/src/websockets.c b/src/websockets.c index f812f0fe0c..7fbce688ec 100644 --- a/src/websockets.c +++ b/src/websockets.c @@ -200,6 +200,8 @@ static int callback_mqtt(struct libwebsocket_context *context, return -1; } + mqtt3_db_message_write(db, mosq); + if(mosq->out_packet && !mosq->current_out_packet){ mosq->current_out_packet = mosq->out_packet; mosq->out_packet = mosq->out_packet->next; @@ -243,7 +245,7 @@ static int callback_mqtt(struct libwebsocket_context *context, packet__cleanup(packet); mosquitto__free(packet); - mosq->last_msg_out = mosquitto_time(); + mosq->next_msg_out = mosquitto_time() + mosq->keepalive; if(mosq->current_out_packet){ libwebsocket_callback_on_writable(mosq->ws_context, mosq->wsi); @@ -553,6 +555,7 @@ struct libwebsocket_context *mosq_websockets_init(struct mosquitto__listener *li } memset(&info, 0, sizeof(info)); + info.iface = listener->host; info.port = listener->port; info.protocols = p; info.gid = -1; @@ -571,6 +574,7 @@ struct libwebsocket_context *mosq_websockets_init(struct mosquitto__listener *li user = mosquitto__calloc(1, sizeof(struct libws_mqtt_hack)); if(!user){ mosquitto__free(p); + log__printf(NULL, MOSQ_LOG_ERR, "Out of memory."); return NULL; } @@ -583,6 +587,7 @@ struct libwebsocket_context *mosq_websockets_init(struct mosquitto__listener *li if(!user->http_dir){ mosquitto__free(user); mosquitto__free(p); + log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to open http dir \"%s\".", user->http_dir); return NULL; } } diff --git a/test/broker/01-connect-anon-denied.py b/test/broker/01-connect-anon-denied.py index 3b2820c739..b3d4036653 100755 --- a/test/broker/01-connect-anon-denied.py +++ b/test/broker/01-connect-anon-denied.py @@ -2,10 +2,6 @@ # Test whether an anonymous connection is correctly denied. -import subprocess -import socket -import time - 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],".."))) diff --git a/test/broker/01-connect-invalid-id-0-311.py b/test/broker/01-connect-invalid-id-0-311.py index 5b9f5136c2..f5deced4b0 100755 --- a/test/broker/01-connect-invalid-id-0-311.py +++ b/test/broker/01-connect-invalid-id-0-311.py @@ -2,10 +2,6 @@ # Test whether a CONNECT with a zero length client id results in the correct CONNACK packet. -import subprocess -import socket -import time - 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],".."))) diff --git a/test/broker/01-connect-invalid-id-0.py b/test/broker/01-connect-invalid-id-0.py index d0031ddee4..94e0a48b7f 100755 --- a/test/broker/01-connect-invalid-id-0.py +++ b/test/broker/01-connect-invalid-id-0.py @@ -2,10 +2,6 @@ # Test whether a CONNECT with a zero length client id results in the correct CONNACK packet. -import subprocess -import socket -import time - 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],".."))) diff --git a/test/broker/01-connect-invalid-id-missing.py b/test/broker/01-connect-invalid-id-missing.py index 3066776c33..b42888fb8e 100755 --- a/test/broker/01-connect-invalid-id-missing.py +++ b/test/broker/01-connect-invalid-id-missing.py @@ -2,10 +2,6 @@ # Test whether a CONNECT with a zero length client id results in the correct CONNACK packet. -import subprocess -import socket -import time - 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],".."))) diff --git a/test/broker/01-connect-invalid-protonum.py b/test/broker/01-connect-invalid-protonum.py index ec9f826b25..61281641c5 100755 --- a/test/broker/01-connect-invalid-protonum.py +++ b/test/broker/01-connect-invalid-protonum.py @@ -2,10 +2,6 @@ # Test whether a CONNECT with an invalid protocol number results in the correct CONNACK packet. -import subprocess -import socket -import time - 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],".."))) diff --git a/test/broker/01-connect-success.py b/test/broker/01-connect-success.py index 45c26a8702..e2311885f0 100755 --- a/test/broker/01-connect-success.py +++ b/test/broker/01-connect-success.py @@ -3,11 +3,6 @@ # Test whether a valid CONNECT results in the correct CONNACK packet. import inspect, os, sys -import os -import subprocess -import socket -import sys -import time # 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],".."))) diff --git a/test/broker/01-connect-uname-no-password-denied.py b/test/broker/01-connect-uname-no-password-denied.py index 85ff082198..3dbd570019 100755 --- a/test/broker/01-connect-uname-no-password-denied.py +++ b/test/broker/01-connect-uname-no-password-denied.py @@ -3,10 +3,6 @@ # Test whether a connection is denied if it provides just a username when it # needs a username and password. -import subprocess -import socket -import time - 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],".."))) diff --git a/test/broker/01-connect-uname-password-denied.py b/test/broker/01-connect-uname-password-denied.py index b3052a1dd0..4cb7a3a559 100755 --- a/test/broker/01-connect-uname-password-denied.py +++ b/test/broker/01-connect-uname-password-denied.py @@ -3,10 +3,6 @@ # Test whether a connection is denied if it provides a correct username but # incorrect password. -import subprocess -import socket -import time - 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],".."))) diff --git a/test/broker/01-connect-uname-password-success-no-tls.py b/test/broker/01-connect-uname-password-success-no-tls.py index 19c509b5ff..5421f70071 100755 --- a/test/broker/01-connect-uname-password-success-no-tls.py +++ b/test/broker/01-connect-uname-password-success-no-tls.py @@ -3,10 +3,6 @@ # Test whether a connection is denied if it provides a correct username but # incorrect password. -import subprocess -import socket -import time - 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],".."))) diff --git a/test/broker/01-connect-uname-password-success.py b/test/broker/01-connect-uname-password-success.py index 19c509b5ff..5421f70071 100755 --- a/test/broker/01-connect-uname-password-success.py +++ b/test/broker/01-connect-uname-password-success.py @@ -3,10 +3,6 @@ # Test whether a connection is denied if it provides a correct username but # incorrect password. -import subprocess -import socket -import time - 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],".."))) diff --git a/test/broker/02-subpub-qos0.py b/test/broker/02-subpub-qos0.py index 0a65d774a2..295b01731b 100755 --- a/test/broker/02-subpub-qos0.py +++ b/test/broker/02-subpub-qos0.py @@ -2,10 +2,6 @@ # Test whether a client subscribed to a topic receives its own message sent to that topic. -import subprocess -import socket -import time - 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],".."))) diff --git a/test/broker/02-subpub-qos1.py b/test/broker/02-subpub-qos1.py index c6ff766897..f321151e01 100755 --- a/test/broker/02-subpub-qos1.py +++ b/test/broker/02-subpub-qos1.py @@ -2,10 +2,6 @@ # Test whether a client subscribed to a topic receives its own message sent to that topic. -import subprocess -import socket -import time - 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],".."))) diff --git a/test/broker/02-subpub-qos2.py b/test/broker/02-subpub-qos2.py index df7d374920..6d32a8c4cf 100755 --- a/test/broker/02-subpub-qos2.py +++ b/test/broker/02-subpub-qos2.py @@ -2,10 +2,6 @@ # Test whether a client subscribed to a topic receives its own message sent to that topic. -import subprocess -import socket -import time - 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],".."))) diff --git a/test/broker/02-subscribe-qos0.py b/test/broker/02-subscribe-qos0.py index 370f67a59b..22f1904fb6 100755 --- a/test/broker/02-subscribe-qos0.py +++ b/test/broker/02-subscribe-qos0.py @@ -2,10 +2,7 @@ # Test whether a SUBSCRIBE to a topic with QoS 0 results in the correct SUBACK packet. -import subprocess -import socket import time - 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],".."))) diff --git a/test/broker/02-subscribe-qos1.py b/test/broker/02-subscribe-qos1.py index bba2dec005..ca150d7321 100755 --- a/test/broker/02-subscribe-qos1.py +++ b/test/broker/02-subscribe-qos1.py @@ -2,10 +2,6 @@ # Test whether a SUBSCRIBE to a topic with QoS 1 results in the correct SUBACK packet. -import subprocess -import socket -import time - 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],".."))) diff --git a/test/broker/02-subscribe-qos2.py b/test/broker/02-subscribe-qos2.py index e56daac5e9..5f2ebdc3dd 100755 --- a/test/broker/02-subscribe-qos2.py +++ b/test/broker/02-subscribe-qos2.py @@ -2,10 +2,6 @@ # Test whether a SUBSCRIBE to a topic with QoS 2 results in the correct SUBACK packet. -import subprocess -import socket -import time - 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],".."))) diff --git a/test/broker/02-unsubscribe-qos0.py b/test/broker/02-unsubscribe-qos0.py index b222129cb0..fbfec67105 100755 --- a/test/broker/02-unsubscribe-qos0.py +++ b/test/broker/02-unsubscribe-qos0.py @@ -3,10 +3,6 @@ # Test whether a UNSUBSCRIBE to a topic with QoS 0 results in the correct UNSUBACK packet. # This doesn't assume a subscription exists. -import subprocess -import socket -import time - 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],".."))) diff --git a/test/broker/02-unsubscribe-qos1.py b/test/broker/02-unsubscribe-qos1.py index db18cfda1a..2bbf31ab14 100755 --- a/test/broker/02-unsubscribe-qos1.py +++ b/test/broker/02-unsubscribe-qos1.py @@ -2,10 +2,6 @@ # Test whether a SUBSCRIBE to a topic with QoS 1 results in the correct SUBACK packet. -import subprocess -import socket -import time - 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],".."))) diff --git a/test/broker/02-unsubscribe-qos2.py b/test/broker/02-unsubscribe-qos2.py index c3b2fd853d..3333ed6a79 100755 --- a/test/broker/02-unsubscribe-qos2.py +++ b/test/broker/02-unsubscribe-qos2.py @@ -2,10 +2,6 @@ # Test whether a SUBSCRIBE to a topic with QoS 2 results in the correct SUBACK packet. -import subprocess -import socket -import time - 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],".."))) diff --git a/test/broker/03-pattern-matching-helper.py b/test/broker/03-pattern-matching-helper.py index b247a34fdf..4db430a21b 100755 --- a/test/broker/03-pattern-matching-helper.py +++ b/test/broker/03-pattern-matching-helper.py @@ -1,9 +1,5 @@ #!/usr/bin/env python -import subprocess -import socket -import time - 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],".."))) @@ -23,6 +19,6 @@ sock.send(publish_packet) rc = 0 sock.close() - + exit(rc) diff --git a/test/broker/03-pattern-matching.py b/test/broker/03-pattern-matching.py index a5d38aba87..4e9a46774a 100755 --- a/test/broker/03-pattern-matching.py +++ b/test/broker/03-pattern-matching.py @@ -1,7 +1,6 @@ #!/usr/bin/env python import subprocess -import socket import time import inspect, os, sys diff --git a/test/broker/03-publish-b2c-disconnect-qos1-helper.py b/test/broker/03-publish-b2c-disconnect-qos1-helper.py index 8c322297aa..3ad3ebaa0e 100755 --- a/test/broker/03-publish-b2c-disconnect-qos1-helper.py +++ b/test/broker/03-publish-b2c-disconnect-qos1-helper.py @@ -1,9 +1,5 @@ #!/usr/bin/env python -import subprocess -import socket -import time - 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],".."))) diff --git a/test/broker/03-publish-b2c-disconnect-qos1.py b/test/broker/03-publish-b2c-disconnect-qos1.py index 590b637e09..b8db6b68d7 100755 --- a/test/broker/03-publish-b2c-disconnect-qos1.py +++ b/test/broker/03-publish-b2c-disconnect-qos1.py @@ -2,8 +2,6 @@ import subprocess import socket -import time -from os import environ import inspect, os, sys # From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder diff --git a/test/broker/03-publish-b2c-disconnect-qos2-helper.py b/test/broker/03-publish-b2c-disconnect-qos2-helper.py index 23ca09131b..afc8cf8856 100755 --- a/test/broker/03-publish-b2c-disconnect-qos2-helper.py +++ b/test/broker/03-publish-b2c-disconnect-qos2-helper.py @@ -2,10 +2,6 @@ # Test whether a PUBLISH to a topic with QoS 2 results in the correct packet flow. -import subprocess -import socket -import time - 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],".."))) diff --git a/test/broker/03-publish-b2c-disconnect-qos2.py b/test/broker/03-publish-b2c-disconnect-qos2.py index ffe306228d..22958f9c21 100755 --- a/test/broker/03-publish-b2c-disconnect-qos2.py +++ b/test/broker/03-publish-b2c-disconnect-qos2.py @@ -1,9 +1,6 @@ #!/usr/bin/env python import subprocess -import socket -import time -from os import environ import inspect, os, sys # From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder diff --git a/test/broker/03-publish-b2c-timeout-qos1-helper.py b/test/broker/03-publish-b2c-timeout-qos1-helper.py new file mode 100755 index 0000000000..18c462635b --- /dev/null +++ b/test/broker/03-publish-b2c-timeout-qos1-helper.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python + +# Test whether a PUBLISH to a topic with QoS 2 results in the correct packet flow. + +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 +connect_packet = mosq_test.gen_connect("test-helper", keepalive=keepalive) +connack_packet = mosq_test.gen_connack(rc=0) + +mid = 128 +publish_packet = mosq_test.gen_publish("qos1/timeout/test", qos=1, mid=mid, payload="timeout-message") +puback_packet = mosq_test.gen_puback(mid) + +sock = mosq_test.do_client_connect(connect_packet, connack_packet, connack_error="helper connack") +sock.send(publish_packet) + +if mosq_test.expect_packet(sock, "helper puback", puback_packet): + rc = 0 + +sock.close() + +exit(rc) + diff --git a/test/broker/03-publish-b2c-timeout-qos1.py b/test/broker/03-publish-b2c-timeout-qos1.py new file mode 100755 index 0000000000..7fb87195be --- /dev/null +++ b/test/broker/03-publish-b2c-timeout-qos1.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python + +# Test whether a SUBSCRIBE to a topic with QoS 2 results in the correct SUBACK packet. + +import subprocess + +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 +mid = 3265 +keepalive = 60 +connect_packet = mosq_test.gen_connect("pub-qos1-timeout-test", keepalive=keepalive) +connack_packet = mosq_test.gen_connack(rc=0) + +subscribe_packet = mosq_test.gen_subscribe(mid, "qos1/timeout/test", 1) +suback_packet = mosq_test.gen_suback(mid, 1) + +mid = 1 +publish_packet = mosq_test.gen_publish("qos1/timeout/test", qos=1, mid=mid, payload="timeout-message") +publish_dup_packet = mosq_test.gen_publish("qos1/timeout/test", qos=1, mid=mid, payload="timeout-message", dup=True) +puback_packet = mosq_test.gen_puback(mid) + +broker = mosq_test.start_broker(filename=os.path.basename(__file__)) + +try: + sock = mosq_test.do_client_connect(connect_packet, connack_packet) + sock.send(subscribe_packet) + + if mosq_test.expect_packet(sock, "suback", suback_packet): + pub = subprocess.Popen(['./03-publish-b2c-timeout-qos1-helper.py']) + pub.wait() + # Should have now received a publish command + + if mosq_test.expect_packet(sock, "publish", publish_packet): + # Wait for longer than 5 seconds to get republish with dup set + # This is covered by the 8 second timeout + + if mosq_test.expect_packet(sock, "dup publish", publish_dup_packet): + sock.send(puback_packet) + rc = 0 + + sock.close() +finally: + broker.terminate() + broker.wait() + if rc: + (stdo, stde) = broker.communicate() + print(stde) + +exit(rc) + diff --git a/test/broker/03-publish-b2c-timeout-qos2-helper.py b/test/broker/03-publish-b2c-timeout-qos2-helper.py new file mode 100755 index 0000000000..45a1e237d6 --- /dev/null +++ b/test/broker/03-publish-b2c-timeout-qos2-helper.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python + +# Test whether a PUBLISH to a topic with QoS 2 results in the correct packet flow. + +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 +connect_packet = mosq_test.gen_connect("test-helper", keepalive=keepalive) +connack_packet = mosq_test.gen_connack(rc=0) + +mid = 312 +publish_packet = mosq_test.gen_publish("qos2/timeout/test", qos=2, mid=mid, payload="timeout-message") +pubrec_packet = mosq_test.gen_pubrec(mid) +pubrel_packet = mosq_test.gen_pubrel(mid) +pubcomp_packet = mosq_test.gen_pubcomp(mid) + +sock = mosq_test.do_client_connect(connect_packet, connack_packet, connack_error="helper connack") +sock.send(publish_packet) + +if mosq_test.expect_packet(sock, "helper pubrec", pubrec_packet): + sock.send(pubrel_packet) + + if mosq_test.expect_packet(sock, "helper pubcomp", pubcomp_packet): + rc = 0 + +sock.close() + +exit(rc) + diff --git a/test/broker/03-publish-b2c-timeout-qos2.py b/test/broker/03-publish-b2c-timeout-qos2.py new file mode 100755 index 0000000000..81efafb0a2 --- /dev/null +++ b/test/broker/03-publish-b2c-timeout-qos2.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python + +# Test whether a SUBSCRIBE to a topic with QoS 2 results in the correct SUBACK packet. + +import subprocess + +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 +mid = 3265 +keepalive = 60 +connect_packet = mosq_test.gen_connect("pub-qo2-timeout-test", keepalive=keepalive) +connack_packet = mosq_test.gen_connack(rc=0) + +subscribe_packet = mosq_test.gen_subscribe(mid, "qos2/timeout/test", 2) +suback_packet = mosq_test.gen_suback(mid, 2) + +mid = 1 +publish_packet = mosq_test.gen_publish("qos2/timeout/test", qos=2, mid=mid, payload="timeout-message") +publish_dup_packet = mosq_test.gen_publish("qos2/timeout/test", qos=2, mid=mid, payload="timeout-message", dup=True) +pubrec_packet = mosq_test.gen_pubrec(mid) +pubrel_packet = mosq_test.gen_pubrel(mid) +pubcomp_packet = mosq_test.gen_pubcomp(mid) + +broker = mosq_test.start_broker(filename=os.path.basename(__file__)) + +try: + sock = mosq_test.do_client_connect(connect_packet, connack_packet) + sock.send(subscribe_packet) + + if mosq_test.expect_packet(sock, "suback", suback_packet): + pub = subprocess.Popen(['./03-publish-b2c-timeout-qos2-helper.py']) + pub.wait() + # Should have now received a publish command + + if mosq_test.expect_packet(sock, "publish", publish_packet): + # Wait for longer than 5 seconds to get republish with dup set + # This is covered by the 8 second timeout + + if mosq_test.expect_packet(sock, "dup publish", publish_dup_packet): + sock.send(pubrec_packet) + + if mosq_test.expect_packet(sock, "pubrel", pubrel_packet): + # Wait for longer than 5 seconds to get republish with dup set + # This is covered by the 8 second timeout + + if mosq_test.expect_packet(sock, "dup pubrel", pubrel_packet): + sock.send(pubcomp_packet) + rc = 0 + + sock.close() +finally: + broker.terminate() + broker.wait() + if rc: + (stdo, stde) = broker.communicate() + print(stde) + +exit(rc) + diff --git a/test/broker/03-publish-c2b-disconnect-qos2.py b/test/broker/03-publish-c2b-disconnect-qos2.py index 2215ab1ff0..c1ccec66c7 100755 --- a/test/broker/03-publish-c2b-disconnect-qos2.py +++ b/test/broker/03-publish-c2b-disconnect-qos2.py @@ -1,10 +1,5 @@ #!/usr/bin/env python -import subprocess -import socket -import time -from os import environ - 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],".."))) diff --git a/test/broker/03-publish-c2b-timeout-qos2.py b/test/broker/03-publish-c2b-timeout-qos2.py new file mode 100755 index 0000000000..e6fdecbb29 --- /dev/null +++ b/test/broker/03-publish-c2b-timeout-qos2.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python + +# Test whether a PUBLISH to a topic with QoS 2 results in the correct packet +# flow. This test introduces delays into the flow in order to force the broker +# to send duplicate PUBREC and PUBCOMP messages. + +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 = 600 +connect_packet = mosq_test.gen_connect("pub-qos2-timeout-test", keepalive=keepalive) +connack_packet = mosq_test.gen_connack(rc=0) + +mid = 1926 +publish_packet = mosq_test.gen_publish("pub/qos2/test", qos=2, mid=mid, payload="timeout-message") +pubrec_packet = mosq_test.gen_pubrec(mid) +pubrel_packet = mosq_test.gen_pubrel(mid) +pubcomp_packet = mosq_test.gen_pubcomp(mid) + +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) + + if mosq_test.expect_packet(sock, "pubrec", pubrec_packet): + # Timeout is 8 seconds which means the broker should repeat the PUBREC. + + if mosq_test.expect_packet(sock, "pubrec", pubrec_packet): + sock.send(pubrel_packet) + + if mosq_test.expect_packet(sock, "pubcomp", pubcomp_packet): + rc = 0 + + sock.close() +finally: + broker.terminate() + broker.wait() + if rc: + (stdo, stde) = broker.communicate() + print(stde) + +exit(rc) + diff --git a/test/broker/03-publish-qos1.py b/test/broker/03-publish-qos1.py index 6d183550f8..ce594c0b46 100755 --- a/test/broker/03-publish-qos1.py +++ b/test/broker/03-publish-qos1.py @@ -2,10 +2,6 @@ # Test whether a PUBLISH to a topic with QoS 1 results in the correct PUBACK packet. -import subprocess -import socket -import time - 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],".."))) diff --git a/test/broker/03-publish-qos2.py b/test/broker/03-publish-qos2.py index 81b612d14f..b69e90e6ab 100755 --- a/test/broker/03-publish-qos2.py +++ b/test/broker/03-publish-qos2.py @@ -2,10 +2,6 @@ # Test whether a PUBLISH to a topic with QoS 2 results in the correct packet flow. -import subprocess -import socket -import time - 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],".."))) diff --git a/test/broker/04-retain-qos0-clear.py b/test/broker/04-retain-qos0-clear.py index 08d76ccf87..e2c41c0d78 100755 --- a/test/broker/04-retain-qos0-clear.py +++ b/test/broker/04-retain-qos0-clear.py @@ -3,9 +3,7 @@ # Test whether a retained PUBLISH is cleared when a zero length retained # message is published to a topic. -import subprocess import socket -import time import inspect, os, sys # From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder diff --git a/test/broker/04-retain-qos0-fresh.py b/test/broker/04-retain-qos0-fresh.py index caee6d8063..39f58ae7af 100755 --- a/test/broker/04-retain-qos0-fresh.py +++ b/test/broker/04-retain-qos0-fresh.py @@ -3,10 +3,6 @@ # Test whether a retained PUBLISH to a topic with QoS 0 is sent with # retain=false to an already subscribed client. -import subprocess -import socket -import time - 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],".."))) diff --git a/test/broker/04-retain-qos0-repeated.py b/test/broker/04-retain-qos0-repeated.py index 63df81a43a..74644a6f43 100755 --- a/test/broker/04-retain-qos0-repeated.py +++ b/test/broker/04-retain-qos0-repeated.py @@ -3,10 +3,6 @@ # Test whether a retained PUBLISH to a topic with QoS 0 is actually retained # and delivered when multiple sub/unsub operations are carried out. -import subprocess -import socket -import time - 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],".."))) diff --git a/test/broker/04-retain-qos0.py b/test/broker/04-retain-qos0.py index c5d6c36a50..b0fa51bc53 100755 --- a/test/broker/04-retain-qos0.py +++ b/test/broker/04-retain-qos0.py @@ -2,10 +2,6 @@ # Test whether a retained PUBLISH to a topic with QoS 0 is actually retained. -import subprocess -import socket -import time - 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],".."))) diff --git a/test/broker/04-retain-qos1-qos0.py b/test/broker/04-retain-qos1-qos0.py index 725d3ed99a..59d4c9dd71 100755 --- a/test/broker/04-retain-qos1-qos0.py +++ b/test/broker/04-retain-qos1-qos0.py @@ -4,10 +4,6 @@ # Subscription is made with QoS 0 so the retained message should also have QoS # 0. -import subprocess -import socket -import time - 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],".."))) diff --git a/test/broker/05-clean-session-qos1-helper.py b/test/broker/05-clean-session-qos1-helper.py index 620099c64b..c3b129dbf7 100755 --- a/test/broker/05-clean-session-qos1-helper.py +++ b/test/broker/05-clean-session-qos1-helper.py @@ -2,10 +2,6 @@ # Test whether a clean session client has a QoS 1 message queued for it. -import subprocess -import socket -import time - 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],".."))) @@ -30,6 +26,6 @@ rc = 0 sock.close() - + exit(rc) diff --git a/test/broker/05-clean-session-qos1.py b/test/broker/05-clean-session-qos1.py index ca9eaa8d87..e82e744dc0 100755 --- a/test/broker/05-clean-session-qos1.py +++ b/test/broker/05-clean-session-qos1.py @@ -3,8 +3,6 @@ # Test whether a clean session client has a QoS 1 message queued for it. import subprocess -import socket -import time import inspect, os, sys # From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder diff --git a/test/broker/06-bridge-b2br-disconnect-qos1.py b/test/broker/06-bridge-b2br-disconnect-qos1.py index 011c8cf32d..e48003e39e 100755 --- a/test/broker/06-bridge-b2br-disconnect-qos1.py +++ b/test/broker/06-bridge-b2br-disconnect-qos1.py @@ -2,10 +2,7 @@ # Does a bridge resend a QoS=1 message correctly after a disconnect? -import os -import subprocess import socket -import time import inspect, os, sys # From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder diff --git a/test/broker/06-bridge-b2br-disconnect-qos2.py b/test/broker/06-bridge-b2br-disconnect-qos2.py index adaa5d044b..243eb61628 100755 --- a/test/broker/06-bridge-b2br-disconnect-qos2.py +++ b/test/broker/06-bridge-b2br-disconnect-qos2.py @@ -2,10 +2,7 @@ # Does a bridge resend a QoS=1 message correctly after a disconnect? -import os -import subprocess import socket -import time import inspect, os, sys # From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder diff --git a/test/broker/06-bridge-br2b-disconnect-qos1-helper.py b/test/broker/06-bridge-br2b-disconnect-qos1-helper.py index 06c67450ba..afa93c320f 100755 --- a/test/broker/06-bridge-br2b-disconnect-qos1-helper.py +++ b/test/broker/06-bridge-br2b-disconnect-qos1-helper.py @@ -1,9 +1,5 @@ #!/usr/bin/env python -import subprocess -import socket -import time - 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],".."))) diff --git a/test/broker/06-bridge-br2b-disconnect-qos1.py b/test/broker/06-bridge-br2b-disconnect-qos1.py index 20546a23e3..9047d455af 100755 --- a/test/broker/06-bridge-br2b-disconnect-qos1.py +++ b/test/broker/06-bridge-br2b-disconnect-qos1.py @@ -2,10 +2,8 @@ # Does a bridge resend a QoS=1 message correctly after a disconnect? -import os import subprocess import socket -import time import inspect, os, sys # From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder diff --git a/test/broker/06-bridge-br2b-disconnect-qos2-helper.py b/test/broker/06-bridge-br2b-disconnect-qos2-helper.py index 7515e5a2c8..d7f04b7105 100755 --- a/test/broker/06-bridge-br2b-disconnect-qos2-helper.py +++ b/test/broker/06-bridge-br2b-disconnect-qos2-helper.py @@ -1,9 +1,5 @@ #!/usr/bin/env python -import subprocess -import socket -import time - 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],".."))) diff --git a/test/broker/06-bridge-br2b-disconnect-qos2.py b/test/broker/06-bridge-br2b-disconnect-qos2.py index a0d6a9b23e..acb105af2c 100755 --- a/test/broker/06-bridge-br2b-disconnect-qos2.py +++ b/test/broker/06-bridge-br2b-disconnect-qos2.py @@ -2,10 +2,8 @@ # Does a bridge resend a QoS=1 message correctly after a disconnect? -import os import subprocess import socket -import time import inspect, os, sys # From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder diff --git a/test/broker/06-bridge-fail-persist-resend-qos1.conf b/test/broker/06-bridge-fail-persist-resend-qos1.conf new file mode 100644 index 0000000000..daaf157174 --- /dev/null +++ b/test/broker/06-bridge-fail-persist-resend-qos1.conf @@ -0,0 +1,11 @@ +port 1889 + +connection bridge-u-test +remote_clientid bridge-u-test +address 127.0.0.1:1888 +topic bridge/# out + +cleansession true +notifications false +restart_timeout 5 +try_private false diff --git a/test/broker/06-bridge-fail-persist-resend-qos1.py b/test/broker/06-bridge-fail-persist-resend-qos1.py new file mode 100755 index 0000000000..5b8219c1de --- /dev/null +++ b/test/broker/06-bridge-fail-persist-resend-qos1.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python + +# Test whether a bridge can cope with an unknown PUBACK + +import socket +import subprocess +import time + +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 +connect_packet = mosq_test.gen_connect("bridge-u-test", keepalive=keepalive, proto_ver=3) +connack_packet = mosq_test.gen_connack(rc=0) + +mid = 180 +mid_unknown = 2000 + +publish_packet = mosq_test.gen_publish("bridge/unknown/qos1", qos=1, payload="bridge-message", mid=mid) +puback_packet = mosq_test.gen_puback(mid) +puback_packet_unknown = mosq_test.gen_puback(mid_unknown) + + +unsubscribe_packet = mosq_test.gen_unsubscribe(1, "bridge/#") +unsuback_packet = mosq_test.gen_unsuback(1) + + +if os.environ.get('MOSQ_USE_VALGRIND') is not None: + sleep_time = 5 +else: + sleep_time = 0.5 + + +sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) +sock.settimeout(10) +sock.bind(('', 1888)) +sock.listen(5) + +broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=1889) +time.sleep(sleep_time) + +try: + (conn, address) = sock.accept() + conn.settimeout(20) + + if mosq_test.expect_packet(conn, "connect", connect_packet): + conn.send(connack_packet) + + if mosq_test.expect_packet(conn, "unsubscribe", unsubscribe_packet): + conn.send(unsuback_packet) + + # Send the unexpected puback packet + conn.send(puback_packet_unknown) + + # Send a legitimate publish packet to verify everything is still ok + conn.send(publish_packet) + + if mosq_test.expect_packet(conn, "puback", puback_packet): + rc = 0 + +finally: + broker.terminate() + broker.wait() + if rc: + (stdo, stde) = broker.communicate() + print(stde) + sock.close() + +exit(rc) + diff --git a/test/broker/06-bridge-fail-persist-resend-qos2.conf b/test/broker/06-bridge-fail-persist-resend-qos2.conf new file mode 100644 index 0000000000..daaf157174 --- /dev/null +++ b/test/broker/06-bridge-fail-persist-resend-qos2.conf @@ -0,0 +1,11 @@ +port 1889 + +connection bridge-u-test +remote_clientid bridge-u-test +address 127.0.0.1:1888 +topic bridge/# out + +cleansession true +notifications false +restart_timeout 5 +try_private false diff --git a/test/broker/06-bridge-fail-persist-resend-qos2.py b/test/broker/06-bridge-fail-persist-resend-qos2.py new file mode 100755 index 0000000000..fc97128bd0 --- /dev/null +++ b/test/broker/06-bridge-fail-persist-resend-qos2.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python + +# Test whether a bridge can cope with an unknown PUBACK + +import socket +import subprocess +import time + +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 +connect_packet = mosq_test.gen_connect("bridge-u-test", keepalive=keepalive, proto_ver=3) +connack_packet = mosq_test.gen_connack(rc=0) + +mid = 180 +mid_unknown = 2000 + +publish_packet = mosq_test.gen_publish("bridge/unknown/qos2", qos=1, payload="bridge-message", mid=mid) +puback_packet = mosq_test.gen_puback(mid) + +pubrec_packet_unknown1 = mosq_test.gen_pubrec(mid_unknown+1) +pubrel_packet_unknown1 = mosq_test.gen_pubrel(mid_unknown+1) + +pubrel_packet_unknown2 = mosq_test.gen_pubrel(mid_unknown+2) +pubcomp_packet_unknown2 = mosq_test.gen_pubcomp(mid_unknown+2) + +pubcomp_packet_unknown3 = mosq_test.gen_pubcomp(mid_unknown+3) + + +unsubscribe_packet = mosq_test.gen_unsubscribe(1, "bridge/#") +unsuback_packet = mosq_test.gen_unsuback(1) + + +if os.environ.get('MOSQ_USE_VALGRIND') is not None: + sleep_time = 5 +else: + sleep_time = 0.5 + + +sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) +sock.settimeout(10) +sock.bind(('', 1888)) +sock.listen(5) + +broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=1889) +time.sleep(sleep_time) + +try: + (conn, address) = sock.accept() + conn.settimeout(20) + + if mosq_test.expect_packet(conn, "connect", connect_packet): + conn.send(connack_packet) + + if mosq_test.expect_packet(conn, "unsubscribe", unsubscribe_packet): + conn.send(unsuback_packet) + + # Send the unexpected pubrec packet + conn.send(pubrec_packet_unknown1) + if mosq_test.expect_packet(conn, "pubrel", pubrel_packet_unknown1): + + conn.send(pubrel_packet_unknown2) + if mosq_test.expect_packet(conn, "pubcomp", pubcomp_packet_unknown2): + + conn.send(pubcomp_packet_unknown3) + + # Send a legitimate publish packet to verify everything is still ok + conn.send(publish_packet) + + if mosq_test.expect_packet(conn, "puback", puback_packet): + rc = 0 + +finally: + broker.terminate() + broker.wait() + if rc: + (stdo, stde) = broker.communicate() + print(stde) + sock.close() + +exit(rc) + diff --git a/test/broker/06-bridge-reconnect-local-out-helper.py b/test/broker/06-bridge-reconnect-local-out-helper.py index 7222acd708..08a5636687 100755 --- a/test/broker/06-bridge-reconnect-local-out-helper.py +++ b/test/broker/06-bridge-reconnect-local-out-helper.py @@ -1,9 +1,5 @@ #!/usr/bin/env python -import subprocess -import socket -import time - 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],".."))) @@ -30,6 +26,6 @@ rc = 0 sock.close() - + exit(rc) diff --git a/test/broker/06-bridge-reconnect-local-out.py b/test/broker/06-bridge-reconnect-local-out.py index 6924475481..e846c69ad3 100755 --- a/test/broker/06-bridge-reconnect-local-out.py +++ b/test/broker/06-bridge-reconnect-local-out.py @@ -3,9 +3,7 @@ # Test whether a bridge topics work correctly after reconnection. # Important point here is that persistence is enabled. -import os import subprocess -import socket import time import inspect, os, sys diff --git a/test/broker/07-will-null-helper.py b/test/broker/07-will-null-helper.py index 458ef58969..8cfa0419cb 100755 --- a/test/broker/07-will-null-helper.py +++ b/test/broker/07-will-null-helper.py @@ -2,11 +2,6 @@ # Connect a client with a will, then disconnect without DISCONNECT. -import struct -import subprocess -import socket -import time - 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],".."))) @@ -23,6 +18,6 @@ sock = mosq_test.do_client_connect(connect_packet, connack_packet) rc = 0 sock.close() - + exit(rc) diff --git a/test/broker/07-will-null-topic.py b/test/broker/07-will-null-topic.py index 83247422d3..bb1ca6580b 100755 --- a/test/broker/07-will-null-topic.py +++ b/test/broker/07-will-null-topic.py @@ -1,9 +1,6 @@ #!/usr/bin/env python import struct -import subprocess -import socket -import time import inspect, os, sys # From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder diff --git a/test/broker/07-will-null.py b/test/broker/07-will-null.py index e2e64a21c0..6d3bd7cfc3 100755 --- a/test/broker/07-will-null.py +++ b/test/broker/07-will-null.py @@ -2,11 +2,7 @@ # Test whether a client will is transmitted correctly with a null character in the middle. -import struct import subprocess -import socket -import time - 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],".."))) diff --git a/test/broker/07-will-qos0-helper.py b/test/broker/07-will-qos0-helper.py index a112f0a87b..8aec34eb68 100755 --- a/test/broker/07-will-qos0-helper.py +++ b/test/broker/07-will-qos0-helper.py @@ -2,10 +2,6 @@ # Connect a client with a will, then disconnect without DISCONNECT. -import subprocess -import socket -import time - 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],".."))) @@ -22,6 +18,6 @@ sock = mosq_test.do_client_connect(connect_packet, connack_packet) rc = 0 sock.close() - + exit(rc) diff --git a/test/broker/07-will-qos0.py b/test/broker/07-will-qos0.py index a6616c4bbb..6bc3b1ead8 100755 --- a/test/broker/07-will-qos0.py +++ b/test/broker/07-will-qos0.py @@ -3,8 +3,6 @@ # Test whether a client will is transmitted correctly. import subprocess -import socket -import time import inspect, os, sys # From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder diff --git a/test/broker/08-ssl-bridge-helper.py b/test/broker/08-ssl-bridge-helper.py index 9d68101f47..20dca406cb 100755 --- a/test/broker/08-ssl-bridge-helper.py +++ b/test/broker/08-ssl-bridge-helper.py @@ -1,9 +1,5 @@ #!/usr/bin/env python -import subprocess -import socket -import time - 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],".."))) @@ -25,5 +21,5 @@ sock.send(publish_packet) sock.send(disconnect_packet) sock.close() - + exit(0) diff --git a/test/broker/08-ssl-bridge.py b/test/broker/08-ssl-bridge.py index 22fe2ede77..3339e32755 100755 --- a/test/broker/08-ssl-bridge.py +++ b/test/broker/08-ssl-bridge.py @@ -1,10 +1,8 @@ #!/usr/bin/env python -import os import subprocess import socket import ssl -import time import inspect, os, sys # From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder diff --git a/test/broker/08-ssl-connect-cert-auth-crl.py b/test/broker/08-ssl-connect-cert-auth-crl.py index c783d784ea..e6343cca4a 100755 --- a/test/broker/08-ssl-connect-cert-auth-crl.py +++ b/test/broker/08-ssl-connect-cert-auth-crl.py @@ -1,16 +1,14 @@ #!/usr/bin/env python -import subprocess import socket import ssl import sys -import time if sys.version < '2.7': print("WARNING: SSL not supported on Python 2.6") exit(0) -import inspect, os, sys +import inspect, os # 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: diff --git a/test/broker/08-ssl-connect-cert-auth-expired.py b/test/broker/08-ssl-connect-cert-auth-expired.py index 6761d643ae..76c291e14b 100755 --- a/test/broker/08-ssl-connect-cert-auth-expired.py +++ b/test/broker/08-ssl-connect-cert-auth-expired.py @@ -3,7 +3,6 @@ # Test whether a valid CONNECT results in the correct CONNACK packet using an # SSL connection with client certificates required. -import subprocess import socket import ssl import sys @@ -13,7 +12,7 @@ print("WARNING: SSL not supported on Python 2.6") exit(0) -import inspect, os, sys +import inspect, os # 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: diff --git a/test/broker/08-ssl-connect-cert-auth-revoked.py b/test/broker/08-ssl-connect-cert-auth-revoked.py index 0b2465509f..d04e672948 100755 --- a/test/broker/08-ssl-connect-cert-auth-revoked.py +++ b/test/broker/08-ssl-connect-cert-auth-revoked.py @@ -1,6 +1,5 @@ #!/usr/bin/env python -import subprocess import socket import ssl import sys @@ -10,7 +9,7 @@ print("WARNING: SSL not supported on Python 2.6") exit(0) -import inspect, os, sys +import inspect, os # 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: diff --git a/test/broker/08-ssl-connect-cert-auth-without.py b/test/broker/08-ssl-connect-cert-auth-without.py index a2fdebba4f..b82a6eb3d0 100755 --- a/test/broker/08-ssl-connect-cert-auth-without.py +++ b/test/broker/08-ssl-connect-cert-auth-without.py @@ -1,10 +1,8 @@ #!/usr/bin/env python # Test whether a client can connect without an SSL certificate if one is required. -# import errno -import subprocess import socket import ssl import sys @@ -14,7 +12,7 @@ print("WARNING: SSL not supported on Python 2.6") exit(0) -import inspect, os, sys +import inspect, os # 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: diff --git a/test/broker/08-ssl-connect-cert-auth.py b/test/broker/08-ssl-connect-cert-auth.py index 42ba651091..6213306904 100755 --- a/test/broker/08-ssl-connect-cert-auth.py +++ b/test/broker/08-ssl-connect-cert-auth.py @@ -2,17 +2,15 @@ # Test whether a valid CONNECT results in the correct CONNACK packet using an SSL connection. -import subprocess import socket import ssl import sys -import time if sys.version < '2.7': print("WARNING: SSL not supported on Python 2.6") exit(0) -import inspect, os, sys +import inspect, os # 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: diff --git a/test/broker/08-ssl-connect-identity.py b/test/broker/08-ssl-connect-identity.py index 8119eb7f21..b8c781f752 100755 --- a/test/broker/08-ssl-connect-identity.py +++ b/test/broker/08-ssl-connect-identity.py @@ -1,7 +1,6 @@ #!/usr/bin/env python # Client connects with a certificate to a server that has use_identity_as_username=true. Shouldn't be rejected. -import subprocess import socket import ssl import sys @@ -11,7 +10,7 @@ print("WARNING: SSL not supported on Python 2.6") exit(0) -import inspect, os, sys +import inspect, os # 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: diff --git a/test/broker/08-ssl-connect-no-auth-wrong-ca.py b/test/broker/08-ssl-connect-no-auth-wrong-ca.py index dc6e782976..c911189af3 100755 --- a/test/broker/08-ssl-connect-no-auth-wrong-ca.py +++ b/test/broker/08-ssl-connect-no-auth-wrong-ca.py @@ -2,7 +2,6 @@ # Test whether a valid CONNECT results in the correct CONNACK packet using an SSL connection. -import subprocess import socket import ssl import sys @@ -12,7 +11,7 @@ print("WARNING: SSL not supported on Python 2.6") exit(0) -import inspect, os, sys +import inspect, os # 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: @@ -40,7 +39,7 @@ time.sleep(0.5) broker.terminate() -broker.wait +broker.wait() if rc: (stdo, stde) = broker.communicate() print(stde) diff --git a/test/broker/08-ssl-connect-no-auth.py b/test/broker/08-ssl-connect-no-auth.py index 27adf48a10..e534532eff 100755 --- a/test/broker/08-ssl-connect-no-auth.py +++ b/test/broker/08-ssl-connect-no-auth.py @@ -2,17 +2,15 @@ # Test whether a valid CONNECT results in the correct CONNACK packet using an SSL connection. -import subprocess import socket import ssl import sys -import time if sys.version < '2.7': print("WARNING: SSL not supported on Python 2.6") exit(0) -import inspect, os, sys +import inspect, os # 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: diff --git a/test/broker/08-ssl-connect-no-identity.py b/test/broker/08-ssl-connect-no-identity.py index 35b94cb6ca..1ce22f5b76 100755 --- a/test/broker/08-ssl-connect-no-identity.py +++ b/test/broker/08-ssl-connect-no-identity.py @@ -1,7 +1,6 @@ #!/usr/bin/env python # Client connects without a certificate to a server that has use_identity_as_username=true. Should be rejected. -import subprocess import socket import ssl import sys @@ -11,7 +10,7 @@ print("WARNING: SSL not supported on Python 2.6") exit(0) -import inspect, os, sys +import inspect, os # 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: diff --git a/test/broker/08-tls-psk-bridge.py b/test/broker/08-tls-psk-bridge.py index 5e2c18bf6d..97c27bb240 100755 --- a/test/broker/08-tls-psk-bridge.py +++ b/test/broker/08-tls-psk-bridge.py @@ -1,7 +1,6 @@ #!/usr/bin/env python import subprocess -import socket import ssl import sys import time @@ -15,7 +14,7 @@ exit(0) -import inspect, os, sys +import inspect, os # 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: @@ -56,7 +55,6 @@ pub = subprocess.Popen(['./c/08-tls-psk-bridge.test'], env=env, stdout=subprocess.PIPE) if pub.wait(): raise ValueError - exit(1) if mosq_test.expect_packet(sock, "publish", publish_packet): rc = 0 diff --git a/test/broker/08-tls-psk-pub.py b/test/broker/08-tls-psk-pub.py index c388e8304f..a552f5ade5 100755 --- a/test/broker/08-tls-psk-pub.py +++ b/test/broker/08-tls-psk-pub.py @@ -1,10 +1,8 @@ #!/usr/bin/env python import subprocess -import socket import ssl import sys -import time if sys.version < '2.7': print("WARNING: SSL not supported on Python 2.6") @@ -15,7 +13,7 @@ exit(0) -import inspect, os, sys +import inspect, os # 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: @@ -53,7 +51,6 @@ pub = subprocess.Popen(['./c/08-tls-psk-pub.test'], env=env) if pub.wait(): raise ValueError - exit(1) if mosq_test.expect_packet(sock, "publish", publish_packet): rc = 0 diff --git a/test/broker/09-plugin-auth-unpwd-fail.py b/test/broker/09-plugin-auth-unpwd-fail.py index a9b9447a66..4d54cdbc11 100755 --- a/test/broker/09-plugin-auth-unpwd-fail.py +++ b/test/broker/09-plugin-auth-unpwd-fail.py @@ -3,10 +3,6 @@ # Test whether a connection is successful with correct username and password # when using a simple auth_plugin. -import subprocess -import socket -import time - 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],".."))) diff --git a/test/broker/09-plugin-auth-unpwd-success.py b/test/broker/09-plugin-auth-unpwd-success.py index 3277cea470..422e8e79e9 100755 --- a/test/broker/09-plugin-auth-unpwd-success.py +++ b/test/broker/09-plugin-auth-unpwd-success.py @@ -3,10 +3,6 @@ # Test whether a connection is successful with correct username and password # when using a simple auth_plugin. -import subprocess -import socket -import time - 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],".."))) diff --git a/test/broker/10-listener-mount-point-helper.py b/test/broker/10-listener-mount-point-helper.py index bacbc4edf0..fa73e9431e 100755 --- a/test/broker/10-listener-mount-point-helper.py +++ b/test/broker/10-listener-mount-point-helper.py @@ -1,9 +1,5 @@ #!/usr/bin/env python -import subprocess -import socket -import time - 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],".."))) @@ -23,6 +19,6 @@ sock.send(publish_packet) rc = 0 sock.close() - + exit(rc) diff --git a/test/broker/10-listener-mount-point.py b/test/broker/10-listener-mount-point.py index c482c04913..62d1545c70 100755 --- a/test/broker/10-listener-mount-point.py +++ b/test/broker/10-listener-mount-point.py @@ -1,9 +1,6 @@ #!/usr/bin/env python -import os import subprocess -import socket -import time import inspect, os, sys # From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder diff --git a/test/broker/Makefile b/test/broker/Makefile index 1a0ed5534a..a84950e9c5 100644 --- a/test/broker/Makefile +++ b/test/broker/Makefile @@ -65,6 +65,8 @@ endif ./06-bridge-br2b-disconnect-qos2.py ./06-bridge-b2br-disconnect-qos1.py ./06-bridge-b2br-disconnect-qos2.py + ./06-bridge-fail-persist-resend-qos1.py + ./06-bridge-fail-persist-resend-qos2.py 07 : ./07-will-qos0.py diff --git a/test/lib/01-con-discon-success.py b/test/lib/01-con-discon-success.py index 1ebe6c976f..5eaf67cdf0 100755 --- a/test/lib/01-con-discon-success.py +++ b/test/lib/01-con-discon-success.py @@ -10,10 +10,8 @@ import inspect import os -import subprocess import socket import sys -import time # 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],".."))) diff --git a/test/lib/01-keepalive-pingreq.py b/test/lib/01-keepalive-pingreq.py index f0dbceaaff..e438964c2f 100755 --- a/test/lib/01-keepalive-pingreq.py +++ b/test/lib/01-keepalive-pingreq.py @@ -9,7 +9,6 @@ import inspect import os -import subprocess import socket import sys import time diff --git a/test/lib/01-no-clean-session.py b/test/lib/01-no-clean-session.py index 9ae2a7c826..15b3c9de41 100755 --- a/test/lib/01-no-clean-session.py +++ b/test/lib/01-no-clean-session.py @@ -7,10 +7,8 @@ import inspect import os -import subprocess import socket import sys -import time # 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],".."))) diff --git a/test/lib/01-unpwd-set.py b/test/lib/01-unpwd-set.py index 67cf4f7b24..b727f23bee 100755 --- a/test/lib/01-unpwd-set.py +++ b/test/lib/01-unpwd-set.py @@ -7,10 +7,8 @@ import inspect import os -import subprocess import socket import sys -import time # 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],".."))) diff --git a/test/lib/01-will-set.py b/test/lib/01-will-set.py index a54f849ac4..48aa4836d4 100755 --- a/test/lib/01-will-set.py +++ b/test/lib/01-will-set.py @@ -9,10 +9,8 @@ import inspect import os -import subprocess import socket import sys -import time # 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],".."))) diff --git a/test/lib/01-will-unpwd-set.py b/test/lib/01-will-unpwd-set.py index b27820da1b..9104f34749 100755 --- a/test/lib/01-will-unpwd-set.py +++ b/test/lib/01-will-unpwd-set.py @@ -9,10 +9,8 @@ import inspect import os -import subprocess import socket import sys -import time # 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],".."))) diff --git a/test/lib/02-subscribe-qos0.py b/test/lib/02-subscribe-qos0.py index 42b9874ef8..7b0d5988d6 100755 --- a/test/lib/02-subscribe-qos0.py +++ b/test/lib/02-subscribe-qos0.py @@ -14,10 +14,8 @@ import inspect import os -import subprocess import socket import sys -import time # 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],".."))) @@ -62,10 +60,10 @@ if mosq_test.expect_packet(conn, "subscribe", subscribe_packet): conn.send(suback_packet) - + if mosq_test.expect_packet(conn, "disconnect", disconnect_packet): rc = 0 - + conn.close() finally: client.terminate() diff --git a/test/lib/02-subscribe-qos1.py b/test/lib/02-subscribe-qos1.py index 6190431ae7..fa5144bc33 100755 --- a/test/lib/02-subscribe-qos1.py +++ b/test/lib/02-subscribe-qos1.py @@ -14,10 +14,8 @@ import inspect import os -import subprocess import socket import sys -import time # 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],".."))) @@ -62,10 +60,10 @@ if mosq_test.expect_packet(conn, "subscribe", subscribe_packet): conn.send(suback_packet) - + if mosq_test.expect_packet(conn, "disconnect", disconnect_packet): rc = 0 - + conn.close() finally: client.terminate() diff --git a/test/lib/02-subscribe-qos2.py b/test/lib/02-subscribe-qos2.py index 411de65be5..0fd1a72a96 100755 --- a/test/lib/02-subscribe-qos2.py +++ b/test/lib/02-subscribe-qos2.py @@ -14,10 +14,8 @@ import inspect import os -import subprocess import socket import sys -import time # 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],".."))) @@ -62,10 +60,10 @@ if mosq_test.expect_packet(conn, "subscribe", subscribe_packet): conn.send(suback_packet) - + if mosq_test.expect_packet(conn, "disconnect", disconnect_packet): rc = 0 - + conn.close() finally: client.terminate() diff --git a/test/lib/02-unsubscribe.py b/test/lib/02-unsubscribe.py index 257f95a69c..ec9805bae5 100755 --- a/test/lib/02-unsubscribe.py +++ b/test/lib/02-unsubscribe.py @@ -4,10 +4,8 @@ import inspect import os -import subprocess import socket import sys -import time # 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],".."))) @@ -52,10 +50,10 @@ if mosq_test.expect_packet(conn, "unsubscribe", unsubscribe_packet): conn.send(unsuback_packet) - + if mosq_test.expect_packet(conn, "disconnect", disconnect_packet): rc = 0 - + conn.close() finally: client.terminate() diff --git a/test/lib/03-publish-b2c-qos1.py b/test/lib/03-publish-b2c-qos1.py index edfbb40f74..53f3db46f9 100755 --- a/test/lib/03-publish-b2c-qos1.py +++ b/test/lib/03-publish-b2c-qos1.py @@ -13,7 +13,6 @@ import inspect import os -import subprocess import socket import sys import time diff --git a/test/lib/03-publish-b2c-qos2.py b/test/lib/03-publish-b2c-qos2.py index 5099ae5f91..72c3154171 100755 --- a/test/lib/03-publish-b2c-qos2.py +++ b/test/lib/03-publish-b2c-qos2.py @@ -18,7 +18,6 @@ import inspect import os -import subprocess import socket import sys import time diff --git a/test/lib/03-publish-c2b-qos1-disconnect.py b/test/lib/03-publish-c2b-qos1-disconnect.py index 4131686293..bcddecceaa 100755 --- a/test/lib/03-publish-c2b-qos1-disconnect.py +++ b/test/lib/03-publish-c2b-qos1-disconnect.py @@ -4,10 +4,8 @@ import inspect import os -import subprocess import socket import sys -import time # 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],".."))) diff --git a/test/lib/03-publish-c2b-qos1-timeout.py b/test/lib/03-publish-c2b-qos1-timeout.py new file mode 100755 index 0000000000..3d0663ea90 --- /dev/null +++ b/test/lib/03-publish-c2b-qos1-timeout.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python + +# Test whether a client sends a correct PUBLISH to a topic with QoS 1 and responds to a delay. + +# The client should connect to port 1888 with keepalive=60, clean session set, +# and client id publish-qos1-test +# The test will send a CONNACK message to the client with rc=0. Upon receiving +# the CONNACK the client should verify that rc==0. If not, it should exit with +# return code=1. +# On a successful CONNACK, the client should send a PUBLISH message with topic +# "pub/qos1/test", payload "message" and QoS=1. +# The test will not respond to the first PUBLISH message, so the client must +# resend the PUBLISH message with dup=1. Note that to keep test durations low, a +# message retry timeout of less than 10 seconds is required for this test. +# On receiving the second PUBLISH message, the test will send the correct +# PUBACK response. On receiving the correct PUBACK response, the client should +# send a DISCONNECT message. + +import inspect +import os +import socket +import 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 +connect_packet = mosq_test.gen_connect("publish-qos1-test", keepalive=keepalive) +connack_packet = mosq_test.gen_connack(rc=0) + +disconnect_packet = mosq_test.gen_disconnect() + +mid = 1 +publish_packet = mosq_test.gen_publish("pub/qos1/test", qos=1, mid=mid, payload="message") +publish_packet_dup = mosq_test.gen_publish("pub/qos1/test", qos=1, mid=mid, payload="message", dup=True) +puback_packet = mosq_test.gen_puback(mid) + +sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) +sock.settimeout(10) +sock.bind(('', 1888)) +sock.listen(5) + +client_args = sys.argv[1:] +env = dict(os.environ) +env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' +try: + pp = env['PYTHONPATH'] +except KeyError: + pp = '' +env['PYTHONPATH'] = '../../lib/python:'+pp +client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env) + +try: + (conn, address) = sock.accept() + conn.settimeout(10) + + if mosq_test.expect_packet(conn, "connect", connect_packet): + conn.send(connack_packet) + + if mosq_test.expect_packet(conn, "publish", publish_packet): + # Delay for > 3 seconds (message retry time) + + if mosq_test.expect_packet(conn, "dup publish", publish_packet_dup): + conn.send(puback_packet) + + if mosq_test.expect_packet(conn, "disconnect", disconnect_packet): + rc = 0 + + conn.close() +finally: + client.terminate() + client.wait() + sock.close() + +exit(rc) diff --git a/test/lib/03-publish-c2b-qos2-disconnect.py b/test/lib/03-publish-c2b-qos2-disconnect.py index 9b36d064b0..711958638a 100755 --- a/test/lib/03-publish-c2b-qos2-disconnect.py +++ b/test/lib/03-publish-c2b-qos2-disconnect.py @@ -4,10 +4,8 @@ import inspect import os -import subprocess import socket import sys -import time # 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],".."))) diff --git a/test/lib/03-publish-c2b-qos2-timeout.py b/test/lib/03-publish-c2b-qos2-timeout.py new file mode 100755 index 0000000000..00543f0ed7 --- /dev/null +++ b/test/lib/03-publish-c2b-qos2-timeout.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python + +# Test whether a client sends a correct PUBLISH to a topic with QoS 1 and responds to a delay. + +# The client should connect to port 1888 with keepalive=60, clean session set, +# and client id publish-qos2-test +# The test will send a CONNACK message to the client with rc=0. Upon receiving +# the CONNACK the client should verify that rc==0. If not, it should exit with +# return code=1. +# On a successful CONNACK, the client should send a PUBLISH message with topic +# "pub/qos2/test", payload "message" and QoS=2. +# The test will not respond to the first PUBLISH message, so the client must +# resend the PUBLISH message with dup=1. Note that to keep test durations low, a +# message retry timeout of less than 10 seconds is required for this test. +# On receiving the second PUBLISH message, the test will send the correct +# PUBREC response. On receiving the correct PUBREC response, the client should +# send a PUBREL message. +# The test will not respond to the first PUBREL message, so the client must +# resend the PUBREL message with dup=1. On receiving the second PUBREL message, +# the test will send the correct PUBCOMP response. On receiving the correct +# PUBCOMP response, the client should send a DISCONNECT message. + +import inspect +import os +import socket +import 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 +connect_packet = mosq_test.gen_connect("publish-qos2-test", keepalive=keepalive) +connack_packet = mosq_test.gen_connack(rc=0) + +disconnect_packet = mosq_test.gen_disconnect() + +mid = 1 +publish_packet = mosq_test.gen_publish("pub/qos2/test", qos=2, mid=mid, payload="message") +publish_dup_packet = mosq_test.gen_publish("pub/qos2/test", qos=2, mid=mid, payload="message", dup=True) +pubrec_packet = mosq_test.gen_pubrec(mid) +pubrel_packet = mosq_test.gen_pubrel(mid) +pubcomp_packet = mosq_test.gen_pubcomp(mid) + +sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) +sock.settimeout(10) +sock.bind(('', 1888)) +sock.listen(5) + +client_args = sys.argv[1:] +env = dict(os.environ) +env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' +try: + pp = env['PYTHONPATH'] +except KeyError: + pp = '' +env['PYTHONPATH'] = '../../lib/python:'+pp +client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env) + +try: + (conn, address) = sock.accept() + conn.settimeout(10) + + if mosq_test.expect_packet(conn, "connect", connect_packet): + conn.send(connack_packet) + + if mosq_test.expect_packet(conn, "publish", publish_packet): + # Delay for > 3 seconds (message retry time) + + if mosq_test.expect_packet(conn, "dup publish", publish_dup_packet): + conn.send(pubrec_packet) + + if mosq_test.expect_packet(conn, "pubrel", pubrel_packet): + if mosq_test.expect_packet(conn, "dup pubrel", pubrel_packet): + conn.send(pubcomp_packet) + + if mosq_test.expect_packet(conn, "disconnect", disconnect_packet): + rc = 0 + + conn.close() +finally: + client.terminate() + client.wait() + sock.close() + +exit(rc) diff --git a/test/lib/03-publish-c2b-qos2.py b/test/lib/03-publish-c2b-qos2.py index 0954ed84eb..f8a604af4d 100755 --- a/test/lib/03-publish-c2b-qos2.py +++ b/test/lib/03-publish-c2b-qos2.py @@ -22,10 +22,8 @@ import inspect import os -import subprocess import socket import sys -import time # 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],".."))) @@ -73,7 +71,7 @@ if mosq_test.expect_packet(conn, "publish", publish_packet): conn.send(pubrec_packet) - + if mosq_test.expect_packet(conn, "pubrel", pubrel_packet): conn.send(pubcomp_packet) diff --git a/test/lib/03-publish-qos0-no-payload.py b/test/lib/03-publish-qos0-no-payload.py index c74aa4d6d3..a858ad5c7f 100755 --- a/test/lib/03-publish-qos0-no-payload.py +++ b/test/lib/03-publish-qos0-no-payload.py @@ -12,10 +12,8 @@ import inspect import os -import subprocess import socket import sys -import time # 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],".."))) @@ -59,7 +57,7 @@ if mosq_test.expect_packet(conn, "publish", publish_packet): if mosq_test.expect_packet(conn, "disconnect", disconnect_packet): rc = 0 - + conn.close() finally: client.terminate() diff --git a/test/lib/03-publish-qos0.py b/test/lib/03-publish-qos0.py index d58f3f515f..1525da1d0f 100755 --- a/test/lib/03-publish-qos0.py +++ b/test/lib/03-publish-qos0.py @@ -12,10 +12,8 @@ import inspect import os -import subprocess import socket import sys -import time # 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],".."))) @@ -59,11 +57,14 @@ if mosq_test.expect_packet(conn, "publish", publish_packet): if mosq_test.expect_packet(conn, "disconnect", disconnect_packet): rc = 0 - + conn.close() finally: client.terminate() client.wait() + if rc: + (stdo, stde) = client.communicate() + print(stde) sock.close() exit(rc) diff --git a/test/lib/04-retain-qos0.py b/test/lib/04-retain-qos0.py index 8860c03620..ef23451368 100755 --- a/test/lib/04-retain-qos0.py +++ b/test/lib/04-retain-qos0.py @@ -4,10 +4,8 @@ import inspect import os -import subprocess import socket import sys -import time # 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],".."))) @@ -49,7 +47,7 @@ if mosq_test.expect_packet(conn, "publish", publish_packet): rc = 0 - + conn.close() finally: client.terminate() diff --git a/test/lib/08-ssl-bad-cacert.py b/test/lib/08-ssl-bad-cacert.py index 8509ff9922..20dc6fede8 100755 --- a/test/lib/08-ssl-bad-cacert.py +++ b/test/lib/08-ssl-bad-cacert.py @@ -2,11 +2,7 @@ import inspect import os -import subprocess -import socket -import ssl import sys -import time # 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],".."))) diff --git a/test/lib/08-ssl-connect-cert-auth-enc.py b/test/lib/08-ssl-connect-cert-auth-enc.py index a4f8113868..854fe1284a 100755 --- a/test/lib/08-ssl-connect-cert-auth-enc.py +++ b/test/lib/08-ssl-connect-cert-auth-enc.py @@ -12,11 +12,9 @@ import inspect import os -import subprocess import socket import ssl import sys -import time # 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],".."))) diff --git a/test/lib/08-ssl-connect-cert-auth.py b/test/lib/08-ssl-connect-cert-auth.py index 71ebdeaf62..cd36e45d30 100755 --- a/test/lib/08-ssl-connect-cert-auth.py +++ b/test/lib/08-ssl-connect-cert-auth.py @@ -12,11 +12,9 @@ import inspect import os -import subprocess import socket import ssl import sys -import time # 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],".."))) diff --git a/test/lib/08-ssl-connect-no-auth.py b/test/lib/08-ssl-connect-no-auth.py index d0c531b933..03d04a876e 100755 --- a/test/lib/08-ssl-connect-no-auth.py +++ b/test/lib/08-ssl-connect-no-auth.py @@ -11,11 +11,9 @@ import inspect import os -import subprocess import socket import ssl import sys -import time # 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],".."))) diff --git a/test/lib/08-ssl-fake-cacert.py b/test/lib/08-ssl-fake-cacert.py index 0e6c29cd2f..1dbc026632 100755 --- a/test/lib/08-ssl-fake-cacert.py +++ b/test/lib/08-ssl-fake-cacert.py @@ -2,7 +2,6 @@ import inspect import os -import subprocess import socket import ssl import sys diff --git a/test/lib/09-util-topic-matching.py b/test/lib/09-util-topic-matching.py index ed54d2c1b9..3dc19e0b63 100755 --- a/test/lib/09-util-topic-matching.py +++ b/test/lib/09-util-topic-matching.py @@ -2,9 +2,7 @@ import inspect import os -import subprocess import sys -import time # 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],".."))) diff --git a/test/lib/09-util-topic-tokenise.py b/test/lib/09-util-topic-tokenise.py index e464284277..6f8dc1228b 100755 --- a/test/lib/09-util-topic-tokenise.py +++ b/test/lib/09-util-topic-tokenise.py @@ -2,9 +2,7 @@ import inspect import os -import subprocess import sys -import time # 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],".."))) diff --git a/test/lib/09-util-utf8-validate.py b/test/lib/09-util-utf8-validate.py new file mode 100755 index 0000000000..99e2543b06 --- /dev/null +++ b/test/lib/09-util-utf8-validate.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python + +import inspect +import os +import subprocess +import sys +import time + +# 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 + +client_args = sys.argv[1:] +env = dict(os.environ) +env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' +try: + pp = env['PYTHONPATH'] +except KeyError: + pp = '' +env['PYTHONPATH'] = '../../lib/python:'+pp + +client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env) +client.wait() +if client.returncode: + (stdo, stde) = client.communicate() + print(stdo) +exit(client.returncode) + diff --git a/test/lib/c/03-publish-qos0.c b/test/lib/c/03-publish-qos0.c index 0bb769212b..442ee098df 100644 --- a/test/lib/c/03-publish-qos0.c +++ b/test/lib/c/03-publish-qos0.c @@ -40,7 +40,7 @@ int main(int argc, char *argv[]) rc = mosquitto_connect(mosq, "localhost", 1888, 60); while(run == -1){ - mosquitto_loop(mosq, -1, 1); + rc = mosquitto_loop(mosq, -1, 1); } mosquitto_lib_cleanup(); diff --git a/test/lib/c/09-util-topic-matching.c b/test/lib/c/09-util-topic-matching.c index dac546b6d9..8b59a63301 100644 --- a/test/lib/c/09-util-topic-matching.c +++ b/test/lib/c/09-util-topic-matching.c @@ -16,6 +16,13 @@ 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); + do_check("test/6/#", "test/3", true); do_check("foo/bar", "foo/bar", false); do_check("foo/+", "foo/bar", false); diff --git a/test/lib/c/09-util-utf8-validate.c b/test/lib/c/09-util-utf8-validate.c new file mode 100644 index 0000000000..1a472328a3 --- /dev/null +++ b/test/lib/c/09-util-utf8-validate.c @@ -0,0 +1,251 @@ +#include +#include +#include +#include + +/* Test data taken from + * http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt but modified for + * updated standard (no 5, 6 byte lengths) */ + +void assert_invalid(const char *str) +{ + if(mosquitto_validate_utf8(str, strlen(str)) == 0){ + printf("ERROR assert_invalid: %s\n", str); + exit(1); + } +} + +void assert_valid_len(const char *str, int len) +{ + if(mosquitto_validate_utf8(str, strlen(str)) != 0){ + printf("ERROR assert_valid: %s\n", str); + exit(1); + } +} + +void assert_valid(const char *str) +{ + assert_valid_len(str, strlen(str)); +} + +int main(int argc, char *argv[]) +{ + /* 1 Some correct UTF-8 text */ + assert_valid("You should see the Greek word 'kosme': \"κόσμε\""); + + /* 2 Boundary condition test cases */ + /* 2.1 First possible sequence of a certain length */ + assert_valid_len("2.1.1 1 byte (U-00000000): \"\0\"", 39); + assert_valid("2.1.2 2 bytes (U-00000080): \"€\""); + assert_valid("2.1.3 3 bytes (U-00000800): \"ࠀ\""); + assert_valid("2.1.4 4 bytes (U-00010000): \"𐀀\""); + + /* 2.2 Last possible sequence of a certain length */ + + assert_valid("2.2.1 1 byte (U-0000007F): \"\""); + assert_valid("2.2.2 2 bytes (U-000007FF): \"߿\""); + assert_valid("2.2.3 3 bytes (U-0000FFFF): \"￿\""); + // FIXME assert_valid("2.2.4 4 bytes (U-001FFFFF): \"\""); + + /* 2.3 Other boundary conditions */ + + assert_valid("2.3.1 U-0000D7FF = ed 9f bf = \"퟿\""); + assert_valid("2.3.2 U-0000E000 = ee 80 80 = \"\""); + assert_valid("2.3.3 U-0000FFFD = ef bf bd = \"�\""); + assert_valid("2.3.4 U-0010FFFF = f4 8f bf bf = \"􏿿\""); + assert_valid("2.3.5 U-00110000 = f4 90 80 80 = \"\""); + + /* 3 Malformed sequences */ + /* 3.1 Unexpected continuation bytes */ + assert_invalid("3.1.1 First continuation byte 0x80: \"\""); + assert_invalid("3.1.2 Last continuation byte 0xbf: \"\""); + assert_invalid("3.1.3 2 continuation bytes: \"\""); + assert_invalid("3.1.4 3 continuation bytes: \"\""); + assert_invalid("3.1.5 4 continuation bytes: \"\""); + assert_invalid("3.1.6 5 continuation bytes: \"\""); + assert_invalid("3.1.7 6 continuation bytes: \"\""); + assert_invalid("3.1.8 7 continuation bytes: \"\""); + + /* 3.1.9 Sequence of all 64 possible continuation bytes (0x80-0xbf): */ + assert_invalid(""); + assert_invalid(""); + assert_invalid(""); + assert_invalid("\""); + + /* 3.2 Lonely start characters */ + + /* 3.2.1 All 32 first bytes of 2-byte sequences (0xc0-0xdf), + each followed by a space character: */ + assert_invalid("\" \""); + + /* 3.2.2 All 16 first bytes of 3-byte sequences (0xe0-0xef), + each followed by a space character: */ + assert_invalid("\" \""); + + /* 3.2.3 All 8 first bytes of 4-byte sequences (0xf0-0xf7), + each followed by a space character: */ + assert_invalid("\" \""); + + /* 3.2.4 All 4 first bytes of 5-byte sequences (0xf8-0xfb), + each followed by a space character: */ + assert_invalid("\" \""); + + /* 3.2.5 All 2 first bytes of 6-byte sequences (0xfc-0xfd), + each followed by a space character: */ + assert_invalid("\" \""); + + /* 3.3 Sequences with last continuation byte missing + + All bytes of an incomplete sequence should be signalled as a single + malformed sequence, i.e., you should see only a single replacement + character in each of the next 10 tests. (Characters as in section 2) */ + + assert_invalid("3.3.1 2-byte sequence with last byte missing (U+0000): \"\""); + assert_invalid("3.3.2 3-byte sequence with last byte missing (U+0000): \"\""); + assert_invalid("3.3.3 4-byte sequence with last byte missing (U+0000): \"\""); + assert_invalid("3.3.4 5-byte sequence with last byte missing (U+0000): \"\""); + assert_invalid("3.3.5 6-byte sequence with last byte missing (U+0000): \"\""); + assert_invalid("3.3.6 2-byte sequence with last byte missing (U-000007FF): \"\""); + assert_invalid("3.3.7 3-byte sequence with last byte missing (U-0000FFFF): \"\""); + assert_invalid("3.3.8 4-byte sequence with last byte missing (U-001FFFFF): \"\""); + assert_invalid("3.3.9 5-byte sequence with last byte missing (U-03FFFFFF): \"\""); + assert_invalid("3.3.10 6-byte sequence with last byte missing (U-7FFFFFFF): \"\""); + + /* 3.4 Concatenation of incomplete sequences + + All the 10 sequences of 3.3 concatenated, you should see 10 malformed + sequences being signalled:*/ + + assert_invalid("\"\""); + + /* 3.5 Impossible bytes + + The following two bytes cannot appear in a correct UTF-8 string */ + + assert_invalid("3.5.1 fe = \"\""); + assert_invalid("3.5.2 ff = \"\""); + assert_invalid("3.5.3 fe fe ff ff = \"\""); + + /* 4 Overlong sequences + + The following sequences are not malformed according to the letter of + the Unicode 2.0 standard. However, they are longer then necessary and + a correct UTF-8 encoder is not allowed to produce them. A "safe UTF-8 + decoder" should reject them just like malformed sequences for two + reasons: (1) It helps to debug applications if overlong sequences are + not treated as valid representations of characters, because this helps + to spot problems more quickly. (2) Overlong sequences provide + alternative representations of characters, that could maliciously be + used to bypass filters that check only for ASCII characters. For + instance, a 2-byte encoded line feed (LF) would not be caught by a + line counter that counts only 0x0a bytes, but it would still be + processed as a line feed by an unsafe UTF-8 decoder later in the + pipeline. From a security point of view, ASCII compatibility of UTF-8 + sequences means also, that ASCII characters are *only* allowed to be + represented by ASCII bytes in the range 0x00-0x7f. To ensure this + aspect of ASCII compatibility, use only "safe UTF-8 decoders" that + reject overlong UTF-8 sequences for which a shorter encoding exists. */ + + /* 4.1 Examples of an overlong ASCII character + + With a safe UTF-8 decoder, all of the following five overlong + representations of the ASCII character slash ("/") should be rejected + like a malformed UTF-8 sequence, for instance by substituting it with + a replacement character. If you see a slash below, you do not have a + safe UTF-8 decoder! */ + + assert_invalid("4.1.1 U+002F = c0 af = \"\""); + assert_invalid("4.1.2 U+002F = e0 80 af = \"\""); + assert_invalid("4.1.3 U+002F = f0 80 80 af = \"\""); + assert_invalid("4.1.4 U+002F = f8 80 80 80 af = \"\""); + assert_invalid("4.1.5 U+002F = fc 80 80 80 80 af = \"\""); + + /* 4.2 Maximum overlong sequences + + Below you see the highest Unicode value that is still resulting in an + overlong sequence if represented with the given number of bytes. This + is a boundary test for safe UTF-8 decoders. All five characters should + be rejected like malformed UTF-8 sequences. */ + + assert_invalid("4.2.1 U-0000007F = c1 bf = \"\""); + assert_invalid("4.2.2 U-000007FF = e0 9f bf = \"\""); + assert_invalid("4.2.3 U-0000FFFF = f0 8f bf bf = \"\""); + assert_invalid("4.2.4 U-001FFFFF = f8 87 bf bf bf = \"\""); + assert_invalid("4.2.5 U-03FFFFFF = fc 83 bf bf bf bf = \"\""); + + /* 4.3 Overlong representation of the NUL character + + The following five sequences should also be rejected like malformed + UTF-8 sequences and should not be treated like the ASCII NUL + character. */ + + assert_invalid("4.3.1 U+0000 = c0 80 = \"\""); + assert_invalid("4.3.2 U+0000 = e0 80 80 = \"\""); + assert_invalid("4.3.3 U+0000 = f0 80 80 80 = \"\""); + assert_invalid("4.3.4 U+0000 = f8 80 80 80 80 = \"\""); + assert_invalid("4.3.5 U+0000 = fc 80 80 80 80 80 = \"\""); + + /* 5 Illegal code positions + + The following UTF-8 sequences should be rejected like malformed + sequences, because they never represent valid ISO 10646 characters and + a UTF-8 decoder that accepts them might introduce security problems + comparable to overlong UTF-8 sequences. */ + + /* 5.1 Single UTF-16 surrogates */ + + assert_invalid("5.1.1 U+D800 = ed a0 80 = \"\""); + assert_invalid("5.1.2 U+DB7F = ed ad bf = \"\""); + assert_invalid("5.1.3 U+DB80 = ed ae 80 = \"\""); + assert_invalid("5.1.4 U+DBFF = ed af bf = \"\""); + assert_invalid("5.1.5 U+DC00 = ed b0 80 = \"\""); + assert_invalid("5.1.6 U+DF80 = ed be 80 = \"\""); + assert_invalid("5.1.7 U+DFFF = ed bf bf = \"\""); + + /* 5.2 Paired UTF-16 surrogates */ + + assert_invalid("5.2.1 U+D800 U+DC00 = ed a0 80 ed b0 80 = \"\""); + assert_invalid("5.2.2 U+D800 U+DFFF = ed a0 80 ed bf bf = \"\""); + assert_invalid("5.2.3 U+DB7F U+DC00 = ed ad bf ed b0 80 = \"\""); + assert_invalid("5.2.4 U+DB7F U+DFFF = ed ad bf ed bf bf = \"\""); + assert_invalid("5.2.5 U+DB80 U+DC00 = ed ae 80 ed b0 80 = \"\""); + assert_invalid("5.2.6 U+DB80 U+DFFF = ed ae 80 ed bf bf = \"\""); + assert_invalid("5.2.7 U+DBFF U+DC00 = ed af bf ed b0 80 = \"\""); + assert_invalid("5.2.8 U+DBFF U+DFFF = ed af bf ed bf bf = \"\""); + + /* 5.3 Noncharacter code positions + + The following "noncharacters" are "reserved for internal use" by + applications, and according to older versions of the Unicode Standard + "should never be interchanged". Unicode Corrigendum #9 dropped the + latter restriction. Nevertheless, their presence in incoming UTF-8 data + can remain a potential security risk, depending on what use is made of + these codes subsequently. Examples of such internal use: + + - Some file APIs with 16-bit characters may use the integer value -1 + = U+FFFF to signal an end-of-file (EOF) or error condition. + + - In some UTF-16 receivers, code point U+FFFE might trigger a + byte-swap operation (to convert between UTF-16LE and UTF-16BE). + + With such internal use of noncharacters, it may be desirable and safer + to block those code points in UTF-8 decoders, as they should never + occur legitimately in incoming UTF-8 data, and could trigger unsafe + behaviour in subsequent processing. + + Particularly problematic noncharacters in 16-bit applications: */ + + assert_valid("5.3.1 U+FFFE = ef bf be = \"￾\""); + assert_valid("5.3.2 U+FFFF = ef bf bf = \"￿\""); + + /* Other noncharacters: */ + + assert_valid("5.3.3 U+FDD0 .. U+FDEF = \"﷐﷑﷒﷓﷔﷕﷖﷗﷘﷙﷚﷛﷜﷝﷞﷟﷠﷡﷢﷣﷤﷥﷦﷧﷨﷩﷪﷫﷬﷭﷮﷯\""); + + /* 5.3.4 U+nFFFE U+nFFFF (for n = 1..10) */ + + assert_valid("\"🿾🿿𯿾𯿿𿿾𿿿񏿾񏿿񟿾񟿿񯿾񯿿񿿾񿿿򏿾򏿿򟿾򟿿򯿾򯿿򿿾򿿿󏿾󏿿󟿾󟿿󯿾󯿿󿿾󿿿􏿾􏿿\""); + + + return 0; +} diff --git a/test/lib/cpp/09-util-utf8-validate.cpp b/test/lib/cpp/09-util-utf8-validate.cpp new file mode 100644 index 0000000000..ea8c021817 --- /dev/null +++ b/test/lib/cpp/09-util-utf8-validate.cpp @@ -0,0 +1,251 @@ +#include +#include +#include +#include + +/* Test data taken from + * http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt but modified for + * updated standard (no 5, 6 byte lengths) */ + +void assert_invalid(const char *str) +{ + if(mosqpp::validate_utf8(str, strlen(str)) == 0){ + printf("ERROR assert_invalid: %s\n", str); + exit(1); + } +} + +void assert_valid_len(const char *str, int len) +{ + if(mosqpp::validate_utf8(str, strlen(str)) != 0){ + printf("ERROR assert_valid: %s\n", str); + exit(1); + } +} + +void assert_valid(const char *str) +{ + assert_valid_len(str, strlen(str)); +} + +int main(int argc, char *argv[]) +{ + /* 1 Some correct UTF-8 text */ + assert_valid("You should see the Greek word 'kosme': \"κόσμε\""); + + /* 2 Boundary condition test cases */ + /* 2.1 First possible sequence of a certain length */ + assert_valid_len("2.1.1 1 byte (U-00000000): \"\0\"", 39); + assert_valid("2.1.2 2 bytes (U-00000080): \"€\""); + assert_valid("2.1.3 3 bytes (U-00000800): \"ࠀ\""); + assert_valid("2.1.4 4 bytes (U-00010000): \"𐀀\""); + + /* 2.2 Last possible sequence of a certain length */ + + assert_valid("2.2.1 1 byte (U-0000007F): \"\""); + assert_valid("2.2.2 2 bytes (U-000007FF): \"߿\""); + assert_valid("2.2.3 3 bytes (U-0000FFFF): \"￿\""); + // FIXME assert_valid("2.2.4 4 bytes (U-001FFFFF): \"\""); + + /* 2.3 Other boundary conditions */ + + assert_valid("2.3.1 U-0000D7FF = ed 9f bf = \"퟿\""); + assert_valid("2.3.2 U-0000E000 = ee 80 80 = \"\""); + assert_valid("2.3.3 U-0000FFFD = ef bf bd = \"�\""); + assert_valid("2.3.4 U-0010FFFF = f4 8f bf bf = \"􏿿\""); + assert_valid("2.3.5 U-00110000 = f4 90 80 80 = \"\""); + + /* 3 Malformed sequences */ + /* 3.1 Unexpected continuation bytes */ + assert_invalid("3.1.1 First continuation byte 0x80: \"\""); + assert_invalid("3.1.2 Last continuation byte 0xbf: \"\""); + assert_invalid("3.1.3 2 continuation bytes: \"\""); + assert_invalid("3.1.4 3 continuation bytes: \"\""); + assert_invalid("3.1.5 4 continuation bytes: \"\""); + assert_invalid("3.1.6 5 continuation bytes: \"\""); + assert_invalid("3.1.7 6 continuation bytes: \"\""); + assert_invalid("3.1.8 7 continuation bytes: \"\""); + + /* 3.1.9 Sequence of all 64 possible continuation bytes (0x80-0xbf): */ + assert_invalid(""); + assert_invalid(""); + assert_invalid(""); + assert_invalid("\""); + + /* 3.2 Lonely start characters */ + + /* 3.2.1 All 32 first bytes of 2-byte sequences (0xc0-0xdf), + each followed by a space character: */ + assert_invalid("\" \""); + + /* 3.2.2 All 16 first bytes of 3-byte sequences (0xe0-0xef), + each followed by a space character: */ + assert_invalid("\" \""); + + /* 3.2.3 All 8 first bytes of 4-byte sequences (0xf0-0xf7), + each followed by a space character: */ + assert_invalid("\" \""); + + /* 3.2.4 All 4 first bytes of 5-byte sequences (0xf8-0xfb), + each followed by a space character: */ + assert_invalid("\" \""); + + /* 3.2.5 All 2 first bytes of 6-byte sequences (0xfc-0xfd), + each followed by a space character: */ + assert_invalid("\" \""); + + /* 3.3 Sequences with last continuation byte missing + + All bytes of an incomplete sequence should be signalled as a single + malformed sequence, i.e., you should see only a single replacement + character in each of the next 10 tests. (Characters as in section 2) */ + + assert_invalid("3.3.1 2-byte sequence with last byte missing (U+0000): \"\""); + assert_invalid("3.3.2 3-byte sequence with last byte missing (U+0000): \"\""); + assert_invalid("3.3.3 4-byte sequence with last byte missing (U+0000): \"\""); + assert_invalid("3.3.4 5-byte sequence with last byte missing (U+0000): \"\""); + assert_invalid("3.3.5 6-byte sequence with last byte missing (U+0000): \"\""); + assert_invalid("3.3.6 2-byte sequence with last byte missing (U-000007FF): \"\""); + assert_invalid("3.3.7 3-byte sequence with last byte missing (U-0000FFFF): \"\""); + assert_invalid("3.3.8 4-byte sequence with last byte missing (U-001FFFFF): \"\""); + assert_invalid("3.3.9 5-byte sequence with last byte missing (U-03FFFFFF): \"\""); + assert_invalid("3.3.10 6-byte sequence with last byte missing (U-7FFFFFFF): \"\""); + + /* 3.4 Concatenation of incomplete sequences + + All the 10 sequences of 3.3 concatenated, you should see 10 malformed + sequences being signalled:*/ + + assert_invalid("\"\""); + + /* 3.5 Impossible bytes + + The following two bytes cannot appear in a correct UTF-8 string */ + + assert_invalid("3.5.1 fe = \"\""); + assert_invalid("3.5.2 ff = \"\""); + assert_invalid("3.5.3 fe fe ff ff = \"\""); + + /* 4 Overlong sequences + + The following sequences are not malformed according to the letter of + the Unicode 2.0 standard. However, they are longer then necessary and + a correct UTF-8 encoder is not allowed to produce them. A "safe UTF-8 + decoder" should reject them just like malformed sequences for two + reasons: (1) It helps to debug applications if overlong sequences are + not treated as valid representations of characters, because this helps + to spot problems more quickly. (2) Overlong sequences provide + alternative representations of characters, that could maliciously be + used to bypass filters that check only for ASCII characters. For + instance, a 2-byte encoded line feed (LF) would not be caught by a + line counter that counts only 0x0a bytes, but it would still be + processed as a line feed by an unsafe UTF-8 decoder later in the + pipeline. From a security point of view, ASCII compatibility of UTF-8 + sequences means also, that ASCII characters are *only* allowed to be + represented by ASCII bytes in the range 0x00-0x7f. To ensure this + aspect of ASCII compatibility, use only "safe UTF-8 decoders" that + reject overlong UTF-8 sequences for which a shorter encoding exists. */ + + /* 4.1 Examples of an overlong ASCII character + + With a safe UTF-8 decoder, all of the following five overlong + representations of the ASCII character slash ("/") should be rejected + like a malformed UTF-8 sequence, for instance by substituting it with + a replacement character. If you see a slash below, you do not have a + safe UTF-8 decoder! */ + + assert_invalid("4.1.1 U+002F = c0 af = \"\""); + assert_invalid("4.1.2 U+002F = e0 80 af = \"\""); + assert_invalid("4.1.3 U+002F = f0 80 80 af = \"\""); + assert_invalid("4.1.4 U+002F = f8 80 80 80 af = \"\""); + assert_invalid("4.1.5 U+002F = fc 80 80 80 80 af = \"\""); + + /* 4.2 Maximum overlong sequences + + Below you see the highest Unicode value that is still resulting in an + overlong sequence if represented with the given number of bytes. This + is a boundary test for safe UTF-8 decoders. All five characters should + be rejected like malformed UTF-8 sequences. */ + + assert_invalid("4.2.1 U-0000007F = c1 bf = \"\""); + assert_invalid("4.2.2 U-000007FF = e0 9f bf = \"\""); + assert_invalid("4.2.3 U-0000FFFF = f0 8f bf bf = \"\""); + assert_invalid("4.2.4 U-001FFFFF = f8 87 bf bf bf = \"\""); + assert_invalid("4.2.5 U-03FFFFFF = fc 83 bf bf bf bf = \"\""); + + /* 4.3 Overlong representation of the NUL character + + The following five sequences should also be rejected like malformed + UTF-8 sequences and should not be treated like the ASCII NUL + character. */ + + assert_invalid("4.3.1 U+0000 = c0 80 = \"\""); + assert_invalid("4.3.2 U+0000 = e0 80 80 = \"\""); + assert_invalid("4.3.3 U+0000 = f0 80 80 80 = \"\""); + assert_invalid("4.3.4 U+0000 = f8 80 80 80 80 = \"\""); + assert_invalid("4.3.5 U+0000 = fc 80 80 80 80 80 = \"\""); + + /* 5 Illegal code positions + + The following UTF-8 sequences should be rejected like malformed + sequences, because they never represent valid ISO 10646 characters and + a UTF-8 decoder that accepts them might introduce security problems + comparable to overlong UTF-8 sequences. */ + + /* 5.1 Single UTF-16 surrogates */ + + assert_invalid("5.1.1 U+D800 = ed a0 80 = \"\""); + assert_invalid("5.1.2 U+DB7F = ed ad bf = \"\""); + assert_invalid("5.1.3 U+DB80 = ed ae 80 = \"\""); + assert_invalid("5.1.4 U+DBFF = ed af bf = \"\""); + assert_invalid("5.1.5 U+DC00 = ed b0 80 = \"\""); + assert_invalid("5.1.6 U+DF80 = ed be 80 = \"\""); + assert_invalid("5.1.7 U+DFFF = ed bf bf = \"\""); + + /* 5.2 Paired UTF-16 surrogates */ + + assert_invalid("5.2.1 U+D800 U+DC00 = ed a0 80 ed b0 80 = \"\""); + assert_invalid("5.2.2 U+D800 U+DFFF = ed a0 80 ed bf bf = \"\""); + assert_invalid("5.2.3 U+DB7F U+DC00 = ed ad bf ed b0 80 = \"\""); + assert_invalid("5.2.4 U+DB7F U+DFFF = ed ad bf ed bf bf = \"\""); + assert_invalid("5.2.5 U+DB80 U+DC00 = ed ae 80 ed b0 80 = \"\""); + assert_invalid("5.2.6 U+DB80 U+DFFF = ed ae 80 ed bf bf = \"\""); + assert_invalid("5.2.7 U+DBFF U+DC00 = ed af bf ed b0 80 = \"\""); + assert_invalid("5.2.8 U+DBFF U+DFFF = ed af bf ed bf bf = \"\""); + + /* 5.3 Noncharacter code positions + + The following "noncharacters" are "reserved for internal use" by + applications, and according to older versions of the Unicode Standard + "should never be interchanged". Unicode Corrigendum #9 dropped the + latter restriction. Nevertheless, their presence in incoming UTF-8 data + can remain a potential security risk, depending on what use is made of + these codes subsequently. Examples of such internal use: + + - Some file APIs with 16-bit characters may use the integer value -1 + = U+FFFF to signal an end-of-file (EOF) or error condition. + + - In some UTF-16 receivers, code point U+FFFE might trigger a + byte-swap operation (to convert between UTF-16LE and UTF-16BE). + + With such internal use of noncharacters, it may be desirable and safer + to block those code points in UTF-8 decoders, as they should never + occur legitimately in incoming UTF-8 data, and could trigger unsafe + behaviour in subsequent processing. + + Particularly problematic noncharacters in 16-bit applications: */ + + assert_valid("5.3.1 U+FFFE = ef bf be = \"￾\""); + assert_valid("5.3.2 U+FFFF = ef bf bf = \"￿\""); + + /* Other noncharacters: */ + + assert_valid("5.3.3 U+FDD0 .. U+FDEF = \"﷐﷑﷒﷓﷔﷕﷖﷗﷘﷙﷚﷛﷜﷝﷞﷟﷠﷡﷢﷣﷤﷥﷦﷧﷨﷩﷪﷫﷬﷭﷮﷯\""); + + /* 5.3.4 U+nFFFE U+nFFFF (for n = 1..10) */ + + assert_valid("\"🿾🿿𯿾𯿿𿿾𿿿񏿾񏿿񟿾񟿿񯿾񯿿񿿾񿿿򏿾򏿿򟿾򟿿򯿾򯿿򿿾򿿿󏿾󏿿󟿾󟿿󯿾󯿿󿿾󿿿􏿾􏿿\""); + + + return 0; +} diff --git a/test/lib/cpp/Makefile b/test/lib/cpp/Makefile index 4d9e86f017..e5a85e5f68 100644 --- a/test/lib/cpp/Makefile +++ b/test/lib/cpp/Makefile @@ -80,6 +80,9 @@ all : 01 02 03 04 08 09 09-util-topic-tokenise.test : 09-util-topic-tokenise.cpp $(CXX) $< -o $@ $(CFLAGS) $(LIBS) +09-util-utf8-validate.test : 09-util-utf8-validate.cpp + $(CXX) $< -o $@ $(CFLAGS) $(LIBS) + 01 : 01-con-discon-success.test 01-will-set.test 01-unpwd-set.test 01-will-unpwd-set.test 01-no-clean-session.test 01-keepalive-pingreq.test 02 : 02-subscribe-qos0.test 02-subscribe-qos1.test 02-subscribe-qos2.test 02-unsubscribe.test @@ -90,7 +93,7 @@ all : 01 02 03 04 08 09 08 : 08-ssl-connect-no-auth.test 08-ssl-connect-cert-auth.test 08-ssl-connect-cert-auth-enc.test 08-ssl-bad-cacert.test 08-ssl-fake-cacert.test -09 : 09-util-topic-matching.test 09-util-topic-tokenise.test +09 : 09-util-topic-matching.test 09-util-topic-tokenise.test 09-util-utf8-validate.test reallyclean : clean -rm -f *.orig diff --git a/test/mosq_test.py b/test/mosq_test.py index 5cbc671c35..0357c64cff 100644 --- a/test/mosq_test.py +++ b/test/mosq_test.py @@ -10,7 +10,7 @@ def start_broker(filename, cmd=None, port=1888): if cmd is None: cmd = ['../../src/mosquitto', '-v', '-c', filename.replace('.py', '.conf')] if os.environ.get('MOSQ_USE_VALGRIND') is not None: - cmd = ['valgrind', '-q', '--log-file='+filename+'.vglog'] + cmd + cmd = ['valgrind', '--trace-children=yes', '-v', '--log-file='+filename+'.vglog'] + cmd delay = 1 broker = subprocess.Popen(cmd, stderr=subprocess.PIPE) @@ -211,7 +211,7 @@ def to_string(packet): (mid, packet) = struct.unpack(pack_format, packet) pack_format = "!" + "B"*len(packet) granted_qos = struct.unpack(pack_format, packet) - + s = "SUBACK, rl="+str(rl)+", mid="+str(mid)+", granted_qos="+str(granted_qos[0]) for i in range(1, len(granted_qos)-1): s = s+", "+str(granted_qos[i])