diff --git a/.github/lock.yml b/.github/lock.yml new file mode 100644 index 00000000..916ac1f7 --- /dev/null +++ b/.github/lock.yml @@ -0,0 +1,35 @@ +# Configuration for Lock Threads - https://github.com/dessant/lock-threads + +# Number of days of inactivity before a closed issue or pull request is locked +daysUntilLock: 90 + +# Skip issues and pull requests created before a given timestamp. Timestamp must +# follow ISO 8601 (`YYYY-MM-DD`). Set to `false` to disable +skipCreatedBefore: false + +# Issues and pull requests with these labels will be ignored. Set to `[]` to disable +exemptLabels: [] + +# Label to add before locking, such as `outdated`. Set to `false` to disable +lockLabel: false + +# Comment to post before locking. Set to `false` to disable +lockComment: false + +# Assign `resolved` as the reason for locking. Set to `false` to disable +setLockReason: true + +# Limit to only `issues` or `pulls` +only: issues + +# Optionally, specify configuration settings just for `issues` or `pulls` +# issues: +# exemptLabels: +# - help-wanted +# lockLabel: outdated + +# pulls: +# daysUntilLock: 30 + +# Repository to extend settings from +# _extends: repo diff --git a/CMakeLists.txt b/CMakeLists.txt index e58d072c..e707229b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ project(mosquitto) cmake_minimum_required(VERSION 2.8) # Only for version 3 and up. cmake_policy(SET CMP0042 NEW) -set (VERSION 1.6.2) +set (VERSION 1.6.3) add_definitions (-DCMAKE -DVERSION=\"${VERSION}\") @@ -116,4 +116,3 @@ install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libmosquittopp.pc" DESTINATION "${CMA # Testing # ======================================== enable_testing() -add_test("test" make -C ${mosquitto_SOURCE_DIR}/test test) diff --git a/ChangeLog.txt b/ChangeLog.txt index 9fe1fe96..6b3f8716 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -1,3 +1,62 @@ +1.6.3 - 20190618 +================ + +Broker: +- Fix detection of incoming v3.1/v3.1.1 bridges. Closes #1263. +- Fix default max_topic_alias listener config not being copied to the in-use + listener when compiled without TLS support. +- Fix random number generation if compiling using `WITH_TLS=no` and on Linux + with glibc >= 2.25. Without this fix, no random numbers would be generated + for e.g. on broker client id generation, and so clients connecting expecting + this feature would be unable to connect. +- Fix compilation problem related to `getrandom()` on non-glibc systems. +- Fix Will message for a persistent client incorrectly being sent when the + client reconnects after a clean disconnect. Closes #1273. +- Fix Will message for a persistent client not being sent on disconnect. + Closes #1273. +- Improve documentation around the upgrading of persistence files. Closes + #1276. +- Add 'extern "C"' on mosquitto_broker.h and mosquitto_plugin.h for C++ plugin + writing. Closes #1290. +- Fix persistent Websockets clients not receiving messages after they + reconnect, having sent DISCONNECT on a previous session. Closes #1227. +- Disable TLS renegotiation. Client initiated renegotiation is considered to + be a potential attack vector against servers. Closes #1257. +- Fix incorrect shared subscription topic '$shared'. +- Fix zero length client ids being rejected for MQTT v5 clients with clean + start set to true. +- Fix MQTT v5 overlapping subscription behaviour. Clients now receive message + from all matching subscriptions rather than the first one encountered, which + ensures the maximum QoS requirement is met. +- Fix incoming/outgoing quota problems for QoS>0. +- Remove obsolete `store_clean_interval` from documentation. +- Fix v4 authentication plugin never calling psk_key_get. + +Client library: +- Fix typo causing build error on Windows when building without TLS support. + Closes #1264. + +Clients: +- Fix -L url parsing when `/topic` part is missing. +- Stop some error messages being printed even when `--quiet` was used. + Closes #1284. +- Fix mosquitto_pub exiting with error code 0 when an error occurred. + Closes #1285. +- Fix mosquitto_pub not using the `-c` option. Closes #1273. +- Fix MQTT v5 clients not being able to specify a password without a username. + Closes #1274. +- Fix `mosquitto_pub -l` not handling network failures. Closes #1152. +- Fix `mosquitto_pub -l` not handling zero length input. Closes #1302. +- Fix double free on exit in mosquitto_pub. Closes #1280. + +Documentation: +- Remove references to Python binding and C++ wrapper in libmosquitto man + page. Closes #1266. + +Build: +- CLIENT_LDFLAGS now uses LDFLAGS. Closes #1294. + + 1.6.2 - 20190430 ================ diff --git a/client/client_shared.c b/client/client_shared.c index 8569651d..7d3ba773 100644 --- a/client/client_shared.c +++ b/client/client_shared.c @@ -18,6 +18,7 @@ Contributors: #include #include +#include #include #include #include @@ -229,7 +230,7 @@ int client_config_load(struct mosq_config *cfg, int pub_or_sub, int argc, char * len = strlen(env) + strlen("/mosquitto_pub") + 1; loc = malloc(len); if(!loc){ - fprintf(stderr, "Error: Out of memory.\n"); + err_printf(cfg, "Error: Out of memory.\n"); return 1; } if(pub_or_sub == CLIENT_PUB){ @@ -246,7 +247,7 @@ int client_config_load(struct mosq_config *cfg, int pub_or_sub, int argc, char * len = strlen(env) + strlen("/.config/mosquitto_pub") + 1; loc = malloc(len); if(!loc){ - fprintf(stderr, "Error: Out of memory.\n"); + err_printf(cfg, "Error: Out of memory.\n"); return 1; } if(pub_or_sub == CLIENT_PUB){ @@ -257,8 +258,6 @@ int client_config_load(struct mosq_config *cfg, int pub_or_sub, int argc, char * snprintf(loc, len, "%s/.config/mosquitto_rr", env); } loc[len-1] = '\0'; - }else{ - fprintf(stderr, "Warning: Unable to locate configuration directory, default config not loaded.\n"); } } @@ -268,7 +267,7 @@ int client_config_load(struct mosq_config *cfg, int pub_or_sub, int argc, char * len = strlen(env) + strlen("\\mosquitto_pub.conf") + 1; loc = malloc(len); if(!loc){ - fprintf(stderr, "Error: Out of memory.\n"); + err_printf(cfg, "Error: Out of memory.\n"); return 1; } if(pub_or_sub == CLIENT_PUB){ @@ -279,8 +278,6 @@ int client_config_load(struct mosq_config *cfg, int pub_or_sub, int argc, char * snprintf(loc, len, "%s\\mosquitto_rr.conf", env); } loc[len-1] = '\0'; - }else{ - fprintf(stderr, "Warning: Unable to locate configuration directory, default config not loaded.\n"); } #endif @@ -328,9 +325,6 @@ int client_config_load(struct mosq_config *cfg, int pub_or_sub, int argc, char * fprintf(stderr, "Error: Will retain given, but no will topic given.\n"); return 1; } - if(cfg->password && !cfg->username){ - if(!cfg->quiet) fprintf(stderr, "Warning: Not using password since username not set.\n"); - } #ifdef WITH_TLS if((cfg->certfile && !cfg->keyfile) || (cfg->keyfile && !cfg->certfile)){ fprintf(stderr, "Error: Both certfile and keyfile must be provided if one of them is set.\n"); @@ -347,23 +341,23 @@ int client_config_load(struct mosq_config *cfg, int pub_or_sub, int argc, char * #endif #ifdef FINAL_WITH_TLS_PSK if((cfg->cafile || cfg->capath) && cfg->psk){ - if(!cfg->quiet) fprintf(stderr, "Error: Only one of --psk or --cafile/--capath may be used at once.\n"); + fprintf(stderr, "Error: Only one of --psk or --cafile/--capath may be used at once.\n"); return 1; } if(cfg->psk && !cfg->psk_identity){ - if(!cfg->quiet) fprintf(stderr, "Error: --psk-identity required if --psk used.\n"); + fprintf(stderr, "Error: --psk-identity required if --psk used.\n"); return 1; } #endif if(cfg->clean_session == false && (cfg->id_prefix || !cfg->id)){ - if(!cfg->quiet) fprintf(stderr, "Error: You must provide a client id if you are using the -c option.\n"); + fprintf(stderr, "Error: You must provide a client id if you are using the -c option.\n"); return 1; } if(pub_or_sub == CLIENT_SUB){ if(cfg->topic_count == 0){ - if(!cfg->quiet) fprintf(stderr, "Error: You must specify a topic to subscribe to.\n"); + fprintf(stderr, "Error: You must specify a topic to subscribe to.\n"); return 1; } } @@ -371,39 +365,39 @@ int client_config_load(struct mosq_config *cfg, int pub_or_sub, int argc, char * if(!cfg->host){ cfg->host = strdup("localhost"); if(!cfg->host){ - if(!cfg->quiet) fprintf(stderr, "Error: Out of memory.\n"); + err_printf(cfg, "Error: Out of memory.\n"); return 1; } } rc = mosquitto_property_check_all(CMD_CONNECT, cfg->connect_props); if(rc){ - if(!cfg->quiet) fprintf(stderr, "Error in CONNECT properties: %s\n", mosquitto_strerror(rc)); + err_printf(cfg, "Error in CONNECT properties: %s\n", mosquitto_strerror(rc)); return 1; } rc = mosquitto_property_check_all(CMD_PUBLISH, cfg->publish_props); if(rc){ - if(!cfg->quiet) fprintf(stderr, "Error in PUBLISH properties: %s\n", mosquitto_strerror(rc)); + err_printf(cfg, "Error in PUBLISH properties: %s\n", mosquitto_strerror(rc)); return 1; } rc = mosquitto_property_check_all(CMD_SUBSCRIBE, cfg->subscribe_props); if(rc){ - if(!cfg->quiet) fprintf(stderr, "Error in SUBSCRIBE properties: %s\n", mosquitto_strerror(rc)); + err_printf(cfg, "Error in SUBSCRIBE properties: %s\n", mosquitto_strerror(rc)); return 1; } rc = mosquitto_property_check_all(CMD_UNSUBSCRIBE, cfg->unsubscribe_props); if(rc){ - if(!cfg->quiet) fprintf(stderr, "Error in UNSUBSCRIBE properties: %s\n", mosquitto_strerror(rc)); + err_printf(cfg, "Error in UNSUBSCRIBE properties: %s\n", mosquitto_strerror(rc)); return 1; } rc = mosquitto_property_check_all(CMD_DISCONNECT, cfg->disconnect_props); if(rc){ - if(!cfg->quiet) fprintf(stderr, "Error in DISCONNECT properties: %s\n", mosquitto_strerror(rc)); + err_printf(cfg, "Error in DISCONNECT properties: %s\n", mosquitto_strerror(rc)); return 1; } rc = mosquitto_property_check_all(CMD_WILL, cfg->will_props); if(rc){ - if(!cfg->quiet) fprintf(stderr, "Error in Will properties: %s\n", mosquitto_strerror(rc)); + err_printf(cfg, "Error in Will properties: %s\n", mosquitto_strerror(rc)); return 1; } @@ -436,7 +430,7 @@ int cfg_add_topic(struct mosq_config *cfg, int type, char *topic, const char *ar cfg->topic_count++; cfg->topics = realloc(cfg->topics, cfg->topic_count*sizeof(char *)); if(!cfg->topics){ - fprintf(stderr, "Error: Out of memory.\n"); + err_printf(cfg, "Error: Out of memory.\n"); return 1; } cfg->topics[cfg->topic_count-1] = strdup(topic); @@ -551,7 +545,7 @@ int client_config_line_proc(struct mosq_config *cfg, int pub_or_sub, int argc, c cfg->pub_mode = MSGMODE_FILE; cfg->file_input = strdup(argv[i+1]); if(!cfg->file_input){ - fprintf(stderr, "Error: Out of memory.\n"); + err_printf(cfg, "Error: Out of memory.\n"); return 1; } } @@ -662,6 +656,10 @@ int client_config_line_proc(struct mosq_config *cfg, int pub_or_sub, int argc, c return 1; } topic = strchr(url, '/'); + if(!topic){ + fprintf(stderr, "Error: Invalid URL for -L argument specified - topic missing.\n"); + return 1; + } *topic++ = 0; if(cfg_add_topic(cfg, pub_or_sub, topic, "-L topic")) @@ -1077,14 +1075,14 @@ int client_opts_set(struct mosquitto *mosq, struct mosq_config *cfg) cfg->will_payloadlen, cfg->will_payload, cfg->will_qos, cfg->will_retain, cfg->will_props)){ - if(!cfg->quiet) fprintf(stderr, "Error: Problem setting will.\n"); + err_printf(cfg, "Error: Problem setting will.\n"); mosquitto_lib_cleanup(); return 1; } cfg->will_props = NULL; - if(cfg->username && mosquitto_username_pw_set(mosq, cfg->username, cfg->password)){ - if(!cfg->quiet) fprintf(stderr, "Error: Problem setting username and password.\n"); + if((cfg->username || cfg->password) && mosquitto_username_pw_set(mosq, cfg->username, cfg->password)){ + err_printf(cfg, "Error: Problem setting username and/or password.\n"); mosquitto_lib_cleanup(); return 1; } @@ -1093,48 +1091,48 @@ int client_opts_set(struct mosquitto *mosq, struct mosq_config *cfg) rc = mosquitto_tls_set(mosq, cfg->cafile, cfg->capath, cfg->certfile, cfg->keyfile, NULL); if(rc){ if(rc == MOSQ_ERR_INVAL){ - if(!cfg->quiet) fprintf(stderr, "Error: Problem setting TLS options: File not found.\n"); + err_printf(cfg, "Error: Problem setting TLS options: File not found.\n"); }else{ - if(!cfg->quiet) fprintf(stderr, "Error: Problem setting TLS options: %s.\n", mosquitto_strerror(rc)); + err_printf(cfg, "Error: Problem setting TLS options: %s.\n", mosquitto_strerror(rc)); } mosquitto_lib_cleanup(); return 1; } } if(cfg->insecure && mosquitto_tls_insecure_set(mosq, true)){ - if(!cfg->quiet) fprintf(stderr, "Error: Problem setting TLS insecure option.\n"); + err_printf(cfg, "Error: Problem setting TLS insecure option.\n"); mosquitto_lib_cleanup(); return 1; } if(cfg->tls_engine && mosquitto_string_option(mosq, MOSQ_OPT_TLS_ENGINE, cfg->tls_engine)){ - if(!cfg->quiet) fprintf(stderr, "Error: Problem setting TLS engine, is %s a valid engine?\n", cfg->tls_engine); + err_printf(cfg, "Error: Problem setting TLS engine, is %s a valid engine?\n", cfg->tls_engine); mosquitto_lib_cleanup(); return 1; } if(cfg->keyform && mosquitto_string_option(mosq, MOSQ_OPT_TLS_KEYFORM, cfg->keyform)){ - if(!cfg->quiet) fprintf(stderr, "Error: Problem setting key form, it must be one of 'pem' or 'engine'.\n"); + err_printf(cfg, "Error: Problem setting key form, it must be one of 'pem' or 'engine'.\n"); mosquitto_lib_cleanup(); return 1; } if(cfg->tls_engine_kpass_sha1 && mosquitto_string_option(mosq, MOSQ_OPT_TLS_ENGINE_KPASS_SHA1, cfg->tls_engine_kpass_sha1)){ - if(!cfg->quiet) fprintf(stderr, "Error: Problem setting TLS engine key pass sha, is it a 40 character hex string?\n"); + err_printf(cfg, "Error: Problem setting TLS engine key pass sha, is it a 40 character hex string?\n"); mosquitto_lib_cleanup(); return 1; } if(cfg->tls_alpn && mosquitto_string_option(mosq, MOSQ_OPT_TLS_ALPN, cfg->tls_alpn)){ - if(!cfg->quiet) fprintf(stderr, "Error: Problem setting TLS ALPN protocol.\n"); + err_printf(cfg, "Error: Problem setting TLS ALPN protocol.\n"); mosquitto_lib_cleanup(); return 1; } # ifdef FINAL_WITH_TLS_PSK if(cfg->psk && mosquitto_tls_psk_set(mosq, cfg->psk, cfg->psk_identity, NULL)){ - if(!cfg->quiet) fprintf(stderr, "Error: Problem setting TLS-PSK options.\n"); + err_printf(cfg, "Error: Problem setting TLS-PSK options.\n"); mosquitto_lib_cleanup(); return 1; } # endif if((cfg->tls_version || cfg->ciphers) && mosquitto_tls_opts_set(mosq, 1, cfg->tls_version, cfg->ciphers)){ - if(!cfg->quiet) fprintf(stderr, "Error: Problem setting TLS options, check the options are valid.\n"); + err_printf(cfg, "Error: Problem setting TLS options, check the options are valid.\n"); mosquitto_lib_cleanup(); return 1; } @@ -1157,7 +1155,7 @@ int client_id_generate(struct mosq_config *cfg) if(cfg->id_prefix){ cfg->id = malloc(strlen(cfg->id_prefix)+10); if(!cfg->id){ - if(!cfg->quiet) fprintf(stderr, "Error: Out of memory.\n"); + err_printf(cfg, "Error: Out of memory.\n"); mosquitto_lib_cleanup(); return 1; } @@ -1203,17 +1201,15 @@ int client_connect(struct mosquitto *mosq, struct mosq_config *cfg) rc = mosquitto_connect_bind_v5(mosq, cfg->host, port, cfg->keepalive, cfg->bind_address, cfg->connect_props); #endif if(rc>0){ - if(!cfg->quiet){ - if(rc == MOSQ_ERR_ERRNO){ + if(rc == MOSQ_ERR_ERRNO){ #ifndef WIN32 - err = strerror(errno); + err = strerror(errno); #else - FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, errno, 0, (LPTSTR)&err, 1024, NULL); + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, errno, 0, (LPTSTR)&err, 1024, NULL); #endif - fprintf(stderr, "Error: %s\n", err); - }else{ - fprintf(stderr, "Unable to connect (%s).\n", mosquitto_strerror(rc)); - } + err_printf(cfg, "Error: %s\n", err); + }else{ + err_printf(cfg, "Unable to connect (%s).\n", mosquitto_strerror(rc)); } mosquitto_lib_cleanup(); return rc; @@ -1280,7 +1276,7 @@ static int mosquitto__parse_socks_url(struct mosq_config *cfg, char *url) if(!strncmp(url, "socks5h://", strlen("socks5h://"))){ str = url + strlen("socks5h://"); }else{ - fprintf(stderr, "Error: Unsupported proxy protocol: %s\n", url); + err_printf(cfg, "Error: Unsupported proxy protocol: %s\n", url); return 1; } @@ -1307,7 +1303,7 @@ static int mosquitto__parse_socks_url(struct mosq_config *cfg, char *url) len = i-start; host = malloc(len + 1); if(!host){ - fprintf(stderr, "Error: Out of memory.\n"); + err_printf(cfg, "Error: Out of memory.\n"); goto cleanup; } memcpy(host, &(str[start]), len); @@ -1320,7 +1316,7 @@ static int mosquitto__parse_socks_url(struct mosq_config *cfg, char *url) len = i-start; username_or_host = malloc(len + 1); if(!username_or_host){ - fprintf(stderr, "Error: Out of memory.\n"); + err_printf(cfg, "Error: Out of memory.\n"); goto cleanup; } memcpy(username_or_host, &(str[start]), len); @@ -1340,7 +1336,7 @@ static int mosquitto__parse_socks_url(struct mosq_config *cfg, char *url) len = i-start; password = malloc(len + 1); if(!password){ - fprintf(stderr, "Error: Out of memory.\n"); + err_printf(cfg, "Error: Out of memory.\n"); goto cleanup; } memcpy(password, &(str[start]), len); @@ -1356,7 +1352,7 @@ static int mosquitto__parse_socks_url(struct mosq_config *cfg, char *url) len = i-start; username = malloc(len + 1); if(!username){ - fprintf(stderr, "Error: Out of memory.\n"); + err_printf(cfg, "Error: Out of memory.\n"); goto cleanup; } memcpy(username, &(str[start]), len); @@ -1374,7 +1370,7 @@ static int mosquitto__parse_socks_url(struct mosq_config *cfg, char *url) * socks5h://username[:password]@host:port */ port = malloc(len + 1); if(!port){ - fprintf(stderr, "Error: Out of memory.\n"); + err_printf(cfg, "Error: Out of memory.\n"); goto cleanup; } memcpy(port, &(str[start]), len); @@ -1386,7 +1382,7 @@ static int mosquitto__parse_socks_url(struct mosq_config *cfg, char *url) username_or_host = NULL; port = malloc(len + 1); if(!port){ - fprintf(stderr, "Error: Out of memory.\n"); + err_printf(cfg, "Error: Out of memory.\n"); goto cleanup; } memcpy(port, &(str[start]), len); @@ -1394,7 +1390,7 @@ static int mosquitto__parse_socks_url(struct mosq_config *cfg, char *url) }else{ host = malloc(len + 1); if(!host){ - fprintf(stderr, "Error: Out of memory.\n"); + err_printf(cfg, "Error: Out of memory.\n"); goto cleanup; } memcpy(host, &(str[start]), len); @@ -1403,7 +1399,7 @@ static int mosquitto__parse_socks_url(struct mosq_config *cfg, char *url) } if(!host){ - fprintf(stderr, "Error: Invalid proxy.\n"); + err_printf(cfg, "Error: Invalid proxy.\n"); goto cleanup; } @@ -1416,7 +1412,7 @@ static int mosquitto__parse_socks_url(struct mosq_config *cfg, char *url) if(port){ port_int = atoi(port); if(port_int < 1 || port_int > 65535){ - fprintf(stderr, "Error: Invalid proxy port %d\n", port_int); + err_printf(cfg, "Error: Invalid proxy port %d\n", port_int); goto cleanup; } free(port); @@ -1438,5 +1434,16 @@ cleanup: if(port) free(port); return 1; } - #endif + +void err_printf(const struct mosq_config *cfg, const char *fmt, ...) +{ + va_list va; + + if(cfg->quiet) return; + + va_start(va, fmt); + vfprintf(stderr, fmt, va); + va_end(va); +} + diff --git a/client/client_shared.h b/client/client_shared.h index 7e65f3d1..232fa909 100644 --- a/client/client_shared.h +++ b/client/client_shared.h @@ -126,4 +126,6 @@ int client_connect(struct mosquitto *mosq, struct mosq_config *cfg); int cfg_parse_property(struct mosq_config *cfg, int argc, char *argv[], int *idx); +void err_printf(const struct mosq_config *cfg, const char *fmt, ...); + #endif diff --git a/client/pub_client.c b/client/pub_client.c index cdf89741..31740391 100644 --- a/client/pub_client.c +++ b/client/pub_client.c @@ -42,7 +42,6 @@ static int last_mid = -1; static int last_mid_sent = -1; static char *line_buf = NULL; static int line_buf_len = 1024; -static bool connected = true; static bool disconnect_sent = false; static int publish_count = 0; static bool ready_for_repeat = false; @@ -104,7 +103,7 @@ void my_disconnect_callback(struct mosquitto *mosq, void *obj, int rc, const mos UNUSED(rc); UNUSED(properties); - connected = false; + status = STATUS_DISCONNECTED; } int my_publish(struct mosquitto *mosq, int *mid, const char *topic, int payloadlen, void *payload, int qos, bool retain) @@ -142,36 +141,34 @@ void my_connect_callback(struct mosquitto *mosq, void *obj, int result, int flag break; } if(rc){ - if(!cfg.quiet){ - switch(rc){ - case MOSQ_ERR_INVAL: - fprintf(stderr, "Error: Invalid input. Does your topic contain '+' or '#'?\n"); - break; - case MOSQ_ERR_NOMEM: - fprintf(stderr, "Error: Out of memory when trying to publish message.\n"); - break; - case MOSQ_ERR_NO_CONN: - fprintf(stderr, "Error: Client not connected when trying to publish.\n"); - break; - case MOSQ_ERR_PROTOCOL: - fprintf(stderr, "Error: Protocol error when communicating with broker.\n"); - break; - case MOSQ_ERR_PAYLOAD_SIZE: - fprintf(stderr, "Error: Message payload is too large.\n"); - break; - case MOSQ_ERR_QOS_NOT_SUPPORTED: - fprintf(stderr, "Error: Message QoS not supported on broker, try a lower QoS.\n"); - break; - } + switch(rc){ + case MOSQ_ERR_INVAL: + err_printf(&cfg, "Error: Invalid input. Does your topic contain '+' or '#'?\n"); + break; + case MOSQ_ERR_NOMEM: + err_printf(&cfg, "Error: Out of memory when trying to publish message.\n"); + break; + case MOSQ_ERR_NO_CONN: + err_printf(&cfg, "Error: Client not connected when trying to publish.\n"); + break; + case MOSQ_ERR_PROTOCOL: + err_printf(&cfg, "Error: Protocol error when communicating with broker.\n"); + break; + case MOSQ_ERR_PAYLOAD_SIZE: + err_printf(&cfg, "Error: Message payload is too large.\n"); + break; + case MOSQ_ERR_QOS_NOT_SUPPORTED: + err_printf(&cfg, "Error: Message QoS not supported on broker, try a lower QoS.\n"); + break; } mosquitto_disconnect_v5(mosq, 0, cfg.disconnect_props); } }else{ - if(result && !cfg.quiet){ + if(result){ if(cfg.protocol_version == MQTT_PROTOCOL_V5){ - fprintf(stderr, "%s\n", mosquitto_reason_string(result)); + err_printf(&cfg, "%s\n", mosquitto_reason_string(result)); }else{ - fprintf(stderr, "%s\n", mosquitto_connack_string(result)); + err_printf(&cfg, "%s\n", mosquitto_connack_string(result)); } } } @@ -185,7 +182,7 @@ void my_publish_callback(struct mosquitto *mosq, void *obj, int mid, int reason_ last_mid_sent = mid; if(reason_code > 127){ - if(!cfg.quiet) fprintf(stderr, "Warning: Publish %d failed: %s.\n", mid, mosquitto_reason_string(reason_code)); + err_printf(&cfg, "Warning: Publish %d failed: %s.\n", mid, mosquitto_reason_string(reason_code)); } publish_count++; @@ -208,7 +205,7 @@ int pub_shared_init(void) { line_buf = malloc(line_buf_len); if(!line_buf){ - fprintf(stderr, "Error: Out of memory.\n"); + err_printf(&cfg, "Error: Out of memory.\n"); return 1; } return 0; @@ -224,6 +221,7 @@ int pub_shared_loop(struct mosquitto *mosq) int buf_len_actual; int mode; int loop_delay = 1000; + bool stdin_finished = false; if(cfg.repeat_count > 1 && (cfg.repeat_delay.tv_sec == 0 || cfg.repeat_delay.tv_usec != 0)){ loop_delay = cfg.repeat_delay.tv_usec / 2000; @@ -233,6 +231,7 @@ int pub_shared_loop(struct mosquitto *mosq) if(mode == MSGMODE_STDIN_LINE){ mosquitto_loop_start(mosq); + stdin_finished = false; } do{ @@ -240,13 +239,13 @@ int pub_shared_loop(struct mosquitto *mosq) if(status == STATUS_CONNACK_RECVD){ pos = 0; read_len = line_buf_len; - while(connected && fgets(&line_buf[pos], read_len, stdin)){ + while(status == STATUS_CONNACK_RECVD && fgets(&line_buf[pos], read_len, stdin)){ buf_len_actual = strlen(line_buf); if(line_buf[buf_len_actual-1] == '\n'){ line_buf[buf_len_actual-1] = '\0'; rc2 = my_publish(mosq, &mid_sent, cfg.topic, buf_len_actual-1, line_buf, cfg.qos, cfg.retain); if(rc2){ - if(!cfg.quiet) fprintf(stderr, "Error: Publish returned %d, disconnecting.\n", rc2); + err_printf(&cfg, "Error: Publish returned %d, disconnecting.\n", rc2); mosquitto_disconnect_v5(mosq, MQTT_RC_DISCONNECT_WITH_WILL_MSG, cfg.disconnect_props); } break; @@ -256,7 +255,7 @@ int pub_shared_loop(struct mosquitto *mosq) read_len = 1024; buf2 = realloc(line_buf, line_buf_len); if(!buf2){ - fprintf(stderr, "Error: Out of memory.\n"); + err_printf(&cfg, "Error: Out of memory.\n"); return MOSQ_ERR_NOMEM; } line_buf = buf2; @@ -272,6 +271,10 @@ int pub_shared_loop(struct mosquitto *mosq) last_mid = mid_sent; status = STATUS_WAITING; } + stdin_finished = true; + }else if(status == STATUS_DISCONNECTED){ + /* Not end of stdin, so we've lost our connection and must + * reconnect */ } }else if(status == STATUS_WAITING){ if(last_mid_sent == last_mid && disconnect_sent == false){ @@ -305,16 +308,20 @@ int pub_shared_loop(struct mosquitto *mosq) break; } if(rc){ - fprintf(stderr, "Error sending repeat publish: %s", mosquitto_strerror(rc)); + err_printf(&cfg, "Error sending repeat publish: %s", mosquitto_strerror(rc)); } } } - }while(rc == MOSQ_ERR_SUCCESS && connected); + }while(rc == MOSQ_ERR_SUCCESS && stdin_finished == false); if(mode == MSGMODE_STDIN_LINE){ mosquitto_loop_stop(mosq, false); } - return 0; + if(status == STATUS_DISCONNECTED){ + return MOSQ_ERR_SUCCESS; + }else{ + return rc; + } } @@ -331,7 +338,7 @@ void print_usage(void) mosquitto_lib_version(&major, &minor, &revision); printf("mosquitto_pub is a simple mqtt client that will publish a message on a single topic and exit.\n"); printf("mosquitto_pub version %s running on libmosquitto %d.%d.%d.\n\n", VERSION, major, minor, revision); - printf("Usage: mosquitto_pub {[-h host] [-p port] [-u username [-P password]] -t topic | -L URL}\n"); + printf("Usage: mosquitto_pub {[-h host] [-p port] [-u username] [-P password] -t topic | -L URL}\n"); printf(" {-f file | -l | -n | -m message}\n"); printf(" [-c] [-k keepalive] [-q qos] [-r] [--repeat N] [--repeat-delay time]\n"); #ifdef WITH_SRV @@ -457,12 +464,12 @@ int main(int argc, char *argv[]) if(cfg.pub_mode == MSGMODE_STDIN_FILE){ if(load_stdin()){ - fprintf(stderr, "Error loading input from stdin.\n"); + err_printf(&cfg, "Error loading input from stdin.\n"); goto cleanup; } }else if(cfg.file_input){ if(load_file(cfg.file_input)){ - fprintf(stderr, "Error loading input file \"%s\".\n", cfg.file_input); + err_printf(&cfg, "Error loading input file \"%s\".\n", cfg.file_input); goto cleanup; } } @@ -478,14 +485,14 @@ int main(int argc, char *argv[]) goto cleanup; } - mosq = mosquitto_new(cfg.id, true, NULL); + mosq = mosquitto_new(cfg.id, cfg.clean_session, NULL); if(!mosq){ switch(errno){ case ENOMEM: - if(!cfg.quiet) fprintf(stderr, "Error: Out of memory.\n"); + err_printf(&cfg, "Error: Out of memory.\n"); break; case EINVAL: - if(!cfg.quiet) fprintf(stderr, "Error: Invalid id.\n"); + err_printf(&cfg, "Error: Invalid id.\n"); break; } goto cleanup; @@ -510,6 +517,7 @@ int main(int argc, char *argv[]) if(cfg.message && cfg.pub_mode == MSGMODE_FILE){ free(cfg.message); + cfg.message = NULL; } mosquitto_destroy(mosq); mosquitto_lib_cleanup(); @@ -517,7 +525,7 @@ int main(int argc, char *argv[]) pub_shared_cleanup(); if(rc){ - fprintf(stderr, "Error: %s\n", mosquitto_strerror(rc)); + err_printf(&cfg, "Error: %s\n", mosquitto_strerror(rc)); } return rc; diff --git a/client/pub_shared.c b/client/pub_shared.c index ead86a34..008bef4e 100644 --- a/client/pub_shared.c +++ b/client/pub_shared.c @@ -36,7 +36,7 @@ Contributors: /* Global variables for use in callbacks. See sub_client.c for an example of * using a struct to hold variables for use in callbacks. */ -int mid_sent = 0; +int mid_sent = -1; int status = STATUS_CONNECTING; struct mosq_config cfg; @@ -61,7 +61,7 @@ int load_stdin(void) rlen = fread(buf, 1, 1024, stdin); aux_message = realloc(cfg.message, pos+rlen); if(!aux_message){ - if(!cfg.quiet) fprintf(stderr, "Error: Out of memory.\n"); + err_printf(&cfg, "Error: Out of memory.\n"); free(cfg.message); return 1; } else @@ -74,7 +74,7 @@ int load_stdin(void) cfg.msglen = pos; if(!cfg.msglen){ - if(!cfg.quiet) fprintf(stderr, "Error: Zero length input.\n"); + err_printf(&cfg, "Error: Zero length input.\n"); return 1; } @@ -88,7 +88,7 @@ int load_file(const char *filename) fptr = fopen(filename, "rb"); if(!fptr){ - if(!cfg.quiet) fprintf(stderr, "Error: Unable to open file \"%s\".\n", filename); + err_printf(&cfg, "Error: Unable to open file \"%s\".\n", filename); return 1; } cfg.pub_mode = MSGMODE_FILE; @@ -96,22 +96,22 @@ int load_file(const char *filename) cfg.msglen = ftell(fptr); if(cfg.msglen > 268435455){ fclose(fptr); - if(!cfg.quiet) fprintf(stderr, "Error: File \"%s\" is too large (>268,435,455 bytes).\n", filename); + err_printf(&cfg, "Error: File \"%s\" is too large (>268,435,455 bytes).\n", filename); return 1; }else if(cfg.msglen == 0){ fclose(fptr); - if(!cfg.quiet) fprintf(stderr, "Error: File \"%s\" is empty.\n", filename); + err_printf(&cfg, "Error: File \"%s\" is empty.\n", filename); return 1; }else if(cfg.msglen < 0){ fclose(fptr); - if(!cfg.quiet) fprintf(stderr, "Error: Unable to determine size of file \"%s\".\n", filename); + err_printf(&cfg, "Error: Unable to determine size of file \"%s\".\n", filename); return 1; } fseek(fptr, 0, SEEK_SET); cfg.message = malloc(cfg.msglen); if(!cfg.message){ fclose(fptr); - if(!cfg.quiet) fprintf(stderr, "Error: Out of memory.\n"); + err_printf(&cfg, "Error: Out of memory.\n"); return 1; } pos = 0; diff --git a/client/pub_shared.h b/client/pub_shared.h index fd7331f7..94d5d11c 100644 --- a/client/pub_shared.h +++ b/client/pub_shared.h @@ -20,6 +20,7 @@ Contributors: #define STATUS_CONNACK_RECVD 1 #define STATUS_WAITING 2 #define STATUS_DISCONNECTING 3 +#define STATUS_DISCONNECTED 4 extern int mid_sent; extern int status; diff --git a/client/rr_client.c b/client/rr_client.c index 5ecd3093..6444c28e 100644 --- a/client/rr_client.c +++ b/client/rr_client.c @@ -121,8 +121,8 @@ void my_connect_callback(struct mosquitto *mosq, void *obj, int result, int flag mosquitto_subscribe_v5(mosq, NULL, cfg.response_topic, cfg.qos, 0, cfg.subscribe_props); }else{ client_state = rr_s_disconnect; - if(result && !cfg.quiet){ - fprintf(stderr, "%s\n", mosquitto_connack_string(result)); + if(result){ + err_printf(&cfg, "%s\n", mosquitto_connack_string(result)); } mosquitto_disconnect_v5(mosq, 0, cfg.disconnect_props); } @@ -135,10 +135,8 @@ void my_subscribe_callback(struct mosquitto *mosq, void *obj, int mid, int qos_c client_state = rr_s_ready_to_publish; }else{ client_state = rr_s_disconnect; - if(!cfg.quiet){ - fprintf(stderr, "%s\n", mosquitto_reason_string(granted_qos[0])); - mosquitto_disconnect_v5(mosq, 0, cfg.disconnect_props); - } + err_printf(&cfg, "%s\n", mosquitto_reason_string(granted_qos[0])); + mosquitto_disconnect_v5(mosq, 0, cfg.disconnect_props); } } @@ -158,7 +156,7 @@ void print_usage(void) printf(" Defaults to MQTT v5, where the Request-Response feature will be used, but v3.1.1 can also be used\n"); printf(" with v3.1.1 brokers.\n"); printf("mosquitto_rr version %s running on libmosquitto %d.%d.%d.\n\n", VERSION, major, minor, revision); - printf("Usage: mosquitto_rr {[-h host] [-p port] [-u username [-P password]] -t topic | -L URL} -e response-topic\n"); + printf("Usage: mosquitto_rr {[-h host] [-p port] [-u username] [-P password] -t topic | -L URL} -e response-topic\n"); printf(" [-c] [-k keepalive] [-q qos] [-R]\n"); printf(" [-F format]\n"); #ifndef WIN32 @@ -282,7 +280,7 @@ int main(int argc, char *argv[]) } rc = mosquitto_property_check_all(CMD_PUBLISH, cfg.publish_props); if(rc){ - if(!cfg.quiet) fprintf(stderr, "Error in PUBLISH properties: Duplicate response topic.\n"); + err_printf(&cfg, "Error in PUBLISH properties: Duplicate response topic.\n"); goto cleanup; } @@ -294,10 +292,10 @@ int main(int argc, char *argv[]) if(!mosq){ switch(errno){ case ENOMEM: - if(!cfg.quiet) fprintf(stderr, "Error: Out of memory.\n"); + err_printf(&cfg, "Error: Out of memory.\n"); break; case EINVAL: - if(!cfg.quiet) fprintf(stderr, "Error: Invalid id and/or clean_session.\n"); + err_printf(&cfg, "Error: Invalid id and/or clean_session.\n"); break; } goto cleanup; diff --git a/client/sub_client.c b/client/sub_client.c index c970537c..f57a48cc 100644 --- a/client/sub_client.c +++ b/client/sub_client.c @@ -35,7 +35,7 @@ Contributors: #include #include "client_shared.h" -static struct mosq_config cfg; +struct mosq_config cfg; bool process_messages = true; int msg_count = 0; struct mosquitto *mosq = NULL; @@ -124,11 +124,11 @@ void my_connect_callback(struct mosquitto *mosq, void *obj, int result, int flag mosquitto_unsubscribe_v5(mosq, NULL, cfg.unsub_topics[i], cfg.unsubscribe_props); } }else{ - if(result && !cfg.quiet){ + if(result){ if(cfg.protocol_version == MQTT_PROTOCOL_V5){ - fprintf(stderr, "%s\n", mosquitto_reason_string(result)); + err_printf(&cfg, "%s\n", mosquitto_reason_string(result)); }else{ - fprintf(stderr, "%s\n", mosquitto_connack_string(result)); + err_printf(&cfg, "%s\n", mosquitto_connack_string(result)); } } mosquitto_disconnect_v5(mosq, 0, cfg.disconnect_props); @@ -168,7 +168,7 @@ void print_usage(void) mosquitto_lib_version(&major, &minor, &revision); printf("mosquitto_sub is a simple mqtt client that will subscribe to a set of topics and print all messages it receives.\n"); printf("mosquitto_sub version %s running on libmosquitto %d.%d.%d.\n\n", VERSION, major, minor, revision); - printf("Usage: mosquitto_sub {[-h host] [-p port] [-u username [-P password]] -t topic | -L URL [-t topic]}\n"); + printf("Usage: mosquitto_sub {[-h host] [-p port] [-u username] [-P password] -t topic | -L URL [-t topic]}\n"); printf(" [-c] [-k keepalive] [-q qos]\n"); printf(" [-C msg_count] [-E] [-R] [--retained-only] [--remove-retained] [-T filter_out] [-U topic ...]\n"); printf(" [-F format]\n"); @@ -307,10 +307,10 @@ int main(int argc, char *argv[]) if(!mosq){ switch(errno){ case ENOMEM: - if(!cfg.quiet) fprintf(stderr, "Error: Out of memory.\n"); + err_printf(&cfg, "Error: Out of memory.\n"); break; case EINVAL: - if(!cfg.quiet) fprintf(stderr, "Error: Invalid id and/or clean_session.\n"); + err_printf(&cfg, "Error: Invalid id and/or clean_session.\n"); break; } goto cleanup; diff --git a/client/sub_client_output.c b/client/sub_client_output.c index c4ff376a..a7cdefb2 100644 --- a/client/sub_client_output.c +++ b/client/sub_client_output.c @@ -37,6 +37,7 @@ Contributors: #include #include "client_shared.h" +extern struct mosq_config cfg; static int get_time(struct tm **ti, long *ns) { @@ -60,7 +61,7 @@ static int get_time(struct tm **ti, long *ns) *ns = tv.tv_usec*1000; #else if(clock_gettime(CLOCK_REALTIME, &ts) != 0){ - fprintf(stderr, "Error obtaining system time.\n"); + err_printf(&cfg, "Error obtaining system time.\n"); return 1; } s = ts.tv_sec; @@ -69,7 +70,7 @@ static int get_time(struct tm **ti, long *ns) *ti = localtime(&s); if(!(*ti)){ - fprintf(stderr, "Error obtaining system time.\n"); + err_printf(&cfg, "Error obtaining system time.\n"); return 1; } @@ -130,7 +131,7 @@ static void json_print(const struct mosquitto_message *message, const struct tm } -static void formatted_print(const struct mosq_config *cfg, const struct mosquitto_message *message) +static void formatted_print(const struct mosq_config *lcfg, const struct mosquitto_message *message) { int len; int i; @@ -139,13 +140,13 @@ static void formatted_print(const struct mosq_config *cfg, const struct mosquitt char strf[3]; char buf[100]; - len = strlen(cfg->format); + len = strlen(lcfg->format); for(i=0; iformat[i] == '%'){ + if(lcfg->format[i] == '%'){ if(i < len-1){ i++; - switch(cfg->format[i]){ + switch(lcfg->format[i]){ case '%': fputc('%', stdout); break; @@ -153,7 +154,7 @@ static void formatted_print(const struct mosq_config *cfg, const struct mosquitt case 'I': if(!ti){ if(get_time(&ti, &ns)){ - fprintf(stderr, "Error obtaining system time.\n"); + err_printf(lcfg, "Error obtaining system time.\n"); return; } } @@ -165,7 +166,7 @@ static void formatted_print(const struct mosq_config *cfg, const struct mosquitt case 'j': if(!ti){ if(get_time(&ti, &ns)){ - fprintf(stderr, "Error obtaining system time.\n"); + err_printf(lcfg, "Error obtaining system time.\n"); return; } } @@ -175,7 +176,7 @@ static void formatted_print(const struct mosq_config *cfg, const struct mosquitt case 'J': if(!ti){ if(get_time(&ti, &ns)){ - fprintf(stderr, "Error obtaining system time.\n"); + err_printf(lcfg, "Error obtaining system time.\n"); return; } } @@ -213,7 +214,7 @@ static void formatted_print(const struct mosq_config *cfg, const struct mosquitt case 'U': if(!ti){ if(get_time(&ti, &ns)){ - fprintf(stderr, "Error obtaining system time.\n"); + err_printf(lcfg, "Error obtaining system time.\n"); return; } } @@ -231,24 +232,24 @@ static void formatted_print(const struct mosq_config *cfg, const struct mosquitt break; } } - }else if(cfg->format[i] == '@'){ + }else if(lcfg->format[i] == '@'){ if(i < len-1){ i++; - if(cfg->format[i] == '@'){ + if(lcfg->format[i] == '@'){ fputc('@', stdout); }else{ if(!ti){ if(get_time(&ti, &ns)){ - fprintf(stderr, "Error obtaining system time.\n"); + err_printf(lcfg, "Error obtaining system time.\n"); return; } } strf[0] = '%'; - strf[1] = cfg->format[i]; + strf[1] = lcfg->format[i]; strf[2] = 0; - if(cfg->format[i] == 'N'){ + if(lcfg->format[i] == 'N'){ printf("%09ld", ns); }else{ if(strftime(buf, 100, strf, ti) != 0){ @@ -257,10 +258,10 @@ static void formatted_print(const struct mosq_config *cfg, const struct mosquitt } } } - }else if(cfg->format[i] == '\\'){ + }else if(lcfg->format[i] == '\\'){ if(i < len-1){ i++; - switch(cfg->format[i]){ + switch(lcfg->format[i]){ case '\\': fputc('\\', stdout); break; @@ -295,10 +296,10 @@ static void formatted_print(const struct mosq_config *cfg, const struct mosquitt } } }else{ - fputc(cfg->format[i], stdout); + fputc(lcfg->format[i], stdout); } } - if(cfg->eol){ + if(lcfg->eol){ fputc('\n', stdout); } fflush(stdout); diff --git a/config.h b/config.h index b7a7616d..f717d00c 100644 --- a/config.h +++ b/config.h @@ -1,4 +1,5 @@ #ifndef CONFIG_H +#define CONFIG_H /* ============================================================ * Platform options * ============================================================ */ @@ -60,4 +61,9 @@ #define UNUSED(A) (void)(A) +/* Android Bionic libpthread implementation doesn't have pthread_cancel */ +#ifndef ANDROID +# define HAVE_PTHREAD_CANCEL +#endif + #endif diff --git a/config.mk b/config.mk index 9c9592a1..e305d0cd 100644 --- a/config.mk +++ b/config.mk @@ -104,7 +104,7 @@ WITH_COVERAGE:=no # Also bump lib/mosquitto.h, CMakeLists.txt, # installer/mosquitto.nsi, installer/mosquitto64.nsi -VERSION=1.6.2 +VERSION=1.6.3 # Client library SO version. Bump if incompatible API/ABI changes are made. SOVERSION=1 @@ -143,7 +143,7 @@ BROKER_LDADD:= CLIENT_CPPFLAGS:=$(CPPFLAGS) -I.. -I../lib CLIENT_CFLAGS:=${CFLAGS} -DVERSION="\"${VERSION}\"" -CLIENT_LDFLAGS:=-L../lib +CLIENT_LDFLAGS:=$(LDFLAGS) -L../lib CLIENT_LDADD:= PASSWD_LDADD:= diff --git a/installer/mosquitto.nsi b/installer/mosquitto.nsi index 79d48f97..15cd507d 100644 --- a/installer/mosquitto.nsi +++ b/installer/mosquitto.nsi @@ -9,7 +9,7 @@ !define env_hklm 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"' Name "Eclipse Mosquitto" -!define VERSION 1.6.2 +!define VERSION 1.6.3 OutFile "mosquitto-${VERSION}-install-windows-x86.exe" InstallDir "$PROGRAMFILES\mosquitto" diff --git a/installer/mosquitto64.nsi b/installer/mosquitto64.nsi index 985c3499..6027fc89 100644 --- a/installer/mosquitto64.nsi +++ b/installer/mosquitto64.nsi @@ -9,7 +9,7 @@ !define env_hklm 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"' Name "Eclipse Mosquitto" -!define VERSION 1.6.2 +!define VERSION 1.6.3 OutFile "mosquitto-${VERSION}-install-windows-x64.exe" !include "x64.nsh" diff --git a/lib/handle_pubrel.c b/lib/handle_pubrel.c index 40a91a64..d4a90652 100644 --- a/lib/handle_pubrel.c +++ b/lib/handle_pubrel.c @@ -83,7 +83,6 @@ int handle__pubrel(struct mosquitto_db *db, struct mosquitto *mosq) }else if(rc != MOSQ_ERR_SUCCESS){ /* 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); } rc = send__pubcomp(mosq, mid); diff --git a/lib/mosquitto.c b/lib/mosquitto.c index ed71604d..4b5768f3 100644 --- a/lib/mosquitto.c +++ b/lib/mosquitto.c @@ -201,11 +201,13 @@ void mosquitto__destroy(struct mosquitto *mosq) if(!mosq) return; #ifdef WITH_THREADING +# ifdef HAVE_PTHREAD_CANCEL 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 = mosq_ts_none; } +# endif if(mosq->id){ /* If mosq->id is not NULL then the client has already been initialised diff --git a/lib/mosquitto.h b/lib/mosquitto.h index 49217460..09bba66e 100644 --- a/lib/mosquitto.h +++ b/lib/mosquitto.h @@ -48,7 +48,7 @@ extern "C" { #define LIBMOSQUITTO_MAJOR 1 #define LIBMOSQUITTO_MINOR 6 -#define LIBMOSQUITTO_REVISION 2 +#define LIBMOSQUITTO_REVISION 3 /* LIBMOSQUITTO_VERSION_NUMBER looks like 1002001 for e.g. version 1.2.1. */ #define LIBMOSQUITTO_VERSION_NUMBER (LIBMOSQUITTO_MAJOR*1000000+LIBMOSQUITTO_MINOR*1000+LIBMOSQUITTO_REVISION) diff --git a/lib/mosquitto_internal.h b/lib/mosquitto_internal.h index 2c551589..ff656a43 100644 --- a/lib/mosquitto_internal.h +++ b/lib/mosquitto_internal.h @@ -180,6 +180,12 @@ enum mosquitto__keyform { }; #endif +struct will_delay_list { + struct mosquitto *context; + struct will_delay_list *prev; + struct will_delay_list *next; +}; + struct mosquitto_msg_data{ #ifdef WITH_BROKER struct mosquitto_client_msg *inflight; @@ -224,6 +230,7 @@ struct mosquitto { struct mosquitto__packet *out_packet; struct mosquitto_message_all *will; struct mosquitto__alias *aliases; + struct will_delay_list *will_delay_entry; uint32_t maximum_packet_size; int alias_count; uint32_t will_delay_interval; diff --git a/lib/net_mosq.c b/lib/net_mosq.c index 745b1709..f207e32a 100644 --- a/lib/net_mosq.c +++ b/lib/net_mosq.c @@ -50,6 +50,7 @@ Contributors: #include #include #include +#include #include #endif @@ -140,7 +141,9 @@ int net__init(void) | OPENSSL_INIT_ADD_ALL_DIGESTS \ | OPENSSL_INIT_LOAD_CONFIG, NULL); # endif +#if !defined(OPENSSL_NO_ENGINE) ENGINE_load_builtin_engines(); +#endif setup_ui_method(); if(tls_ex_index_mosq == -1){ tls_ex_index_mosq = SSL_get_ex_new_index(0, "client context", NULL, NULL, NULL); @@ -598,6 +601,7 @@ static int net__init_ssl_ctx(struct mosquitto *mosq) SSL_CTX_set_mode(mosq->ssl_ctx, SSL_MODE_RELEASE_BUFFERS); #endif +#if !defined(OPENSSL_NO_ENGINE) if(mosq->tls_engine){ engine = ENGINE_by_id(mosq->tls_engine); if(!engine){ @@ -614,12 +618,15 @@ static int net__init_ssl_ctx(struct mosquitto *mosq) ENGINE_set_default(engine, ENGINE_METHOD_ALL); ENGINE_free(engine); /* release the structural reference from ENGINE_by_id() */ } +#endif if(mosq->tls_ciphers){ ret = SSL_CTX_set_cipher_list(mosq->ssl_ctx, mosq->tls_ciphers); if(ret == 0){ log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to set TLS ciphers. Check cipher list \"%s\".", mosq->tls_ciphers); +#if !defined(OPENSSL_NO_ENGINE) ENGINE_FINISH(engine); +#endif COMPAT_CLOSE(mosq->sock); mosq->sock = INVALID_SOCKET; net__print_ssl_error(mosq); @@ -646,7 +653,9 @@ static int net__init_ssl_ctx(struct mosquitto *mosq) log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load CA certificates, check capath \"%s\".", mosq->tls_capath); } #endif +#if !defined(OPENSSL_NO_ENGINE) ENGINE_FINISH(engine); +#endif COMPAT_CLOSE(mosq->sock); mosq->sock = INVALID_SOCKET; net__print_ssl_error(mosq); @@ -671,7 +680,9 @@ static int net__init_ssl_ctx(struct mosquitto *mosq) #else log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load client certificate \"%s\".", mosq->tls_certfile); #endif +#if !defined(OPENSSL_NO_ENGINE) ENGINE_FINISH(engine); +#endif COMPAT_CLOSE(mosq->sock); mosq->sock = INVALID_SOCKET; net__print_ssl_error(mosq); @@ -680,6 +691,7 @@ static int net__init_ssl_ctx(struct mosquitto *mosq) } if(mosq->tls_keyfile){ if(mosq->tls_keyform == mosq_k_engine){ +#if !defined(OPENSSL_NO_ENGINE) UI_METHOD *ui_method = net__get_ui_method(); if(mosq->tls_engine_kpass_sha1){ if(!ENGINE_ctrl_cmd(engine, ENGINE_SECRET_MODE, ENGINE_SECRET_MODE_SHA, NULL, NULL, 0)){ @@ -713,6 +725,7 @@ static int net__init_ssl_ctx(struct mosquitto *mosq) net__print_ssl_error(mosq); return MOSQ_ERR_TLS; } +#endif }else{ ret = SSL_CTX_use_PrivateKey_file(mosq->ssl_ctx, mosq->tls_keyfile, SSL_FILETYPE_PEM); if(ret != 1){ @@ -721,7 +734,9 @@ static int net__init_ssl_ctx(struct mosquitto *mosq) #else log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load client key file \"%s\".", mosq->tls_keyfile); #endif +#if !defined(OPENSSL_NO_ENGINE) ENGINE_FINISH(engine); +#endif COMPAT_CLOSE(mosq->sock); mosq->sock = INVALID_SOCKET; net__print_ssl_error(mosq); @@ -731,7 +746,9 @@ static int net__init_ssl_ctx(struct mosquitto *mosq) ret = SSL_CTX_check_private_key(mosq->ssl_ctx); if(ret != 1){ log__printf(mosq, MOSQ_LOG_ERR, "Error: Client certificate/key are inconsistent."); +#if !defined(OPENSSL_NO_ENGINE) ENGINE_FINISH(engine); +#endif COMPAT_CLOSE(mosq->sock); mosq->sock = INVALID_SOCKET; net__print_ssl_error(mosq); diff --git a/lib/options.c b/lib/options.c index 005b7810..7bab7366 100644 --- a/lib/options.c +++ b/lib/options.c @@ -69,6 +69,12 @@ int mosquitto_username_pw_set(struct mosquitto *mosq, const char *username, cons { if(!mosq) return MOSQ_ERR_INVAL; + if(mosq->protocol == mosq_p_mqtt311 || mosq->protocol == mosq_p_mqtt31){ + if(password != NULL && username == NULL){ + return MOSQ_ERR_INVAL; + } + } + mosquitto__free(mosq->username); mosq->username = NULL; @@ -81,13 +87,14 @@ int mosquitto_username_pw_set(struct mosquitto *mosq, const char *username, cons } mosq->username = mosquitto__strdup(username); if(!mosq->username) return MOSQ_ERR_NOMEM; - if(password){ - mosq->password = mosquitto__strdup(password); - if(!mosq->password){ - mosquitto__free(mosq->username); - mosq->username = NULL; - return MOSQ_ERR_NOMEM; - } + } + + if(password){ + mosq->password = mosquitto__strdup(password); + if(!mosq->password){ + mosquitto__free(mosq->username); + mosq->username = NULL; + return MOSQ_ERR_NOMEM; } } return MOSQ_ERR_SUCCESS; @@ -255,6 +262,7 @@ int mosquitto_string_option(struct mosquitto *mosq, enum mosq_opt_t option, cons switch(option){ case MOSQ_OPT_TLS_ENGINE: #ifdef WITH_TLS +# if !defined(OPENSSL_NO_ENGINE) eng = ENGINE_by_id(value); if(!eng){ return MOSQ_ERR_INVAL; @@ -265,6 +273,7 @@ int mosquitto_string_option(struct mosquitto *mosq, enum mosq_opt_t option, cons return MOSQ_ERR_NOMEM; } return MOSQ_ERR_SUCCESS; +#endif #else return MOSQ_ERR_NOT_SUPPORTED; #endif diff --git a/lib/send_connect.c b/lib/send_connect.c index 60c529a5..210f125a 100644 --- a/lib/send_connect.c +++ b/lib/send_connect.c @@ -111,11 +111,21 @@ int send__connect(struct mosquitto *mosq, uint16_t keepalive, bool clean_session payloadlen += will_proplen + varbytes; } } + + /* After this check we can be sure that the username and password are + * always valid for the current protocol, so there is no need to check + * username before checking password. */ + if(mosq->protocol == mosq_p_mqtt31 || mosq->protocol == mosq_p_mqtt311){ + if(password != NULL && username == NULL){ + return MOSQ_ERR_INVAL; + } + } + if(username){ payloadlen += 2+strlen(username); - if(password){ - payloadlen += 2+strlen(password); - } + } + if(password){ + payloadlen += 2+strlen(password); } packet->command = CMD_CONNECT; @@ -145,9 +155,9 @@ int send__connect(struct mosquitto *mosq, uint16_t keepalive, bool clean_session } if(username){ byte = byte | 0x1<<7; - if(mosq->password){ - byte = byte | 0x1<<6; - } + } + if(mosq->password){ + byte = byte | 0x1<<6; } packet__write_byte(packet, byte); packet__write_uint16(packet, keepalive); @@ -173,11 +183,12 @@ int send__connect(struct mosquitto *mosq, uint16_t keepalive, bool clean_session packet__write_string(packet, mosq->will->msg.topic, strlen(mosq->will->msg.topic)); packet__write_string(packet, (const char *)mosq->will->msg.payload, mosq->will->msg.payloadlen); } + if(username){ packet__write_string(packet, username, strlen(username)); - if(password){ - packet__write_string(packet, password, strlen(password)); - } + } + if(password){ + packet__write_string(packet, password, strlen(password)); } mosq->keepalive = keepalive; diff --git a/lib/thread_mosq.c b/lib/thread_mosq.c index 39f6e353..7bde453d 100644 --- a/lib/thread_mosq.c +++ b/lib/thread_mosq.c @@ -27,7 +27,7 @@ void *mosquitto__thread_main(void *obj); int mosquitto_loop_start(struct mosquitto *mosq) { -#ifdef WITH_THREADING +#if defined(WITH_THREADING) && defined(HAVE_PTHREAD_CANCEL) if(!mosq || mosq->threaded != mosq_ts_none) return MOSQ_ERR_INVAL; mosq->threaded = mosq_ts_self; @@ -43,7 +43,7 @@ int mosquitto_loop_start(struct mosquitto *mosq) int mosquitto_loop_stop(struct mosquitto *mosq, bool force) { -#ifdef WITH_THREADING +#if defined(WITH_THREADING) && defined(HAVE_PTHREAD_CANCEL) # ifndef WITH_BROKER char sockpair_data = 0; # endif diff --git a/lib/util_mosq.c b/lib/util_mosq.c index 8f3610a8..5e1065e7 100644 --- a/lib/util_mosq.c +++ b/lib/util_mosq.c @@ -28,9 +28,10 @@ Contributors: # include #endif -#if !defined(WITH_TLS) && defined(__linux__) -# if defined(__GLIBC__) && __GLIBC_PREREQ(2, 25) +#if !defined(WITH_TLS) && defined(__linux__) && defined(__GLIBC__) +# if __GLIBC_PREREQ(2, 25) # include +# define HAVE_GETRANDOM 1 # endif #endif @@ -325,12 +326,12 @@ int util__random_bytes(void *bytes, int count) if(RAND_bytes(bytes, count) == 1){ rc = MOSQ_ERR_SUCCESS; } -#elif defined(__GLIBC__) && __GLIBC_PREREQ(2, 25) - if(getrandom(bytes, count, 0) == 0){ +#elif defined(HAVE_GETRANDOM) + if(getrandom(bytes, count, 0) == count){ rc = MOSQ_ERR_SUCCESS; } #elif defined(WIN32) - HRYPTPROV provider; + HCRYPTPROV provider; if(!CryptAcquireContext(&provider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)){ return MOSQ_ERR_UNKNOWN; diff --git a/man/libmosquitto.3.xml b/man/libmosquitto.3.xml index 0048ee4a..1aa308ad 100644 --- a/man/libmosquitto.3.xml +++ b/man/libmosquitto.3.xml @@ -18,11 +18,10 @@ Description This is an overview of how to use libmosquitto to create MQTT aware client programs. There may be separate man pages on each of the - functions described here in the future. There is also a binding for - libmosquitto for C++ and a Python implementation. They are not - documented here but operate in a similar way. - This is fairly incomplete, please see mosquitto.h for a better - description of the functions. + functions described here in the future. + This man page is woefully incomplete, please see the comments + in mosquitto.h for missing functions and a description of the + functions. diff --git a/man/mosquitto.conf.5.xml b/man/mosquitto.conf.5.xml index 8d09c122..3a9bb7f7 100644 --- a/man/mosquitto.conf.5.xml +++ b/man/mosquitto.conf.5.xml @@ -686,6 +686,13 @@ log_timestamp_format %Y-%m-%dT%H:%M:%S signal. If false, the data will be stored in memory only. Defaults to false. + The persistence file may change its format in a new + version. The broker can currently read all old formats, + but will only save in the latest format. It should always + be safe to upgrade, but cautious users may wish to take a + copy of the persistence file before installing a new + version so that they can roll back to an earlier version + if necessary. Reloaded on reload signal. @@ -806,19 +813,6 @@ log_timestamp_format %Y-%m-%dT%H:%M:%S Reloaded on reload signal. - - seconds - - The integer number of seconds between the internal - message store being cleaned of messages that are no - longer referenced. Lower values will result in lower - memory usage but more processor time, higher values - will have the opposite effect. Setting a value of 0 - means the unreferenced messages will be disposed of as - quickly as possible. Defaults to 10 seconds. - Reloaded on reload signal. - - seconds diff --git a/man/mosquitto_pub.1.xml b/man/mosquitto_pub.1.xml index 46bf09ba..2c1a425c 100644 --- a/man/mosquitto_pub.1.xml +++ b/man/mosquitto_pub.1.xml @@ -21,10 +21,8 @@ hostname port-number - - username - password - + username + password message-topic URL @@ -366,8 +364,8 @@ Provide a password to be used for authenticating with the broker. Using this argument without also specifying - a username is invalid. See also the - option. + a username is invalid when using MQTT v3.1 or v3.1.1. + See also the option. diff --git a/man/mosquitto_rr.1.xml b/man/mosquitto_rr.1.xml index 0828a5a2..5af06af0 100644 --- a/man/mosquitto_rr.1.xml +++ b/man/mosquitto_rr.1.xml @@ -21,10 +21,8 @@ hostname port-number - - username - password - + username + password message-topic @@ -387,8 +385,8 @@ Provide a password to be used for authenticating with the broker. Using this argument without also specifying - a username is invalid. See also the - option. + a username is invalid when using MQTT v3.1 or v3.1.1. + See also the option. diff --git a/man/mosquitto_sub.1.xml b/man/mosquitto_sub.1.xml index 0547288f..83a500ac 100644 --- a/man/mosquitto_sub.1.xml +++ b/man/mosquitto_sub.1.xml @@ -21,10 +21,8 @@ hostname port-number - - username - password - + username + password message-topic @@ -396,8 +394,8 @@ Provide a password to be used for authenticating with the broker. Using this argument without also specifying - a username is invalid. See also the - option. + a username is invalid when using MQTT v3.1 or v3.1.1. + See also the option. diff --git a/mosquitto.conf b/mosquitto.conf index a2b845bb..c12193fe 100644 --- a/mosquitto.conf +++ b/mosquitto.conf @@ -178,14 +178,6 @@ # of packets being sent. #set_tcp_nodelay false -# Time in seconds between cleaning the internal message store of -# unreferenced messages. Lower values will result in lower memory -# usage but more processor time, higher values will have the -# opposite effect. -# Setting a value of 0 means the unreferenced messages will be -# disposed of as quickly as possible. -#store_clean_interval 10 - # Time in seconds between updates of the $SYS tree. # Set to 0 to disable the publishing of the $SYS tree. #sys_interval 10 diff --git a/set-version.sh b/set-version.sh index fa0b9e87..0e99da2f 100755 --- a/set-version.sh +++ b/set-version.sh @@ -2,7 +2,7 @@ MAJOR=1 MINOR=6 -REVISION=2 +REVISION=3 sed -i "s/^VERSION=.*/VERSION=${MAJOR}.${MINOR}.${REVISION}/" config.mk diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 5096fb9e..cae454fa 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -1,5 +1,5 @@ name: mosquitto -version: 1.6.2 +version: 1.6.3 summary: Eclipse Mosquitto MQTT broker description: This is a message broker that supports version 3.1 and 3.1.1 of the MQTT protocol. diff --git a/src/conf.c b/src/conf.c index 4fe05cf5..53c622cf 100644 --- a/src/conf.c +++ b/src/conf.c @@ -504,12 +504,12 @@ int config__parse_args(struct mosquitto_db *db, struct mosquitto__config *config config->listeners[config->listener_count-1].client_count = 0; config->listeners[config->listener_count-1].use_username_as_clientid = config->default_listener.use_username_as_clientid; config->listeners[config->listener_count-1].maximum_qos = config->default_listener.maximum_qos; + config->listeners[config->listener_count-1].max_topic_alias = config->default_listener.max_topic_alias; #ifdef WITH_TLS config->listeners[config->listener_count-1].tls_version = config->default_listener.tls_version; config->listeners[config->listener_count-1].tls_engine = config->default_listener.tls_engine; config->listeners[config->listener_count-1].tls_keyform = config->default_listener.tls_keyform; config->listeners[config->listener_count-1].tls_engine_kpass_sha1 = config->default_listener.tls_engine_kpass_sha1; - config->listeners[config->listener_count-1].max_topic_alias = config->default_listener.max_topic_alias; config->listeners[config->listener_count-1].cafile = config->default_listener.cafile; config->listeners[config->listener_count-1].capath = config->default_listener.capath; config->listeners[config->listener_count-1].certfile = config->default_listener.certfile; diff --git a/src/context.c b/src/context.c index a78d9f84..f024d171 100644 --- a/src/context.c +++ b/src/context.c @@ -25,6 +25,7 @@ Contributors: #include "packet_mosq.h" #include "property_mosq.h" #include "time_mosq.h" +#include "will_mosq.h" #include "uthash.h" @@ -218,13 +219,7 @@ void context__send_will(struct mosquitto_db *db, struct mosquitto *ctxt) &ctxt->will->properties); } } - if(ctxt->will){ - mosquitto_property_free_all(&ctxt->will->properties); - mosquitto__free(ctxt->will->msg.topic); - mosquitto__free(ctxt->will->msg.payload); - mosquitto__free(ctxt->will); - ctxt->will = NULL; - } + will__clear(ctxt); } @@ -232,8 +227,8 @@ void context__disconnect(struct mosquitto_db *db, struct mosquitto *context) { net__socket_close(db, context); + context__send_will(db, context); if(context->session_expiry_interval == 0){ - context__send_will(db, context); #ifdef WITH_BRIDGE if(!context->bridge) diff --git a/src/database.c b/src/database.c index 2a32b82c..87daf68c 100644 --- a/src/database.c +++ b/src/database.c @@ -157,7 +157,7 @@ static void subhier_clean(struct mosquitto_db *db, struct mosquitto__subhier **s leaf = nextleaf; } if(peer->retained){ - db__msg_store_deref(db, &peer->retained); + db__msg_store_ref_dec(db, &peer->retained); } subhier_clean(db, &peer->children); mosquitto__free(peer->topic); @@ -232,7 +232,12 @@ void db__msg_store_clean(struct mosquitto_db *db) } } -void db__msg_store_deref(struct mosquitto_db *db, struct mosquitto_msg_store **store) +void db__msg_store_ref_inc(struct mosquitto_msg_store *store) +{ + store->ref_count++; +} + +void db__msg_store_ref_dec(struct mosquitto_db *db, struct mosquitto_msg_store **store) { (*store)->ref_count--; if((*store)->ref_count == 0){ @@ -271,7 +276,7 @@ static void db__message_remove(struct mosquitto_db *db, struct mosquitto_msg_dat msg_data->msg_count12--; msg_data->msg_bytes12 -= item->store->payloadlen; } - db__msg_store_deref(db, &item->store); + db__msg_store_ref_dec(db, &item->store); } mosquitto_property_free_all(&item->properties); @@ -286,6 +291,9 @@ void db__message_dequeue_first(struct mosquitto *context, struct mosquitto_msg_d msg = msg_data->queued; DL_DELETE(msg_data->queued, msg); DL_APPEND(msg_data->inflight, msg); + if(msg_data->inflight_quota > 0){ + msg_data->inflight_quota--; + } } @@ -359,7 +367,8 @@ int db__message_insert(struct mosquitto_db *db, struct mosquitto *context, uint1 * multiple times for overlapping subscriptions, although this is only the * case for SUBSCRIPTION with multiple subs in so is a minor concern. */ - if(db->config->allow_duplicate_messages == false + if(context->protocol != mosq_p_mqtt5 + && db->config->allow_duplicate_messages == false && dir == mosq_md_out && retain == false && stored->dest_ids){ for(i=0; idest_id_count; i++){ @@ -450,7 +459,7 @@ int db__message_insert(struct mosquitto_db *db, struct mosquitto *context, uint1 msg->prev = NULL; msg->next = NULL; msg->store = stored; - msg->store->ref_count++; + db__msg_store_ref_inc(msg->store); msg->mid = mid; msg->timestamp = mosquitto_time(); msg->direction = dir; @@ -543,7 +552,7 @@ void db__messages_delete_list(struct mosquitto_db *db, struct mosquitto_client_m DL_FOREACH_SAFE(*head, tail, tmp){ DL_DELETE(*head, tail); - db__msg_store_deref(db, &tail->store); + db__msg_store_ref_dec(db, &tail->store); mosquitto_property_free_all(&tail->properties); mosquitto__free(tail); } @@ -751,9 +760,7 @@ int db__message_reconnect_reset_outgoing(struct mosquitto_db *db, struct mosquit if(msg->qos > 0){ context->msgs_out.msg_count12++; context->msgs_out.msg_bytes12 += msg->store->payloadlen; - if(context->msgs_out.inflight_quota > 0){ - context->msgs_out.inflight_quota--; - } + util__decrement_receive_quota(context); } switch(msg->qos){ @@ -822,9 +829,7 @@ int db__message_reconnect_reset_incoming(struct mosquitto_db *db, struct mosquit if(msg->qos > 0){ context->msgs_in.msg_count12++; context->msgs_in.msg_bytes12 += msg->store->payloadlen; - if(context->msgs_in.inflight_quota > 0){ - context->msgs_in.inflight_quota--; - } + util__decrement_receive_quota(context); } if(msg->qos != 2){ @@ -1101,7 +1106,7 @@ int db__message_write(struct mosquitto_db *db, struct mosquitto *context) } DL_FOREACH_SAFE(context->msgs_in.queued, tail, tmp){ - if(context->msgs_out.inflight_maximum != 0 && msg_count >= context->msgs_out.inflight_maximum){ + if(context->msgs_out.inflight_maximum != 0 && context->msgs_in.inflight_quota == 0){ break; } @@ -1120,7 +1125,7 @@ int db__message_write(struct mosquitto_db *db, struct mosquitto *context) } DL_FOREACH_SAFE(context->msgs_out.queued, tail, tmp){ - if(context->msgs_out.inflight_maximum != 0 && msg_count >= context->msgs_out.inflight_maximum){ + if(context->msgs_out.inflight_maximum != 0 && context->msgs_out.inflight_quota == 0){ break; } diff --git a/src/handle_auth.c b/src/handle_auth.c index 70b21adf..03bf4771 100644 --- a/src/handle_auth.c +++ b/src/handle_auth.c @@ -25,6 +25,7 @@ Contributors: #include "packet_mosq.h" #include "property_mosq.h" #include "send_mosq.h" +#include "will_mosq.h" int handle__auth(struct mosquitto_db *db, struct mosquitto *context) @@ -119,11 +120,7 @@ int handle__auth(struct mosquitto_db *db, struct mosquitto *context) free(auth_data_out); if(context->state == mosq_cs_authenticating && context->will){ /* Free will without sending if this is our first authentication attempt */ - mosquitto_property_free_all(&context->will->properties); - mosquitto__free(context->will->msg.payload); - mosquitto__free(context->will->msg.topic); - mosquitto__free(context->will); - context->will = NULL; + will__clear(context); } if(rc == MOSQ_ERR_AUTH){ send__connack(db, context, 0, MQTT_RC_NOT_AUTHORIZED, NULL); diff --git a/src/handle_connect.c b/src/handle_connect.c index 0cedbc6b..9de98dd9 100644 --- a/src/handle_connect.c +++ b/src/handle_connect.c @@ -30,6 +30,7 @@ Contributors: #include "time_mosq.h" #include "tls_mosq.h" #include "util_mosq.h" +#include "will_mosq.h" #ifdef WITH_WEBSOCKETS # include @@ -91,7 +92,7 @@ void connection_check_acl(struct mosquitto_db *db, struct mosquitto *context, st msg_tail->store->qos, msg_tail->store->retain, MOSQ_ACL_READ) != MOSQ_ERR_SUCCESS){ DL_DELETE((*head), msg_tail); - db__msg_store_deref(db, &msg_tail->store); + db__msg_store_ref_dec(db, &msg_tail->store); mosquitto_property_free_all(&msg_tail->properties); mosquitto__free(msg_tail); } @@ -160,6 +161,8 @@ int connect__on_authorised(struct mosquitto_db *db, struct mosquitto *context, v } session_expiry__remove(found_context); + will_delay__remove(found_context); + will__clear(found_context); found_context->clean_start = true; found_context->session_expiry_interval = 0; @@ -426,9 +429,16 @@ int handle__connect(struct mosquitto_db *db, struct mosquitto *context) goto handle_connect_error; } context->protocol = mosq_p_mqtt31; + if((protocol_version&0x80) == 0x80){ + context->is_bridge = true; + } }else if(!strcmp(protocol_name, PROTOCOL_NAME)){ if((protocol_version&0x7F) == PROTOCOL_VERSION_v311){ context->protocol = mosq_p_mqtt311; + + if((protocol_version&0x80) == 0x80){ + context->is_bridge = true; + } }else if((protocol_version&0x7F) == PROTOCOL_VERSION_v5){ context->protocol = mosq_p_mqtt5; }else{ @@ -530,8 +540,12 @@ int handle__connect(struct mosquitto_db *db, struct mosquitto *context) }else{ allow_zero_length_clientid = db->config->security_options.allow_zero_length_clientid; } - if(clean_start == 0 || allow_zero_length_clientid == false){ - send__connack(db, context, 0, CONNACK_REFUSED_IDENTIFIER_REJECTED, NULL); + if((context->protocol == mosq_p_mqtt311 && clean_start == 0) || allow_zero_length_clientid == false){ + if(context->protocol == mosq_p_mqtt311){ + send__connack(db, context, 0, CONNACK_REFUSED_IDENTIFIER_REJECTED, NULL); + }else{ + send__connack(db, context, 0, MQTT_RC_UNSPECIFIED, NULL); + } rc = MOSQ_ERR_PROTOCOL; goto handle_connect_error; }else{ @@ -576,25 +590,10 @@ int handle__connect(struct mosquitto_db *db, struct mosquitto *context) if(username_flag){ rc = packet__read_string(&context->in_packet, &username, &slen); - if(rc == MOSQ_ERR_SUCCESS){ - if(password_flag){ - rc = packet__read_binary(&context->in_packet, (uint8_t **)&password, &slen); - if(rc == MOSQ_ERR_NOMEM){ - rc = MOSQ_ERR_NOMEM; - goto handle_connect_error; - }else if(rc == MOSQ_ERR_PROTOCOL){ - if(context->protocol == mosq_p_mqtt31){ - /* Password flag given, but no password. Ignore. */ - }else{ - rc = MOSQ_ERR_PROTOCOL; - goto handle_connect_error; - } - } - } - }else if(rc == MOSQ_ERR_NOMEM){ + if(rc == MOSQ_ERR_NOMEM){ rc = MOSQ_ERR_NOMEM; goto handle_connect_error; - }else{ + }else if(rc != MOSQ_ERR_SUCCESS){ if(context->protocol == mosq_p_mqtt31){ /* Username flag given, but no username. Ignore. */ username_flag = 0; @@ -604,7 +603,7 @@ int handle__connect(struct mosquitto_db *db, struct mosquitto *context) } } }else{ - if(context->protocol == mosq_p_mqtt311 || context->protocol == mosq_p_mqtt5){ + if(context->protocol == mosq_p_mqtt311 || context->protocol == mosq_p_mqtt31){ if(password_flag){ /* username_flag == 0 && password_flag == 1 is forbidden */ log__printf(NULL, MOSQ_LOG_ERR, "Protocol error from %s: password without username, closing connection.", client_id); @@ -613,6 +612,21 @@ int handle__connect(struct mosquitto_db *db, struct mosquitto *context) } } } + if(password_flag){ + rc = packet__read_binary(&context->in_packet, (uint8_t **)&password, &slen); + if(rc == MOSQ_ERR_NOMEM){ + rc = MOSQ_ERR_NOMEM; + goto handle_connect_error; + }else if(rc == MOSQ_ERR_PROTOCOL){ + if(context->protocol == mosq_p_mqtt31){ + /* Password flag given, but no password. Ignore. */ + }else{ + rc = MOSQ_ERR_PROTOCOL; + goto handle_connect_error; + } + } + } + if(context->in_packet.pos != context->in_packet.remaining_length){ /* Surplus data at end of packet, this must be an error. */ rc = MOSQ_ERR_PROTOCOL; @@ -745,7 +759,7 @@ int handle__connect(struct mosquitto_db *db, struct mosquitto *context) #endif /* FINAL_WITH_TLS_PSK */ }else{ #endif /* WITH_TLS */ - if(username_flag){ + if(username_flag || password_flag){ /* FIXME - these ensure the mosquitto_client_id() and * mosquitto_client_username() functions work, but is hacky */ context->id = client_id; @@ -776,9 +790,7 @@ int handle__connect(struct mosquitto_db *db, struct mosquitto *context) context->password = password; username = NULL; /* Avoid free() in error: below. */ password = NULL; - } - - if(!username_flag){ + }else{ if((db->config->per_listener_settings && context->listener->security_options.allow_anonymous == false) || (!db->config->per_listener_settings && db->config->security_options.allow_anonymous == false)){ @@ -829,13 +841,7 @@ int handle__connect(struct mosquitto_db *db, struct mosquitto *context) return rc; }else{ free(auth_data_out); - if(context->will){ - mosquitto_property_free_all(&context->will->properties); - mosquitto__free(context->will->msg.payload); - mosquitto__free(context->will->msg.topic); - mosquitto__free(context->will); - context->will = NULL; - } + will__clear(context); if(rc == MOSQ_ERR_AUTH){ send__connack(db, context, 0, MQTT_RC_NOT_AUTHORIZED, NULL); mosquitto__free(context->id); diff --git a/src/handle_disconnect.c b/src/handle_disconnect.c index 18b0ee80..72db7dd3 100644 --- a/src/handle_disconnect.c +++ b/src/handle_disconnect.c @@ -21,6 +21,7 @@ Contributors: #include "packet_mosq.h" #include "property_mosq.h" #include "send_mosq.h" +#include "will_mosq.h" int handle__disconnect(struct mosquitto_db *db, struct mosquitto *context) @@ -66,6 +67,7 @@ int handle__disconnect(struct mosquitto_db *db, struct mosquitto *context) if(reason_code == MQTT_RC_DISCONNECT_WITH_WILL_MSG){ context__set_state(context, mosq_cs_disconnect_with_will); }else{ + will__clear(context); context__set_state(context, mosq_cs_disconnecting); } do_disconnect(db, context, MOSQ_ERR_SUCCESS); diff --git a/src/logging.c b/src/logging.c index d956643d..bcb6a156 100644 --- a/src/logging.c +++ b/src/logging.c @@ -373,10 +373,19 @@ int log__printf(struct mosquitto *mosq, int priority, const char *fmt, ...) void log__internal(const char *fmt, ...) { va_list va; + char buf[200]; + int len; va_start(va, fmt); - log__vprintf(MOSQ_LOG_INTERNAL, fmt, va); + len = vsnprintf(buf, 200, fmt, va); va_end(va); + + if(len >= 200){ + log__printf(NULL, MOSQ_LOG_INTERNAL, "Internal log buffer too short (%d)", len); + return; + } + + log__printf(NULL, MOSQ_LOG_INTERNAL, "%s%s%s", "\e[32m", buf, "\e[0m"); } int mosquitto_log_vprintf(int level, const char *fmt, va_list va) diff --git a/src/loop.c b/src/loop.c index fc18399e..50959599 100644 --- a/src/loop.c +++ b/src/loop.c @@ -614,12 +614,19 @@ void do_disconnect(struct mosquitto_db *db, struct mosquitto *context, int reaso #ifdef WITH_EPOLL struct epoll_event ev; #endif +#ifdef WITH_WEBSOCKETS + bool is_duplicate = false; +#endif if(context->state == mosq_cs_disconnected){ return; } #ifdef WITH_WEBSOCKETS if(context->wsi){ + if(context->state == mosq_cs_duplicate){ + is_duplicate = true; + } + if(context->state != mosq_cs_disconnecting && context->state != mosq_cs_disconnect_with_will){ context__set_state(context, mosq_cs_disconnect_ws); } @@ -636,7 +643,15 @@ void do_disconnect(struct mosquitto_db *db, struct mosquitto *context, int reaso context->sock = INVALID_SOCKET; context->pollfd_index = -1; } - context__remove_from_by_id(db, context); + if(is_duplicate){ + /* This occurs if another client is taking over the same client id. + * It is important to remove this from the by_id hash here, so it + * doesn't leave us with multiple clients in the hash with the same + * id. Websockets doesn't actually close the connection here, + * unlike for normal clients, which means there is extra time when + * there could be two clients with the same id in the hash. */ + context__remove_from_by_id(db, context); + } }else #endif { diff --git a/src/mosquitto_broker.h b/src/mosquitto_broker.h index d8e41f73..f6f2cc86 100644 --- a/src/mosquitto_broker.h +++ b/src/mosquitto_broker.h @@ -17,6 +17,10 @@ Contributors: #ifndef MOSQUITTO_BROKER_H #define MOSQUITTO_BROKER_H +#ifdef __cplusplus +extern "C" { +#endif + #include struct mosquitto; @@ -161,4 +165,8 @@ const char *mosquitto_client_username(const struct mosquitto *client); */ int mosquitto_set_username(struct mosquitto *client, const char *username); +#ifdef __cplusplus +} +#endif + #endif diff --git a/src/mosquitto_broker_internal.h b/src/mosquitto_broker_internal.h index 38cc1360..540dbcbe 100644 --- a/src/mosquitto_broker_internal.h +++ b/src/mosquitto_broker_internal.h @@ -619,7 +619,8 @@ int db__message_store(struct mosquitto_db *db, const struct mosquitto *source, u int db__message_store_find(struct mosquitto *context, uint16_t mid, struct mosquitto_msg_store **stored); void db__msg_store_add(struct mosquitto_db *db, struct mosquitto_msg_store *store); void db__msg_store_remove(struct mosquitto_db *db, struct mosquitto_msg_store *store); -void db__msg_store_deref(struct mosquitto_db *db, struct mosquitto_msg_store **store); +void db__msg_store_ref_inc(struct mosquitto_msg_store *store); +void db__msg_store_ref_dec(struct mosquitto_db *db, struct mosquitto_msg_store **store); void db__msg_store_clean(struct mosquitto_db *db); void db__msg_store_compact(struct mosquitto_db *db); int db__message_reconnect_reset(struct mosquitto_db *db, struct mosquitto *context); @@ -740,6 +741,7 @@ void do_disconnect(struct mosquitto_db *db, struct mosquitto *context, int reaso int will_delay__add(struct mosquitto *context); void will_delay__check(struct mosquitto_db *db, time_t now); void will_delay__send_all(struct mosquitto_db *db); +void will_delay__remove(struct mosquitto *mosq); #endif diff --git a/src/mosquitto_plugin.h b/src/mosquitto_plugin.h index c4b872a1..83e55ebd 100644 --- a/src/mosquitto_plugin.h +++ b/src/mosquitto_plugin.h @@ -17,6 +17,10 @@ Contributors: #ifndef MOSQUITTO_PLUGIN_H #define MOSQUITTO_PLUGIN_H +#ifdef __cplusplus +extern "C" { +#endif + #define MOSQ_AUTH_PLUGIN_VERSION 4 #define MOSQ_ACL_NONE 0x00 @@ -77,6 +81,16 @@ struct mosquitto_acl_msg { * denied. */ +/* ========================================================================= + * + * Helper Functions + * + * ========================================================================= */ + +/* There are functions that are available for plugin developers to use in + * mosquitto_broker.h, including logging and accessor functions. + */ + /* ========================================================================= * @@ -295,4 +309,10 @@ int mosquitto_auth_psk_key_get(void *user_data, struct mosquitto *client, const int mosquitto_auth_start(void *user_data, struct mosquitto *client, const char *method, bool reauth, const void *data_in, uint16_t data_in_len, void **data_out, uint16_t *data_out_len); int mosquitto_auth_continue(void *user_data, struct mosquitto *client, const char *method, const void *data_in, uint16_t data_in_len, void **data_out, uint16_t *data_out_len); + + +#ifdef __cplusplus +} +#endif + #endif diff --git a/src/net.c b/src/net.c index 74b4ee83..0b57558c 100644 --- a/src/net.c +++ b/src/net.c @@ -368,6 +368,10 @@ static int mosquitto__tls_server_ctx(struct mosquitto__listener *listener) #endif #endif +#ifdef SSL_OP_NO_RENEGOTIATION + SSL_CTX_set_options(listener->ssl_ctx, SSL_OP_NO_RENEGOTIATION); +#endif + snprintf(buf, 256, "mosquitto-%d", listener->port); SSL_CTX_set_session_id_context(listener->ssl_ctx, (unsigned char *)buf, strlen(buf)); @@ -534,6 +538,7 @@ int net__socket_listen(struct mosquitto__listener *listener) return 1; } if(listener->tls_engine){ +#if !defined(OPENSSL_NO_ENGINE) engine = ENGINE_by_id(listener->tls_engine); if(!engine){ log__printf(NULL, MOSQ_LOG_ERR, "Error loading %s engine\n", listener->tls_engine); @@ -548,6 +553,7 @@ int net__socket_listen(struct mosquitto__listener *listener) } ENGINE_set_default(engine, ENGINE_METHOD_ALL); ENGINE_free(engine); /* release the structural reference from ENGINE_by_id() */ +#endif } /* FIXME user data? */ if(listener->require_certificate){ @@ -560,10 +566,13 @@ int net__socket_listen(struct mosquitto__listener *listener) log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to load server certificate \"%s\". Check certfile.", listener->certfile); net__print_error(MOSQ_LOG_ERR, "Error: %s"); COMPAT_CLOSE(sock); +#if !defined(OPENSSL_NO_ENGINE) ENGINE_FINISH(engine); +#endif return 1; } if(listener->tls_keyform == mosq_k_engine){ +#if !defined(OPENSSL_NO_ENGINE) UI_METHOD *ui_method = net__get_ui_method(); if(listener->tls_engine_kpass_sha1){ if(!ENGINE_ctrl_cmd(engine, ENGINE_SECRET_MODE, ENGINE_SECRET_MODE_SHA, NULL, NULL, 0)){ @@ -593,13 +602,16 @@ int net__socket_listen(struct mosquitto__listener *listener) ENGINE_FINISH(engine); return 1; } +#endif }else{ rc = SSL_CTX_use_PrivateKey_file(listener->ssl_ctx, listener->keyfile, SSL_FILETYPE_PEM); if(rc != 1){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to load server key file \"%s\". Check keyfile.", listener->keyfile); net__print_error(MOSQ_LOG_ERR, "Error: %s"); COMPAT_CLOSE(sock); +#if !defined(OPENSSL_NO_ENGINE) ENGINE_FINISH(engine); +#endif return 1; } } @@ -608,7 +620,9 @@ int net__socket_listen(struct mosquitto__listener *listener) log__printf(NULL, MOSQ_LOG_ERR, "Error: Server certificate/key are inconsistent."); net__print_error(MOSQ_LOG_ERR, "Error: %s"); COMPAT_CLOSE(sock); +#if !defined(OPENSSL_NO_ENGINE) ENGINE_FINISH(engine); +#endif return 1; } /* Load CRLs if they exist. */ @@ -618,7 +632,9 @@ int net__socket_listen(struct mosquitto__listener *listener) log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to obtain TLS store."); net__print_error(MOSQ_LOG_ERR, "Error: %s"); COMPAT_CLOSE(sock); +#if !defined(OPENSSL_NO_ENGINE) ENGINE_FINISH(engine); +#endif return 1; } lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()); @@ -627,7 +643,9 @@ int net__socket_listen(struct mosquitto__listener *listener) log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to load certificate revocation file \"%s\". Check crlfile.", listener->crlfile); net__print_error(MOSQ_LOG_ERR, "Error: %s"); COMPAT_CLOSE(sock); +#if !defined(OPENSSL_NO_ENGINE) ENGINE_FINISH(engine); +#endif return 1; } X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK); @@ -644,7 +662,9 @@ int net__socket_listen(struct mosquitto__listener *listener) if(mosquitto__tls_server_ctx(listener)){ COMPAT_CLOSE(sock); +#if !defined(OPENSSL_NO_ENGINE) ENGINE_FINISH(engine); +#endif return 1; } SSL_CTX_set_psk_server_callback(listener->ssl_ctx, psk_server_callback); @@ -654,7 +674,9 @@ int net__socket_listen(struct mosquitto__listener *listener) log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to set TLS PSK hint."); net__print_error(MOSQ_LOG_ERR, "Error: %s"); COMPAT_CLOSE(sock); +#if !defined(OPENSSL_NO_ENGINE) ENGINE_FINISH(engine); +#endif return 1; } } diff --git a/src/persist_read.c b/src/persist_read.c index 1ae2f7a6..48437e46 100644 --- a/src/persist_read.c +++ b/src/persist_read.c @@ -138,7 +138,7 @@ static int persist__client_msg_restore(struct mosquitto_db *db, struct P_client_ return 1; } cmsg->store = load->store; - cmsg->store->ref_count++; + db__msg_store_ref_inc(cmsg->store); context = persist__find_or_add_context(db, chunk->client_id, 0); if(!context){ diff --git a/src/security.c b/src/security.c index 8162ae66..2ccd7370 100644 --- a/src/security.c +++ b/src/security.c @@ -752,7 +752,7 @@ int mosquitto_psk_key_get(struct mosquitto_db *db, struct mosquitto *context, co } for(i=0; iauth_plugin_config_count; i++){ - if(opts->auth_plugin_configs[i].plugin.version == 3 + if(opts->auth_plugin_configs[i].plugin.version == 4 && opts->auth_plugin_configs[i].plugin.psk_key_get_v4){ rc = opts->auth_plugin_configs[i].plugin.psk_key_get_v4( diff --git a/src/subs.c b/src/subs.c index 11093819..aae32666 100644 --- a/src/subs.c +++ b/src/subs.c @@ -146,14 +146,14 @@ static int subs__process(struct mosquitto_db *db, struct mosquitto__subhier *hie } #endif if(hier->retained){ - db__msg_store_deref(db, &hier->retained); + db__msg_store_ref_dec(db, &hier->retained); #ifdef WITH_SYS_TREE db->retained_count--; #endif } if(stored->payloadlen){ hier->retained = stored; - hier->retained->ref_count++; + db__msg_store_ref_inc(hier->retained); #ifdef WITH_SYS_TREE db->retained_count++; #endif @@ -706,7 +706,7 @@ int sub__add(struct mosquitto_db *db, struct mosquitto *context, const char *sub if(sub__topic_tokenise(sub, &tokens)) return 1; - if(!strcmp(tokens->topic, "$shared")){ + if(!strcmp(tokens->topic, "$share")){ if(!tokens->next || !tokens->next->next){ sub__topic_tokens_free(tokens); return MOSQ_ERR_PROTOCOL; @@ -756,7 +756,7 @@ int sub__remove(struct mosquitto_db *db, struct mosquitto *context, const char * if(sub__topic_tokenise(sub, &tokens)) return 1; - if(!strcmp(tokens->topic, "$shared")){ + if(!strcmp(tokens->topic, "$share")){ if(!tokens->next || !tokens->next->next){ sub__topic_tokens_free(tokens); return MOSQ_ERR_PROTOCOL; @@ -803,7 +803,7 @@ int sub__messages_queue(struct mosquitto_db *db, const char *source_id, const ch clients - this is required because websockets client calls db__message_write(), which could remove the message if ref_count==0. */ - (*stored)->ref_count++; + db__msg_store_ref_inc(*stored); HASH_FIND(hh, db->subs, tokens->topic, tokens->topic_len, subhier); if(subhier){ @@ -818,7 +818,7 @@ int sub__messages_queue(struct mosquitto_db *db, const char *source_id, const ch sub__topic_tokens_free(tokens); /* Remove our reference and free if needed. */ - db__msg_store_deref(db, stored); + db__msg_store_ref_dec(db, stored); return rc; } @@ -981,7 +981,7 @@ static int retain__process(struct mosquitto_db *db, struct mosquitto__subhier *b struct mosquitto_msg_store *retained; if(branch->retained->message_expiry_time > 0 && now > branch->retained->message_expiry_time){ - db__msg_store_deref(db, &branch->retained); + db__msg_store_ref_dec(db, &branch->retained); branch->retained = NULL; #ifdef WITH_SYS_TREE db->retained_count--; diff --git a/src/will_delay.c b/src/will_delay.c index b3ff8670..4bed7a87 100644 --- a/src/will_delay.c +++ b/src/will_delay.c @@ -24,12 +24,6 @@ Contributors: #include "memory_mosq.h" #include "time_mosq.h" -struct will_delay_list { - struct mosquitto *context; - struct will_delay_list *prev; - struct will_delay_list *next; -}; - static struct will_delay_list *delay_list = NULL; static time_t last_check = 0; @@ -48,6 +42,7 @@ int will_delay__add(struct mosquitto *context) if(!item) return MOSQ_ERR_NOMEM; item->context = context; + context->will_delay_entry = item; item->context->will_delay_time = time(NULL) + item->context->will_delay_interval; DL_INSERT_INORDER(delay_list, item, will_delay__cmp); @@ -64,6 +59,7 @@ void will_delay__send_all(struct mosquitto_db *db) DL_FOREACH_SAFE(delay_list, item, tmp){ DL_DELETE(delay_list, item); item->context->will_delay_interval = 0; + item->context->will_delay_entry = NULL; context__send_will(db, item->context); mosquitto__free(item); } @@ -82,8 +78,11 @@ void will_delay__check(struct mosquitto_db *db, time_t now) if(item->context->will_delay_time < now){ DL_DELETE(delay_list, item); item->context->will_delay_interval = 0; + item->context->will_delay_entry = NULL; context__send_will(db, item->context); - context__add_to_disused(db, item->context); + if(item->context->session_expiry_interval == 0){ + context__add_to_disused(db, item->context); + } mosquitto__free(item); }else{ return; @@ -92,3 +91,12 @@ void will_delay__check(struct mosquitto_db *db, time_t now) } + +void will_delay__remove(struct mosquitto *mosq) +{ + if(mosq->will_delay_entry != NULL){ + DL_DELETE(delay_list, mosq->will_delay_entry); + mosq->will_delay_entry = NULL; + } +} + diff --git a/test/broker/01-connect-uname-password-denied-no-will.py b/test/broker/01-connect-uname-password-denied-no-will.py index b32b91b3..88888024 100755 --- a/test/broker/01-connect-uname-password-denied-no-will.py +++ b/test/broker/01-connect-uname-password-denied-no-will.py @@ -31,9 +31,6 @@ mid = 1 subscribe_packet = mosq_test.gen_subscribe(mid, topic="will/test", qos=0) suback_packet = mosq_test.gen_suback(mid, 0) -pingreq_packet = mosq_test.gen_pingreq() -pingresp_packet = mosq_test.gen_pingresp() - connect2_packet = mosq_test.gen_connect("connect-uname-pwd-test", keepalive=keepalive, username="user", password="password9") connack2_packet = mosq_test.gen_connack(rc=5) @@ -47,7 +44,7 @@ try: sock2.close() # If we receive a will here, this is an error - mosq_test.do_send_receive(sock1, pingreq_packet, pingresp_packet) + mosq_test.do_ping(sock1) sock1.close() rc = 0 diff --git a/test/broker/02-shared-qos0-v5.py b/test/broker/02-shared-qos0-v5.py index 96576c69..eaebcb20 100755 --- a/test/broker/02-shared-qos0-v5.py +++ b/test/broker/02-shared-qos0-v5.py @@ -3,10 +3,10 @@ # Test whether shared subscriptions work # Client 1 subscribes to #, non shared. Should receive everything. -# Client 2 subscribes to $shared/one/share-test -# Client 3 subscribes to $shared/one/share-test and $shared/two/share-test -# Client 4 subscribes to $shared/two/share-test -# Client 5 subscribes to $shared/one/share-test +# Client 2 subscribes to $share/one/share-test +# Client 3 subscribes to $share/one/share-test and $share/two/share-test +# Client 4 subscribes to $share/two/share-test +# Client 5 subscribes to $share/one/share-test # A publish to "share-test" should always go to client 1. # The first publish should also go to client 2 (share one) and client 3 (share two) @@ -37,19 +37,19 @@ connack5_packet = mosq_test.gen_connack(rc=0, proto_ver=5) subscribe1_packet = mosq_test.gen_subscribe(mid, "#", 0, proto_ver=5) suback1_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) -subscribe2_packet = mosq_test.gen_subscribe(mid, "$shared/one/share-test", 0, proto_ver=5) +subscribe2_packet = mosq_test.gen_subscribe(mid, "$share/one/share-test", 0, proto_ver=5) suback2_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) -subscribe3a_packet = mosq_test.gen_subscribe(mid, "$shared/one/share-test", 0, proto_ver=5) +subscribe3a_packet = mosq_test.gen_subscribe(mid, "$share/one/share-test", 0, proto_ver=5) suback3a_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) -subscribe3b_packet = mosq_test.gen_subscribe(mid, "$shared/two/share-test", 0, proto_ver=5) +subscribe3b_packet = mosq_test.gen_subscribe(mid, "$share/two/share-test", 0, proto_ver=5) suback3b_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) -subscribe4_packet = mosq_test.gen_subscribe(mid, "$shared/two/share-test", 0, proto_ver=5) +subscribe4_packet = mosq_test.gen_subscribe(mid, "$share/two/share-test", 0, proto_ver=5) suback4_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) -subscribe5_packet = mosq_test.gen_subscribe(mid, "$shared/one/share-test", 0, proto_ver=5) +subscribe5_packet = mosq_test.gen_subscribe(mid, "$share/one/share-test", 0, proto_ver=5) suback5_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) publish1_packet = mosq_test.gen_publish("share-test", qos=0, payload="message1", proto_ver=5) @@ -60,19 +60,19 @@ mid = 2 unsubscribe1_packet = mosq_test.gen_unsubscribe(mid, "#", proto_ver=5) unsuback1_packet = mosq_test.gen_unsuback(mid, proto_ver=5) -unsubscribe2_packet = mosq_test.gen_unsubscribe(mid, "$shared/one/share-test", proto_ver=5) +unsubscribe2_packet = mosq_test.gen_unsubscribe(mid, "$share/one/share-test", proto_ver=5) unsuback2_packet = mosq_test.gen_unsuback(mid, proto_ver=5) -unsubscribe3a_packet = mosq_test.gen_unsubscribe(mid, "$shared/one/share-test", proto_ver=5) +unsubscribe3a_packet = mosq_test.gen_unsubscribe(mid, "$share/one/share-test", proto_ver=5) unsuback3a_packet = mosq_test.gen_unsuback(mid, proto_ver=5) -unsubscribe3b_packet = mosq_test.gen_unsubscribe(mid, "$shared/two/share-test", proto_ver=5) +unsubscribe3b_packet = mosq_test.gen_unsubscribe(mid, "$share/two/share-test", proto_ver=5) unsuback3b_packet = mosq_test.gen_unsuback(mid, proto_ver=5) -unsubscribe4_packet = mosq_test.gen_unsubscribe(mid, "$shared/two/share-test", proto_ver=5) +unsubscribe4_packet = mosq_test.gen_unsubscribe(mid, "$share/two/share-test", proto_ver=5) unsuback4_packet = mosq_test.gen_unsuback(mid, proto_ver=5) -unsubscribe5_packet = mosq_test.gen_unsubscribe(mid, "$shared/one/share-test", proto_ver=5) +unsubscribe5_packet = mosq_test.gen_unsubscribe(mid, "$share/one/share-test", proto_ver=5) unsuback5_packet = mosq_test.gen_unsuback(mid, proto_ver=5) diff --git a/test/broker/02-subpub-qos1-message-expiry-retain.py b/test/broker/02-subpub-qos1-message-expiry-retain.py index e475c0f3..6974a505 100755 --- a/test/broker/02-subpub-qos1-message-expiry-retain.py +++ b/test/broker/02-subpub-qos1-message-expiry-retain.py @@ -42,9 +42,6 @@ mid=1 publish2r_packet = mosq_test.gen_publish("subpub/kept", mid=mid, qos=1, retain=True, payload="message2", proto_ver=5) puback2r_packet = mosq_test.gen_puback(mid, proto_ver=5, reason_code=mqtt5_rc.MQTT_RC_NO_MATCHING_SUBSCRIBERS) -pingreq_packet = mosq_test.gen_pingreq() -pingresp_packet = mosq_test.gen_pingresp() - port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) diff --git a/test/broker/03-publish-b2c-qos1-len.py b/test/broker/03-publish-b2c-qos1-len.py index f0d44d0b..a9fe7c6a 100755 --- a/test/broker/03-publish-b2c-qos1-len.py +++ b/test/broker/03-publish-b2c-qos1-len.py @@ -20,9 +20,6 @@ def len_test(test, puback_packet): mid = 1 publish_packet = mosq_test.gen_publish("qos1/len/test", qos=1, mid=mid, payload="len-message", proto_ver=5) - pingreq_packet = mosq_test.gen_pingreq() - pingresp_packet = mosq_test.gen_pingresp() - broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: @@ -38,7 +35,7 @@ def len_test(test, puback_packet): if mosq_test.expect_packet(sock, "publish", publish_packet): sock.send(puback_packet) - mosq_test.do_send_receive(sock, pingreq_packet, pingresp_packet, "pingreq") + mosq_test.do_ping(sock) rc = 0 sock.close() diff --git a/test/broker/03-publish-b2c-qos2-len.py b/test/broker/03-publish-b2c-qos2-len.py index 05ce9207..a354749f 100755 --- a/test/broker/03-publish-b2c-qos2-len.py +++ b/test/broker/03-publish-b2c-qos2-len.py @@ -21,9 +21,6 @@ def len_test(test, pubrec_packet, pubcomp_packet): publish_packet = mosq_test.gen_publish("qos2/len/test", qos=2, mid=mid, payload="len-message", proto_ver=5) pubrel_packet = mosq_test.gen_pubrel(mid) - pingreq_packet = mosq_test.gen_pingreq() - pingresp_packet = mosq_test.gen_pingresp() - broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: @@ -40,7 +37,7 @@ def len_test(test, pubrec_packet, pubcomp_packet): mosq_test.do_send_receive(sock, pubrec_packet, pubrel_packet, "pubrel") sock.send(pubcomp_packet) - mosq_test.do_send_receive(sock, pingreq_packet, pingresp_packet, "pingresp") + mosq_test.do_ping(sock) rc = 0 sock.close() diff --git a/test/broker/03-publish-c2b-qos2-len.py b/test/broker/03-publish-c2b-qos2-len.py index 47b8e3e6..3febae64 100755 --- a/test/broker/03-publish-c2b-qos2-len.py +++ b/test/broker/03-publish-c2b-qos2-len.py @@ -19,9 +19,6 @@ def len_test(test, pubrel_packet): pubrec_packet = mosq_test.gen_pubrec(mid) pubcomp_packet = mosq_test.gen_pubcomp(mid) - pingreq_packet = mosq_test.gen_pingreq() - pingresp_packet = mosq_test.gen_pingresp() - broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: @@ -30,7 +27,7 @@ def len_test(test, pubrel_packet): mosq_test.do_send_receive(sock, publish_packet, pubrec_packet, "pubrec") mosq_test.do_send_receive(sock, pubrel_packet, pubcomp_packet, "pubcomp") - mosq_test.do_send_receive(sock, pingreq_packet, pingresp_packet, "pingresp") + mosq_test.do_ping(sock) rc = 0 sock.close() diff --git a/test/broker/04-retain-check-source-persist-diff-port.py b/test/broker/04-retain-check-source-persist-diff-port.py index 356d769d..aa6fc246 100755 --- a/test/broker/04-retain-check-source-persist-diff-port.py +++ b/test/broker/04-retain-check-source-persist-diff-port.py @@ -62,9 +62,6 @@ def do_test(per_listener, username): subscribe_packet = mosq_test.gen_subscribe(mid, "test/topic", 0) suback_packet = mosq_test.gen_suback(mid, 0) - pingreq_packet = mosq_test.gen_pingreq() - pingresp_packet = mosq_test.gen_pingresp() - broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port1) try: @@ -88,7 +85,7 @@ def do_test(per_listener, username): sock = mosq_test.do_client_connect(connect2_packet, connack2_packet, port=port2) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback 2") # If we receive the retained message here, it is a failure. - mosq_test.do_send_receive(sock, pingreq_packet, pingresp_packet, "pingresp") + mosq_test.do_ping(sock) rc = 0 sock.close() diff --git a/test/broker/04-retain-check-source-persist.py b/test/broker/04-retain-check-source-persist.py index 4876e033..0fbd475b 100755 --- a/test/broker/04-retain-check-source-persist.py +++ b/test/broker/04-retain-check-source-persist.py @@ -51,9 +51,6 @@ def do_test(per_listener, username): subscribe_packet = mosq_test.gen_subscribe(mid, "test/topic", 0) suback_packet = mosq_test.gen_suback(mid, 0) - pingreq_packet = mosq_test.gen_pingreq() - pingresp_packet = mosq_test.gen_pingresp() - broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: @@ -77,7 +74,7 @@ def do_test(per_listener, username): sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback 2") # If we receive the retained message here, it is a failure. - mosq_test.do_send_receive(sock, pingreq_packet, pingresp_packet, "pingresp") + mosq_test.do_ping(sock) rc = 0 sock.close() diff --git a/test/broker/04-retain-check-source.py b/test/broker/04-retain-check-source.py index 373309d0..b3adecb4 100755 --- a/test/broker/04-retain-check-source.py +++ b/test/broker/04-retain-check-source.py @@ -39,9 +39,6 @@ def do_test(per_listener): subscribe_packet = mosq_test.gen_subscribe(mid, "test/topic", 0) suback_packet = mosq_test.gen_suback(mid, 0) - pingreq_packet = mosq_test.gen_pingreq() - pingresp_packet = mosq_test.gen_pingresp() - broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: @@ -62,7 +59,7 @@ def do_test(per_listener): sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback 2") # If we receive the retained message here, it is a failure. - mosq_test.do_send_receive(sock, pingreq_packet, pingresp_packet, "pingresp") + mosq_test.do_ping(sock) rc = 0 sock.close() diff --git a/test/broker/04-retain-qos0-clear.py b/test/broker/04-retain-qos0-clear.py index e71ae1c6..48ca6036 100755 --- a/test/broker/04-retain-qos0-clear.py +++ b/test/broker/04-retain-qos0-clear.py @@ -41,13 +41,12 @@ try: # Subscribe to topic, we shouldn't get anything back apart # from the SUBACK. mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") - try: - retain_clear = sock.recv(256) - except socket.timeout: - # This is the expected event - rc = 0 - else: - print("FAIL: Received unexpected message.") + time.sleep(1) + # If we do get something back, it should be before this ping, so if + # this succeeds then we're ok. + mosq_test.do_ping(sock) + # This is the expected event + rc = 0 sock.close() finally: diff --git a/test/broker/06-bridge-b2br-late-connection-retain.py b/test/broker/06-bridge-b2br-late-connection-retain.py index 6625cbdf..ba646217 100755 --- a/test/broker/06-bridge-b2br-late-connection-retain.py +++ b/test/broker/06-bridge-b2br-late-connection-retain.py @@ -40,9 +40,6 @@ mid = 1 publish_packet = mosq_test.gen_publish("bridge/test", qos=1, mid=mid, payload="message", retain=True) puback_packet = mosq_test.gen_puback(mid) -pingreq_packet = mosq_test.gen_pingreq() -pingresp_packet = mosq_test.gen_pingresp() - ssock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) ssock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) ssock.settimeout(40) @@ -75,7 +72,7 @@ try: # Guard against multiple retained messages of the same type by # sending a pingreq to give us something to expect back. If we get # a publish, it's a fail. - mosq_test.do_send_receive(bridge, pingreq_packet, pingresp_packet, "pingresp") + mosq_test.do_ping(bridge) rc = 0 bridge.close() diff --git a/test/broker/06-bridge-br2b-remapping.py b/test/broker/06-bridge-br2b-remapping.py index 62092a08..ab29a08b 100755 --- a/test/broker/06-bridge-br2b-remapping.py +++ b/test/broker/06-bridge-br2b-remapping.py @@ -79,17 +79,11 @@ def test(bridge, sock): )) return 1 else: - bridge.settimeout(3) - try: - bridge.recv(1) - print("FAIL: Received data when nothing is expected") - print("Fail on cases local_topic=%r, remote_topic=%r" % ( - local_topic, remote_topic, + time.sleep(1) + mosq_test.do_ping(bridge, + "FAIL: Received data when nothing is expected\nFail on cases local_topic=%r, remote_topic=%r" % ( + local_topic, remote_topic, )) - return 1 - except socket.timeout: - pass - bridge.settimeout(20) return 0 try: diff --git a/test/broker/06-bridge-no-local.py b/test/broker/06-bridge-no-local.py new file mode 100755 index 00000000..7b54a14e --- /dev/null +++ b/test/broker/06-bridge-no-local.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 + +# Check whether an incoming bridge connection receives its own messages. It +# shouldn't because for v3.1 and v3.1.1 we have no-local set for all bridges. + +from mosq_test_helper import * + +port = mosq_test.get_port() + +def do_test(proto_ver_connect, proto_ver_msgs, sub_opts): + rc = 1 + keepalive = 60 + connect_packet = mosq_test.gen_connect("bridge-test", keepalive=keepalive, proto_ver=proto_ver_connect) + connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver_msgs) + + mid = 1 + subscribe_packet = mosq_test.gen_subscribe(mid, "loop/test", 0 | sub_opts, proto_ver=proto_ver_msgs) + suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver_msgs) + + publish_packet = mosq_test.gen_publish("loop/test", qos=0, payload="message", proto_ver=proto_ver_msgs) + + port = mosq_test.get_port() + broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) + + try: + sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port) + + mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") + + sock.send(publish_packet) + mosq_test.do_ping(sock) + rc = 0 + + sock.close() + finally: + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde.decode('utf-8')) + exit(rc) + +do_test(128+3, 3, 0) +do_test(128+4, 4, 0) +do_test(5, 5, mqtt5_opts.MQTT_SUB_OPT_NO_LOCAL) + +exit(0) + + diff --git a/test/broker/07-will-delay-reconnect.py b/test/broker/07-will-delay-reconnect.py index 1e3b82f8..2f435f36 100755 --- a/test/broker/07-will-delay-reconnect.py +++ b/test/broker/07-will-delay-reconnect.py @@ -25,9 +25,6 @@ def do_test(): subscribe_packet = mosq_test.gen_subscribe(mid, "will/test", 0, proto_ver=5) suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) - pingreq_packet = mosq_test.gen_pingreq() - pingresp_packet = mosq_test.gen_pingresp() - port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) @@ -47,7 +44,7 @@ def do_test(): # whether we get the old will. We should not. sock2.close() - mosq_test.do_send_receive(sock1, pingreq_packet, pingresp_packet, "pingresp") + mosq_test.do_ping(sock1) rc = 0 sock1.close() diff --git a/test/broker/07-will-delay-recover.py b/test/broker/07-will-delay-recover.py index 20320833..852c5499 100755 --- a/test/broker/07-will-delay-recover.py +++ b/test/broker/07-will-delay-recover.py @@ -26,9 +26,6 @@ def do_test(clean_session): subscribe_packet = mosq_test.gen_subscribe(mid, "will/test", 0, proto_ver=5) suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) - pingreq_packet = mosq_test.gen_pingreq() - pingresp_packet = mosq_test.gen_pingresp() - port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) @@ -45,7 +42,7 @@ def do_test(clean_session): # The client2 has reconnected within the will delay interval, which has now # passed. We should not have received the will at this point. - mosq_test.do_send_receive(sock1, pingreq_packet, pingresp_packet, "pingresp") + mosq_test.do_ping(sock1) rc = 0 sock1.close() diff --git a/test/broker/07-will-qos0-helper.py b/test/broker/07-will-qos0-helper.py deleted file mode 100755 index 031ed4a6..00000000 --- a/test/broker/07-will-qos0-helper.py +++ /dev/null @@ -1,18 +0,0 @@ -#!/usr/bin/env python3 - -# Connect a client with a will, then disconnect without DISCONNECT. - -from mosq_test_helper import * - -rc = 1 -keepalive = 60 -connect_packet = mosq_test.gen_connect("test-helper", keepalive=keepalive, will_topic="will/qos0/test", will_payload=b"will-message") -connack_packet = mosq_test.gen_connack(rc=0) - -port = mosq_test.get_port() -sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) -rc = 0 -sock.close() - -exit(rc) - diff --git a/test/broker/07-will-qos0.py b/test/broker/07-will-qos0.py index 5d73a822..bf284398 100755 --- a/test/broker/07-will-qos0.py +++ b/test/broker/07-will-qos0.py @@ -4,38 +4,54 @@ from mosq_test_helper import * -rc = 1 -mid = 53 -keepalive = 60 -connect_packet = mosq_test.gen_connect("will-qos0-test", keepalive=keepalive) -connack_packet = mosq_test.gen_connack(rc=0) -subscribe_packet = mosq_test.gen_subscribe(mid, "will/qos0/test", 0) -suback_packet = mosq_test.gen_suback(mid, 0) +def do_test(proto_ver, clean_session): + rc = 1 + mid = 53 + keepalive = 60 + connect1_packet = mosq_test.gen_connect("will-qos0-test", keepalive=keepalive, proto_ver=proto_ver) + connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) -publish_packet = mosq_test.gen_publish("will/qos0/test", qos=0, payload="will-message") + if proto_ver == 5: + props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_SESSION_EXPIRY_INTERVAL, 100) + else: + props = None -port = mosq_test.get_port() -broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) + connect2_packet = mosq_test.gen_connect("test-helper", keepalive=keepalive, will_topic="will/qos0/test", will_payload=b"will-message", clean_session=clean_session, proto_ver=proto_ver, properties=props) + connack2_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) -try: - sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=30, port=port) - mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") + subscribe_packet = mosq_test.gen_subscribe(mid, "will/qos0/test", 0, proto_ver=proto_ver) + suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver) - will = subprocess.Popen(['./07-will-qos0-helper.py', str(port)], stdout=subprocess.PIPE, stderr=subprocess.PIPE) - will.wait() - (stdo, stde) = will.communicate() + publish_packet = mosq_test.gen_publish("will/qos0/test", qos=0, payload="will-message", proto_ver=proto_ver) - if mosq_test.expect_packet(sock, "publish", publish_packet): - rc = 0 + port = mosq_test.get_port() + broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) - sock.close() -finally: - broker.terminate() - broker.wait() - (stdo, stde) = broker.communicate() - if rc: - print(stde.decode('utf-8')) + try: + sock = mosq_test.do_client_connect(connect1_packet, connack1_packet, timeout=5, port=port) + mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") -exit(rc) + sock2 = mosq_test.do_client_connect(connect2_packet, connack2_packet, port=port, timeout=5) + sock2.close() + + if mosq_test.expect_packet(sock, "publish", publish_packet): + rc = 0 + + sock.close() + except Exception as e: + print(e) + finally: + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde.decode('utf-8')) + exit(rc) + +do_test(4, True) +do_test(4, False) +do_test(5, True) +do_test(5, False) +exit(0) diff --git a/test/broker/07-will-reconnect-1273.py b/test/broker/07-will-reconnect-1273.py new file mode 100755 index 00000000..bd8a5a5a --- /dev/null +++ b/test/broker/07-will-reconnect-1273.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 + +# Test whether a persistent client that disconnects with DISCONNECT has its +# will published when it reconnects. It shouldn't. Bug 1273: +# https://github.com/eclipse/mosquitto/issues/1273 + +from mosq_test_helper import * + + +def do_test(proto_ver): + rc = 1 + keepalive = 60 + + connect1_packet = mosq_test.gen_connect("will-sub", keepalive=keepalive, proto_ver=proto_ver) + connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) + + mid = 1 + subscribe1_packet = mosq_test.gen_subscribe(mid, "will/test", 0, proto_ver=proto_ver) + suback1_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver) + + if proto_ver == 5: + props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_SESSION_EXPIRY_INTERVAL, 100) + else: + props = None + + connect2_packet = mosq_test.gen_connect("will-1273", keepalive=keepalive, will_topic="will/test", will_payload=b"will msg",clean_session=False, proto_ver=proto_ver, properties=props) + connack2a_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) + connack2b_packet = mosq_test.gen_connack(rc=0, flags=1, proto_ver=proto_ver) + + disconnect_packet = mosq_test.gen_disconnect(proto_ver=proto_ver) + + publish_packet = mosq_test.gen_publish("will/test", qos=0, payload="alive", proto_ver=proto_ver) + + port = mosq_test.get_port() + broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) + + try: + # Connect and subscribe will-sub + sock1 = mosq_test.do_client_connect(connect1_packet, connack1_packet, timeout=30, port=port, connack_error="connack1") + mosq_test.do_send_receive(sock1, subscribe1_packet, suback1_packet, "suback") + + # Connect will-1273 + sock2 = mosq_test.do_client_connect(connect2_packet, connack2a_packet, timeout=30, port=port) + # Publish our "alive" message + sock2.send(publish_packet) + # Clean disconnect + sock2.send(disconnect_packet) + + # will-1273 should get the "alive" + mosq_test.expect_packet(sock1, "publish1", publish_packet) + + sock2.close() + + # Reconnect + sock2 = mosq_test.do_client_connect(connect2_packet, connack2b_packet, timeout=30, port=port, connack_error="connack2") + # will-1273 to publish "alive" again, and will-sub to receive it. + sock2.send(publish_packet) + mosq_test.expect_packet(sock1, "publish2", publish_packet) + # Do a ping to make sure there are no other packets received. + mosq_test.do_ping(sock1) + rc = 0 + + sock1.close() + sock2.close() + finally: + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde.decode('utf-8')) + exit(rc) + +do_test(4) +do_test(5) +exit(0) diff --git a/test/broker/09-acl-access-variants.py b/test/broker/09-acl-access-variants.py index 03ec9be5..f20a5eed 100755 --- a/test/broker/09-acl-access-variants.py +++ b/test/broker/09-acl-access-variants.py @@ -45,14 +45,12 @@ def single_test(port, per_listener, username, topic, expect_deny): mid=1 publish1r_packet = mosq_test.gen_publish(topic=topic, mid=mid, qos=1, payload="message") - pingreq_packet = mosq_test.gen_pingreq() - pingresp_packet = mosq_test.gen_pingresp() sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") mosq_test.do_send_receive(sock, publish1s_packet, puback1s_packet, "puback") if expect_deny: - mosq_test.do_send_receive(sock, pingreq_packet, pingresp_packet, "pingresp") + mosq_test.do_ping(sock) else: mosq_test.expect_packet(sock, "publish1r", publish1r_packet) sock.close() diff --git a/test/broker/09-acl-change.py b/test/broker/09-acl-change.py index 435dc705..09d16f6b 100755 --- a/test/broker/09-acl-change.py +++ b/test/broker/09-acl-change.py @@ -62,9 +62,6 @@ mid = 3 publish4s_packet = mosq_test.gen_publish(topic="topic/two", mid=mid, qos=1, payload="message4") puback4s_packet = mosq_test.gen_puback(mid) -pingreq_packet = mosq_test.gen_pingreq() -pingresp_packet = mosq_test.gen_pingresp() - rc = 1 port = mosq_test.get_port() @@ -108,7 +105,7 @@ try: mosq_test.do_send_receive(sock, publish4s_packet, puback4s_packet, "puback4") # Check for non delivery with a ping - mosq_test.do_send_receive(sock, pingreq_packet, pingresp_packet, "pingresp") + mosq_test.do_ping(sock) sock.close() rc = 0 diff --git a/test/broker/09-acl-empty-file.py b/test/broker/09-acl-empty-file.py index d970f497..ab02c248 100755 --- a/test/broker/09-acl-empty-file.py +++ b/test/broker/09-acl-empty-file.py @@ -34,9 +34,6 @@ def do_test(port, per_listener): subscribe_packet = mosq_test.gen_subscribe(mid, "test/topic", 0) suback_packet = mosq_test.gen_suback(mid, 0) - pingreq_packet = mosq_test.gen_pingreq() - pingresp_packet = mosq_test.gen_pingresp() - broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: @@ -46,7 +43,7 @@ def do_test(port, per_listener): sock.send(publish_packet) # If we receive the message, this will fail. - mosq_test.do_send_receive(sock, pingreq_packet, pingresp_packet, "pingresp") + mosq_test.do_ping(sock) rc = 0 sock.close() diff --git a/test/broker/09-extended-auth-change-username.py b/test/broker/09-extended-auth-change-username.py index 05b4447a..89b4a823 100755 --- a/test/broker/09-extended-auth-change-username.py +++ b/test/broker/09-extended-auth-change-username.py @@ -37,9 +37,6 @@ def do_test(per_listener): publish1_packet = mosq_test.gen_publish("topic/one", qos=1, mid=mid, payload="message", proto_ver=5) puback1_packet = mosq_test.gen_puback(mid, proto_ver=5, reason_code=mqtt5_rc.MQTT_RC_NOT_AUTHORIZED) - pingreq_packet = mosq_test.gen_pingreq() - pingresp_packet = mosq_test.gen_pingresp() - # Connect without a username, but have the plugin change it props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_METHOD, "change") connect2_packet = mosq_test.gen_connect("client-params-test2", keepalive=42, proto_ver=5, properties=props) @@ -59,14 +56,14 @@ def do_test(per_listener): sock = mosq_test.do_client_connect(connect1_packet, connack1_packet, timeout=20, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback1") mosq_test.do_send_receive(sock, publish1_packet, puback1_packet, "puback1") - mosq_test.do_send_receive(sock, pingreq_packet, pingresp_packet, "pingresp") + mosq_test.do_ping(sock) sock.close() sock = mosq_test.do_client_connect(connect2_packet, connack2_packet, timeout=20, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback2") mosq_test.do_send_receive(sock, publish2s_packet, puback2s_packet, "puback2") if mosq_test.expect_packet(sock, "publish2", publish2r_packet): - mosq_test.do_send_receive(sock, pingreq_packet, pingresp_packet, "pingresp") + mosq_test.do_ping(sock) rc = 0 sock.close() diff --git a/test/broker/09-extended-auth-multistep-reauth.py b/test/broker/09-extended-auth-multistep-reauth.py index a9991d3b..0f3ae978 100755 --- a/test/broker/09-extended-auth-multistep-reauth.py +++ b/test/broker/09-extended-auth-multistep-reauth.py @@ -62,19 +62,16 @@ reauth3_packet = mosq_test.gen_auth(reason_code=mqtt5_rc.MQTT_RC_REAUTHENTICATE, disconnect3_packet = mosq_test.gen_disconnect(reason_code=mqtt5_rc.MQTT_RC_PROTOCOL_ERROR, proto_ver=5) -pingreq_packet = mosq_test.gen_pingreq() -pingresp_packet = mosq_test.gen_pingresp() - broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect1_packet, auth1_1_packet, timeout=20, port=port, connack_error="auth1") mosq_test.do_send_receive(sock, auth1_2_packet, connack1_packet, "connack1") - mosq_test.do_send_receive(sock, pingreq_packet, pingresp_packet, "pingresp1") + mosq_test.do_ping(sock, "pingresp1") mosq_test.do_send_receive(sock, reauth2_packet, auth2_1_packet, "auth2_1") mosq_test.do_send_receive(sock, auth2_2_packet, auth2_3_packet, "auth2_3") - mosq_test.do_send_receive(sock, pingreq_packet, pingresp_packet, "pingresp2") + mosq_test.do_ping(sock, "pingresp2") mosq_test.do_send_receive(sock, reauth3_packet, disconnect3_packet, "disconnect3") diff --git a/test/broker/09-plugin-auth-acl-pub.py b/test/broker/09-plugin-auth-acl-pub.py index 1e5d07dc..d546f95f 100755 --- a/test/broker/09-plugin-auth-acl-pub.py +++ b/test/broker/09-plugin-auth-acl-pub.py @@ -37,9 +37,6 @@ mid = 2 publish2_packet = mosq_test.gen_publish("writeable", qos=1, mid=mid, payload="message") puback2_packet = mosq_test.gen_puback(mid) -pingreq_packet = mosq_test.gen_pingreq() -pingresp_packet = mosq_test.gen_pingresp() - broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: @@ -51,7 +48,7 @@ try: sock = mosq_test.do_client_connect(connect2_packet, connack2_packet, timeout=20, port=port) mosq_test.do_send_receive(sock, publish2_packet, puback2_packet, "puback2") - mosq_test.do_send_receive(sock, pingreq_packet, pingresp_packet, "pingresp") + mosq_test.do_ping(sock) rc = 0 diff --git a/test/broker/11-message-expiry.py b/test/broker/11-message-expiry.py index c755e59f..d7b107c5 100755 --- a/test/broker/11-message-expiry.py +++ b/test/broker/11-message-expiry.py @@ -44,7 +44,7 @@ publish1s_packet = mosq_test.gen_publish("subpub/qos1", mid=mid, qos=1, payload= puback1s_packet = mosq_test.gen_puback(mid) mid=2 -props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_MESSAGE_EXPIRY_INTERVAL, 10) +props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_MESSAGE_EXPIRY_INTERVAL, 100) publish2s_packet = mosq_test.gen_publish("subpub/qos1", mid=mid, qos=1, payload="message2", proto_ver=5, properties=props) puback2s_packet = mosq_test.gen_puback(mid) @@ -75,11 +75,14 @@ try: sock.close() broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) - time.sleep(2) + if os.environ.get('TRAVIS') is not None: + time.sleep(5) + else: + time.sleep(2) sock = mosq_test.do_client_connect(connect_packet, connack2_packet, timeout=20, port=port) packet = sock.recv(len(publish2s_packet)) - for i in range(9, 1, -1): + for i in range(100, 1, -1): props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_MESSAGE_EXPIRY_INTERVAL, i) publish2r_packet = mosq_test.gen_publish("subpub/qos1", mid=2, qos=1, payload="message2", proto_ver=5, properties=props) if packet == publish2r_packet: diff --git a/test/broker/12-prop-assigned-client-identifier.py b/test/broker/12-prop-assigned-client-identifier.py index 376d10ce..28f5037b 100755 --- a/test/broker/12-prop-assigned-client-identifier.py +++ b/test/broker/12-prop-assigned-client-identifier.py @@ -5,43 +5,48 @@ from mosq_test_helper import * -rc = 1 - -keepalive = 10 -connect_packet = mosq_test.gen_connect(None, proto_ver=5, keepalive=keepalive) -props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_ASSIGNED_CLIENT_IDENTIFIER, "auto-00000000-0000-0000-0000-000000000000") -connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props) +def do_test(clean_start): + rc = 1 + keepalive = 10 + connect_packet = mosq_test.gen_connect(None, proto_ver=5, keepalive=keepalive, clean_session=clean_start) -props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_SESSION_EXPIRY_INTERVAL, 1) -disconnect_client_packet = mosq_test.gen_disconnect(proto_ver=5, properties=props) + props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_ASSIGNED_CLIENT_IDENTIFIER, "auto-00000000-0000-0000-0000-000000000000") + connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props) -disconnect_server_packet = mosq_test.gen_disconnect(proto_ver=5, reason_code=130) + props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_SESSION_EXPIRY_INTERVAL, 1) + disconnect_client_packet = mosq_test.gen_disconnect(proto_ver=5, properties=props) -port = mosq_test.get_port() -broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) + disconnect_server_packet = mosq_test.gen_disconnect(proto_ver=5, reason_code=130) -try: - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - sock.settimeout(10) - sock.connect(("localhost", port)) + port = mosq_test.get_port() + broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) - sock.send(connect_packet) - connack_recvd = sock.recv(len(connack_packet)) + try: + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.settimeout(10) + sock.connect(("localhost", port)) - if connack_recvd[0:12] == connack_packet[0:12]: - # FIXME - this test could be tightened up a lot - rc = 0 + sock.send(connect_packet) + connack_recvd = sock.recv(len(connack_packet)) + + if connack_recvd[0:12] == connack_packet[0:12]: + # FIXME - this test could be tightened up a lot + rc = 0 - sock.close() -finally: - broker.terminate() - broker.wait() - (stdo, stde) = broker.communicate() - if rc: - print(stde.decode('utf-8')) + sock.close() + finally: + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde.decode('utf-8')) + exit(rc) -exit(rc) + +do_test(True) +do_test(False) +exit(0) diff --git a/test/broker/12-prop-maximum-packet-size-publish-qos1.py b/test/broker/12-prop-maximum-packet-size-publish-qos1.py index bded92ce..4fee7c76 100755 --- a/test/broker/12-prop-maximum-packet-size-publish-qos1.py +++ b/test/broker/12-prop-maximum-packet-size-publish-qos1.py @@ -24,9 +24,6 @@ mid=2 publish2_packet = mosq_test.gen_publish(topic="test/topic", mid=mid, qos=1, payload="7890", proto_ver=5) puback2_packet = mosq_test.gen_puback(mid, proto_ver=5) -pingreq_packet = mosq_test.gen_pingreq() -pingresp_packet = mosq_test.gen_pingresp() - port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) @@ -37,7 +34,7 @@ try: mosq_test.do_send_receive(sock, publish1_packet, puback1_packet, "puback 1") # We shouldn't receive the publish here because it is > MAXIMUM_PACKET_SIZE - mosq_test.do_send_receive(sock, pingreq_packet, pingresp_packet) + mosq_test.do_ping(sock) mosq_test.do_send_receive(sock, publish2_packet, puback2_packet, "puback 2") diff --git a/test/broker/12-prop-maximum-packet-size-publish-qos2.py b/test/broker/12-prop-maximum-packet-size-publish-qos2.py index 834acf70..d7895202 100755 --- a/test/broker/12-prop-maximum-packet-size-publish-qos2.py +++ b/test/broker/12-prop-maximum-packet-size-publish-qos2.py @@ -28,9 +28,6 @@ pubrec2_packet = mosq_test.gen_pubrec(mid, proto_ver=5) pubrel2_packet = mosq_test.gen_pubrel(mid, proto_ver=5) pubcomp2_packet = mosq_test.gen_pubcomp(mid, proto_ver=5) -pingreq_packet = mosq_test.gen_pingreq() -pingresp_packet = mosq_test.gen_pingresp() - port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) @@ -42,7 +39,7 @@ try: mosq_test.do_send_receive(sock, pubrel1_packet, pubcomp1_packet, "pubcomp 1") # We shouldn't receive the publish here because it is > MAXIMUM_PACKET_SIZE - mosq_test.do_send_receive(sock, pingreq_packet, pingresp_packet) + mosq_test.do_ping(sock) mosq_test.do_send_receive(sock, publish2_packet, pubrec2_packet, "pubrec 2") mosq_test.do_send_receive(sock, pubrel2_packet, pubcomp2_packet, "pubcomp 2") diff --git a/test/broker/12-prop-maximum-packet-size-publish.py b/test/broker/12-prop-maximum-packet-size-publish.py index f3d739a7..dca9e34e 100755 --- a/test/broker/12-prop-maximum-packet-size-publish.py +++ b/test/broker/12-prop-maximum-packet-size-publish.py @@ -19,9 +19,6 @@ suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) publish1_packet = mosq_test.gen_publish(topic="test/topic", qos=0, payload="12345678901234567890", proto_ver=5) publish2_packet = mosq_test.gen_publish(topic="test/topic", qos=0, payload="67890", proto_ver=5) -pingreq_packet = mosq_test.gen_pingreq() -pingresp_packet = mosq_test.gen_pingresp() - port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) @@ -30,9 +27,9 @@ try: mosq_test.do_send_receive(sock, subscribe_packet, suback_packet) sock.send(publish1_packet) # We shouldn't receive the publish here because it is > MAXIMUM_PACKET_SIZE - mosq_test.do_send_receive(sock, pingreq_packet, pingresp_packet) + mosq_test.do_ping(sock, "pingresp1") mosq_test.do_send_receive(sock, publish2_packet, publish2_packet) - mosq_test.do_send_receive(sock, pingreq_packet, pingresp_packet) + mosq_test.do_ping(sock, "pingresp2") rc = 0 finally: broker.terminate() diff --git a/test/broker/Makefile b/test/broker/Makefile index 292f0f82..ba91fb8a 100644 --- a/test/broker/Makefile +++ b/test/broker/Makefile @@ -129,6 +129,7 @@ endif ./06-bridge-br2b-remapping.py ./06-bridge-fail-persist-resend-qos1.py ./06-bridge-fail-persist-resend-qos2.py + ./06-bridge-no-local.py ./06-bridge-per-listener-settings.py ./06-bridge-reconnect-local-out.py @@ -143,6 +144,7 @@ endif ./07-will-null.py ./07-will-properties.py ./07-will-qos0.py + ./07-will-reconnect-1273.py 08 : ifeq ($(WITH_TLS),yes) diff --git a/test/broker/test.py b/test/broker/test.py index 77bad2c1..497e3627 100755 --- a/test/broker/test.py +++ b/test/broker/test.py @@ -96,16 +96,17 @@ tests = [ (2, './06-bridge-b2br-disconnect-qos1.py'), (2, './06-bridge-b2br-disconnect-qos2.py'), + (2, './06-bridge-b2br-late-connection-retain.py'), + (2, './06-bridge-b2br-late-connection.py'), (2, './06-bridge-b2br-remapping.py'), (2, './06-bridge-br2b-disconnect-qos1.py'), (2, './06-bridge-br2b-disconnect-qos2.py'), (2, './06-bridge-br2b-remapping.py'), (2, './06-bridge-fail-persist-resend-qos1.py'), (2, './06-bridge-fail-persist-resend-qos2.py'), - (2, './06-bridge-reconnect-local-out.py'), + (1, './06-bridge-no-local.py'), (3, './06-bridge-per-listener-settings.py'), - (2, './06-bridge-b2br-late-connection.py'), - (2, './06-bridge-b2br-late-connection-retain.py'), + (2, './06-bridge-reconnect-local-out.py'), (1, './07-will-delay-reconnect.py'), (1, './07-will-delay-recover.py'), @@ -117,6 +118,7 @@ tests = [ (1, './07-will-null.py'), (1, './07-will-properties.py'), (1, './07-will-qos0.py'), + (1, './07-will-reconnect-1273.py'), (2, './08-ssl-bridge.py'), (2, './08-ssl-connect-cert-auth-crl.py'), diff --git a/test/mosq_test.py b/test/mosq_test.py index faba32cb..2093e326 100644 --- a/test/mosq_test.py +++ b/test/mosq_test.py @@ -603,6 +603,10 @@ def get_lib_port(): return 1888 +def do_ping(sock, error_string="pingresp"): + do_send_receive(sock, gen_pingreq(), gen_pingresp(), error_string) + + @atexit.register def test_cleanup(): global vg_logfiles diff --git a/test/unit/persist_read_stubs.c b/test/unit/persist_read_stubs.c index 7e0d18c4..568016ec 100644 --- a/test/unit/persist_read_stubs.c +++ b/test/unit/persist_read_stubs.c @@ -142,3 +142,9 @@ int sub__messages_queue(struct mosquitto_db *db, const char *source_id, const ch return MOSQ_ERR_SUCCESS; } + +void db__msg_store_ref_inc(struct mosquitto_msg_store *store) +{ + store->ref_count++; +} + diff --git a/www/pages/download.md b/www/pages/download.md index 3fbe4946..13aa8ddc 100644 --- a/www/pages/download.md +++ b/www/pages/download.md @@ -11,7 +11,7 @@ # Source -* [mosquitto-1.6.2.tar.gz](https://mosquitto.org/files/source/mosquitto-1.6.2.tar.gz) (319kB) ([GPG signature](https://mosquitto.org/files/source/mosquitto-1.6.2.tar.gz.asc)) +* [mosquitto-1.6.3.tar.gz](https://mosquitto.org/files/source/mosquitto-1.6.3.tar.gz) (319kB) ([GPG signature](https://mosquitto.org/files/source/mosquitto-1.6.3.tar.gz.asc)) * [Git source code repository](https://github.com/eclipse/mosquitto) (github.com) Older downloads are available at [https://mosquitto.org/files/](../files/) @@ -24,8 +24,8 @@ distributions. ## Windows -* [mosquitto-1.6.2-install-windows-x64.exe](https://mosquitto.org/files/binary/win64/mosquitto-1.6.2-install-windows-x64.exe) (~360 kB) (64-bit build, Windows Vista and up, built with Visual Studio Community 2017) -* [mosquitto-1.6.2-install-windows-x32.exe](https://mosquitto.org/files/binary/win32/mosquitto-1.6.2-install-windows-x86.exe) (~360 kB) (32-bit build, Windows Vista and up, built with Visual Studio Community 2017) +* [mosquitto-1.6.3-install-windows-x64.exe](https://mosquitto.org/files/binary/win64/mosquitto-1.6.3-install-windows-x64.exe) (~1.4 MB) (64-bit build, Windows Vista and up, built with Visual Studio Community 2017) +* [mosquitto-1.6.3-install-windows-x32.exe](https://mosquitto.org/files/binary/win32/mosquitto-1.6.2-install-windows-x86.exe) (~1.4 MB) (32-bit build, Windows Vista and up, built with Visual Studio Community 2017) See also readme-windows.txt after installing. diff --git a/www/posts/2019/06/version-1-6-3-released.md b/www/posts/2019/06/version-1-6-3-released.md new file mode 100644 index 00000000..5edcfff2 --- /dev/null +++ b/www/posts/2019/06/version-1-6-3-released.md @@ -0,0 +1,84 @@ + + +This is a bugfix release. + +## Broker +- Fix detection of incoming v3.1/v3.1.1 bridges. Closes [#1263]. +- Fix default `max_topic_alias` listener config not being copied to the in-use + listener when compiled without TLS support. +- Fix random number generation if compiling using `WITH_TLS=no` and on Linux + with glibc >= 2.25. Without this fix, no random numbers would be generated + for e.g. on broker client id generation, and so clients connecting expecting + this feature would be unable to connect. +- Fix compilation problem related to `getrandom()` on non-glibc systems. +- Fix Will message for a persistent client incorrectly being sent when the + client reconnects after a clean disconnect. Closes [#1273]. +- Fix Will message for a persistent client not being sent on disconnect. + Closes [#1273]. +- Improve documentation around the upgrading of persistence files. Closes + [#1276]. +- Add 'extern "C"' on mosquitto_broker.h and mosquitto_plugin.h for C++ plugin + writing. Closes [#1290]. +- Fix persistent Websockets clients not receiving messages after they + reconnect, having sent DISCONNECT on a previous session. Closes [#1227]. +- Disable TLS renegotiation. Client initiated renegotiation is considered to + be a potential attack vector against servers. Closes [#1257]. +- Fix incorrect shared subscription topic '$shared'. +- Fix zero length client ids being rejected for MQTT v5 clients with clean + start set to true. +- Fix MQTT v5 overlapping subscription behaviour. Clients now receive message + from all matching subscriptions rather than the first one encountered, which + ensures the maximum QoS requirement is met. +- Fix incoming/outgoing quota problems for QoS>0. +- Remove obsolete `store_clean_interval` from documentation. + +## Client library +- Fix typo causing build error on Windows when building without TLS support. + Closes [#1264]. + +## Clients +- Fix -L url parsing when `/topic` part is missing. +- Stop some error messages being printed even when `--quiet` was used. + Closes [#1284]. +- Fix `mosquitto_pub` exiting with error code 0 when an error occurred. + Closes [#1285]. +- Fix `mosquitto_pub` not using the `-c` option. Closes [#1273]. +- Fix MQTT v5 clients not being able to specify a password without a username. + Closes [#1274]. +- Fix `mosquitto_pub -l` not handling network failures. Closes [#1152]. +- Fix `mosquitto_pub -l` not handling zero length input. Closes [#1302]. +- Fix double free on exit in `mosquitto_pub`. Closes [#1280]. + +## Documentation: +- Remove references to Python binding and C++ wrapper in libmosquitto man + page. Closes [#1266]. + +## Build +- CLIENT_LDFLAGS now uses LDFLAGS. Closes [#1294]. + +[#1152]: https://github.com/eclipse/mosquitto/issues/1152 +[#1227]: https://github.com/eclipse/mosquitto/issues/1227 +[#1257]: https://github.com/eclipse/mosquitto/issues/1257 +[#1263]: https://github.com/eclipse/mosquitto/issues/1263 +[#1264]: https://github.com/eclipse/mosquitto/issues/1264 +[#1266]: https://github.com/eclipse/mosquitto/issues/1266 +[#1273]: https://github.com/eclipse/mosquitto/issues/1273 +[#1273]: https://github.com/eclipse/mosquitto/issues/1273 +[#1273]: https://github.com/eclipse/mosquitto/issues/1273 +[#1274]: https://github.com/eclipse/mosquitto/issues/1274 +[#1276]: https://github.com/eclipse/mosquitto/issues/1276 +[#1280]: https://github.com/eclipse/mosquitto/issues/1280 +[#1284]: https://github.com/eclipse/mosquitto/issues/1284 +[#1285]: https://github.com/eclipse/mosquitto/issues/1285 +[#1290]: https://github.com/eclipse/mosquitto/issues/1290 +[#1294]: https://github.com/eclipse/mosquitto/issues/1294 +[#1302]: https://github.com/eclipse/mosquitto/issues/1302 diff --git a/www/themes/mosquitto/templates/base_helper.tmpl b/www/themes/mosquitto/templates/base_helper.tmpl index 98439c1e..d580aa63 100644 --- a/www/themes/mosquitto/templates/base_helper.tmpl +++ b/www/themes/mosquitto/templates/base_helper.tmpl @@ -22,16 +22,6 @@ lang="${lang}"> - - - - % if use_base_tag: % endif