diff --git a/CMakeLists.txt b/CMakeLists.txt index 187fd882e..327280bad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ cmake_minimum_required(VERSION 3.0) cmake_policy(SET CMP0042 NEW) project(mosquitto) -set (VERSION 2.0.4) +set (VERSION 2.0.5) list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/") diff --git a/ChangeLog.txt b/ChangeLog.txt index d65a9fa10..4e8bde1f2 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -1,3 +1,38 @@ +2.0.5 - 2021-01-11 +================== + +Broker: +- Fix `auth_method` not being provided to the extended auth plugin event. + Closes #1975. +- Fix large packets not being completely published to slow clients. + Closes #1977. +- Fix bridge connection not relinquishing POLLOUT after messages are sent. + Closes #1979. +- Fix apparmor incorrectly denying access to + /var/lib/mosquitto/mosquitto.db.new. Closes #1978. +- Fix potential intermittent initial bridge connections when using poll(). +- Fix `bind_interface` option. Closes #1999. +- Fix invalid behaviour in dynsec plugin if a group or client is deleted + before a role that was attached to the group or client is deleted. + Closes #1998. +- Improve logging in dynsec addGroupRole command. Closes #2005. +- Improve logging in dynsec addGroupClient command. Closes #2008. + +Client library: +- Improve documentation around the `_v5()` and non-v5 functions, e.g. + `mosquitto_publish()` and `mosquitto_publish_v5(). + +Build: +- `install` Makefile target should depend on `all`, not `mosquitto`, to ensure + that man pages are always built. Closes #1989. +- Fixes for lots of minor build warnings highlighted by Visual Studio. + +Apps: +- Disallow control characters in mosquitto_passwd usernames. +- Fix incorrect description in mosquitto_ctrl man page. Closes #1995. +- Fix `mosquitto_ctrl dynsec getGroup` not showing roles. Closes #1997. + + 2.0.4 - 2020-12-22 ================== diff --git a/Makefile b/Makefile index 17c76daeb..ebc2ee0ea 100644 --- a/Makefile +++ b/Makefile @@ -87,7 +87,7 @@ ptest : mosquitto utest : mosquitto $(MAKE) -C test utest -install : mosquitto +install : all set -e; for d in ${DIRS}; do $(MAKE) -C $${d} install; done ifeq ($(WITH_DOCS),yes) set -e; for d in ${DOCDIRS}; do $(MAKE) -C $${d} install; done diff --git a/apps/db_dump/stubs.c b/apps/db_dump/stubs.c index 3515674ea..5fb08a8cb 100644 --- a/apps/db_dump/stubs.c +++ b/apps/db_dump/stubs.c @@ -38,12 +38,22 @@ enum mosquitto_client_state mosquitto__get_state(struct mosquitto *mosq) return mosq_cs_new; } +int mux__add_out(struct mosquitto *mosq) +{ + return 0; +} + +int mux__remove_out(struct mosquitto *mosq) +{ + return 0; +} + ssize_t net__read(struct mosquitto *mosq, void *buf, size_t count) { return 0; } -ssize_t net__write(struct mosquitto *mosq, void *buf, size_t count) +ssize_t net__write(struct mosquitto *mosq, const void *buf, size_t count) { return 0; } diff --git a/apps/mosquitto_ctrl/client.c b/apps/mosquitto_ctrl/client.c index 0d1aa905c..ea04e2e38 100644 --- a/apps/mosquitto_ctrl/client.c +++ b/apps/mosquitto_ctrl/client.c @@ -36,6 +36,8 @@ static void on_message(struct mosquitto *mosq, void *obj, const struct mosquitto { struct mosq_ctrl *ctrl = obj; + UNUSED(properties); + if(ctrl->payload_callback){ ctrl->payload_callback(ctrl, msg->payloadlen, msg->payload); } @@ -47,6 +49,10 @@ static void on_message(struct mosquitto *mosq, void *obj, const struct mosquitto static void on_publish(struct mosquitto *mosq, void *obj, int mid, int reason_code, const mosquitto_property *properties) { + UNUSED(obj); + UNUSED(mid); + UNUSED(properties); + if(reason_code > 127){ fprintf(stderr, "Publish error: %s\n", mosquitto_reason_string(reason_code)); run = 0; @@ -59,6 +65,9 @@ static void on_subscribe(struct mosquitto *mosq, void *obj, int mid, int qos_cou { struct mosq_ctrl *ctrl = obj; + UNUSED(mid); + UNUSED(properties); + if(qos_count == 1){ if(granted_qos[0] < 128){ /* Success */ @@ -87,6 +96,9 @@ static void on_connect(struct mosquitto *mosq, void *obj, int reason_code, int f { struct mosq_ctrl *ctrl = obj; + UNUSED(flags); + UNUSED(properties); + if(reason_code == 0){ if(ctrl->response_topic){ mosquitto_subscribe(mosq, NULL, ctrl->response_topic, ctrl->cfg.qos); diff --git a/apps/mosquitto_ctrl/dynsec.c b/apps/mosquitto_ctrl/dynsec.c index 35d29e9d5..796207e64 100644 --- a/apps/mosquitto_ctrl/dynsec.c +++ b/apps/mosquitto_ctrl/dynsec.c @@ -124,6 +124,37 @@ static void print_list(cJSON *j_response, const char *arrayname, const char *key } +static void print_roles(cJSON *j_roles, size_t slen) +{ + bool first; + cJSON *j_elem, *jtmp; + + if(j_roles && cJSON_IsArray(j_roles)){ + first = true; + cJSON_ArrayForEach(j_elem, j_roles){ + jtmp = cJSON_GetObjectItem(j_elem, "rolename"); + if(jtmp && cJSON_IsString(jtmp)){ + if(first){ + first = false; + printf("%-*s %s", (int)slen, "Roles:", jtmp->valuestring); + }else{ + printf("%-*s %s", (int)slen, "", jtmp->valuestring); + } + jtmp = cJSON_GetObjectItem(j_elem, "priority"); + if(jtmp && cJSON_IsNumber(jtmp)){ + printf(" (priority: %d)", (int)jtmp->valuedouble); + }else{ + printf(" (priority: -1)"); + } + printf("\n"); + } + } + }else{ + printf("Roles:\n"); + } +} + + static void print_client(cJSON *j_response) { cJSON *j_data, *j_client, *j_array, *j_elem, *jtmp; @@ -161,29 +192,8 @@ static void print_client(cJSON *j_response) } j_array = cJSON_GetObjectItem(j_client, "roles"); - if(j_array && cJSON_IsArray(j_array)){ - first = true; - cJSON_ArrayForEach(j_elem, j_array){ - jtmp = cJSON_GetObjectItem(j_elem, "rolename"); - if(jtmp && cJSON_IsString(jtmp)){ - if(first){ - first = false; - printf("Roles: %s", jtmp->valuestring); - }else{ - printf(" %s", jtmp->valuestring); - } - jtmp = cJSON_GetObjectItem(j_elem, "priority"); - if(jtmp && cJSON_IsNumber(jtmp)){ - printf(" (priority: %d)", (int)jtmp->valuedouble); - }else{ - printf(" (priority: -1)"); - } - printf("\n"); - } - } - }else{ - printf("Roles:\n"); - } + print_roles(j_array, strlen("Username:")); + j_array = cJSON_GetObjectItem(j_client, "groups"); if(j_array && cJSON_IsArray(j_array)){ first = true; @@ -236,27 +246,7 @@ static void print_group(cJSON *j_response) printf("Groupname: %s\n", jtmp->valuestring); j_array = cJSON_GetObjectItem(j_group, "roles"); - if(j_array && cJSON_IsArray(j_array)){ - first = true; - cJSON_ArrayForEach(j_elem, j_array){ - jtmp = cJSON_GetObjectItem(j_elem, "groupname"); - if(jtmp && cJSON_IsString(jtmp)){ - if(first){ - first = false; - printf("Roles: %s", jtmp->valuestring); - }else{ - printf(" %s", jtmp->valuestring); - } - jtmp = cJSON_GetObjectItem(j_elem, "priority"); - if(jtmp && cJSON_IsNumber(jtmp)){ - printf(" (priority: %d)", (int)jtmp->valuedouble); - }else{ - printf(" (priority: -1)"); - } - printf("\n"); - } - } - } + print_roles(j_array, strlen("Groupname:")); j_array = cJSON_GetObjectItem(j_group, "clients"); if(j_array && cJSON_IsArray(j_array)){ @@ -393,7 +383,13 @@ static void dynsec__payload_callback(struct mosq_ctrl *ctrl, long payloadlen, co { cJSON *tree, *j_responses, *j_response, *j_command, *j_error; + UNUSED(ctrl); + +#if CJSON_VERSION_FULL < 1007013 tree = cJSON_Parse(payload); +#else + tree = cJSON_ParseWithLength(payload, payloadlen); +#endif if(tree == NULL){ fprintf(stderr, "Error: Payload not JSON.\n"); return; @@ -502,6 +498,9 @@ static int dynsec__set_default_acl_access(int argc, char *argv[], cJSON *j_comma static int dynsec__get_default_acl_access(int argc, char *argv[], cJSON *j_command) { + UNUSED(argc); + UNUSED(argv); + if(cJSON_AddStringToObject(j_command, "command", "getDefaultACLAccess") == NULL ){ diff --git a/apps/mosquitto_ctrl/dynsec_group.c b/apps/mosquitto_ctrl/dynsec_group.c index 4b5ceca57..5fc31b849 100644 --- a/apps/mosquitto_ctrl/dynsec_group.c +++ b/apps/mosquitto_ctrl/dynsec_group.c @@ -15,6 +15,8 @@ SPDX-License-Identifier: EPL-2.0 OR EDL-1.0 Contributors: Roger Light - initial implementation and documentation. */ +#include "config.h" + #include #include #include @@ -66,6 +68,9 @@ int dynsec_group__delete(int argc, char *argv[], cJSON *j_command) int dynsec_group__get_anonymous(int argc, char *argv[], cJSON *j_command) { + UNUSED(argc); + UNUSED(argv); + if(cJSON_AddStringToObject(j_command, "command", "getAnonymousGroup") == NULL ){ diff --git a/apps/mosquitto_passwd/mosquitto_passwd.c b/apps/mosquitto_passwd/mosquitto_passwd.c index 9495c3c5f..268468819 100644 --- a/apps/mosquitto_passwd/mosquitto_passwd.c +++ b/apps/mosquitto_passwd/mosquitto_passwd.c @@ -18,6 +18,7 @@ SPDX-License-Identifier: EPL-2.0 OR EDL-1.0 #include "config.h" +#include #include #include #include @@ -240,6 +241,10 @@ static int pwfile_iterate(FILE *fptr, FILE *ftmp, * ====================================================================== */ static int delete_pwuser_cb(FILE *fptr, FILE *ftmp, const char *username, const char *password, const char *line, struct cb_helper *helper) { + UNUSED(fptr); + UNUSED(password); + UNUSED(line); + if(strcmp(username, helper->username)){ /* If this isn't the username to delete, write it to the new file */ fprintf(ftmp, "%s", line); @@ -273,6 +278,9 @@ int delete_pwuser(FILE *fptr, FILE *ftmp, const char *username) * ====================================================================== */ static int update_file_cb(FILE *fptr, FILE *ftmp, const char *username, const char *password, const char *line, struct cb_helper *helper) { + UNUSED(fptr); + UNUSED(line); + if(helper){ return output_new_password(ftmp, username, password, helper->iterations); }else{ @@ -293,6 +301,9 @@ static int update_pwuser_cb(FILE *fptr, FILE *ftmp, const char *username, const { int rc = 0; + UNUSED(fptr); + UNUSED(password); + if(strcmp(username, helper->username)){ /* If this isn't the matching user, then writing out the exiting line */ fprintf(ftmp, "%s", line); @@ -378,6 +389,32 @@ void handle_sigint(int signal) exit(0); } + +static bool is_username_valid(const char *username) +{ + int i; + size_t slen; + + if(username){ + slen = strlen(username); + if(slen > 65535){ + fprintf(stderr, "Error: Username must be less than 65536 characters long.\n"); + return false; + } + for(i=0; i 65535){ - fprintf(stderr, "Error: Username must be less than 65536 characters long.\n"); - return 1; - } - if(strchr(username, ':')){ - fprintf(stderr, "Error: Username must not contain the ':' character.\n"); - return 1; - } + if(!is_username_valid(username)){ + return 1; } if(password_cmd && strlen(password_cmd) > 65535){ fprintf(stderr, "Error: Password must be less than 65536 characters long.\n"); diff --git a/client/rr_client.c b/client/rr_client.c index edaed70af..464d735e6 100644 --- a/client/rr_client.c +++ b/client/rr_client.c @@ -54,7 +54,7 @@ extern struct mosq_config cfg; bool process_messages = true; int msg_count = 0; -struct mosquitto *mosq = NULL; +struct mosquitto *g_mosq = NULL; static bool timed_out = false; static int connack_result = 0; @@ -63,7 +63,7 @@ void my_signal_handler(int signum) { if(signum == SIGALRM){ process_messages = false; - mosquitto_disconnect_v5(mosq, MQTT_RC_DISCONNECT_WITH_WILL_MSG, cfg.disconnect_props); + mosquitto_disconnect_v5(g_mosq, MQTT_RC_DISCONNECT_WITH_WILL_MSG, cfg.disconnect_props); timed_out = true; } } @@ -82,6 +82,10 @@ int my_publish(struct mosquitto *mosq, int *mid, const char *topic, int payloadl void my_message_callback(struct mosquitto *mosq, void *obj, const struct mosquitto_message *message, const mosquitto_property *properties) { + UNUSED(mosq); + UNUSED(obj); + UNUSED(properties); + print_message(&cfg, message, properties); switch(cfg.pub_mode){ case MSGMODE_CMD: @@ -125,6 +129,10 @@ void my_message_callback(struct mosquitto *mosq, void *obj, const struct mosquit void my_connect_callback(struct mosquitto *mosq, void *obj, int result, int flags, const mosquitto_property *properties) { + UNUSED(obj); + UNUSED(flags); + UNUSED(properties); + connack_result = result; if(!result){ client_state = rr_s_connected; @@ -145,6 +153,10 @@ void my_connect_callback(struct mosquitto *mosq, void *obj, int result, int flag void my_subscribe_callback(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos) { + UNUSED(obj); + UNUSED(mid); + UNUSED(qos_count); + if(granted_qos[0] < 128){ client_state = rr_s_ready_to_publish; }else{ @@ -157,6 +169,12 @@ void my_subscribe_callback(struct mosquitto *mosq, void *obj, int mid, int qos_c void my_publish_callback(struct mosquitto *mosq, void *obj, int mid, int reason_code, const mosquitto_property *properties) { + UNUSED(mosq); + UNUSED(obj); + UNUSED(mid); + UNUSED(reason_code); + UNUSED(properties); + client_state = rr_s_wait_for_response; } @@ -328,8 +346,8 @@ int main(int argc, char *argv[]) goto cleanup; } - mosq = mosquitto_new(cfg.id, cfg.clean_session, &cfg); - if(!mosq){ + g_mosq = mosquitto_new(cfg.id, cfg.clean_session, &cfg); + if(!g_mosq){ switch(errno){ case ENOMEM: err_printf(&cfg, "Error: Out of memory.\n"); @@ -340,17 +358,17 @@ int main(int argc, char *argv[]) } goto cleanup; } - if(client_opts_set(mosq, &cfg)){ + if(client_opts_set(g_mosq, &cfg)){ goto cleanup; } if(cfg.debug){ - mosquitto_log_callback_set(mosq, my_log_callback); + mosquitto_log_callback_set(g_mosq, my_log_callback); } - mosquitto_connect_v5_callback_set(mosq, my_connect_callback); - mosquitto_subscribe_callback_set(mosq, my_subscribe_callback); - mosquitto_message_v5_callback_set(mosq, my_message_callback); + mosquitto_connect_v5_callback_set(g_mosq, my_connect_callback); + mosquitto_subscribe_callback_set(g_mosq, my_subscribe_callback); + mosquitto_message_v5_callback_set(g_mosq, my_message_callback); - rc = client_connect(mosq, &cfg); + rc = client_connect(g_mosq, &cfg); if(rc){ goto cleanup; } @@ -371,17 +389,17 @@ int main(int argc, char *argv[]) #endif do{ - rc = mosquitto_loop(mosq, -1, 1); + rc = mosquitto_loop(g_mosq, -1, 1); if(client_state == rr_s_ready_to_publish){ client_state = rr_s_wait_for_response; switch(cfg.pub_mode){ case MSGMODE_CMD: case MSGMODE_FILE: case MSGMODE_STDIN_FILE: - rc = my_publish(mosq, &mid_sent, cfg.topic, cfg.msglen, cfg.message, cfg.qos, cfg.retain); + rc = my_publish(g_mosq, &mid_sent, cfg.topic, cfg.msglen, cfg.message, cfg.qos, cfg.retain); break; case MSGMODE_NULL: - rc = my_publish(mosq, &mid_sent, cfg.topic, 0, NULL, cfg.qos, cfg.retain); + rc = my_publish(g_mosq, &mid_sent, cfg.topic, 0, NULL, cfg.qos, cfg.retain); break; case MSGMODE_STDIN_LINE: /* FIXME */ @@ -390,7 +408,7 @@ int main(int argc, char *argv[]) } }while(rc == MOSQ_ERR_SUCCESS && client_state != rr_s_disconnect); - mosquitto_destroy(mosq); + mosquitto_destroy(g_mosq); mosquitto_lib_cleanup(); if(cfg.msg_count>0 && rc == MOSQ_ERR_NO_CONN){ diff --git a/client/sub_client.c b/client/sub_client.c index 02f98ad43..352c46945 100644 --- a/client/sub_client.c +++ b/client/sub_client.c @@ -41,7 +41,7 @@ SPDX-License-Identifier: EPL-2.0 OR EDL-1.0 struct mosq_config cfg; bool process_messages = true; int msg_count = 0; -struct mosquitto *mosq = NULL; +struct mosquitto *g_mosq = NULL; int last_mid = 0; static bool timed_out = false; static int connack_result = 0; @@ -53,7 +53,7 @@ void my_signal_handler(int signum) if(signum == SIGALRM || signum == SIGTERM || signum == SIGINT){ if(connack_received){ process_messages = false; - mosquitto_disconnect_v5(mosq, MQTT_RC_DISCONNECT_WITH_WILL_MSG, cfg.disconnect_props); + mosquitto_disconnect_v5(g_mosq, MQTT_RC_DISCONNECT_WITH_WILL_MSG, cfg.disconnect_props); }else{ exit(-1); } @@ -358,8 +358,8 @@ int main(int argc, char *argv[]) goto cleanup; } - mosq = mosquitto_new(cfg.id, cfg.clean_session, &cfg); - if(!mosq){ + g_mosq = mosquitto_new(cfg.id, cfg.clean_session, &cfg); + if(!g_mosq){ switch(errno){ case ENOMEM: err_printf(&cfg, "Error: Out of memory.\n"); @@ -370,17 +370,17 @@ int main(int argc, char *argv[]) } goto cleanup; } - if(client_opts_set(mosq, &cfg)){ + if(client_opts_set(g_mosq, &cfg)){ goto cleanup; } if(cfg.debug){ - mosquitto_log_callback_set(mosq, my_log_callback); + mosquitto_log_callback_set(g_mosq, my_log_callback); } - mosquitto_subscribe_callback_set(mosq, my_subscribe_callback); - mosquitto_connect_v5_callback_set(mosq, my_connect_callback); - mosquitto_message_v5_callback_set(mosq, my_message_callback); + mosquitto_subscribe_callback_set(g_mosq, my_subscribe_callback); + mosquitto_connect_v5_callback_set(g_mosq, my_connect_callback); + mosquitto_message_v5_callback_set(g_mosq, my_message_callback); - rc = client_connect(mosq, &cfg); + rc = client_connect(g_mosq, &cfg); if(rc){ goto cleanup; } @@ -410,9 +410,9 @@ int main(int argc, char *argv[]) } #endif - rc = mosquitto_loop_forever(mosq, -1, 1); + rc = mosquitto_loop_forever(g_mosq, -1, 1); - mosquitto_destroy(mosq); + mosquitto_destroy(g_mosq); mosquitto_lib_cleanup(); if(cfg.msg_count>0 && rc == MOSQ_ERR_NO_CONN){ @@ -432,7 +432,7 @@ int main(int argc, char *argv[]) } cleanup: - mosquitto_destroy(mosq); + mosquitto_destroy(g_mosq); mosquitto_lib_cleanup(); client_config_cleanup(&cfg); return 1; diff --git a/client/sub_client_output.c b/client/sub_client_output.c index a70d59ec7..0163b0e21 100644 --- a/client/sub_client_output.c +++ b/client/sub_client_output.c @@ -443,7 +443,7 @@ static void formatted_print_percent(const struct mosq_config *lcfg, const struct uint8_t i8value; uint16_t i16value; uint32_t i32value; - char *binvalue, *strname, *strvalue; + char *binvalue = NULL, *strname, *strvalue; const mosquitto_property *prop; @@ -627,8 +627,8 @@ static void formatted_print(const struct mosq_config *lcfg, const struct mosquit size_t len; int i; struct tm *ti = NULL; - long ns; - char strf[3]; + long ns = 0; + char strf[3] = {0, 0 ,0}; char buf[100]; char align, pad; int field_width, precision; @@ -769,7 +769,7 @@ void rand_init(void) } -void print_message(struct mosq_config *cfg, const struct mosquitto_message *message, const mosquitto_property *properties) +void print_message(struct mosq_config *lcfg, const struct mosquitto_message *message, const mosquitto_property *properties) { #ifdef WIN32 unsigned int r = 0; @@ -777,27 +777,27 @@ void print_message(struct mosq_config *cfg, const struct mosquitto_message *mess long r = 0; #endif - if(cfg->random_filter < 10000){ + if(lcfg->random_filter < 10000){ #ifdef WIN32 rand_s(&r); #else r = random(); #endif - if((r%10000) >= cfg->random_filter){ + if((long)(r%10000) >= lcfg->random_filter){ return; } } - if(cfg->format){ - formatted_print(cfg, message, properties); - }else if(cfg->verbose){ + if(lcfg->format){ + formatted_print(lcfg, message, properties); + }else if(lcfg->verbose){ if(message->payloadlen){ printf("%s ", message->topic); write_payload(message->payload, message->payloadlen, false, 0, 0, 0, 0); - if(cfg->eol){ + if(lcfg->eol){ printf("\n"); } }else{ - if(cfg->eol){ + if(lcfg->eol){ printf("%s (null)\n", message->topic); } } @@ -805,7 +805,7 @@ void print_message(struct mosq_config *cfg, const struct mosquitto_message *mess }else{ if(message->payloadlen){ write_payload(message->payload, message->payloadlen, false, 0, 0, 0, 0); - if(cfg->eol){ + if(lcfg->eol){ printf("\n"); } fflush(stdout); diff --git a/config.mk b/config.mk index a807c6062..178c8baa9 100644 --- a/config.mk +++ b/config.mk @@ -125,7 +125,7 @@ WITH_XTREPORT=no # Also bump lib/mosquitto.h, CMakeLists.txt, # installer/mosquitto.nsi, installer/mosquitto64.nsi -VERSION=2.0.4 +VERSION=2.0.5 # Client library SO version. Bump if incompatible API/ABI changes are made. SOVERSION=1 diff --git a/include/mosquitto.h b/include/mosquitto.h index b2c15cc6f..cd10ea60b 100644 --- a/include/mosquitto.h +++ b/include/mosquitto.h @@ -66,7 +66,7 @@ extern "C" { #define LIBMOSQUITTO_MAJOR 2 #define LIBMOSQUITTO_MINOR 0 -#define LIBMOSQUITTO_REVISION 4 +#define LIBMOSQUITTO_REVISION 5 /* 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) @@ -340,6 +340,9 @@ libmosq_EXPORT int mosquitto_reinitialise(struct mosquitto *mosq, const char *id * Configure will information for a mosquitto instance. By default, clients do * not have a will. This must be called before calling . * + * It is valid to use this function for clients using all MQTT protocol versions. + * If you need to set MQTT v5 Will properties, use instead. + * * Parameters: * mosq - a valid mosquitto instance. * topic - the topic on which to publish the will. @@ -367,6 +370,14 @@ libmosq_EXPORT int mosquitto_will_set(struct mosquitto *mosq, const char *topic, * properties. By default, clients do not have a will. This must be called * before calling . * + * If the mosquitto instance `mosq` is using MQTT v5, the `properties` argument + * will be applied to the Will. For MQTT v3.1.1 and below, the `properties` + * argument will be ignored. + * + * Set your client to use MQTT v5 immediately after it is created: + * + * mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5); + * * Parameters: * mosq - a valid mosquitto instance. * topic - the topic on which to publish the will. @@ -450,6 +461,10 @@ libmosq_EXPORT int mosquitto_username_pw_set(struct mosquitto *mosq, const char * * Connect to an MQTT broker. * + * It is valid to use this function for clients using all MQTT protocol versions. + * If you need to set MQTT v5 CONNECT properties, use + * instead. + * * Parameters: * mosq - a valid mosquitto instance. * host - the hostname or ip address of the broker to connect to. @@ -517,6 +532,14 @@ libmosq_EXPORT int mosquitto_connect_bind(struct mosquitto *mosq, const char *ho * properties, then attach them to this publish. Properties need freeing with * . * + * If the mosquitto instance `mosq` is using MQTT v5, the `properties` argument + * will be applied to the CONNECT message. For MQTT v3.1.1 and below, the + * `properties` argument will be ignored. + * + * Set your client to use MQTT v5 immediately after it is created: + * + * mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5); + * * Parameters: * mosq - a valid mosquitto instance. * host - the hostname or ip address of the broker to connect to. @@ -716,6 +739,10 @@ libmosq_EXPORT int mosquitto_reconnect_async(struct mosquitto *mosq); * * Disconnect from the broker. * + * It is valid to use this function for clients using all MQTT protocol versions. + * If you need to set MQTT v5 DISCONNECT properties, use + * instead. + * * Parameters: * mosq - a valid mosquitto instance. * @@ -735,6 +762,14 @@ libmosq_EXPORT int mosquitto_disconnect(struct mosquitto *mosq); * properties, then attach them to this publish. Properties need freeing with * . * + * If the mosquitto instance `mosq` is using MQTT v5, the `properties` argument + * will be applied to the DISCONNECT message. For MQTT v3.1.1 and below, the + * `properties` argument will be ignored. + * + * Set your client to use MQTT v5 immediately after it is created: + * + * mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5); + * * Parameters: * mosq - a valid mosquitto instance. * reason_code - the disconnect reason code. @@ -760,6 +795,10 @@ libmosq_EXPORT int mosquitto_disconnect_v5(struct mosquitto *mosq, int reason_co * * Publish a message on a given topic. * + * It is valid to use this function for clients using all MQTT protocol versions. + * If you need to set MQTT v5 PUBLISH properties, use + * instead. + * * Parameters: * mosq - a valid mosquitto instance. * mid - pointer to an int. If not NULL, the function will set this @@ -807,7 +846,13 @@ libmosq_EXPORT int mosquitto_publish(struct mosquitto *mosq, int *mid, const cha * properties, then attach them to this publish. Properties need freeing with * . * - * Requires the mosquitto instance to be connected with MQTT 5. + * If the mosquitto instance `mosq` is using MQTT v5, the `properties` argument + * will be applied to the PUBLISH message. For MQTT v3.1.1 and below, the + * `properties` argument will be ignored. + * + * Set your client to use MQTT v5 immediately after it is created: + * + * mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5); * * Parameters: * mosq - a valid mosquitto instance. @@ -860,6 +905,10 @@ libmosq_EXPORT int mosquitto_publish_v5( * * Subscribe to a topic. * + * It is valid to use this function for clients using all MQTT protocol versions. + * If you need to set MQTT v5 SUBSCRIBE properties, use + * instead. + * * Parameters: * mosq - a valid mosquitto instance. * mid - a pointer to an int. If not NULL, the function will set this to @@ -889,8 +938,13 @@ libmosq_EXPORT int mosquitto_subscribe(struct mosquitto *mosq, int *mid, const c * properties, then attach them to this publish. Properties need freeing with * . * - * Requires the mosquitto instance to be connected with MQTT 5. + * If the mosquitto instance `mosq` is using MQTT v5, the `properties` argument + * will be applied to the PUBLISH message. For MQTT v3.1.1 and below, the + * `properties` argument will be ignored. * + * Set your client to use MQTT v5 immediately after it is created: + * + * mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5); * * Parameters: * mosq - a valid mosquitto instance. @@ -981,10 +1035,22 @@ libmosq_EXPORT int mosquitto_unsubscribe(struct mosquitto *mosq, int *mid, const * * Unsubscribe from a topic, with attached MQTT properties. * + * It is valid to use this function for clients using all MQTT protocol versions. + * If you need to set MQTT v5 UNSUBSCRIBE properties, use + * instead. + * * Use e.g. and similar to create a list of * properties, then attach them to this publish. Properties need freeing with * . * + * If the mosquitto instance `mosq` is using MQTT v5, the `properties` argument + * will be applied to the PUBLISH message. For MQTT v3.1.1 and below, the + * `properties` argument will be ignored. + * + * Set your client to use MQTT v5 immediately after it is created: + * + * mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5); + * * Parameters: * mosq - a valid mosquitto instance. * mid - a pointer to an int. If not NULL, the function will set this to @@ -1891,6 +1957,10 @@ libmosq_EXPORT void mosquitto_connect_with_flags_callback_set(struct mosquitto * * Set the connect callback. This is called when the broker sends a CONNACK * message in response to a connection. * + * It is valid to set this callback for all MQTT protocol versions. If it is + * used with MQTT clients that use MQTT v3.1.1 or earlier, then the `props` + * argument will always be NULL. + * * Parameters: * mosq - a valid mosquitto instance. * on_connect - a callback function in the following form: @@ -1935,6 +2005,10 @@ libmosq_EXPORT void mosquitto_disconnect_callback_set(struct mosquitto *mosq, vo * Set the disconnect callback. This is called when the broker has received the * DISCONNECT command and has disconnected the client. * + * It is valid to set this callback for all MQTT protocol versions. If it is + * used with MQTT clients that use MQTT v3.1.1 or earlier, then the `props` + * argument will always be NULL. + * * Parameters: * mosq - a valid mosquitto instance. * on_disconnect - a callback function in the following form: @@ -1948,7 +2022,7 @@ libmosq_EXPORT void mosquitto_disconnect_callback_set(struct mosquitto *mosq, vo * indicates that the disconnect is unexpected. * props - list of MQTT 5 properties, or NULL */ -libmosq_EXPORT void mosquitto_disconnect_v5_callback_set(struct mosquitto *mosq, void (*on_disconnect)(struct mosquitto *, void *, int, const mosquitto_property *)); +libmosq_EXPORT void mosquitto_disconnect_v5_callback_set(struct mosquitto *mosq, void (*on_disconnect)(struct mosquitto *, void *, int, const mosquitto_property *props)); /* * Function: mosquitto_publish_callback_set @@ -1976,6 +2050,10 @@ libmosq_EXPORT void mosquitto_publish_callback_set(struct mosquitto *mosq, void * called both if the message is sent successfully, or if the broker responded * with an error, which will be reflected in the reason_code parameter. * + * It is valid to set this callback for all MQTT protocol versions. If it is + * used with MQTT clients that use MQTT v3.1.1 or earlier, then the `props` + * argument will always be NULL. + * * Parameters: * mosq - a valid mosquitto instance. * on_publish - a callback function in the following form: @@ -1988,7 +2066,7 @@ libmosq_EXPORT void mosquitto_publish_callback_set(struct mosquitto *mosq, void * reason_code - the MQTT 5 reason code * props - list of MQTT 5 properties, or NULL */ -libmosq_EXPORT void mosquitto_publish_v5_callback_set(struct mosquitto *mosq, void (*on_publish)(struct mosquitto *, void *, int, int, const mosquitto_property *)); +libmosq_EXPORT void mosquitto_publish_v5_callback_set(struct mosquitto *mosq, void (*on_publish)(struct mosquitto *, void *, int, int, const mosquitto_property *props)); /* * Function: mosquitto_message_callback_set @@ -2019,6 +2097,10 @@ libmosq_EXPORT void mosquitto_message_callback_set(struct mosquitto *mosq, void * Set the message callback. This is called when a message is received from the * broker. * + * It is valid to set this callback for all MQTT protocol versions. If it is + * used with MQTT clients that use MQTT v3.1.1 or earlier, then the `props` + * argument will always be NULL. + * * Parameters: * mosq - a valid mosquitto instance. * on_message - a callback function in the following form: @@ -2035,7 +2117,7 @@ libmosq_EXPORT void mosquitto_message_callback_set(struct mosquitto *mosq, void * See Also: * */ -libmosq_EXPORT void mosquitto_message_v5_callback_set(struct mosquitto *mosq, void (*on_message)(struct mosquitto *, void *, const struct mosquitto_message *, const mosquitto_property *)); +libmosq_EXPORT void mosquitto_message_v5_callback_set(struct mosquitto *mosq, void (*on_message)(struct mosquitto *, void *, const struct mosquitto_message *, const mosquitto_property *props)); /* * Function: mosquitto_subscribe_callback_set @@ -2064,6 +2146,10 @@ libmosq_EXPORT void mosquitto_subscribe_callback_set(struct mosquitto *mosq, voi * Set the subscribe callback. This is called when the broker responds to a * subscription request. * + * It is valid to set this callback for all MQTT protocol versions. If it is + * used with MQTT clients that use MQTT v3.1.1 or earlier, then the `props` + * argument will always be NULL. + * * Parameters: * mosq - a valid mosquitto instance. * on_subscribe - a callback function in the following form: @@ -2078,7 +2164,7 @@ libmosq_EXPORT void mosquitto_subscribe_callback_set(struct mosquitto *mosq, voi * the subscriptions. * props - list of MQTT 5 properties, or NULL */ -libmosq_EXPORT void mosquitto_subscribe_v5_callback_set(struct mosquitto *mosq, void (*on_subscribe)(struct mosquitto *, void *, int, int, const int *, const mosquitto_property *)); +libmosq_EXPORT void mosquitto_subscribe_v5_callback_set(struct mosquitto *mosq, void (*on_subscribe)(struct mosquitto *, void *, int, int, const int *, const mosquitto_property *props)); /* * Function: mosquitto_unsubscribe_callback_set @@ -2104,6 +2190,10 @@ libmosq_EXPORT void mosquitto_unsubscribe_callback_set(struct mosquitto *mosq, v * Set the unsubscribe callback. This is called when the broker responds to a * unsubscription request. * + * It is valid to set this callback for all MQTT protocol versions. If it is + * used with MQTT clients that use MQTT v3.1.1 or earlier, then the `props` + * argument will always be NULL. + * * Parameters: * mosq - a valid mosquitto instance. * on_unsubscribe - a callback function in the following form: @@ -2115,7 +2205,7 @@ libmosq_EXPORT void mosquitto_unsubscribe_callback_set(struct mosquitto *mosq, v * mid - the message id of the unsubscribe message. * props - list of MQTT 5 properties, or NULL */ -libmosq_EXPORT void mosquitto_unsubscribe_v5_callback_set(struct mosquitto *mosq, void (*on_unsubscribe)(struct mosquitto *, void *, int, const mosquitto_property *)); +libmosq_EXPORT void mosquitto_unsubscribe_v5_callback_set(struct mosquitto *mosq, void (*on_unsubscribe)(struct mosquitto *, void *, int, const mosquitto_property *props)); /* * Function: mosquitto_log_callback_set diff --git a/include/mosquitto_broker.h b/include/mosquitto_broker.h index d1a263199..9768975f9 100644 --- a/include/mosquitto_broker.h +++ b/include/mosquitto_broker.h @@ -117,7 +117,8 @@ struct mosquitto_evt_extended_auth { void *data_out; uint16_t data_in_len; uint16_t data_out_len; - void *future2[4]; + const char *auth_method; + void *future2[3]; }; /* Data for the MOSQ_EVT_CONTROL event */ diff --git a/installer/mosquitto.nsi b/installer/mosquitto.nsi index 2498c845a..0ad72cd49 100644 --- a/installer/mosquitto.nsi +++ b/installer/mosquitto.nsi @@ -9,7 +9,7 @@ !define env_hklm 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"' Name "Eclipse Mosquitto" -!define VERSION 2.0.4 +!define VERSION 2.0.5 OutFile "mosquitto-${VERSION}-install-windows-x86.exe" InstallDir "$PROGRAMFILES\mosquitto" diff --git a/installer/mosquitto64.nsi b/installer/mosquitto64.nsi index 783c2e660..4bf999ee5 100644 --- a/installer/mosquitto64.nsi +++ b/installer/mosquitto64.nsi @@ -9,7 +9,7 @@ !define env_hklm 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"' Name "Eclipse Mosquitto" -!define VERSION 2.0.4 +!define VERSION 2.0.5 OutFile "mosquitto-${VERSION}-install-windows-x64.exe" !include "x64.nsh" diff --git a/lib/handle_publish.c b/lib/handle_publish.c index 31d2dba81..12cced6f8 100644 --- a/lib/handle_publish.c +++ b/lib/handle_publish.c @@ -39,7 +39,7 @@ int handle__publish(struct mosquitto *mosq) uint8_t header; struct mosquitto_message_all *message; int rc = 0; - uint16_t mid; + uint16_t mid = 0; uint16_t slen; mosquitto_property *properties = NULL; diff --git a/lib/mosquitto_internal.h b/lib/mosquitto_internal.h index 8b57a4f2a..2d30f7e87 100644 --- a/lib/mosquitto_internal.h +++ b/lib/mosquitto_internal.h @@ -350,9 +350,7 @@ struct mosquitto { struct session_expiry_list *expiry_list_item; uint16_t remote_port; #endif -#ifdef WITH_EPOLL uint32_t events; -#endif }; #define STREMPTY(str) (str[0] == '\0') diff --git a/lib/net_mosq.c b/lib/net_mosq.c index 0d8f91335..f1da60f85 100644 --- a/lib/net_mosq.c +++ b/lib/net_mosq.c @@ -393,9 +393,6 @@ static int net__try_connect_tcp(const char *host, uint16_t port, mosq_sock_t *so struct addrinfo *ainfo_bind, *rp_bind; int s; int rc = MOSQ_ERR_SUCCESS; -#ifdef WIN32 - uint32_t val = 1; -#endif ainfo_bind = NULL; @@ -995,7 +992,7 @@ ssize_t net__read(struct mosquitto *mosq, void *buf, size_t count) #endif } -ssize_t net__write(struct mosquitto *mosq, void *buf, size_t count) +ssize_t net__write(struct mosquitto *mosq, const void *buf, size_t count) { #ifdef WITH_TLS int ret; diff --git a/lib/net_mosq.h b/lib/net_mosq.h index cc4f2feff..6dc2a1a42 100644 --- a/lib/net_mosq.h +++ b/lib/net_mosq.h @@ -69,7 +69,7 @@ int net__socket_nonblock(mosq_sock_t *sock); int net__socketpair(mosq_sock_t *sp1, mosq_sock_t *sp2); ssize_t net__read(struct mosquitto *mosq, void *buf, size_t count); -ssize_t net__write(struct mosquitto *mosq, void *buf, size_t count); +ssize_t net__write(struct mosquitto *mosq, const void *buf, size_t count); #ifdef WITH_TLS void net__print_ssl_error(struct mosquitto *mosq); diff --git a/lib/net_mosq_ocsp.c b/lib/net_mosq_ocsp.c index 371c873ca..0f6c30028 100644 --- a/lib/net_mosq_ocsp.c +++ b/lib/net_mosq_ocsp.c @@ -65,6 +65,8 @@ int mosquitto__verify_ocsp_status_cb(SSL * ssl, void *arg) X509_STORE *st = NULL; STACK_OF(X509) *ch = NULL; + UNUSED(ssl); + long len = SSL_get_tlsext_status_ocsp_resp(mosq->ssl, &p); log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: SSL_get_tlsext_status_ocsp_resp returned %ld bytes", len); diff --git a/lib/packet_mosq.c b/lib/packet_mosq.c index 5cf78fa9a..e576c67ca 100644 --- a/lib/packet_mosq.c +++ b/lib/packet_mosq.c @@ -215,6 +215,10 @@ int packet__write(struct mosquitto *mosq) if(!mosq) return MOSQ_ERR_INVAL; if(mosq->sock == INVALID_SOCKET) return MOSQ_ERR_NO_CONN; +#ifdef WITH_BROKER + mux__add_out(mosq); +#endif + pthread_mutex_lock(&mosq->current_out_packet_mutex); pthread_mutex_lock(&mosq->out_packet_mutex); if(mosq->out_packet && !mosq->current_out_packet){ @@ -314,6 +318,9 @@ int packet__write(struct mosquitto *mosq) #ifdef WITH_BROKER mosq->next_msg_out = db.now_s + mosq->keepalive; + if(mosq->current_out_packet == NULL){ + mux__remove_out(mosq); + } #else pthread_mutex_lock(&mosq->msgtime_mutex); mosq->next_msg_out = mosquitto_time() + mosq->keepalive; diff --git a/lib/thread_mosq.c b/lib/thread_mosq.c index 141cf30c4..a8e5747a5 100644 --- a/lib/thread_mosq.c +++ b/lib/thread_mosq.c @@ -55,6 +55,7 @@ int mosquitto_loop_start(struct mosquitto *mosq) return MOSQ_ERR_ERRNO; } #else + UNUSED(mosq); return MOSQ_ERR_NOT_SUPPORTED; #endif } @@ -91,6 +92,8 @@ int mosquitto_loop_stop(struct mosquitto *mosq, bool force) return MOSQ_ERR_SUCCESS; #else + UNUSED(mosq); + UNUSED(force); return MOSQ_ERR_NOT_SUPPORTED; #endif } diff --git a/man/CMakeLists.txt b/man/CMakeLists.txt index 4390c1efb..98a457d1e 100644 --- a/man/CMakeLists.txt +++ b/man/CMakeLists.txt @@ -1,4 +1,9 @@ if(NOT WIN32) + find_program(XSLTPROC xsltproc REQUIRED) + if(NOT XSLTPROC) + message(FATAL_ERROR "xsltproc not found: manpages cannot be built") + endif() + function(compile_manpage page) add_custom_command(OUTPUT ${CMAKE_SOURCE_DIR}/man/${page} COMMAND xsltproc ${CMAKE_SOURCE_DIR}/man/${page}.xml -o ${CMAKE_SOURCE_DIR}/man/ diff --git a/man/mosquitto.conf.5.xml b/man/mosquitto.conf.5.xml index db3bef1e4..6ea2b10fb 100644 --- a/man/mosquitto.conf.5.xml +++ b/man/mosquitto.conf.5.xml @@ -1035,21 +1035,13 @@ log_timestamp_format %Y-%m-%dT%H:%M:%S option but is useful when an interface has multiple addresses or the address may change. - It is valid to use this option together with + If used at the same time as the for the default listener, or the bind - address/host part of the - definition. Care should - be taken to ensure that the address being bound to - is on the interface being bound to. If you set the - to be - eth0, and - to - 127.0.0.1, then the - broker will start correctly but you will be unable - to connect. - This option is currently only available on - Linux, and requires elevated privileges. + address/host part of the + , then + will take priority. + This option is not available on Windows. Not reloaded on reload signal. diff --git a/man/mosquitto_ctrl.1.xml b/man/mosquitto_ctrl.1.xml index 104498e3a..1f1f29f27 100644 --- a/man/mosquitto_ctrl.1.xml +++ b/man/mosquitto_ctrl.1.xml @@ -78,9 +78,7 @@ Description - mosquitto_ctrl is a simple MQTT version 5/3.1.1 - client that will publish a single message on a topic and - exit. + mosquitto_ctrl is a tool for helping configure a Mosquitto broker instance. diff --git a/mosquitto.conf b/mosquitto.conf index c1f17c1b3..4e407417b 100644 --- a/mosquitto.conf +++ b/mosquitto.conf @@ -229,11 +229,10 @@ # Bind the listener to a specific interface. This is similar to # the [ip address/host name] part of the listener definition, but is useful -# when an interface has multiple addresses or the address may change. It is -# valid to use this with the [ip address/host name] part of the listener -# definition, but take care that the interface you are binding to contains the -# address you are binding to, otherwise you will not be able to connect. -# Only available on Linux and requires elevated privileges. +# when an interface has multiple addresses or the address may change. If used +# with the [ip address/host name] part of the listener definition, then the +# bind_interface option will take priority. +# Not available on Windows. # # Example: bind_interface eth0 #bind_interface diff --git a/plugins/dynamic-security/acl.c b/plugins/dynamic-security/acl.c index cc8558eb4..7aaf3f8c1 100644 --- a/plugins/dynamic-security/acl.c +++ b/plugins/dynamic-security/acl.c @@ -35,8 +35,8 @@ typedef int (*MOSQ_FUNC_acl_check)(struct mosquitto_evt_acl_check *, struct dyns static int acl_check_publish_c_recv(struct mosquitto_evt_acl_check *ed, struct dynsec__rolelist *base_rolelist) { - struct dynsec__rolelist *rolelist, *rolelist_tmp; - struct dynsec__acl *acl, *acl_tmp; + struct dynsec__rolelist *rolelist, *rolelist_tmp = NULL; + struct dynsec__acl *acl, *acl_tmp = NULL; bool result; HASH_ITER(hh, base_rolelist, rolelist, rolelist_tmp){ @@ -63,8 +63,8 @@ static int acl_check_publish_c_recv(struct mosquitto_evt_acl_check *ed, struct d static int acl_check_publish_c_send(struct mosquitto_evt_acl_check *ed, struct dynsec__rolelist *base_rolelist) { - struct dynsec__rolelist *rolelist, *rolelist_tmp; - struct dynsec__acl *acl, *acl_tmp; + struct dynsec__rolelist *rolelist, *rolelist_tmp = NULL; + struct dynsec__acl *acl, *acl_tmp = NULL; bool result; HASH_ITER(hh, base_rolelist, rolelist, rolelist_tmp){ @@ -91,8 +91,8 @@ static int acl_check_publish_c_send(struct mosquitto_evt_acl_check *ed, struct d static int acl_check_subscribe(struct mosquitto_evt_acl_check *ed, struct dynsec__rolelist *base_rolelist) { - struct dynsec__rolelist *rolelist, *rolelist_tmp; - struct dynsec__acl *acl, *acl_tmp; + struct dynsec__rolelist *rolelist, *rolelist_tmp = NULL; + struct dynsec__acl *acl, *acl_tmp = NULL; size_t len; len = strlen(ed->topic); @@ -128,8 +128,8 @@ static int acl_check_subscribe(struct mosquitto_evt_acl_check *ed, struct dynsec static int acl_check_unsubscribe(struct mosquitto_evt_acl_check *ed, struct dynsec__rolelist *base_rolelist) { - struct dynsec__rolelist *rolelist, *rolelist_tmp; - struct dynsec__acl *acl, *acl_tmp; + struct dynsec__rolelist *rolelist, *rolelist_tmp = NULL; + struct dynsec__acl *acl, *acl_tmp = NULL; size_t len; len = strlen(ed->topic); @@ -163,10 +163,10 @@ static int acl_check_unsubscribe(struct mosquitto_evt_acl_check *ed, struct dyns * # * ################################################################ */ -static int acl_check(struct mosquitto_evt_acl_check *ed, MOSQ_FUNC_acl_check check, bool default_access) +static int acl_check(struct mosquitto_evt_acl_check *ed, MOSQ_FUNC_acl_check check, bool acl_default_access) { struct dynsec__client *client; - struct dynsec__grouplist *grouplist, *grouplist_tmp; + struct dynsec__grouplist *grouplist, *grouplist_tmp = NULL; const char *username; int rc; @@ -196,7 +196,7 @@ static int acl_check(struct mosquitto_evt_acl_check *ed, MOSQ_FUNC_acl_check che } } - if(default_access == false){ + if(acl_default_access == false){ return MOSQ_ERR_PLUGIN_DEFER; }else{ if(!strncmp(ed->topic, "$CONTROL", strlen("$CONTROL"))){ @@ -220,6 +220,9 @@ int dynsec__acl_check_callback(int event, void *event_data, void *userdata) { struct mosquitto_evt_acl_check *ed = event_data; + UNUSED(event); + UNUSED(userdata); + /* ACL checks are made in the order below until a match occurs, at which * point the decision is made. * diff --git a/plugins/dynamic-security/auth.c b/plugins/dynamic-security/auth.c index 901f68904..9ca9b1327 100644 --- a/plugins/dynamic-security/auth.c +++ b/plugins/dynamic-security/auth.c @@ -37,7 +37,7 @@ SPDX-License-Identifier: EPL-2.0 OR EDL-1.0 int dynsec_auth__base64_encode(unsigned char *in, int in_len, char **encoded) { BIO *bmem, *b64; - BUF_MEM *bptr; + BUF_MEM *bptr = NULL; if(in_len < 0) return 1; @@ -178,6 +178,9 @@ int dynsec_auth__basic_auth_callback(int event, void *event_data, void *userdata unsigned char password_hash[64]; /* For SHA512 */ const char *clientid; + UNUSED(event); + UNUSED(userdata); + if(ed->username == NULL || ed->password == NULL) return MOSQ_ERR_PLUGIN_DEFER; client = dynsec_clients__find(ed->username); diff --git a/plugins/dynamic-security/clients.c b/plugins/dynamic-security/clients.c index 50a0922e9..de9092dd1 100644 --- a/plugins/dynamic-security/clients.c +++ b/plugins/dynamic-security/clients.c @@ -35,6 +35,7 @@ SPDX-License-Identifier: EPL-2.0 OR EDL-1.0 * ################################################################ */ static int dynsec__remove_client_from_all_groups(const char *username); +static void client__remove_all_roles(struct dynsec__client *client); /* ################################################################ * # @@ -482,6 +483,7 @@ int dynsec_clients__process_delete(cJSON *j_responses, struct mosquitto *context client = dynsec_clients__find(username); if(client){ dynsec__remove_client_from_all_groups(username); + client__remove_all_roles(client); client__free_item(client); dynsec__config_save(); dynsec__command_reply(j_responses, context, "deleteClient", NULL, correlation_data); diff --git a/plugins/dynamic-security/groups.c b/plugins/dynamic-security/groups.c index 144b32442..d66e4dba6 100644 --- a/plugins/dynamic-security/groups.c +++ b/plugins/dynamic-security/groups.c @@ -44,6 +44,7 @@ struct dynsec__group *dynsec_anonymous_group = NULL; * ################################################################ */ static int dynsec__remove_all_clients_from_group(struct dynsec__group *group); +static int dynsec__remove_all_roles_from_group(struct dynsec__group *group); static cJSON *add_group_to_json(struct dynsec__group *group); @@ -115,6 +116,7 @@ int dynsec_groups__process_add_role(cJSON *j_responses, struct mosquitto *contex struct dynsec__role *role; int priority; const char *admin_clientid, *admin_username; + int rc; if(json_get_string(command, "groupname", &groupname, false) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "addGroupRole", "Invalid/missing groupname", correlation_data); @@ -150,13 +152,20 @@ int dynsec_groups__process_add_role(cJSON *j_responses, struct mosquitto *contex admin_clientid = mosquitto_client_id(context); admin_username = mosquitto_client_username(context); - mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | addGroupRole | groupname=%s | rolename=%s | priority=%d", - admin_clientid, admin_username, groupname, rolename, priority); - - if(dynsec_rolelist__group_add(group, role, priority) != MOSQ_ERR_SUCCESS){ + rc = dynsec_rolelist__group_add(group, role, priority); + if(rc == MOSQ_ERR_SUCCESS){ + /* Continue */ + }else if(rc == MOSQ_ERR_ALREADY_EXISTS){ + dynsec__command_reply(j_responses, context, "addGroupRole", "Group is already in this role", correlation_data); + return MOSQ_ERR_ALREADY_EXISTS; + }else{ dynsec__command_reply(j_responses, context, "addGroupRole", "Internal error", correlation_data); return MOSQ_ERR_UNKNOWN; } + + mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | addGroupRole | groupname=%s | rolename=%s | priority=%d", + admin_clientid, admin_username, groupname, rolename, priority); + dynsec__config_save(); dynsec__command_reply(j_responses, context, "addGroupRole", NULL, correlation_data); @@ -169,7 +178,7 @@ int dynsec_groups__process_add_role(cJSON *j_responses, struct mosquitto *contex void dynsec_groups__cleanup(void) { - struct dynsec__group *group, *group_tmp; + struct dynsec__group *group, *group_tmp = NULL; HASH_ITER(hh, local_groups, group, group_tmp){ group__free_item(group); @@ -299,7 +308,7 @@ int dynsec_groups__config_load(cJSON *tree) static int dynsec__config_add_groups(cJSON *j_groups) { - struct dynsec__group *group, *group_tmp; + struct dynsec__group *group, *group_tmp = NULL; cJSON *j_group, *j_clients, *j_roles; HASH_ITER(hh, local_groups, group, group_tmp){ @@ -460,6 +469,7 @@ int dynsec_groups__process_delete(cJSON *j_responses, struct mosquitto *context, /* Enforce any changes */ group__kick_all(group); + dynsec__remove_all_roles_from_group(group); group__free_item(group); dynsec__config_save(); dynsec__command_reply(j_responses, context, "deleteGroup", NULL, correlation_data); @@ -497,7 +507,7 @@ int dynsec_groups__add_client(const char *username, const char *groupname, int p HASH_FIND(hh, group->clientlist, username, strlen(username), clientlist); if(clientlist != NULL){ /* Client is already in the group */ - return MOSQ_ERR_SUCCESS; + return MOSQ_ERR_ALREADY_EXISTS; } rc = dynsec_clientlist__add(&group->clientlist, client, priority); @@ -557,6 +567,8 @@ int dynsec_groups__process_add_client(cJSON *j_responses, struct mosquitto *cont dynsec__command_reply(j_responses, context, "addGroupClient", "Client not found", correlation_data); }else if(rc == ERR_GROUP_NOT_FOUND){ dynsec__command_reply(j_responses, context, "addGroupClient", "Group not found", correlation_data); + }else if(rc == MOSQ_ERR_ALREADY_EXISTS){ + dynsec__command_reply(j_responses, context, "addGroupClient", "Client is already in this group", correlation_data); }else{ dynsec__command_reply(j_responses, context, "addGroupClient", "Internal error", correlation_data); } @@ -570,7 +582,7 @@ int dynsec_groups__process_add_client(cJSON *j_responses, struct mosquitto *cont static int dynsec__remove_all_clients_from_group(struct dynsec__group *group) { - struct dynsec__clientlist *clientlist, *clientlist_tmp; + struct dynsec__clientlist *clientlist, *clientlist_tmp = NULL; HASH_ITER(hh, group->clientlist, clientlist, clientlist_tmp){ /* Remove client stored group reference */ @@ -583,6 +595,17 @@ static int dynsec__remove_all_clients_from_group(struct dynsec__group *group) return MOSQ_ERR_SUCCESS; } +static int dynsec__remove_all_roles_from_group(struct dynsec__group *group) +{ + struct dynsec__rolelist *rolelist, *rolelist_tmp = NULL; + + HASH_ITER(hh, group->rolelist, rolelist, rolelist_tmp){ + dynsec_rolelist__group_remove(group, rolelist->role); + } + + return MOSQ_ERR_SUCCESS; +} + int dynsec_groups__remove_client(const char *username, const char *groupname, bool update_config) { struct dynsec__client *client; @@ -657,7 +680,7 @@ int dynsec_groups__process_remove_client(cJSON *j_responses, struct mosquitto *c static cJSON *add_group_to_json(struct dynsec__group *group) { cJSON *j_group, *jtmp, *j_clientlist, *j_client, *j_rolelist; - struct dynsec__clientlist *clientlist, *clientlist_tmp; + struct dynsec__clientlist *clientlist, *clientlist_tmp = NULL; j_group = cJSON_CreateObject(); if(j_group == NULL){ @@ -705,7 +728,7 @@ int dynsec_groups__process_list(cJSON *j_responses, struct mosquitto *context, c { bool verbose; cJSON *tree, *j_groups, *j_group, *j_data; - struct dynsec__group *group, *group_tmp; + struct dynsec__group *group, *group_tmp = NULL; int i, count, offset; const char *admin_clientid, *admin_username; @@ -1029,6 +1052,8 @@ int dynsec_groups__process_get_anonymous_group(cJSON *j_responses, struct mosqui const char *groupname; const char *admin_clientid, *admin_username; + UNUSED(command); + tree = cJSON_CreateObject(); if(tree == NULL){ dynsec__command_reply(j_responses, context, "getAnonymousGroup", "Internal error", correlation_data); diff --git a/plugins/dynamic-security/plugin.c b/plugins/dynamic-security/plugin.c index dd76a9da4..1ee89890d 100644 --- a/plugins/dynamic-security/plugin.c +++ b/plugins/dynamic-security/plugin.c @@ -41,6 +41,8 @@ void dynsec__command_reply(cJSON *j_responses, struct mosquitto *context, const { cJSON *j_response; + UNUSED(context); + j_response = cJSON_CreateObject(); if(j_response == NULL) return; @@ -82,6 +84,9 @@ static int dynsec_control_callback(int event, void *event_data, void *userdata) cJSON *tree, *commands; cJSON *j_response_tree, *j_responses; + UNUSED(event); + UNUSED(userdata); + /* Create object for responses */ j_response_tree = cJSON_CreateObject(); if(j_response_tree == NULL){ @@ -174,6 +179,8 @@ int dynsec__process_get_default_acl_access(cJSON *j_responses, struct mosquitto cJSON *tree, *jtmp, *j_data, *j_acls, *j_acl; const char *admin_clientid, *admin_username; + UNUSED(command); + tree = cJSON_CreateObject(); if(tree == NULL){ dynsec__command_reply(j_responses, context, "getDefaultACLAccess", "Internal error", correlation_data); @@ -465,6 +472,8 @@ int mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, s { int i; + UNUSED(user_data); + for(i=0; iclientlist); @@ -137,7 +137,7 @@ static void role__kick_all(struct dynsec__role *role) static int add_single_acl_to_json(cJSON *j_array, const char *acl_type, struct dynsec__acl *acl) { - struct dynsec__acl *iter, *tmp; + struct dynsec__acl *iter, *tmp = NULL; cJSON *j_acl; HASH_ITER(hh, acl, iter, tmp){ @@ -185,7 +185,7 @@ static int add_acls_to_json(cJSON *j_role, struct dynsec__role *role) int dynsec_roles__config_save(cJSON *tree) { cJSON *j_roles, *j_role; - struct dynsec__role *role, *role_tmp; + struct dynsec__role *role, *role_tmp = NULL; if((j_roles = cJSON_AddArrayToObject(tree, "roles")) == NULL){ return 1; @@ -433,7 +433,7 @@ int dynsec_roles__process_create(cJSON *j_responses, struct mosquitto *context, static void role__remove_all_clients(struct dynsec__role *role) { - struct dynsec__clientlist *clientlist, *clientlist_tmp; + struct dynsec__clientlist *clientlist, *clientlist_tmp = NULL; HASH_ITER(hh, role->clientlist, clientlist, clientlist_tmp){ mosquitto_kick_client_by_username(clientlist->client->username, false); @@ -444,7 +444,7 @@ static void role__remove_all_clients(struct dynsec__role *role) static void role__remove_all_groups(struct dynsec__role *role) { - struct dynsec__grouplist *grouplist, *grouplist_tmp; + struct dynsec__grouplist *grouplist, *grouplist_tmp = NULL; HASH_ITER(hh, role->grouplist, grouplist, grouplist_tmp){ if(grouplist->group == dynsec_anonymous_group){ @@ -526,7 +526,7 @@ static cJSON *add_role_to_json(struct dynsec__role *role, bool verbose) int dynsec_roles__process_list(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data) { bool verbose; - struct dynsec__role *role, *role_tmp; + struct dynsec__role *role, *role_tmp = NULL; cJSON *tree, *j_roles, *j_role, *j_data; int i, count, offset; const char *admin_clientid, *admin_username; diff --git a/plugins/payload-modification/mosquitto_payload_modification.c b/plugins/payload-modification/mosquitto_payload_modification.c index af4da4081..ac11e1c1f 100644 --- a/plugins/payload-modification/mosquitto_payload_modification.c +++ b/plugins/payload-modification/mosquitto_payload_modification.c @@ -32,8 +32,6 @@ SPDX-License-Identifier: EPL-2.0 OR EDL-1.0 * * Note that this only works on Mosquitto 2.0 or later. */ - - #include #include @@ -42,6 +40,8 @@ SPDX-License-Identifier: EPL-2.0 OR EDL-1.0 #include "mosquitto.h" #include "mqtt_protocol.h" +#define UNUSED(A) (void)(A) + static mosquitto_plugin_id_t *mosq_pid = NULL; static int callback_message(int event, void *event_data, void *userdata) @@ -50,6 +50,9 @@ static int callback_message(int event, void *event_data, void *userdata) char *new_payload; uint32_t new_payloadlen; + UNUSED(event); + UNUSED(userdata); + /* This simply adds "hello " to the front of every payload. You can of * course do much more complicated message processing if needed. */ @@ -91,11 +94,19 @@ int mosquitto_plugin_version(int supported_version_count, const int *supported_v int mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *opts, int opt_count) { + UNUSED(user_data); + UNUSED(opts); + UNUSED(opt_count); + mosq_pid = identifier; return mosquitto_callback_register(mosq_pid, MOSQ_EVT_MESSAGE, callback_message, NULL, NULL); } int mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *opts, int opt_count) { + UNUSED(user_data); + UNUSED(opts); + UNUSED(opt_count); + return mosquitto_callback_unregister(mosq_pid, MOSQ_EVT_MESSAGE, callback_message, NULL); } diff --git a/security/mosquitto.apparmor b/security/mosquitto.apparmor index 705de6c41..622c11b39 100644 --- a/security/mosquitto.apparmor +++ b/security/mosquitto.apparmor @@ -9,6 +9,7 @@ /etc/mosquitto/conf.d/* r, /var/lib/mosquitto/ r, /var/lib/mosquitto/mosquitto.db rwk, + /var/lib/mosquitto/mosquitto.db.new rwk, /var/run/mosquitto.pid rw, network inet stream, diff --git a/set-version.sh b/set-version.sh index a062b1ad9..dc6cf349f 100755 --- a/set-version.sh +++ b/set-version.sh @@ -2,7 +2,7 @@ MAJOR=2 MINOR=0 -REVISION=4 +REVISION=5 sed -i "s/^VERSION=.*/VERSION=${MAJOR}.${MINOR}.${REVISION}/" config.mk diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 0e485bdf9..41495ef28 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -1,5 +1,5 @@ name: mosquitto -version: 2.0.4 +version: 2.0.5 summary: Eclipse Mosquitto MQTT broker description: This is a message broker that supports version 5.0, 3.1.1, and 3.1 of the MQTT protocol. diff --git a/src/bridge.c b/src/bridge.c index 6379c1a40..458164187 100644 --- a/src/bridge.c +++ b/src/bridge.c @@ -86,7 +86,7 @@ int bridge__new(struct mosquitto__bridge *bridge) mosquitto__free(local_id); }else{ /* id wasn't found, so generate a new context */ - new_context = context__init(-1); + new_context = context__init(INVALID_SOCKET); if(!new_context){ mosquitto__free(local_id); return MOSQ_ERR_NOMEM; @@ -568,19 +568,17 @@ int bridge__on_connect(struct mosquitto *context) int bridge__register_local_connections(void) { -#ifdef WITH_EPOLL struct mosquitto *context, *ctxt_tmp = NULL; HASH_ITER(hh_sock, db.contexts_by_sock, context, ctxt_tmp){ if(context->bridge){ if(mux__add_in(context)){ - log__printf(NULL, MOSQ_LOG_ERR, "Error in epoll initial registering bridge: %s", strerror(errno)); + log__printf(NULL, MOSQ_LOG_ERR, "Error in initial bridge registration: %s", strerror(errno)); return MOSQ_ERR_UNKNOWN; } mux__add_out(context); } } -#endif return MOSQ_ERR_SUCCESS; } diff --git a/src/conf.c b/src/conf.c index 2c663a9aa..611a62035 100644 --- a/src/conf.c +++ b/src/conf.c @@ -1824,7 +1824,6 @@ int config__read_file_core(struct mosquitto__config *config, bool reload, struct }else if(!strcmp(token, "websockets")){ #ifdef WITH_WEBSOCKETS cur_listener->protocol = mp_websockets; - config->have_websockets_listener = true; #else log__printf(NULL, MOSQ_LOG_ERR, "Error: Websockets support not available."); return MOSQ_ERR_INVAL; @@ -2047,7 +2046,7 @@ int config__read_file_core(struct mosquitto__config *config, bool reload, struct token = strtok_r(NULL, " ", &saveptr); if(token){ if (token[0] == '#'){ - strtok_r(NULL, "", &saveptr); + (void)strtok_r(NULL, "", &saveptr); } qos = (uint8_t)atoi(token); if(qos > 2){ @@ -2060,7 +2059,7 @@ int config__read_file_core(struct mosquitto__config *config, bool reload, struct if(!strcmp(token, "\"\"") || token[0] == '#'){ local_prefix = NULL; if (token[0] == '#'){ - strtok_r(NULL, "", &saveptr); + (void)strtok_r(NULL, "", &saveptr); } }else{ local_prefix = token; @@ -2275,7 +2274,7 @@ static int config__check(struct mosquitto__config *config) if(!config->listeners[i].security_options.auto_id_prefix){ return MOSQ_ERR_NOMEM; } - config->listeners[i].security_options.auto_id_prefix_len = strlen("auto-"); + config->listeners[i].security_options.auto_id_prefix_len = (uint16_t)strlen("auto-"); } } }else{ @@ -2284,7 +2283,7 @@ static int config__check(struct mosquitto__config *config) if(!config->security_options.auto_id_prefix){ return MOSQ_ERR_NOMEM; } - config->security_options.auto_id_prefix_len = strlen("auto-"); + config->security_options.auto_id_prefix_len = (uint16_t)strlen("auto-"); } } diff --git a/src/conf_includedir.c b/src/conf_includedir.c index 42f471567..4a082c23f 100644 --- a/src/conf_includedir.c +++ b/src/conf_includedir.c @@ -79,7 +79,7 @@ int scmp_p(const void *p1, const void *p2) #ifdef WIN32 int config__get_dir_files(const char *include_dir, char ***files, int *file_count) { - int len; + size_t len; int i; char **l_files = NULL; int l_file_count = 0; diff --git a/src/control.c b/src/control.c index d158a4ace..b6fcf4238 100644 --- a/src/control.c +++ b/src/control.c @@ -124,7 +124,7 @@ int control__unregister_callback(struct mosquitto__security_options *opts, MOSQ_ if(strncmp(topic, "$CONTROL/", strlen("$CONTROL/"))) return MOSQ_ERR_INVAL; HASH_FIND(hh, opts->plugin_callbacks.control, topic, topic_len, cb_found); - if(cb_found){ + if(cb_found && cb_found->cb == cb_func){ HASH_DELETE(hh, opts->plugin_callbacks.control, cb_found); mosquitto__free(cb_found->data); mosquitto__free(cb_found); diff --git a/src/database.c b/src/database.c index eec4f1572..ebdf6ecd6 100644 --- a/src/database.c +++ b/src/database.c @@ -90,8 +90,8 @@ bool db__ready_for_queue(struct mosquitto *context, int qos, struct mosquitto_ms { int source_count; int adjust_count; - unsigned long source_bytes; - unsigned long adjust_bytes = db.config->max_inflight_bytes; + size_t source_bytes; + size_t adjust_bytes = db.config->max_inflight_bytes; bool valid_bytes; bool valid_count; @@ -148,10 +148,10 @@ int db__open(struct mosquitto__config *config) db.subs = NULL; - subhier = sub__add_hier_entry(NULL, &db.subs, "", strlen("")); + subhier = sub__add_hier_entry(NULL, &db.subs, "", 0); if(!subhier) return MOSQ_ERR_NOMEM; - subhier = sub__add_hier_entry(NULL, &db.subs, "$SYS", strlen("$SYS")); + subhier = sub__add_hier_entry(NULL, &db.subs, "$SYS", (uint16_t)strlen("$SYS")); if(!subhier) return MOSQ_ERR_NOMEM; retain__init(); @@ -312,6 +312,8 @@ void db__message_dequeue_first(struct mosquitto *context, struct mosquitto_msg_d { struct mosquitto_client_msg *msg; + UNUSED(context); + msg = msg_data->queued; DL_DELETE(msg_data->queued, msg); DL_APPEND(msg_data->inflight, msg); diff --git a/src/handle_unsubscribe.c b/src/handle_unsubscribe.c index e4930f2c0..8e336f093 100644 --- a/src/handle_unsubscribe.c +++ b/src/handle_unsubscribe.c @@ -33,7 +33,7 @@ int handle__unsubscribe(struct mosquitto *context) char *sub; uint16_t slen; int rc; - uint8_t reason; + uint8_t reason = 0; int reason_code_count = 0; int reason_code_max; uint8_t *reason_codes = NULL, *reason_tmp; diff --git a/src/keepalive.c b/src/keepalive.c index dcdf03b29..38ff4ff1a 100644 --- a/src/keepalive.c +++ b/src/keepalive.c @@ -27,6 +27,8 @@ static time_t last_keepalive_check = 0; int keepalive__add(struct mosquitto *context) { + UNUSED(context); + return MOSQ_ERR_SUCCESS; } @@ -58,6 +60,8 @@ void keepalive__check(void) int keepalive__remove(struct mosquitto *context) { + UNUSED(context); + return MOSQ_ERR_SUCCESS; } diff --git a/src/mosquitto.c b/src/mosquitto.c index eef66c44c..53cd85f2c 100644 --- a/src/mosquitto.c +++ b/src/mosquitto.c @@ -140,6 +140,8 @@ int drop_privileges(struct mosquitto__config *config) log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Mosquitto should not be run as root/administrator."); } } +#else + UNUSED(config); #endif return MOSQ_ERR_SUCCESS; } @@ -237,7 +239,7 @@ int listeners__start_single_mqtt(struct mosquitto__listener *listener) #ifdef WITH_WEBSOCKETS -void listeners__add_websockets(struct lws_context *ws_context, int fd) +void listeners__add_websockets(struct lws_context *ws_context, mosq_sock_t fd) { int i; struct mosquitto__listener *listener = NULL; @@ -624,6 +626,10 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine char *saveptr = NULL; int rc; + UNUSED(hInstance); + UNUSED(hPrevInstance); + UNUSED(nCmdShow); + argv = mosquitto__malloc(sizeof(char *)*1); argv[0] = "mosquitto"; token = strtok_r(lpCmdLine, " ", &saveptr); diff --git a/src/mosquitto_broker_internal.h b/src/mosquitto_broker_internal.h index fbe21d35f..1f6df3ccb 100644 --- a/src/mosquitto_broker_internal.h +++ b/src/mosquitto_broker_internal.h @@ -294,7 +294,6 @@ struct mosquitto__config { #ifdef WITH_WEBSOCKETS int websockets_log_level; uint16_t websockets_headers_size; - bool have_websockets_listener; #endif #ifdef WITH_BRIDGE struct mosquitto__bridge *bridges; @@ -747,7 +746,7 @@ int mux__cleanup(void); void listener__set_defaults(struct mosquitto__listener *listener); void listeners__reload_all_certificates(void); #ifdef WITH_WEBSOCKETS -void listeners__add_websockets(struct lws_context *ws_context, int fd); +void listeners__add_websockets(struct lws_context *ws_context, mosq_sock_t fd); #endif /* ============================================================ diff --git a/src/mux_epoll.c b/src/mux_epoll.c index 2749845f5..87d766921 100644 --- a/src/mux_epoll.c +++ b/src/mux_epoll.c @@ -115,8 +115,8 @@ int mux_epoll__add_out(struct mosquitto *context) { struct epoll_event ev; - memset(&ev, 0, sizeof(struct epoll_event)); if(!(context->events & EPOLLOUT)) { + memset(&ev, 0, sizeof(struct epoll_event)); ev.data.ptr = context; ev.events = EPOLLIN | EPOLLOUT; if(epoll_ctl(db.epollfd, EPOLL_CTL_ADD, context->sock, &ev) == -1) { @@ -134,8 +134,8 @@ int mux_epoll__remove_out(struct mosquitto *context) { struct epoll_event ev; - memset(&ev, 0, sizeof(struct epoll_event)); if(context->events & EPOLLOUT) { + memset(&ev, 0, sizeof(struct epoll_event)); ev.data.ptr = context; ev.events = EPOLLIN; if(epoll_ctl(db.epollfd, EPOLL_CTL_ADD, context->sock, &ev) == -1) { diff --git a/src/mux_poll.c b/src/mux_poll.c index 947a512ed..3e0977dcc 100644 --- a/src/mux_poll.c +++ b/src/mux_poll.c @@ -56,7 +56,7 @@ SPDX-License-Identifier: EPL-2.0 OR EDL-1.0 #include "util_mosq.h" #include "mux.h" -static void loop_handle_reads_writes(struct pollfd *pollfds); +static void loop_handle_reads_writes(void); static struct pollfd *pollfds = NULL; static size_t pollfd_max, pollfd_current_max; @@ -110,23 +110,26 @@ int mux_poll__add_out(struct mosquitto *context) { int i; - if(context->pollfd_index != -1){ - pollfds[context->pollfd_index].fd = context->sock; - pollfds[context->pollfd_index].events = POLLIN | POLLOUT; - pollfds[context->pollfd_index].revents = 0; - }else{ - for(i=0; isock; - pollfds[i].events = POLLIN | POLLOUT; - pollfds[i].revents = 0; - context->pollfd_index = i; - if(i > pollfd_current_max){ - pollfd_current_max = (size_t )i; + if(!(context->events & POLLOUT)) { + if(context->pollfd_index != -1){ + pollfds[context->pollfd_index].fd = context->sock; + pollfds[context->pollfd_index].events = POLLIN | POLLOUT; + pollfds[context->pollfd_index].revents = 0; + }else{ + for(i=0; isock; + pollfds[i].events = POLLIN | POLLOUT; + pollfds[i].revents = 0; + context->pollfd_index = i; + if(i > pollfd_current_max){ + pollfd_current_max = (size_t )i; + } + break; } - break; } } + context->events = POLLIN | POLLOUT; } return MOSQ_ERR_SUCCESS; @@ -135,7 +138,11 @@ int mux_poll__add_out(struct mosquitto *context) int mux_poll__remove_out(struct mosquitto *context) { - return mux_poll__add_in(context); + if(context->events & POLLOUT) { + return mux_poll__add_in(context); + }else{ + return MOSQ_ERR_SUCCESS; + } } @@ -161,6 +168,7 @@ int mux_poll__add_in(struct mosquitto *context) } } } + context->events = POLLIN; return MOSQ_ERR_SUCCESS; } @@ -227,7 +235,7 @@ int mux_poll__handle(struct mosquitto__listener_sock *listensock, int listensock log__printf(NULL, MOSQ_LOG_ERR, "Error in poll: %s.", strerror(errno)); } }else{ - loop_handle_reads_writes(pollfds); + loop_handle_reads_writes(); for(i=0; i #include #include -#include +#include #else #include #include @@ -607,6 +607,52 @@ int net__tls_load_verify(struct mosquitto__listener *listener) } +#ifndef WIN32 +static int net__bind_interface(struct mosquitto__listener *listener, mosq_sock_t sock, struct addrinfo *rp) +{ + /* + * This binds the listener sock to a network interface. + * The use of SO_BINDTODEVICE requires root access, which we don't have, so instead + * use getifaddrs to find the interface addresses, and attempt to bind to + * the IP of the matching interface. + */ + struct ifaddrs *ifaddr, *ifa; + if(getifaddrs(&ifaddr) < 0){ + net__print_error(MOSQ_LOG_ERR, "Error: %s"); + return MOSQ_ERR_ERRNO; + } + + for(ifa=ifaddr; ifa!=NULL; ifa=ifa->ifa_next){ + if(ifa->ifa_addr == NULL){ + continue; + } + + if(!strcasecmp(listener->bind_interface, ifa->ifa_name) + && ifa->ifa_addr->sa_family == rp->ai_addr->sa_family){ + + if(rp->ai_addr->sa_family == AF_INET){ + memcpy(&((struct sockaddr_in *)rp->ai_addr)->sin_addr, + &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr, + sizeof(struct in_addr)); + + freeifaddrs(ifaddr); + return MOSQ_ERR_SUCCESS; + }else if(rp->ai_addr->sa_family == AF_INET6){ + memcpy(&((struct sockaddr_in6 *)rp->ai_addr)->sin6_addr, + &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr, + sizeof(struct in6_addr)); + freeifaddrs(ifaddr); + return MOSQ_ERR_SUCCESS; + } + } + } + freeifaddrs(ifaddr); + log__printf(NULL, MOSQ_LOG_ERR, "Error: Interface %s not found.", listener->bind_interface); + return MOSQ_ERR_NOT_FOUND; +} +#endif + + static int net__socket_listen_tcp(struct mosquitto__listener *listener) { mosq_sock_t sock = INVALID_SOCKET; @@ -615,9 +661,6 @@ static int net__socket_listen_tcp(struct mosquitto__listener *listener) char service[10]; int rc; int ss_opt = 1; -#ifdef SO_BINDTODEVICE - struct ifreq ifr; -#endif if(!listener) return MOSQ_ERR_INVAL; @@ -680,14 +723,9 @@ static int net__socket_listen_tcp(struct mosquitto__listener *listener) return 1; } -#ifdef SO_BINDTODEVICE +#ifndef WIN32 if(listener->bind_interface){ - memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_name, listener->bind_interface, sizeof(ifr.ifr_name)-1); - ifr.ifr_name[sizeof(ifr.ifr_name)-1] = '\0'; - log__printf(NULL, MOSQ_LOG_INFO, "Binding listener to interface \"%s\".", ifr.ifr_name); - if(setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr)) < 0) { - net__print_error(MOSQ_LOG_ERR, "Error: %s"); + if(net__bind_interface(listener, sock, rp)){ COMPAT_CLOSE(sock); freeaddrinfo(ainfo); mosquitto__free(listener->socks); diff --git a/src/persist_read.c b/src/persist_read.c index 50a27e632..8d17024ff 100644 --- a/src/persist_read.c +++ b/src/persist_read.c @@ -54,7 +54,7 @@ static struct mosquitto *persist__find_or_add_context(const char *client_id, uin context = NULL; HASH_FIND(hh_id, db.contexts_by_id, client_id, strlen(client_id), context); if(!context){ - context = context__init(-1); + context = context__init(INVALID_SOCKET); if(!context) return NULL; context->id = mosquitto__strdup(client_id); if(!context->id){ diff --git a/src/retain.c b/src/retain.c index c64599f4c..40045401a 100644 --- a/src/retain.c +++ b/src/retain.c @@ -62,10 +62,10 @@ int retain__init(void) { struct mosquitto__retainhier *retainhier; - retainhier = retain__add_hier_entry(NULL, &db.retains, "", (int)strlen("")); + retainhier = retain__add_hier_entry(NULL, &db.retains, "", 0); if(!retainhier) return MOSQ_ERR_NOMEM; - retainhier = retain__add_hier_entry(NULL, &db.retains, "$SYS", (int)strlen("$SYS")); + retainhier = retain__add_hier_entry(NULL, &db.retains, "$SYS", (uint16_t)strlen("$SYS")); if(!retainhier) return MOSQ_ERR_NOMEM; return MOSQ_ERR_SUCCESS; diff --git a/src/security.c b/src/security.c index 002b27eab..9bf46059e 100644 --- a/src/security.c +++ b/src/security.c @@ -936,6 +936,7 @@ int mosquitto_security_auth_start(struct mosquitto *context, bool reauth, const DL_FOREACH(opts->plugin_callbacks.ext_auth_start, cb_base){ memset(&event_data, 0, sizeof(event_data)); event_data.client = context; + event_data.auth_method = context->auth_method; event_data.data_in = data_in; event_data.data_out = NULL; event_data.data_in_len = data_in_len; diff --git a/src/security_default.c b/src/security_default.c index d732e774a..6bbe70af5 100644 --- a/src/security_default.c +++ b/src/security_default.c @@ -47,7 +47,7 @@ int mosquitto_security_init_default(bool reload) int rc; int i; char *pwf; - char *pskf; + char *pskf = NULL; UNUSED(reload); @@ -136,7 +136,7 @@ int mosquitto_security_init_default(bool reload) } } }else{ - char *pskf = db.config->security_options.psk_file; + pskf = db.config->security_options.psk_file; if(pskf){ rc = psk__file_parse(&db.config->security_options.psk_id, pskf); if(rc){ @@ -372,6 +372,9 @@ static int mosquitto_acl_check_default(int event, void *event_data, void *userda char *s; struct mosquitto__security_options *security_opts = NULL; + UNUSED(event); + UNUSED(userdata); + if(ed->client->bridge) return MOSQ_ERR_SUCCESS; if(ed->access == MOSQ_ACL_SUBSCRIBE || ed->access == MOSQ_ACL_UNSUBSCRIBE) return MOSQ_ERR_SUCCESS; /* FIXME - implement ACL subscription strings. */ @@ -667,7 +670,7 @@ static void acl__cleanup_single(struct mosquitto__security_options *security_opt static int acl__cleanup(bool reload) { - struct mosquitto *context, *ctxt_tmp; + struct mosquitto *context, *ctxt_tmp = NULL; int i; UNUSED(reload); @@ -833,7 +836,7 @@ void unpwd__free_item(struct mosquitto__unpwd **unpwd, struct mosquitto__unpwd * #ifdef WITH_TLS static int unpwd__decode_passwords(struct mosquitto__unpwd **unpwd) { - struct mosquitto__unpwd *u, *tmp; + struct mosquitto__unpwd *u, *tmp = NULL; char *token; unsigned char *salt; unsigned int salt_len; @@ -939,7 +942,7 @@ static int unpwd__file_parse(struct mosquitto__unpwd **unpwd, const char *passwo static int psk__file_parse(struct mosquitto__unpwd **psk_id, const char *psk_file) { int rc; - struct mosquitto__unpwd *u, *tmp; + struct mosquitto__unpwd *u, *tmp = NULL; if(!db.config || !psk_id) return MOSQ_ERR_INVAL; @@ -993,6 +996,9 @@ static int mosquitto_unpwd_check_default(int event, void *event_data, void *user int rc; #endif + UNUSED(event); + UNUSED(userdata); + if(ed->client->username == NULL){ return MOSQ_ERR_PLUGIN_DEFER; } @@ -1038,7 +1044,7 @@ static int mosquitto_unpwd_check_default(int event, void *event_data, void *user static int unpwd__cleanup(struct mosquitto__unpwd **root, bool reload) { - struct mosquitto__unpwd *u, *tmp; + struct mosquitto__unpwd *u, *tmp = NULL; UNUSED(reload); @@ -1079,7 +1085,7 @@ static void security__disconnect_auth(struct mosquitto *context) */ int mosquitto_security_apply_default(void) { - struct mosquitto *context, *ctxt_tmp; + struct mosquitto *context, *ctxt_tmp = NULL; struct mosquitto__acl_user *acl_user_tail; bool allow_anonymous; struct mosquitto__security_options *security_opts = NULL; @@ -1289,7 +1295,7 @@ int mosquitto_security_apply_default(void) int mosquitto_psk_key_get_default(struct mosquitto *context, const char *hint, const char *identity, char *key, int max_key_len) { - struct mosquitto__unpwd *u, *tmp; + struct mosquitto__unpwd *u, *tmp = NULL; struct mosquitto__unpwd *psk_id_ref = NULL; if(!hint || !identity || !key) return MOSQ_ERR_INVAL; diff --git a/src/service.c b/src/service.c index 2b032691a..86b74c8ea 100644 --- a/src/service.c +++ b/src/service.c @@ -31,7 +31,7 @@ int main(int argc, char *argv[]); static void print_error(void) { - char *buf; + char *buf = NULL; FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), LANG_NEUTRAL, (LPTSTR)&buf, 0, NULL); @@ -70,6 +70,9 @@ void __stdcall service_main(DWORD dwArgc, LPTSTR *lpszArgv) char conf_path[MAX_PATH + 20]; int rc; + UNUSED(dwArgc); + UNUSED(lpszArgv); + service_handle = RegisterServiceCtrlHandler("mosquitto", service_handler); if(service_handle){ memset(conf_path, 0, sizeof(conf_path)); diff --git a/src/signals.c b/src/signals.c index afed51939..71b586770 100644 --- a/src/signals.c +++ b/src/signals.c @@ -92,6 +92,8 @@ DWORD WINAPI SigThreadProc(void* data) static HANDLE evt[3]; int pid = GetCurrentProcessId(); + UNUSED(data); + sprintf_s(evt_name, MAX_PATH, "mosq%d_shutdown", pid); evt[0] = CreateEvent(NULL, TRUE, FALSE, evt_name); sprintf_s(evt_name, MAX_PATH, "mosq%d_reload", pid); diff --git a/src/subs.c b/src/subs.c index a129f53e4..d96cacb46 100644 --- a/src/subs.c +++ b/src/subs.c @@ -253,7 +253,7 @@ static int sub__add_shared(struct mosquitto *context, uint8_t qos, uint32_t iden } } if(shared_ref){ - shared_subs = mosquitto__realloc(context->shared_subs, sizeof(struct mosquitto__subhier_ref *)*(size_t)(context->shared_sub_count + 1)); + shared_subs = mosquitto__realloc(context->shared_subs, sizeof(struct mosquitto__subshared_ref *)*(size_t)(context->shared_sub_count + 1)); if(!shared_subs){ mosquitto__free(shared_ref); context->shared_subs[context->shared_sub_count-1] = NULL; diff --git a/src/websockets.c b/src/websockets.c index 4b9d42220..ac966876a 100644 --- a/src/websockets.c +++ b/src/websockets.c @@ -605,24 +605,47 @@ static int callback_http( break; case LWS_CALLBACK_ADD_POLL_FD: - case LWS_CALLBACK_DEL_POLL_FD: - case LWS_CALLBACK_CHANGE_MODE_POLL_FD: HASH_FIND(hh_sock, db.contexts_by_sock, &pollargs->fd, sizeof(pollargs->fd), mosq); if(mosq){ - if(pollargs->events & POLLOUT){ + if(pollargs->events & LWS_POLLOUT){ mux__add_out(mosq); mosq->ws_want_write = true; }else{ mux__remove_out(mosq); } }else{ - if(reason == LWS_CALLBACK_ADD_POLL_FD && (pollargs->events & POLLIN)){ + if(pollargs->events & POLLIN){ /* Assume this is a new listener */ listeners__add_websockets(lws_get_context(wsi), pollargs->fd); } } break; + case LWS_CALLBACK_DEL_POLL_FD: + HASH_FIND(hh_sock, db.contexts_by_sock, &pollargs->fd, sizeof(pollargs->fd), mosq); + if(mosq){ + mux__delete(mosq); + }else{ + return 1; + } + break; + + case LWS_CALLBACK_CHANGE_MODE_POLL_FD: + HASH_FIND(hh_sock, db.contexts_by_sock, &pollargs->fd, sizeof(pollargs->fd), mosq); + if(mosq){ + if(pollargs->events & LWS_POLLHUP){ + return 1; + }else if(pollargs->events & LWS_POLLOUT){ + mux__add_out(mosq); + mosq->ws_want_write = true; + }else{ + mux__remove_out(mosq); + } + }else{ + return 1; + } + break; + #ifdef WITH_TLS case LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION: if(!len || (SSL_get_verify_result((SSL*)in) != X509_V_OK)){ diff --git a/test/broker/14-dynsec-group.py b/test/broker/14-dynsec-group.py index 947f2806f..f2f2d9277 100755 --- a/test/broker/14-dynsec-group.py +++ b/test/broker/14-dynsec-group.py @@ -84,7 +84,7 @@ def command_check(sock, command_payload, expected_response, msg=""): add_client_to_group_command = {"commands": [{"command":"addGroupClient", "username":"user_one", "groupname": "group_one", "correlationData":"1234"}]} add_client_to_group_response = {'responses':[{'command': 'addGroupClient', 'correlationData': '1234'}]} -add_duplicate_client_to_group_response = {'responses':[{'command': 'addGroupClient', 'correlationData': '1234'}]} +add_duplicate_client_to_group_response = {'responses':[{'command': 'addGroupClient', 'error':'Client is already in this group', 'correlationData': '1234'}]} add_client_to_group2_command = {"commands": [{"command":"addGroupClient", "username":"user_one", "groupname": "group_two", "correlationData":"1234"}]} diff --git a/www/pages/download.md b/www/pages/download.md index fb141c5d6..526847b86 100644 --- a/www/pages/download.md +++ b/www/pages/download.md @@ -1,7 +1,7 @@ + +Version 2.0.5 of Mosquitto has been released. This is a bugfix release. + +# Broker +- Fix `auth_method` not being provided to the extended auth plugin event. + Closes [#1975]. +- Fix large packets not being completely published to slow clients. + Closes [#1977]. +- Fix bridge connection not relinquishing POLLOUT after messages are sent. + Closes [#1979]. +- Fix apparmor incorrectly denying access to + /var/lib/mosquitto/mosquitto.db.new. Closes [#1978]. +- Fix potential intermittent initial bridge connections when using poll(). +- Fix `bind_interface` option. Closes [#1999]. +- Fix invalid behaviour in dynsec plugin if a group or client is deleted + before a role that was attached to the group or client is deleted. + Closes [#1998]. +- Improve logging in dynsec addGroupRole command. Closes [#2005]. +- Improve logging in dynsec addGroupClient command. Closes [#2008]. + +# Client library +- Improve documentation around the `_v5()` and non-v5 functions, e.g. + `mosquitto_publish()` and `mosquitto_publish_v5(). + +# Build +- `install` Makefile target should depend on `all`, not `mosquitto`, to ensure + that man pages are always built. Closes [#1989]. +- Fixes for lots of minor build warnings highlighted by Visual Studio. + +# Apps +- Disallow control characters in mosquitto_passwd usernames. +- Fix incorrect description in mosquitto_ctrl man page. Closes [#1995]. +- Fix `mosquitto_ctrl dynsec getGroup` not showing roles. Closes [#1997]. + + +[#1975]: https://github.com/eclipse/mosquitto/issues/1975 +[#1977]: https://github.com/eclipse/mosquitto/issues/1977 +[#1978]: https://github.com/eclipse/mosquitto/issues/1978 +[#1979]: https://github.com/eclipse/mosquitto/issues/1979 +[#1989]: https://github.com/eclipse/mosquitto/issues/1989 +[#1995]: https://github.com/eclipse/mosquitto/issues/1995 +[#1997]: https://github.com/eclipse/mosquitto/issues/1997 +[#1998]: https://github.com/eclipse/mosquitto/issues/1998 +[#1999]: https://github.com/eclipse/mosquitto/issues/1999 +[#2005]: https://github.com/eclipse/mosquitto/issues/2005 +[#2008]: https://github.com/eclipse/mosquitto/issues/2008