diff -Nru mosquitto-1.6.9/about.html mosquitto-2.0.15/about.html
--- mosquitto-1.6.9/about.html 2020-02-27 23:49:51.000000000 +0000
+++ mosquitto-2.0.15/about.html 2022-08-16 13:34:02.000000000 +0000
@@ -5,31 +5,30 @@
About This Content
-
-May 8, 2014
+
+May 8, 2014
License
-The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise
+
The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise
indicated below, the Content is provided to you under the terms and conditions of the
-Eclipse Public License Version 1.0 ("EPL") and Eclipse Distribution License Version 1.0 ("EDL").
-A copy of the EPL is available at
-http://www.eclipse.org/legal/epl-v10.html
-and a copy of the EDL is available at
-http://www.eclipse.org/org/documents/edl-v10.php.
+Eclipse Public License Version 2.0 ("EPL") and Eclipse Distribution License Version 1.0 ("EDL").
+A copy of the EPL is available at https://www.eclipse.org/legal/epl-2.0/
+and a copy of the EDL is available at
+http://www.eclipse.org/org/documents/edl-v10.php.
For purposes of the EPL, "Program" will mean the Content.
-If you did not receive this Content directly from the Eclipse Foundation, the Content is
+
If you did not receive this Content directly from the Eclipse Foundation, the Content is
being redistributed by another party ("Redistributor") and different terms and conditions may
-apply to your use of any object code in the Content. Check the Redistributor's license that was
+apply to your use of any object code in the Content. Check the Redistributor's license that was
provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise
indicated below, the terms and conditions of the EPL still apply to any source code in the Content
and such source code may be obtained at http://www.eclipse.org.
-
+
Third Party Content
- The Content includes items that have been sourced from third parties as set out below. If you
- did not receive this Content directly from the Eclipse Foundation, the following is provided
- for informational purposes only, and you should look to the Redistributor's license for
+
The Content includes items that have been sourced from third parties as set out below. If you
+ did not receive this Content directly from the Eclipse Foundation, the following is provided
+ for informational purposes only, and you should look to the Redistributor's license for
terms and conditions of use.
libwebsockets 2.4.2
diff -Nru mosquitto-1.6.9/apps/CMakeLists.txt mosquitto-2.0.15/apps/CMakeLists.txt
--- mosquitto-1.6.9/apps/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000
+++ mosquitto-2.0.15/apps/CMakeLists.txt 2022-08-16 13:34:02.000000000 +0000
@@ -0,0 +1,2 @@
+add_subdirectory(mosquitto_ctrl)
+add_subdirectory(mosquitto_passwd)
diff -Nru mosquitto-1.6.9/apps/db_dump/db_dump.c mosquitto-2.0.15/apps/db_dump/db_dump.c
--- mosquitto-1.6.9/apps/db_dump/db_dump.c 1970-01-01 00:00:00.000000000 +0000
+++ mosquitto-2.0.15/apps/db_dump/db_dump.c 2022-08-16 13:34:02.000000000 +0000
@@ -0,0 +1,503 @@
+/*
+Copyright (c) 2010-2019 Roger Light
+
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License 2.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
+
+The Eclipse Public License is available at
+ https://www.eclipse.org/legal/epl-2.0/
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
+
+SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+
+Contributors:
+ Roger Light - initial implementation and documentation.
+*/
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "db_dump.h"
+#include
+#include
+#include
+
+#define mosquitto__malloc(A) malloc((A))
+#define mosquitto__free(A) free((A))
+#define _mosquitto_malloc(A) malloc((A))
+#define _mosquitto_free(A) free((A))
+#include
+
+#include "db_dump.h"
+
+struct client_data
+{
+ UT_hash_handle hh_id;
+ char *id;
+ uint32_t subscriptions;
+ uint32_t subscription_size;
+ int messages;
+ long message_size;
+};
+
+struct msg_store_chunk
+{
+ UT_hash_handle hh;
+ dbid_t store_id;
+ uint32_t length;
+};
+
+struct mosquitto_db db;
+
+extern uint32_t db_version;
+static int stats = 0;
+static int client_stats = 0;
+static int do_print = 1;
+
+/* Counts */
+static long cfg_count = 0;
+static long client_count = 0;
+static long client_msg_count = 0;
+static long msg_store_count = 0;
+static long retain_count = 0;
+static long sub_count = 0;
+/* ====== */
+
+
+struct client_data *clients_by_id = NULL;
+struct msg_store_chunk *msgs_by_id = NULL;
+
+
+static void free__sub(struct P_sub *chunk)
+{
+ free(chunk->client_id);
+ free(chunk->topic);
+}
+
+static void free__client(struct P_client *chunk)
+{
+ free(chunk->client_id);
+}
+
+
+static void free__client_msg(struct P_client_msg *chunk)
+{
+ free(chunk->client_id);
+ mosquitto_property_free_all(&chunk->properties);
+}
+
+
+static void free__msg_store(struct P_msg_store *chunk)
+{
+ free(chunk->topic);
+ free(chunk->payload);
+ mosquitto_property_free_all(&chunk->properties);
+}
+
+
+static int dump__cfg_chunk_process(FILE *db_fd, uint32_t length)
+{
+ struct PF_cfg chunk;
+ int rc;
+
+ cfg_count++;
+
+ memset(&chunk, 0, sizeof(struct PF_cfg));
+
+ if(db_version == 6 || db_version == 5){
+ rc = persist__chunk_cfg_read_v56(db_fd, &chunk);
+ }else{
+ rc = persist__chunk_cfg_read_v234(db_fd, &chunk);
+ }
+ if(rc){
+ fprintf(stderr, "Error: Corrupt persistent database.");
+ fclose(db_fd);
+ return rc;
+ }
+
+ if(do_print) printf("DB_CHUNK_CFG:\n");
+ if(do_print) printf("\tLength: %d\n", length);
+ if(do_print) printf("\tShutdown: %d\n", chunk.shutdown);
+ if(do_print) printf("\tDB ID size: %d\n", chunk.dbid_size);
+ if(chunk.dbid_size != sizeof(dbid_t)){
+ fprintf(stderr, "Error: Incompatible database configuration (dbid size is %d bytes, expected %zu)",
+ chunk.dbid_size, sizeof(dbid_t));
+ fclose(db_fd);
+ return 1;
+ }
+ if(do_print) printf("\tLast DB ID: %" PRIu64 "\n", chunk.last_db_id);
+
+ return 0;
+}
+
+
+static int dump__client_chunk_process(FILE *db_fd, uint32_t length)
+{
+ struct P_client chunk;
+ int rc = 0;
+ struct client_data *cc;
+
+ client_count++;
+
+ memset(&chunk, 0, sizeof(struct P_client));
+
+ if(db_version == 6 || db_version == 5){
+ rc = persist__chunk_client_read_v56(db_fd, &chunk, db_version);
+ }else{
+ rc = persist__chunk_client_read_v234(db_fd, &chunk, db_version);
+ }
+ if(rc){
+ fprintf(stderr, "Error: Corrupt persistent database.");
+ return rc;
+ }
+
+ if(client_stats){
+ cc = calloc(1, sizeof(struct client_data));
+ if(!cc){
+ fprintf(stderr, "Error: Out of memory.\n");
+ fclose(db_fd);
+ free(chunk.client_id);
+ return 1;
+ }
+ cc->id = strdup(chunk.client_id);
+ HASH_ADD_KEYPTR(hh_id, clients_by_id, cc->id, strlen(cc->id), cc);
+ }
+
+ if(do_print) {
+ print__client(&chunk, length);
+ }
+ free__client(&chunk);
+
+ return 0;
+}
+
+
+static int dump__client_msg_chunk_process(FILE *db_fd, uint32_t length)
+{
+ struct P_client_msg chunk;
+ struct client_data *cc;
+ struct msg_store_chunk *msc;
+ int rc;
+
+ client_msg_count++;
+
+ memset(&chunk, 0, sizeof(struct P_client_msg));
+ if(db_version == 6 || db_version == 5){
+ rc = persist__chunk_client_msg_read_v56(db_fd, &chunk, length);
+ }else{
+ rc = persist__chunk_client_msg_read_v234(db_fd, &chunk);
+ }
+ if(rc){
+ fprintf(stderr, "Error: Corrupt persistent database.");
+ fclose(db_fd);
+ return rc;
+ }
+
+ if(client_stats){
+ HASH_FIND(hh_id, clients_by_id, chunk.client_id, strlen(chunk.client_id), cc);
+ if(cc){
+ cc->messages++;
+ cc->message_size += length;
+
+ HASH_FIND(hh, msgs_by_id, &chunk.F.store_id, sizeof(dbid_t), msc);
+ if(msc){
+ cc->message_size += msc->length;
+ }
+ }
+ }
+
+ if(do_print) {
+ print__client_msg(&chunk, length);
+ }
+ free__client_msg(&chunk);
+ return 0;
+}
+
+
+static int dump__msg_store_chunk_process(FILE *db_fptr, uint32_t length)
+{
+ struct P_msg_store chunk;
+ struct mosquitto_msg_store *stored = NULL;
+ struct mosquitto_msg_store_load *load;
+ int64_t message_expiry_interval64;
+ uint32_t message_expiry_interval;
+ int rc = 0;
+ struct msg_store_chunk *mcs;
+
+ msg_store_count++;
+
+ memset(&chunk, 0, sizeof(struct P_msg_store));
+ if(db_version == 6 || db_version == 5){
+ rc = persist__chunk_msg_store_read_v56(db_fptr, &chunk, length);
+ }else{
+ rc = persist__chunk_msg_store_read_v234(db_fptr, &chunk, db_version);
+ }
+ if(rc){
+ fprintf(stderr, "Error: Corrupt persistent database.");
+ fclose(db_fptr);
+ return rc;
+ }
+
+ load = mosquitto__calloc(1, sizeof(struct mosquitto_msg_store_load));
+ if(!load){
+ fclose(db_fptr);
+ mosquitto__free(chunk.source.id);
+ mosquitto__free(chunk.source.username);
+ mosquitto__free(chunk.topic);
+ mosquitto__free(chunk.payload);
+ log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory.");
+ return MOSQ_ERR_NOMEM;
+ }
+
+ if(chunk.F.expiry_time > 0){
+ message_expiry_interval64 = chunk.F.expiry_time - time(NULL);
+ if(message_expiry_interval64 < 0 || message_expiry_interval64 > UINT32_MAX){
+ message_expiry_interval = 0;
+ }else{
+ message_expiry_interval = (uint32_t)message_expiry_interval64;
+ }
+ }else{
+ message_expiry_interval = 0;
+ }
+
+ stored = mosquitto__calloc(1, sizeof(struct mosquitto_msg_store));
+ if(stored == NULL){
+ mosquitto__free(load);
+ fclose(db_fptr);
+ mosquitto__free(chunk.source.id);
+ mosquitto__free(chunk.source.username);
+ mosquitto__free(chunk.topic);
+ mosquitto__free(chunk.payload);
+ return MOSQ_ERR_NOMEM;
+ }
+ stored->source_mid = chunk.F.source_mid;
+ stored->topic = chunk.topic;
+ stored->qos = chunk.F.qos;
+ stored->retain = chunk.F.retain;
+ stored->payloadlen = chunk.F.payloadlen;
+ stored->payload = chunk.payload;
+ stored->properties = chunk.properties;
+
+ rc = db__message_store(&chunk.source, stored, message_expiry_interval,
+ chunk.F.store_id, mosq_mo_client);
+
+ mosquitto__free(chunk.source.id);
+ mosquitto__free(chunk.source.username);
+ chunk.source.id = NULL;
+ chunk.source.username = NULL;
+
+ if(rc == MOSQ_ERR_SUCCESS){
+ stored->source_listener = chunk.source.listener;
+ load->db_id = stored->db_id;
+ load->store = stored;
+
+ HASH_ADD(hh, db.msg_store_load, db_id, sizeof(dbid_t), load);
+ }else{
+ mosquitto__free(load);
+ fclose(db_fptr);
+ return rc;
+ }
+
+ if(client_stats){
+ mcs = calloc(1, sizeof(struct msg_store_chunk));
+ if(!mcs){
+ errno = ENOMEM;
+ return 1;
+ }
+ mcs->store_id = chunk.F.store_id;
+ mcs->length = length;
+ HASH_ADD(hh, msgs_by_id, store_id, sizeof(dbid_t), mcs);
+ }
+
+ if(do_print){
+ print__msg_store(&chunk, length);
+ }
+ free__msg_store(&chunk);
+
+ return 0;
+}
+
+
+static int dump__retain_chunk_process(FILE *db_fd, uint32_t length)
+{
+ struct P_retain chunk;
+ int rc;
+
+ retain_count++;
+ if(do_print) printf("DB_CHUNK_RETAIN:\n");
+ if(do_print) printf("\tLength: %d\n", length);
+
+ if(db_version == 6 || db_version == 5){
+ rc = persist__chunk_retain_read_v56(db_fd, &chunk);
+ }else{
+ rc = persist__chunk_retain_read_v234(db_fd, &chunk);
+ }
+ if(rc){
+ fclose(db_fd);
+ return rc;
+ }
+
+ if(do_print) printf("\tStore ID: %" PRIu64 "\n", chunk.F.store_id);
+ return 0;
+}
+
+
+static int dump__sub_chunk_process(FILE *db_fd, uint32_t length)
+{
+ int rc = 0;
+ struct P_sub chunk;
+ struct client_data *cc;
+
+ sub_count++;
+
+ memset(&chunk, 0, sizeof(struct P_sub));
+ if(db_version == 6 || db_version == 5){
+ rc = persist__chunk_sub_read_v56(db_fd, &chunk);
+ }else{
+ rc = persist__chunk_sub_read_v234(db_fd, &chunk);
+ }
+ if(rc){
+ fprintf(stderr, "Error: Corrupt persistent database.");
+ fclose(db_fd);
+ return rc;
+ }
+
+ if(client_stats){
+ HASH_FIND(hh_id, clients_by_id, chunk.client_id, strlen(chunk.client_id), cc);
+ if(cc){
+ cc->subscriptions++;
+ cc->subscription_size += length;
+ }
+ }
+
+ if(do_print) {
+ print__sub(&chunk, length);
+ }
+ free__sub(&chunk);
+
+ return 0;
+}
+
+
+int main(int argc, char *argv[])
+{
+ FILE *fd;
+ char header[15];
+ int rc = 0;
+ uint32_t crc;
+ uint32_t i32temp;
+ uint32_t length;
+ uint32_t chunk;
+ char *filename;
+ struct client_data *cc, *cc_tmp;
+
+ if(argc == 2){
+ filename = argv[1];
+ }else if(argc == 3 && !strcmp(argv[1], "--stats")){
+ stats = 1;
+ do_print = 0;
+ filename = argv[2];
+ }else if(argc == 3 && !strcmp(argv[1], "--client-stats")){
+ client_stats = 1;
+ do_print = 0;
+ filename = argv[2];
+ }else{
+ fprintf(stderr, "Usage: db_dump [--stats | --client-stats] \n");
+ return 1;
+ }
+ memset(&db, 0, sizeof(struct mosquitto_db));
+ fd = fopen(filename, "rb");
+ if(!fd){
+ fprintf(stderr, "Error: Unable to open %s\n", filename);
+ return 0;
+ }
+ read_e(fd, &header, 15);
+ if(!memcmp(header, magic, 15)){
+ if(do_print) printf("Mosquitto DB dump\n");
+ /* Restore DB as normal */
+ read_e(fd, &crc, sizeof(uint32_t));
+ if(do_print) printf("CRC: %d\n", crc);
+ read_e(fd, &i32temp, sizeof(uint32_t));
+ db_version = ntohl(i32temp);
+ if(do_print) printf("DB version: %d\n", db_version);
+
+ if(db_version > MOSQ_DB_VERSION){
+ if(do_print) printf("Warning: mosquitto_db_dump does not support this DB version, continuing but expecting errors.\n");
+ }
+
+ while(persist__chunk_header_read(fd, &chunk, &length) == MOSQ_ERR_SUCCESS){
+ switch(chunk){
+ case DB_CHUNK_CFG:
+ if(dump__cfg_chunk_process(fd, length)) return 1;
+ break;
+
+ case DB_CHUNK_MSG_STORE:
+ if(dump__msg_store_chunk_process(fd, length)) return 1;
+ break;
+
+ case DB_CHUNK_CLIENT_MSG:
+ if(dump__client_msg_chunk_process(fd, length)) return 1;
+ break;
+
+ case DB_CHUNK_RETAIN:
+ if(dump__retain_chunk_process(fd, length)) return 1;
+ break;
+
+ case DB_CHUNK_SUB:
+ if(dump__sub_chunk_process(fd, length)) return 1;
+ break;
+
+ case DB_CHUNK_CLIENT:
+ if(dump__client_chunk_process(fd, length)) return 1;
+ break;
+
+ default:
+ fprintf(stderr, "Warning: Unsupported chunk \"%d\" in persistent database file. Ignoring.\n", chunk);
+ if(fseek(fd, length, SEEK_CUR) < 0){
+ fprintf(stderr, "Error seeking in file.\n");
+ return 1;
+ }
+ break;
+ }
+ }
+ }else{
+ fprintf(stderr, "Error: Unrecognised file format.");
+ rc = 1;
+ }
+
+ fclose(fd);
+
+ if(stats){
+ printf("DB_CHUNK_CFG: %ld\n", cfg_count);
+ printf("DB_CHUNK_MSG_STORE: %ld\n", msg_store_count);
+ printf("DB_CHUNK_CLIENT_MSG: %ld\n", client_msg_count);
+ printf("DB_CHUNK_RETAIN: %ld\n", retain_count);
+ printf("DB_CHUNK_SUB: %ld\n", sub_count);
+ printf("DB_CHUNK_CLIENT: %ld\n", client_count);
+ }
+
+ if(client_stats){
+ HASH_ITER(hh_id, clients_by_id, cc, cc_tmp){
+ printf("SC: %d SS: %d MC: %d MS: %ld ", cc->subscriptions, cc->subscription_size, cc->messages, cc->message_size);
+ printf("%s\n", cc->id);
+ free(cc->id);
+ }
+ }
+
+ return rc;
+error:
+ fprintf(stderr, "Error: %s.", strerror(errno));
+ if(fd) fclose(fd);
+ return 1;
+}
+
diff -Nru mosquitto-1.6.9/apps/db_dump/db_dump.h mosquitto-2.0.15/apps/db_dump/db_dump.h
--- mosquitto-1.6.9/apps/db_dump/db_dump.h 1970-01-01 00:00:00.000000000 +0000
+++ mosquitto-2.0.15/apps/db_dump/db_dump.h 2022-08-16 13:34:02.000000000 +0000
@@ -0,0 +1,28 @@
+#ifndef DB_DUMP_H
+#define DB_DUMP_H
+/*
+Copyright (c) 2010-2019 Roger Light
+
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License 2.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
+
+The Eclipse Public License is available at
+ https://www.eclipse.org/legal/epl-2.0/
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
+
+SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+
+Contributors:
+ Roger Light - initial implementation and documentation.
+*/
+
+#include
+
+void print__client(struct P_client *chunk, uint32_t length);
+void print__client_msg(struct P_client_msg *chunk, uint32_t length);
+void print__msg_store(struct P_msg_store *chunk, uint32_t length);
+void print__sub(struct P_sub *chunk, uint32_t length);
+
+#endif
diff -Nru mosquitto-1.6.9/apps/db_dump/Makefile mosquitto-2.0.15/apps/db_dump/Makefile
--- mosquitto-1.6.9/apps/db_dump/Makefile 1970-01-01 00:00:00.000000000 +0000
+++ mosquitto-2.0.15/apps/db_dump/Makefile 2022-08-16 13:34:02.000000000 +0000
@@ -0,0 +1,88 @@
+include ../../config.mk
+
+CFLAGS_FINAL=${CFLAGS} -I../../include -I../../ -I../../lib -I../../src -I../../deps -DWITH_BROKER -DWITH_PERSISTENCE
+
+OBJS = \
+ db_dump.o \
+ print.o \
+ \
+ memory_mosq.o \
+ memory_public.o \
+ packet_datatypes.o \
+ packet_mosq.o \
+ persist_read.o \
+ persist_read_v234.o \
+ persist_read_v5.o \
+ property_mosq.o \
+ send_disconnect.o \
+ stubs.o \
+ time_mosq.o \
+ topic_tok.o \
+ utf8_mosq.o
+
+.PHONY: all clean reallyclean
+
+all : mosquitto_db_dump
+
+mosquitto_db_dump : ${OBJS}
+ ${CROSS_COMPILE}${CC} $^ -o $@ ${LDFLAGS} ${LIBS}
+
+db_dump.o : db_dump.c db_dump.h ../../src/persist.h
+ ${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@
+
+print.o : print.c db_dump.h ../../src/persist.h
+ ${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@
+
+memory_mosq.o : ../../lib/memory_mosq.c ../../lib/memory_mosq.h
+ ${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@
+
+memory_public.o : ../../src/memory_public.c
+ ${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@
+
+net_mosq.o : ../../lib/net_mosq.c ../../lib/net_mosq.h
+ ${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@
+
+packet_datatypes.o : ../../lib/packet_datatypes.c ../../lib/packet_mosq.h
+ ${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@
+
+packet_mosq.o : ../../lib/packet_mosq.c ../../lib/packet_mosq.h
+ ${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@
+
+persist_read.o : ../../src/persist_read.c ../../src/persist.h ../../src/mosquitto_broker_internal.h
+ ${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@
+
+persist_read_v234.o : ../../src/persist_read_v234.c ../../src/persist.h ../../src/mosquitto_broker_internal.h
+ ${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@
+
+persist_read_v5.o : ../../src/persist_read_v5.c ../../src/persist.h ../../src/mosquitto_broker_internal.h
+ ${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@
+
+property_mosq.o : ../../lib/property_mosq.c ../../lib/property_mosq.h
+ ${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@
+
+read_handle.o : ../../src/read_handle.c
+ ${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@
+
+stubs.o : stubs.c
+ ${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@
+
+send_disconnect.o : ../../lib/send_disconnect.c
+ ${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@
+
+time_mosq.o : ../../lib/time_mosq.c
+ ${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@
+
+topic_tok.o : ../../src/topic_tok.c
+ ${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@
+
+utf8_mosq.o : ../../lib/utf8_mosq.c
+ ${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@
+
+reallyclean: clean
+
+clean :
+ -rm -f *.o mosquitto_db_dump
+
+install:
+
+uninstall:
diff -Nru mosquitto-1.6.9/apps/db_dump/print.c mosquitto-2.0.15/apps/db_dump/print.c
--- mosquitto-1.6.9/apps/db_dump/print.c 1970-01-01 00:00:00.000000000 +0000
+++ mosquitto-2.0.15/apps/db_dump/print.c 2022-08-16 13:34:02.000000000 +0000
@@ -0,0 +1,218 @@
+/*
+Copyright (c) 2010-2019 Roger Light
+
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License 2.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
+
+The Eclipse Public License is available at
+ https://www.eclipse.org/legal/epl-2.0/
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
+
+SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+
+Contributors:
+ Roger Light - initial implementation and documentation.
+*/
+
+#include
+#include
+
+#include "db_dump.h"
+#include
+#include
+#include
+#include
+#include
+
+
+static void print__properties(mosquitto_property *properties)
+{
+ int i;
+
+ if(properties == NULL) return;
+
+ printf("\tProperties:\n");
+
+ while(properties){
+ switch(properties->identifier){
+ case MQTT_PROP_PAYLOAD_FORMAT_INDICATOR:
+ printf("\t\tPayload format indicator: %d\n", properties->value.i8);
+ break;
+ case MQTT_PROP_REQUEST_PROBLEM_INFORMATION:
+ printf("\t\tRequest problem information: %d\n", properties->value.i8);
+ break;
+ case MQTT_PROP_REQUEST_RESPONSE_INFORMATION:
+ printf("\t\tRequest response information: %d\n", properties->value.i8);
+ break;
+ case MQTT_PROP_MAXIMUM_QOS:
+ printf("\t\tMaximum QoS: %d\n", properties->value.i8);
+ break;
+ case MQTT_PROP_RETAIN_AVAILABLE:
+ printf("\t\tRetain available: %d\n", properties->value.i8);
+ break;
+ case MQTT_PROP_WILDCARD_SUB_AVAILABLE:
+ printf("\t\tWildcard sub available: %d\n", properties->value.i8);
+ break;
+ case MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE:
+ printf("\t\tSubscription ID available: %d\n", properties->value.i8);
+ break;
+ case MQTT_PROP_SHARED_SUB_AVAILABLE:
+ printf("\t\tShared subscription available: %d\n", properties->value.i8);
+ break;
+
+ case MQTT_PROP_SERVER_KEEP_ALIVE:
+ printf("\t\tServer keep alive: %d\n", properties->value.i16);
+ break;
+ case MQTT_PROP_RECEIVE_MAXIMUM:
+ printf("\t\tReceive maximum: %d\n", properties->value.i16);
+ break;
+ case MQTT_PROP_TOPIC_ALIAS_MAXIMUM:
+ printf("\t\tTopic alias maximum: %d\n", properties->value.i16);
+ break;
+ case MQTT_PROP_TOPIC_ALIAS:
+ printf("\t\tTopic alias: %d\n", properties->value.i16);
+ break;
+
+ case MQTT_PROP_MESSAGE_EXPIRY_INTERVAL:
+ printf("\t\tMessage expiry interval: %d\n", properties->value.i32);
+ break;
+ case MQTT_PROP_SESSION_EXPIRY_INTERVAL:
+ printf("\t\tSession expiry interval: %d\n", properties->value.i32);
+ break;
+ case MQTT_PROP_WILL_DELAY_INTERVAL:
+ printf("\t\tWill delay interval: %d\n", properties->value.i32);
+ break;
+ case MQTT_PROP_MAXIMUM_PACKET_SIZE:
+ printf("\t\tMaximum packet size: %d\n", properties->value.i32);
+ break;
+
+ case MQTT_PROP_SUBSCRIPTION_IDENTIFIER:
+ printf("\t\tSubscription identifier: %d\n", properties->value.varint);
+ break;
+
+ case MQTT_PROP_CONTENT_TYPE:
+ printf("\t\tContent type: %s\n", properties->value.s.v);
+ break;
+ case MQTT_PROP_RESPONSE_TOPIC:
+ printf("\t\tResponse topic: %s\n", properties->value.s.v);
+ break;
+ case MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER:
+ printf("\t\tAssigned client identifier: %s\n", properties->value.s.v);
+ break;
+ case MQTT_PROP_AUTHENTICATION_METHOD:
+ printf("\t\tAuthentication method: %s\n", properties->value.s.v);
+ break;
+ case MQTT_PROP_RESPONSE_INFORMATION:
+ printf("\t\tResponse information: %s\n", properties->value.s.v);
+ break;
+ case MQTT_PROP_SERVER_REFERENCE:
+ printf("\t\tServer reference: %s\n", properties->value.s.v);
+ break;
+ case MQTT_PROP_REASON_STRING:
+ printf("\t\tReason string: %s\n", properties->value.s.v);
+ break;
+
+ case MQTT_PROP_AUTHENTICATION_DATA:
+ printf("\t\tAuthentication data: ");
+ for(i=0; ivalue.bin.len; i++){
+ printf("%02X", properties->value.bin.v[i]);
+ }
+ printf("\n");
+ break;
+ case MQTT_PROP_CORRELATION_DATA:
+ printf("\t\tCorrelation data: ");
+ for(i=0; ivalue.bin.len; i++){
+ printf("%02X", properties->value.bin.v[i]);
+ }
+ printf("\n");
+ break;
+
+ case MQTT_PROP_USER_PROPERTY:
+ printf("\t\tUser property: %s , %s\n", properties->name.v, properties->value.s.v);
+ break;
+
+ default:
+ printf("\t\tInvalid property type: %d\n", properties->identifier);
+ break;
+ }
+
+ properties = properties->next;
+ }
+}
+
+
+void print__client(struct P_client *chunk, uint32_t length)
+{
+ printf("DB_CHUNK_CLIENT:\n");
+ printf("\tLength: %d\n", length);
+ printf("\tClient ID: %s\n", chunk->client_id);
+ if(chunk->username){
+ printf("\tUsername: %s\n", chunk->username);
+ }
+ if(chunk->F.listener_port > 0){
+ printf("\tListener port: %u\n", chunk->F.listener_port);
+ }
+ printf("\tLast MID: %d\n", chunk->F.last_mid);
+ printf("\tSession expiry time: %" PRIu64 "\n", chunk->F.session_expiry_time);
+ printf("\tSession expiry interval: %u\n", chunk->F.session_expiry_interval);
+}
+
+
+void print__client_msg(struct P_client_msg *chunk, uint32_t length)
+{
+ printf("DB_CHUNK_CLIENT_MSG:\n");
+ printf("\tLength: %d\n", length);
+ printf("\tClient ID: %s\n", chunk->client_id);
+ printf("\tStore ID: %" PRIu64 "\n", chunk->F.store_id);
+ printf("\tMID: %d\n", chunk->F.mid);
+ printf("\tQoS: %d\n", chunk->F.qos);
+ printf("\tRetain: %d\n", (chunk->F.retain_dup&0xF0)>>4);
+ printf("\tDirection: %d\n", chunk->F.direction);
+ printf("\tState: %d\n", chunk->F.state);
+ printf("\tDup: %d\n", chunk->F.retain_dup&0x0F);
+ print__properties(chunk->properties);
+}
+
+
+void print__msg_store(struct P_msg_store *chunk, uint32_t length)
+{
+ uint8_t *payload;
+
+ printf("DB_CHUNK_MSG_STORE:\n");
+ printf("\tLength: %d\n", length);
+ printf("\tStore ID: %" PRIu64 "\n", chunk->F.store_id);
+ /* printf("\tSource ID: %s\n", chunk->source_id); */
+ /* printf("\tSource Username: %s\n", chunk->source_username); */
+ printf("\tSource Port: %d\n", chunk->F.source_port);
+ printf("\tSource MID: %d\n", chunk->F.source_mid);
+ printf("\tTopic: %s\n", chunk->topic);
+ printf("\tQoS: %d\n", chunk->F.qos);
+ printf("\tRetain: %d\n", chunk->F.retain);
+ printf("\tPayload Length: %d\n", chunk->F.payloadlen);
+ printf("\tExpiry Time: %" PRIu64 "\n", chunk->F.expiry_time);
+
+ payload = chunk->payload;
+ if(chunk->F.payloadlen < 256){
+ /* Print payloads with UTF-8 data below an arbitrary limit of 256 bytes */
+ if(mosquitto_validate_utf8((char *)payload, (uint16_t)chunk->F.payloadlen) == MOSQ_ERR_SUCCESS){
+ printf("\tPayload: %s\n", payload);
+ }
+ }
+ print__properties(chunk->properties);
+}
+
+
+void print__sub(struct P_sub *chunk, uint32_t length)
+{
+ printf("DB_CHUNK_SUB:\n");
+ printf("\tLength: %u\n", length);
+ printf("\tClient ID: %s\n", chunk->client_id);
+ printf("\tTopic: %s\n", chunk->topic);
+ printf("\tQoS: %d\n", chunk->F.qos);
+ printf("\tSubscription ID: %d\n", chunk->F.identifier);
+ printf("\tOptions: 0x%02X\n", chunk->F.options);
+}
+
+
diff -Nru mosquitto-1.6.9/apps/db_dump/stubs.c mosquitto-2.0.15/apps/db_dump/stubs.c
--- mosquitto-1.6.9/apps/db_dump/stubs.c 1970-01-01 00:00:00.000000000 +0000
+++ mosquitto-2.0.15/apps/db_dump/stubs.c 2022-08-16 13:34:02.000000000 +0000
@@ -0,0 +1,148 @@
+#include
+#include
+
+#include "misc_mosq.h"
+#include "mosquitto_broker_internal.h"
+#include "mosquitto_internal.h"
+#include "util_mosq.h"
+
+#ifndef UNUSED
+# define UNUSED(A) (void)(A)
+#endif
+
+struct mosquitto *context__init(mosq_sock_t sock)
+{
+ UNUSED(sock);
+
+ return NULL;
+}
+
+void context__add_to_by_id(struct mosquitto *context)
+{
+ UNUSED(context);
+}
+
+int db__message_store(const struct mosquitto *source, struct mosquitto_msg_store *stored, uint32_t message_expiry_interval, dbid_t store_id, enum mosquitto_msg_origin origin)
+{
+ UNUSED(source);
+ UNUSED(stored);
+ UNUSED(message_expiry_interval);
+ UNUSED(store_id);
+ UNUSED(origin);
+ return 0;
+}
+
+void db__msg_store_ref_inc(struct mosquitto_msg_store *store)
+{
+ UNUSED(store);
+}
+
+int handle__packet(struct mosquitto *context)
+{
+ UNUSED(context);
+ return 0;
+}
+
+int log__printf(struct mosquitto *mosq, unsigned int level, const char *fmt, ...)
+{
+ UNUSED(mosq);
+ UNUSED(level);
+ UNUSED(fmt);
+ return 0;
+}
+
+FILE *mosquitto__fopen(const char *path, const char *mode, bool restrict_read)
+{
+ UNUSED(path);
+ UNUSED(mode);
+ UNUSED(restrict_read);
+ return NULL;
+}
+
+enum mosquitto_client_state mosquitto__get_state(struct mosquitto *mosq)
+{
+ UNUSED(mosq);
+ return mosq_cs_new;
+}
+
+int mux__add_out(struct mosquitto *mosq)
+{
+ UNUSED(mosq);
+ return 0;
+}
+
+int mux__remove_out(struct mosquitto *mosq)
+{
+ UNUSED(mosq);
+ return 0;
+}
+
+ssize_t net__read(struct mosquitto *mosq, void *buf, size_t count)
+{
+ UNUSED(mosq);
+ UNUSED(buf);
+ UNUSED(count);
+ return 0;
+}
+
+ssize_t net__write(struct mosquitto *mosq, const void *buf, size_t count)
+{
+ UNUSED(mosq);
+ UNUSED(buf);
+ UNUSED(count);
+ return 0;
+}
+
+int retain__store(const char *topic, struct mosquitto_msg_store *stored, char **split_topics)
+{
+ UNUSED(topic);
+ UNUSED(stored);
+ UNUSED(split_topics);
+ return 0;
+}
+
+int sub__add(struct mosquitto *context, const char *sub, uint8_t qos, uint32_t identifier, int options, struct mosquitto__subhier **root)
+{
+ UNUSED(context);
+ UNUSED(sub);
+ UNUSED(qos);
+ UNUSED(identifier);
+ UNUSED(options);
+ UNUSED(root);
+ return 0;
+}
+
+int sub__messages_queue(const char *source_id, const char *topic, uint8_t qos, int retain, struct mosquitto_msg_store **stored)
+{
+ UNUSED(source_id);
+ UNUSED(topic);
+ UNUSED(qos);
+ UNUSED(retain);
+ UNUSED(stored);
+ return 0;
+}
+
+int keepalive__update(struct mosquitto *context)
+{
+ UNUSED(context);
+ return 0;
+}
+
+void db__msg_add_to_inflight_stats(struct mosquitto_msg_data *msg_data, struct mosquitto_client_msg *msg)
+{
+ UNUSED(msg_data);
+ UNUSED(msg);
+}
+
+void db__msg_add_to_queued_stats(struct mosquitto_msg_data *msg_data, struct mosquitto_client_msg *msg)
+{
+ UNUSED(msg_data);
+ UNUSED(msg);
+}
+
+int session_expiry__add_from_persistence(struct mosquitto *context, time_t expiry_time)
+{
+ UNUSED(context);
+ UNUSED(expiry_time);
+ return 0;
+}
diff -Nru mosquitto-1.6.9/apps/Makefile mosquitto-2.0.15/apps/Makefile
--- mosquitto-1.6.9/apps/Makefile 1970-01-01 00:00:00.000000000 +0000
+++ mosquitto-2.0.15/apps/Makefile 2022-08-16 13:34:02.000000000 +0000
@@ -0,0 +1,28 @@
+DIRS= \
+ db_dump \
+ mosquitto_ctrl \
+ mosquitto_passwd
+
+.PHONY : all binary check clean reallyclean test install uninstall
+
+all :
+ set -e; for d in ${DIRS}; do $(MAKE) -C $${d}; done
+
+binary :
+ set -e; for d in ${DIRS}; do $(MAKE) -C $${d} $@; done
+
+clean :
+ set -e; for d in ${DIRS}; do $(MAKE) -C $${d} $@; done
+
+reallyclean :
+ set -e; for d in ${DIRS}; do $(MAKE) -C $${d} $@; done
+
+check : test
+test :
+ set -e; for d in ${DIRS}; do $(MAKE) -C $${d} $@; done
+
+install :
+ set -e; for d in ${DIRS}; do $(MAKE) -C $${d} $@; done
+
+uninstall :
+ set -e; for d in ${DIRS}; do $(MAKE) -C $${d} $@; done
diff -Nru mosquitto-1.6.9/apps/mosquitto_ctrl/client.c mosquitto-2.0.15/apps/mosquitto_ctrl/client.c
--- mosquitto-1.6.9/apps/mosquitto_ctrl/client.c 1970-01-01 00:00:00.000000000 +0000
+++ mosquitto-2.0.15/apps/mosquitto_ctrl/client.c 2022-08-16 13:34:02.000000000 +0000
@@ -0,0 +1,157 @@
+/*
+Copyright (c) 2020 Roger Light
+
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License 2.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
+
+The Eclipse Public License is available at
+ https://www.eclipse.org/legal/epl-2.0/
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
+
+SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+
+Contributors:
+ Roger Light - initial implementation and documentation.
+*/
+
+#include "config.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include "mosquitto_ctrl.h"
+
+static int run = 1;
+
+static void on_message(struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg, const mosquitto_property *properties)
+{
+ struct mosq_ctrl *ctrl = obj;
+
+ UNUSED(properties);
+
+ if(ctrl->payload_callback){
+ ctrl->payload_callback(ctrl, msg->payloadlen, msg->payload);
+ }
+
+ mosquitto_disconnect_v5(mosq, 0, NULL);
+ run = 0;
+}
+
+
+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;
+ mosquitto_disconnect_v5(mosq, 0, NULL);
+ }
+}
+
+
+static void on_subscribe(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos, const mosquitto_property *properties)
+{
+ struct mosq_ctrl *ctrl = obj;
+
+ UNUSED(mid);
+ UNUSED(properties);
+
+ if(qos_count == 1){
+ if(granted_qos[0] < 128){
+ /* Success */
+ mosquitto_publish(mosq, NULL, ctrl->request_topic, (int)strlen(ctrl->payload), ctrl->payload, ctrl->cfg.qos, 0);
+ free(ctrl->request_topic);
+ ctrl->request_topic = NULL;
+ free(ctrl->payload);
+ ctrl->payload = NULL;
+ }else{
+ if(ctrl->cfg.protocol_version == MQTT_PROTOCOL_V5){
+ fprintf(stderr, "Subscribe error: %s\n", mosquitto_reason_string(granted_qos[0]));
+ }else{
+ fprintf(stderr, "Subscribe error: Subscription refused.\n");
+ }
+ run = 0;
+ mosquitto_disconnect_v5(mosq, 0, NULL);
+ }
+ }else{
+ run = 0;
+ mosquitto_disconnect_v5(mosq, 0, NULL);
+ }
+}
+
+
+static void on_connect(struct mosquitto *mosq, void *obj, int reason_code, int flags, const mosquitto_property *properties)
+{
+ 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);
+ free(ctrl->response_topic);
+ ctrl->response_topic = NULL;
+ }
+ }else{
+ if(ctrl->cfg.protocol_version == MQTT_PROTOCOL_V5){
+ if(reason_code == MQTT_RC_UNSUPPORTED_PROTOCOL_VERSION){
+ fprintf(stderr, "Connection error: %s. Try connecting to an MQTT v5 broker, or use MQTT v3.x mode.\n", mosquitto_reason_string(reason_code));
+ }else{
+ fprintf(stderr, "Connection error: %s\n", mosquitto_reason_string(reason_code));
+ }
+ }else{
+ fprintf(stderr, "Connection error: %s\n", mosquitto_connack_string(reason_code));
+ }
+ run = 0;
+ mosquitto_disconnect_v5(mosq, 0, NULL);
+ }
+}
+
+
+int client_request_response(struct mosq_ctrl *ctrl)
+{
+ struct mosquitto *mosq;
+ int rc;
+ time_t start;
+
+ if(ctrl->cfg.cafile == NULL && ctrl->cfg.capath == NULL){
+ fprintf(stderr, "Warning: You are running mosquitto_ctrl without encryption.\nThis means all of the configuration changes you are making are visible on the network, including passwords.\n\n");
+ }
+
+ mosquitto_lib_init();
+
+ mosq = mosquitto_new(ctrl->cfg.id, true, ctrl);
+ rc = client_opts_set(mosq, &ctrl->cfg);
+ if(rc) goto cleanup;
+
+ mosquitto_connect_v5_callback_set(mosq, on_connect);
+ mosquitto_subscribe_v5_callback_set(mosq, on_subscribe);
+ mosquitto_publish_v5_callback_set(mosq, on_publish);
+ mosquitto_message_v5_callback_set(mosq, on_message);
+
+ rc = client_connect(mosq, &ctrl->cfg);
+ if(rc) goto cleanup;
+
+ start = time(NULL);
+ while(run && start+10 > time(NULL)){
+ mosquitto_loop(mosq, -1, 1);
+ }
+
+cleanup:
+ mosquitto_destroy(mosq);
+ mosquitto_lib_cleanup();
+ return rc;
+}
diff -Nru mosquitto-1.6.9/apps/mosquitto_ctrl/CMakeLists.txt mosquitto-2.0.15/apps/mosquitto_ctrl/CMakeLists.txt
--- mosquitto-1.6.9/apps/mosquitto_ctrl/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000
+++ mosquitto-2.0.15/apps/mosquitto_ctrl/CMakeLists.txt 2022-08-16 13:34:02.000000000 +0000
@@ -0,0 +1,48 @@
+if (WITH_TLS AND CJSON_FOUND)
+ add_definitions("-DWITH_CJSON")
+
+ include_directories(${mosquitto_SOURCE_DIR} ${mosquitto_SOURCE_DIR}/include
+ ${mosquitto_SOURCE_DIR}/lib ${mosquitto_SOURCE_DIR}/src
+ ${OPENSSL_INCLUDE_DIR} ${STDBOOL_H_PATH} ${STDINT_H_PATH}
+ ${CJSON_INCLUDE_DIRS} ${mosquitto_SOURCE_DIR}/apps/mosquitto_passwd)
+
+ link_directories(${CJSON_DIR})
+
+ add_executable(mosquitto_ctrl
+ mosquitto_ctrl.c mosquitto_ctrl.h
+ client.c
+ dynsec.c
+ dynsec_client.c
+ dynsec_group.c
+ dynsec_role.c
+ ../mosquitto_passwd/get_password.c ../mosquitto_passwd/get_password.h
+ ../../lib/memory_mosq.c ../../lib/memory_mosq.h
+ ../../src/memory_public.c
+ options.c
+ ../../src/password_mosq.c ../../src/password_mosq.h
+ )
+
+ if (WITH_STATIC_LIBRARIES)
+ target_link_libraries(mosquitto_ctrl libmosquitto_static)
+ else()
+ target_link_libraries(mosquitto_ctrl libmosquitto)
+ endif()
+
+ if (UNIX)
+ if (APPLE)
+ target_link_libraries(mosquitto_ctrl dl)
+ elseif (${CMAKE_SYSTEM_NAME} MATCHES "OpenBSD")
+ #
+ elseif (${CMAKE_SYSTEM_NAME} MATCHES "NetBSD")
+ #
+ elseif(QNX)
+ #
+ else(APPLE)
+ target_link_libraries(mosquitto_ctrl dl)
+ endif (APPLE)
+ endif (UNIX)
+
+ target_link_libraries(mosquitto_ctrl ${OPENSSL_LIBRARIES} ${CJSON_LIBRARIES})
+
+ install(TARGETS mosquitto_ctrl RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
+endif (WITH_TLS AND CJSON_FOUND)
diff -Nru mosquitto-1.6.9/apps/mosquitto_ctrl/dynsec.c mosquitto-2.0.15/apps/mosquitto_ctrl/dynsec.c
--- mosquitto-1.6.9/apps/mosquitto_ctrl/dynsec.c 1970-01-01 00:00:00.000000000 +0000
+++ mosquitto-2.0.15/apps/mosquitto_ctrl/dynsec.c 2022-08-16 13:34:02.000000000 +0000
@@ -0,0 +1,897 @@
+/*
+Copyright (c) 2020 Roger Light
+
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License 2.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
+
+The Eclipse Public License is available at
+ https://www.eclipse.org/legal/epl-2.0/
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
+
+SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+
+Contributors:
+ Roger Light - initial implementation and documentation.
+*/
+#include "config.h"
+
+#include
+#include
+#include
+#include
+
+#ifndef WIN32
+# include
+#endif
+
+#include "mosquitto_ctrl.h"
+#include "mosquitto.h"
+#include "password_mosq.h"
+#include "get_password.h"
+
+void dynsec__print_usage(void)
+{
+ printf("\nDynamic Security module\n");
+ printf("=======================\n");
+ printf("\nInitialisation\n--------------\n");
+ printf("Create a new configuration file with an admin user:\n");
+ printf(" mosquitto_ctrl dynsec init [admin-password]\n");
+
+ printf("\nGeneral\n-------\n");
+ printf("Get ACL default access: getDefaultACLAccess\n");
+ printf("Set ACL default access: setDefaultACLAccess allow|deny\n");
+ printf("Get group for anonymous clients: getAnonymousGroup\n");
+ printf("Set group for anonymous clients: setAnonymousGroup \n");
+
+ printf("\nClients\n-------\n");
+ printf("Create a new client: createClient [-c clientid] [-p password]\n");
+ printf("Delete a client: deleteClient \n");
+ printf("Set a client password: setClientPassword [password]\n");
+ printf("Set a client id: setClientId [clientid]\n");
+ printf("Add a role to a client: addClientRole [priority]\n");
+ printf(" Higher priority (larger numerical value) roles are evaluated first.\n");
+ printf("Remove role from a client: removeClientRole \n");
+ printf("Get client information: getClient \n");
+ printf("List all clients: listClients [count [offset]]\n");
+ printf("Enable client: enableClient \n");
+ printf("Disable client: disableClient \n");
+
+ printf("\nGroups\n------\n");
+ printf("Create a new group: createGroup \n");
+ printf("Delete a group: deleteGroup \n");
+ printf("Add a role to a group: addGroupRole [priority]\n");
+ printf(" Higher priority (larger numerical value) roles are evaluated first.\n");
+ printf("Remove role from a group: removeGroupRole \n");
+ printf("Add client to a group: addGroupClient [priority]\n");
+ printf(" Priority sets the group priority for the given client only.\n");
+ printf(" Higher priority (larger numerical value) groups are evaluated first.\n");
+ printf("Remove client from a group: removeGroupClient \n");
+ printf("Get group information: getGroup \n");
+ printf("List all groups: listGroups [count [offset]]\n");
+
+ printf("\nRoles\n------\n");
+ printf("Create a new role: createRole \n");
+ printf("Delete a role: deleteRole \n");
+ printf("Add an ACL to a role: addRoleACL [priority]\n");
+ printf(" Higher priority (larger numerical value) ACLs are evaluated first.\n");
+ printf("Remove ACL from a role: removeRoleACL \n");
+ printf("Get role information: getRole \n");
+ printf("List all roles: listRoles [count [offset]]\n");
+ printf("\naclspec: allow|deny\n");
+ printf("acltype: publishClientSend|publishClientReceive\n");
+ printf(" |subscribeLiteral|subscribePattern\n");
+ printf(" |unsubscribeLiteral|unsubscribePattern\n");
+ printf("\nFor more information see:\n");
+ printf(" https://mosquitto.org/documentation/dynamic-security/\n\n");
+}
+
+cJSON *cJSON_AddIntToObject(cJSON * const object, const char * const name, int number)
+{
+ char buf[30];
+
+ snprintf(buf, sizeof(buf), "%d", number);
+ return cJSON_AddRawToObject(object, name, buf);
+}
+
+/* ################################################################
+ * #
+ * # Payload callback
+ * #
+ * ################################################################ */
+
+static void print_list(cJSON *j_response, const char *arrayname, const char *keyname)
+{
+ cJSON *j_data, *j_array, *j_elem, *j_name;
+
+ j_data = cJSON_GetObjectItem(j_response, "data");
+ if(j_data == NULL){
+ fprintf(stderr, "Error: Invalid response from server.\n");
+ return;
+ }
+
+ j_array = cJSON_GetObjectItem(j_data, arrayname);
+ if(j_array == NULL || !cJSON_IsArray(j_array)){
+ fprintf(stderr, "Error: Invalid response from server.\n");
+ return;
+ }
+
+ cJSON_ArrayForEach(j_elem, j_array){
+ if(cJSON_IsObject(j_elem)){
+ j_name = cJSON_GetObjectItem(j_elem, keyname);
+ if(j_name && cJSON_IsString(j_name)){
+ printf("%s\n", j_name->valuestring);
+ }
+ }else if(cJSON_IsString(j_elem)){
+ printf("%s\n", j_elem->valuestring);
+ }
+ }
+}
+
+
+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;
+ bool first;
+
+ j_data = cJSON_GetObjectItem(j_response, "data");
+ if(j_data == NULL || !cJSON_IsObject(j_data)){
+ fprintf(stderr, "Error: Invalid response from server.\n");
+ return;
+ }
+
+ j_client = cJSON_GetObjectItem(j_data, "client");
+ if(j_client == NULL || !cJSON_IsObject(j_client)){
+ fprintf(stderr, "Error: Invalid response from server.\n");
+ return;
+ }
+
+ jtmp = cJSON_GetObjectItem(j_client, "username");
+ if(jtmp == NULL || !cJSON_IsString(jtmp)){
+ fprintf(stderr, "Error: Invalid response from server.\n");
+ return;
+ }
+ printf("Username: %s\n", jtmp->valuestring);
+
+ jtmp = cJSON_GetObjectItem(j_client, "clientid");
+ if(jtmp && cJSON_IsString(jtmp)){
+ printf("Clientid: %s\n", jtmp->valuestring);
+ }else{
+ printf("Clientid:\n");
+ }
+
+ jtmp = cJSON_GetObjectItem(j_client, "disabled");
+ if(jtmp && cJSON_IsBool(jtmp)){
+ printf("Disabled: %s\n", cJSON_IsTrue(jtmp)?"true":"false");
+ }
+
+ j_array = cJSON_GetObjectItem(j_client, "roles");
+ print_roles(j_array, strlen("Username:"));
+
+ j_array = cJSON_GetObjectItem(j_client, "groups");
+ 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){
+ printf("Groups: %s", jtmp->valuestring);
+ first = false;
+ }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("Groups:\n");
+ }
+}
+
+
+static void print_group(cJSON *j_response)
+{
+ cJSON *j_data, *j_group, *j_array, *j_elem, *jtmp;
+ bool first;
+
+ j_data = cJSON_GetObjectItem(j_response, "data");
+ if(j_data == NULL || !cJSON_IsObject(j_data)){
+ fprintf(stderr, "Error: Invalid response from server.\n");
+ return;
+ }
+
+ j_group = cJSON_GetObjectItem(j_data, "group");
+ if(j_group == NULL || !cJSON_IsObject(j_group)){
+ fprintf(stderr, "Error: Invalid response from server.\n");
+ return;
+ }
+
+ jtmp = cJSON_GetObjectItem(j_group, "groupname");
+ if(jtmp == NULL || !cJSON_IsString(jtmp)){
+ fprintf(stderr, "Error: Invalid response from server.\n");
+ return;
+ }
+ printf("Groupname: %s\n", jtmp->valuestring);
+
+ j_array = cJSON_GetObjectItem(j_group, "roles");
+ print_roles(j_array, strlen("Groupname:"));
+
+ j_array = cJSON_GetObjectItem(j_group, "clients");
+ if(j_array && cJSON_IsArray(j_array)){
+ first = true;
+ cJSON_ArrayForEach(j_elem, j_array){
+ jtmp = cJSON_GetObjectItem(j_elem, "username");
+ if(jtmp && cJSON_IsString(jtmp)){
+ if(first){
+ first = false;
+ printf("Clients: %s\n", jtmp->valuestring);
+ }else{
+ printf(" %s\n", jtmp->valuestring);
+ }
+ }
+ }
+ }
+}
+
+
+static void print_role(cJSON *j_response)
+{
+ cJSON *j_data, *j_role, *j_array, *j_elem, *jtmp;
+ bool first;
+
+ j_data = cJSON_GetObjectItem(j_response, "data");
+ if(j_data == NULL || !cJSON_IsObject(j_data)){
+ fprintf(stderr, "Error: Invalid response from server.\n");
+ return;
+ }
+
+ j_role = cJSON_GetObjectItem(j_data, "role");
+ if(j_role == NULL || !cJSON_IsObject(j_role)){
+ fprintf(stderr, "Error: Invalid response from server.\n");
+ return;
+ }
+
+ jtmp = cJSON_GetObjectItem(j_role, "rolename");
+ if(jtmp == NULL || !cJSON_IsString(jtmp)){
+ fprintf(stderr, "Error: Invalid response from server.\n");
+ return;
+ }
+ printf("Rolename: %s\n", jtmp->valuestring);
+
+ j_array = cJSON_GetObjectItem(j_role, "acls");
+ if(j_array && cJSON_IsArray(j_array)){
+ first = true;
+ cJSON_ArrayForEach(j_elem, j_array){
+ jtmp = cJSON_GetObjectItem(j_elem, "acltype");
+ if(jtmp && cJSON_IsString(jtmp)){
+ if(first){
+ first = false;
+ printf("ACLs: %-20s", jtmp->valuestring);
+ }else{
+ printf(" %-20s", jtmp->valuestring);
+ }
+
+ jtmp = cJSON_GetObjectItem(j_elem, "allow");
+ if(jtmp && cJSON_IsBool(jtmp)){
+ printf(" : %s", cJSON_IsTrue(jtmp)?"allow":"deny ");
+ }
+ jtmp = cJSON_GetObjectItem(j_elem, "topic");
+ if(jtmp && cJSON_IsString(jtmp)){
+ 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");
+ }
+ }
+ }
+}
+
+
+static void print_anonymous_group(cJSON *j_response)
+{
+ cJSON *j_data, *j_group, *j_groupname;
+
+ j_data = cJSON_GetObjectItem(j_response, "data");
+ if(j_data == NULL || !cJSON_IsObject(j_data)){
+ fprintf(stderr, "Error: Invalid response from server.\n");
+ return;
+ }
+
+ j_group = cJSON_GetObjectItem(j_data, "group");
+ if(j_group == NULL || !cJSON_IsObject(j_group)){
+ fprintf(stderr, "Error: Invalid response from server.\n");
+ return;
+ }
+
+ j_groupname = cJSON_GetObjectItem(j_group, "groupname");
+ if(j_groupname == NULL || !cJSON_IsString(j_groupname)){
+ fprintf(stderr, "Error: Invalid response from server.\n");
+ return;
+ }
+ printf("%s\n", j_groupname->valuestring);
+}
+
+static void print_default_acl_access(cJSON *j_response)
+{
+ cJSON *j_data, *j_acls, *j_acl, *j_acltype, *j_allow;
+
+ j_data = cJSON_GetObjectItem(j_response, "data");
+ if(j_data == NULL || !cJSON_IsObject(j_data)){
+ fprintf(stderr, "Error: Invalid response from server.\n");
+ return;
+ }
+
+ j_acls = cJSON_GetObjectItem(j_data, "acls");
+ if(j_acls == NULL || !cJSON_IsArray(j_acls)){
+ fprintf(stderr, "Error: Invalid response from server.\n");
+ return;
+ }
+
+ cJSON_ArrayForEach(j_acl, j_acls){
+ j_acltype = cJSON_GetObjectItem(j_acl, "acltype");
+ j_allow = cJSON_GetObjectItem(j_acl, "allow");
+
+ if(j_acltype == NULL || !cJSON_IsString(j_acltype)
+ || j_allow == NULL || !cJSON_IsBool(j_allow)
+ ){
+
+ fprintf(stderr, "Error: Invalid response from server.\n");
+ return;
+ }
+ printf("%-20s : %s\n", j_acltype->valuestring, cJSON_IsTrue(j_allow)?"allow":"deny");
+ }
+}
+
+static void dynsec__payload_callback(struct mosq_ctrl *ctrl, long payloadlen, const void *payload)
+{
+ cJSON *tree, *j_responses, *j_response, *j_command, *j_error;
+
+ UNUSED(ctrl);
+
+#if CJSON_VERSION_FULL < 1007013
+ UNUSED(payloadlen);
+ tree = cJSON_Parse(payload);
+#else
+ tree = cJSON_ParseWithLength(payload, (size_t)payloadlen);
+#endif
+ if(tree == NULL){
+ fprintf(stderr, "Error: Payload not JSON.\n");
+ return;
+ }
+
+ j_responses = cJSON_GetObjectItem(tree, "responses");
+ if(j_responses == NULL || !cJSON_IsArray(j_responses)){
+ fprintf(stderr, "Error: Payload missing data.\n");
+ cJSON_Delete(tree);
+ return;
+ }
+
+ j_response = cJSON_GetArrayItem(j_responses, 0);
+ if(j_response == NULL){
+ fprintf(stderr, "Error: Payload missing data.\n");
+ cJSON_Delete(tree);
+ return;
+ }
+
+ j_command = cJSON_GetObjectItem(j_response, "command");
+ if(j_command == NULL){
+ fprintf(stderr, "Error: Payload missing data.\n");
+ cJSON_Delete(tree);
+ return;
+ }
+
+ j_error = cJSON_GetObjectItem(j_response, "error");
+ if(j_error){
+ fprintf(stderr, "%s: Error: %s\n", j_command->valuestring, j_error->valuestring);
+ }else{
+ if(!strcasecmp(j_command->valuestring, "listClients")){
+ print_list(j_response, "clients", "username");
+ }else if(!strcasecmp(j_command->valuestring, "listGroups")){
+ print_list(j_response, "groups", "groupname");
+ }else if(!strcasecmp(j_command->valuestring, "listRoles")){
+ print_list(j_response, "roles", "rolename");
+ }else if(!strcasecmp(j_command->valuestring, "getClient")){
+ print_client(j_response);
+ }else if(!strcasecmp(j_command->valuestring, "getGroup")){
+ print_group(j_response);
+ }else if(!strcasecmp(j_command->valuestring, "getRole")){
+ print_role(j_response);
+ }else if(!strcasecmp(j_command->valuestring, "getDefaultACLAccess")){
+ print_default_acl_access(j_response);
+ }else if(!strcasecmp(j_command->valuestring, "getAnonymousGroup")){
+ print_anonymous_group(j_response);
+ }else{
+ /* fprintf(stderr, "%s: Success\n", j_command->valuestring); */
+ }
+ }
+ cJSON_Delete(tree);
+}
+
+/* ################################################################
+ * #
+ * # Default ACL access
+ * #
+ * ################################################################ */
+
+static int dynsec__set_default_acl_access(int argc, char *argv[], cJSON *j_command)
+{
+ char *acltype, *access;
+ bool b_access;
+ cJSON *j_acls, *j_acl;
+
+ if(argc == 2){
+ acltype = argv[0];
+ access = argv[1];
+ }else{
+ return MOSQ_ERR_INVAL;
+ }
+
+ if(strcasecmp(acltype, "publishClientSend")
+ && strcasecmp(acltype, "publishClientReceive")
+ && strcasecmp(acltype, "subscribe")
+ && strcasecmp(acltype, "unsubscribe")){
+
+ return MOSQ_ERR_INVAL;
+ }
+
+ if(!strcasecmp(access, "allow")){
+ b_access = true;
+ }else if(!strcasecmp(access, "deny")){
+ b_access = false;
+ }else{
+ fprintf(stderr, "Error: access must be \"allow\" or \"deny\".\n");
+ return MOSQ_ERR_INVAL;
+ }
+
+ if(cJSON_AddStringToObject(j_command, "command", "setDefaultACLAccess") == NULL
+ || (j_acls = cJSON_AddArrayToObject(j_command, "acls")) == NULL
+ ){
+
+ return MOSQ_ERR_NOMEM;
+ }
+
+ j_acl = cJSON_CreateObject();
+ if(j_acl == NULL){
+ return MOSQ_ERR_NOMEM;
+ }
+ cJSON_AddItemToArray(j_acls, j_acl);
+ if(cJSON_AddStringToObject(j_acl, "acltype", acltype) == NULL
+ || cJSON_AddBoolToObject(j_acl, "allow", b_access) == NULL
+ ){
+
+ return MOSQ_ERR_NOMEM;
+ }
+
+ return MOSQ_ERR_SUCCESS;
+}
+
+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
+ ){
+
+ return MOSQ_ERR_NOMEM;
+ }
+
+ return MOSQ_ERR_SUCCESS;
+}
+
+/* ################################################################
+ * #
+ * # Init
+ * #
+ * ################################################################ */
+
+static cJSON *init_add_acl_to_role(cJSON *j_acls, const char *type, const char *topic)
+{
+ cJSON *j_acl;
+
+ j_acl = cJSON_CreateObject();
+ if(j_acl == NULL) return NULL;
+
+ if(cJSON_AddStringToObject(j_acl, "acltype", type) == NULL
+ || cJSON_AddStringToObject(j_acl, "topic", topic) == NULL
+ || cJSON_AddBoolToObject(j_acl, "allow", true) == NULL
+ ){
+
+ cJSON_Delete(j_acl);
+ return NULL;
+ }
+ cJSON_AddItemToArray(j_acls, j_acl);
+ return j_acl;
+}
+
+static cJSON *init_add_role(const char *rolename)
+{
+ cJSON *j_role, *j_acls;
+
+ j_role = cJSON_CreateObject();
+ if(j_role == NULL){
+ return NULL;
+ }
+ if(cJSON_AddStringToObject(j_role, "rolename", rolename) == NULL){
+ cJSON_Delete(j_role);
+ return NULL;
+ }
+
+ j_acls = cJSON_CreateArray();
+ if(j_acls == NULL){
+ cJSON_Delete(j_role);
+ return NULL;
+ }
+ cJSON_AddItemToObject(j_role, "acls", j_acls);
+ if(init_add_acl_to_role(j_acls, "publishClientSend", "$CONTROL/dynamic-security/#") == NULL
+ || init_add_acl_to_role(j_acls, "publishClientReceive", "$CONTROL/dynamic-security/#") == NULL
+ || init_add_acl_to_role(j_acls, "subscribePattern", "$CONTROL/dynamic-security/#") == NULL
+ || init_add_acl_to_role(j_acls, "publishClientReceive", "$SYS/#") == NULL
+ || init_add_acl_to_role(j_acls, "subscribePattern", "$SYS/#") == NULL
+ || init_add_acl_to_role(j_acls, "publishClientReceive", "#") == NULL
+ || init_add_acl_to_role(j_acls, "subscribePattern", "#") == NULL
+ || init_add_acl_to_role(j_acls, "unsubscribePattern", "#") == NULL
+ ){
+
+ cJSON_Delete(j_role);
+ return NULL;
+ }
+ return j_role;
+}
+
+static cJSON *init_add_client(const char *username, const char *password, const char *rolename)
+{
+ cJSON *j_client, *j_roles, *j_role;
+ struct mosquitto_pw pw;
+ char *salt64 = NULL, *hash64 = NULL;
+ char buf[10];
+
+ memset(&pw, 0, sizeof(pw));
+ pw.hashtype = pw_sha512_pbkdf2;
+
+ if(pw__hash(password, &pw, true, PW_DEFAULT_ITERATIONS) != 0){
+ return NULL;
+ }
+ if(base64__encode(pw.salt, sizeof(pw.salt), &salt64)
+ || base64__encode(pw.password_hash, sizeof(pw.password_hash), &hash64)
+ ){
+
+ fprintf(stderr, "dynsec init: Internal error while encoding password.\n");
+ free(salt64);
+ free(hash64);
+ return NULL;
+ }
+
+ j_client = cJSON_CreateObject();
+ if(j_client == NULL){
+ free(salt64);
+ free(hash64);
+ return NULL;
+ }
+
+ snprintf(buf, sizeof(buf), "%d", PW_DEFAULT_ITERATIONS);
+ if(cJSON_AddStringToObject(j_client, "username", username) == NULL
+ || cJSON_AddStringToObject(j_client, "textName", "Dynsec admin user") == NULL
+ || cJSON_AddStringToObject(j_client, "password", hash64) == NULL
+ || cJSON_AddStringToObject(j_client, "salt", salt64) == NULL
+ || cJSON_AddRawToObject(j_client, "iterations", buf) == NULL
+ ){
+
+ free(salt64);
+ free(hash64);
+ cJSON_Delete(j_client);
+ return NULL;
+ }
+ free(salt64);
+ free(hash64);
+
+ j_roles = cJSON_CreateArray();
+ if(j_roles == NULL){
+ cJSON_Delete(j_client);
+ return NULL;
+ }
+ cJSON_AddItemToObject(j_client, "roles", j_roles);
+
+ j_role = cJSON_CreateObject();
+ if(j_role == NULL){
+ cJSON_Delete(j_client);
+ return NULL;
+ }
+ cJSON_AddItemToArray(j_roles, j_role);
+ if(cJSON_AddStringToObject(j_role, "rolename", rolename) == NULL){
+ cJSON_Delete(j_client);
+ return NULL;
+ }
+
+ return j_client;
+}
+
+static cJSON *init_create(const char *username, const char *password, const char *rolename)
+{
+ cJSON *tree, *j_clients, *j_client, *j_roles, *j_role;
+ cJSON *j_default_access;
+
+ tree = cJSON_CreateObject();
+ if(tree == NULL) return NULL;
+
+ if((j_clients = cJSON_AddArrayToObject(tree, "clients")) == NULL
+ || (j_roles = cJSON_AddArrayToObject(tree, "roles")) == NULL
+ || (j_default_access = cJSON_AddObjectToObject(tree, "defaultACLAccess")) == NULL
+ ){
+
+ cJSON_Delete(tree);
+ return NULL;
+ }
+
+ /* Set default behaviour:
+ * * Client can not publish to the broker by default.
+ * * Broker *CAN* publish to the client by default.
+ * * Client con not subscribe to topics by default.
+ * * Client *CAN* unsubscribe from topics by default.
+ */
+ if(cJSON_AddBoolToObject(j_default_access, "publishClientSend", false) == NULL
+ || cJSON_AddBoolToObject(j_default_access, "publishClientReceive", true) == NULL
+ || cJSON_AddBoolToObject(j_default_access, "subscribe", false) == NULL
+ || cJSON_AddBoolToObject(j_default_access, "unsubscribe", true) == NULL
+ ){
+
+ cJSON_Delete(tree);
+ return NULL;
+ }
+
+ j_client = init_add_client(username, password, rolename);
+ if(j_client == NULL){
+ cJSON_Delete(tree);
+ return NULL;
+ }
+ cJSON_AddItemToArray(j_clients, j_client);
+
+ j_role = init_add_role(rolename);
+ if(j_role == NULL){
+ cJSON_Delete(tree);
+ return NULL;
+ }
+ cJSON_AddItemToArray(j_roles, j_role);
+
+ return tree;
+}
+
+/* mosquitto_ctrl dynsec init [role-name] */
+static int dynsec_init(int argc, char *argv[])
+{
+ char *filename;
+ char *admin_user;
+ char *admin_password;
+ char *json_str;
+ cJSON *tree;
+ FILE *fptr;
+ char prompt[200], verify_prompt[200];
+ char password[200];
+ int rc;
+
+ if(argc < 2){
+ fprintf(stderr, "dynsec init: Not enough arguments - filename, or admin-user missing.\n");
+ return MOSQ_ERR_INVAL;
+ }
+
+ if(argc > 3){
+ fprintf(stderr, "dynsec init: Too many arguments.\n");
+ return MOSQ_ERR_INVAL;
+ }
+
+ filename = argv[0];
+ admin_user = argv[1];
+
+ if(argc == 3){
+ admin_password = argv[2];
+ }else{
+ snprintf(prompt, sizeof(prompt), "New password for %s: ", admin_user);
+ snprintf(verify_prompt, sizeof(verify_prompt), "Reenter password for %s: ", admin_user);
+ rc = get_password(prompt, verify_prompt, false, password, sizeof(password));
+ if(rc){
+ mosquitto_lib_cleanup();
+ return -1;
+ }
+ admin_password = password;
+ }
+
+ fptr = fopen(filename, "rb");
+ if(fptr){
+ fclose(fptr);
+ fprintf(stderr, "dynsec init: '%s' already exists. Remove the file or use a different location..\n", filename);
+ return -1;
+ }
+
+ tree = init_create(admin_user, admin_password, "admin");
+ if(tree == NULL){
+ fprintf(stderr, "dynsec init: Out of memory.\n");
+ return MOSQ_ERR_NOMEM;
+ }
+ json_str = cJSON_Print(tree);
+ cJSON_Delete(tree);
+
+ fptr = fopen(filename, "wb");
+ if(fptr){
+ fprintf(fptr, "%s", json_str);
+ free(json_str);
+ fclose(fptr);
+ }else{
+ free(json_str);
+ fprintf(stderr, "dynsec init: Unable to open '%s' for writing.\n", filename);
+ return -1;
+ }
+
+ printf("The client '%s' has been created in the file '%s'.\n", admin_user, filename);
+ printf("This client is configured to allow you to administer the dynamic security plugin only.\n");
+ printf("It does not have access to publish messages to normal topics.\n");
+ printf("You should create your application clients to do that, for example:\n");
+ printf(" mosquitto_ctrl dynsec createClient \n");
+ printf(" mosquitto_ctrl dynsec createRole \n");
+ printf(" mosquitto_ctrl dynsec addRoleACL publishClientSend my/topic [priority]\n");
+ printf(" mosquitto_ctrl dynsec addClientRole [priority]\n");
+ printf("See https://mosquitto.org/documentation/dynamic-security/ for details of all commands.\n");
+
+ return -1; /* Suppress client connection */
+}
+
+/* ################################################################
+ * #
+ * # Main
+ * #
+ * ################################################################ */
+
+int dynsec__main(int argc, char *argv[], struct mosq_ctrl *ctrl)
+{
+ int rc = -1;
+ cJSON *j_tree;
+ cJSON *j_commands, *j_command;
+
+ if(!strcasecmp(argv[0], "help")){
+ dynsec__print_usage();
+ return -1;
+ }else if(!strcasecmp(argv[0], "init")){
+ return dynsec_init(argc-1, &argv[1]);
+ }
+
+ /* The remaining commands need a network connection and JSON command. */
+
+ ctrl->payload_callback = dynsec__payload_callback;
+ ctrl->request_topic = strdup("$CONTROL/dynamic-security/v1");
+ ctrl->response_topic = strdup("$CONTROL/dynamic-security/v1/response");
+ if(ctrl->request_topic == NULL || ctrl->response_topic == NULL){
+ return MOSQ_ERR_NOMEM;
+ }
+ j_tree = cJSON_CreateObject();
+ if(j_tree == NULL) return MOSQ_ERR_NOMEM;
+ j_commands = cJSON_AddArrayToObject(j_tree, "commands");
+ if(j_commands == NULL){
+ cJSON_Delete(j_tree);
+ j_tree = NULL;
+ return MOSQ_ERR_NOMEM;
+ }
+ j_command = cJSON_CreateObject();
+ if(j_command == NULL){
+ cJSON_Delete(j_tree);
+ j_tree = NULL;
+ return MOSQ_ERR_NOMEM;
+ }
+ cJSON_AddItemToArray(j_commands, j_command);
+
+ if(!strcasecmp(argv[0], "setDefaultACLAccess")){
+ rc = dynsec__set_default_acl_access(argc-1, &argv[1], j_command);
+ }else if(!strcasecmp(argv[0], "getDefaultACLAccess")){
+ rc = dynsec__get_default_acl_access(argc-1, &argv[1], j_command);
+
+ }else if(!strcasecmp(argv[0], "createClient")){
+ rc = dynsec_client__create(argc-1, &argv[1], j_command);
+ }else if(!strcasecmp(argv[0], "deleteClient")){
+ rc = dynsec_client__delete(argc-1, &argv[1], j_command);
+ }else if(!strcasecmp(argv[0], "getClient")){
+ rc = dynsec_client__get(argc-1, &argv[1], j_command);
+ }else if(!strcasecmp(argv[0], "listClients")){
+ rc = dynsec_client__list_all(argc-1, &argv[1], j_command);
+ }else if(!strcasecmp(argv[0], "setClientId")){
+ rc = dynsec_client__set_id(argc-1, &argv[1], j_command);
+ }else if(!strcasecmp(argv[0], "setClientPassword")){
+ rc = dynsec_client__set_password(argc-1, &argv[1], j_command);
+ }else if(!strcasecmp(argv[0], "addClientRole")){
+ rc = dynsec_client__add_remove_role(argc-1, &argv[1], j_command, argv[0]);
+ }else if(!strcasecmp(argv[0], "removeClientRole")){
+ rc = dynsec_client__add_remove_role(argc-1, &argv[1], j_command, argv[0]);
+ }else if(!strcasecmp(argv[0], "enableClient")){
+ rc = dynsec_client__enable_disable(argc-1, &argv[1], j_command, argv[0]);
+ }else if(!strcasecmp(argv[0], "disableClient")){
+ rc = dynsec_client__enable_disable(argc-1, &argv[1], j_command, argv[0]);
+
+ }else if(!strcasecmp(argv[0], "createGroup")){
+ rc = dynsec_group__create(argc-1, &argv[1], j_command);
+ }else if(!strcasecmp(argv[0], "deleteGroup")){
+ rc = dynsec_group__delete(argc-1, &argv[1], j_command);
+ }else if(!strcasecmp(argv[0], "getGroup")){
+ rc = dynsec_group__get(argc-1, &argv[1], j_command);
+ }else if(!strcasecmp(argv[0], "listGroups")){
+ rc = dynsec_group__list_all(argc-1, &argv[1], j_command);
+ }else if(!strcasecmp(argv[0], "addGroupRole")){
+ rc = dynsec_group__add_remove_role(argc-1, &argv[1], j_command, argv[0]);
+ }else if(!strcasecmp(argv[0], "removeGroupRole")){
+ rc = dynsec_group__add_remove_role(argc-1, &argv[1], j_command, argv[0]);
+ }else if(!strcasecmp(argv[0], "addGroupClient")){
+ rc = dynsec_group__add_remove_client(argc-1, &argv[1], j_command, argv[0]);
+ }else if(!strcasecmp(argv[0], "removeGroupClient")){
+ rc = dynsec_group__add_remove_client(argc-1, &argv[1], j_command, argv[0]);
+ }else if(!strcasecmp(argv[0], "setAnonymousGroup")){
+ rc = dynsec_group__set_anonymous(argc-1, &argv[1], j_command);
+ }else if(!strcasecmp(argv[0], "getAnonymousGroup")){
+ rc = dynsec_group__get_anonymous(argc-1, &argv[1], j_command);
+
+ }else if(!strcasecmp(argv[0], "createRole")){
+ rc = dynsec_role__create(argc-1, &argv[1], j_command);
+ }else if(!strcasecmp(argv[0], "deleteRole")){
+ rc = dynsec_role__delete(argc-1, &argv[1], j_command);
+ }else if(!strcasecmp(argv[0], "getRole")){
+ rc = dynsec_role__get(argc-1, &argv[1], j_command);
+ }else if(!strcasecmp(argv[0], "listRoles")){
+ rc = dynsec_role__list_all(argc-1, &argv[1], j_command);
+ }else if(!strcasecmp(argv[0], "addRoleACL")){
+ rc = dynsec_role__add_acl(argc-1, &argv[1], j_command);
+ }else if(!strcasecmp(argv[0], "removeRoleACL")){
+ rc = dynsec_role__remove_acl(argc-1, &argv[1], j_command);
+
+ }else{
+ fprintf(stderr, "Command '%s' not recognised.\n", argv[0]);
+ return MOSQ_ERR_UNKNOWN;
+ }
+
+ if(rc == MOSQ_ERR_SUCCESS){
+ ctrl->payload = cJSON_PrintUnformatted(j_tree);
+ cJSON_Delete(j_tree);
+ if(ctrl->payload == NULL){
+ fprintf(stderr, "Error: Out of memory.\n");
+ return MOSQ_ERR_NOMEM;
+ }
+ }
+ return rc;
+}
diff -Nru mosquitto-1.6.9/apps/mosquitto_ctrl/dynsec_client.c mosquitto-2.0.15/apps/mosquitto_ctrl/dynsec_client.c
--- mosquitto-1.6.9/apps/mosquitto_ctrl/dynsec_client.c 1970-01-01 00:00:00.000000000 +0000
+++ mosquitto-2.0.15/apps/mosquitto_ctrl/dynsec_client.c 2022-08-16 13:34:02.000000000 +0000
@@ -0,0 +1,259 @@
+/*
+Copyright (c) 2020 Roger Light
+
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License 2.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
+
+The Eclipse Public License is available at
+ https://www.eclipse.org/legal/epl-2.0/
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
+
+SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+
+Contributors:
+ Roger Light - initial implementation and documentation.
+*/
+#include
+#include
+#include
+#include
+
+#include "mosquitto.h"
+#include "mosquitto_ctrl.h"
+#include "get_password.h"
+#include "password_mosq.h"
+
+int dynsec_client__create(int argc, char *argv[], cJSON *j_command)
+{
+ char *username = NULL, *password = NULL, *clientid = NULL;
+ char prompt[200], verify_prompt[200];
+ char password_buf[200];
+ int rc;
+ int i;
+ bool request_password = true;
+
+ if(argc == 0){
+ return MOSQ_ERR_INVAL;
+ }
+ username = argv[0];
+
+ for(i=1; i 0 && cJSON_AddIntToObject(j_command, "count", count) == NULL)
+ || (offset > 0 && cJSON_AddIntToObject(j_command, "offset", offset) == NULL)
+ ){
+
+ return MOSQ_ERR_NOMEM;
+ }else{
+ return MOSQ_ERR_SUCCESS;
+ }
+}
diff -Nru mosquitto-1.6.9/apps/mosquitto_ctrl/dynsec_group.c mosquitto-2.0.15/apps/mosquitto_ctrl/dynsec_group.c
--- mosquitto-1.6.9/apps/mosquitto_ctrl/dynsec_group.c 1970-01-01 00:00:00.000000000 +0000
+++ mosquitto-2.0.15/apps/mosquitto_ctrl/dynsec_group.c 2022-08-16 13:34:02.000000000 +0000
@@ -0,0 +1,203 @@
+/*
+Copyright (c) 2020 Roger Light
+
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License 2.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
+
+The Eclipse Public License is available at
+ https://www.eclipse.org/legal/epl-2.0/
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
+
+SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+
+Contributors:
+ Roger Light - initial implementation and documentation.
+*/
+#include "config.h"
+
+#include
+#include
+#include
+#include
+
+#include "mosquitto.h"
+#include "mosquitto_ctrl.h"
+#include "password_mosq.h"
+
+int dynsec_group__create(int argc, char *argv[], cJSON *j_command)
+{
+ char *groupname = NULL;
+
+ if(argc == 1){
+ groupname = argv[0];
+ }else{
+ return MOSQ_ERR_INVAL;
+ }
+
+ if(cJSON_AddStringToObject(j_command, "command", "createGroup") == NULL
+ || cJSON_AddStringToObject(j_command, "groupname", groupname) == NULL
+ ){
+
+ return MOSQ_ERR_NOMEM;
+ }else{
+ return MOSQ_ERR_SUCCESS;
+ }
+}
+
+int dynsec_group__delete(int argc, char *argv[], cJSON *j_command)
+{
+ char *groupname = NULL;
+
+ if(argc == 1){
+ groupname = argv[0];
+ }else{
+ return MOSQ_ERR_INVAL;
+ }
+
+ if(cJSON_AddStringToObject(j_command, "command", "deleteGroup") == NULL
+ || cJSON_AddStringToObject(j_command, "groupname", groupname) == NULL
+ ){
+
+ return MOSQ_ERR_NOMEM;
+ }else{
+ return MOSQ_ERR_SUCCESS;
+ }
+}
+
+int dynsec_group__get_anonymous(int argc, char *argv[], cJSON *j_command)
+{
+ UNUSED(argc);
+ UNUSED(argv);
+
+ if(cJSON_AddStringToObject(j_command, "command", "getAnonymousGroup") == NULL
+ ){
+
+ return MOSQ_ERR_NOMEM;
+ }else{
+ return MOSQ_ERR_SUCCESS;
+ }
+}
+
+int dynsec_group__set_anonymous(int argc, char *argv[], cJSON *j_command)
+{
+ char *groupname = NULL;
+
+ if(argc == 1){
+ groupname = argv[0];
+ }else{
+ return MOSQ_ERR_INVAL;
+ }
+
+ if(cJSON_AddStringToObject(j_command, "command", "setAnonymousGroup") == NULL
+ || cJSON_AddStringToObject(j_command, "groupname", groupname) == NULL
+ ){
+
+ return MOSQ_ERR_NOMEM;
+ }else{
+ return MOSQ_ERR_SUCCESS;
+ }
+}
+
+int dynsec_group__get(int argc, char *argv[], cJSON *j_command)
+{
+ char *groupname = NULL;
+
+ if(argc == 1){
+ groupname = argv[0];
+ }else{
+ return MOSQ_ERR_INVAL;
+ }
+
+ if(cJSON_AddStringToObject(j_command, "command", "getGroup") == NULL
+ || cJSON_AddStringToObject(j_command, "groupname", groupname) == NULL
+ ){
+
+ return MOSQ_ERR_NOMEM;
+ }else{
+ return MOSQ_ERR_SUCCESS;
+ }
+}
+
+int dynsec_group__add_remove_role(int argc, char *argv[], cJSON *j_command, const char *command)
+{
+ char *groupname = NULL, *rolename = NULL;
+ int priority = -1;
+
+ if(argc == 2){
+ groupname = argv[0];
+ rolename = argv[1];
+ }else if(argc == 3){
+ groupname = argv[0];
+ rolename = argv[1];
+ priority = atoi(argv[2]);
+ }else{
+ return MOSQ_ERR_INVAL;
+ }
+
+ if(cJSON_AddStringToObject(j_command, "command", command) == NULL
+ || cJSON_AddStringToObject(j_command, "groupname", groupname) == NULL
+ || cJSON_AddStringToObject(j_command, "rolename", rolename) == NULL
+ || (priority != -1 && cJSON_AddIntToObject(j_command, "priority", priority) == NULL)
+ ){
+
+ return MOSQ_ERR_NOMEM;
+ }else{
+ return MOSQ_ERR_SUCCESS;
+ }
+}
+
+int dynsec_group__list_all(int argc, char *argv[], cJSON *j_command)
+{
+ int count = -1, offset = -1;
+
+ if(argc == 0){
+ /* All groups */
+ }else if(argc == 1){
+ count = atoi(argv[0]);
+ }else if(argc == 2){
+ count = atoi(argv[0]);
+ offset = atoi(argv[1]);
+ }else{
+ return MOSQ_ERR_INVAL;
+ }
+
+ if(cJSON_AddStringToObject(j_command, "command", "listGroups") == NULL
+ || (count > 0 && cJSON_AddIntToObject(j_command, "count", count) == NULL)
+ || (offset > 0 && cJSON_AddIntToObject(j_command, "offset", offset) == NULL)
+ ){
+
+ return MOSQ_ERR_NOMEM;
+ }else{
+ return MOSQ_ERR_SUCCESS;
+ }
+}
+
+int dynsec_group__add_remove_client(int argc, char *argv[], cJSON *j_command, const char *command)
+{
+ char *username, *groupname;
+ int priority = -1;
+
+ if(argc == 2){
+ groupname = argv[0];
+ username = argv[1];
+ }else if(argc == 3){
+ groupname = argv[0];
+ username = argv[1];
+ priority = atoi(argv[2]);
+ }else{
+ return MOSQ_ERR_INVAL;
+ }
+
+ if(cJSON_AddStringToObject(j_command, "command", command) == NULL
+ || cJSON_AddStringToObject(j_command, "username", username) == NULL
+ || cJSON_AddStringToObject(j_command, "groupname", groupname) == NULL
+ || (priority != -1 && cJSON_AddIntToObject(j_command, "priority", priority) == NULL)
+ ){
+
+ return MOSQ_ERR_NOMEM;
+ }else{
+ return MOSQ_ERR_SUCCESS;
+ }
+}
diff -Nru mosquitto-1.6.9/apps/mosquitto_ctrl/dynsec_role.c mosquitto-2.0.15/apps/mosquitto_ctrl/dynsec_role.c
--- mosquitto-1.6.9/apps/mosquitto_ctrl/dynsec_role.c 1970-01-01 00:00:00.000000000 +0000
+++ mosquitto-2.0.15/apps/mosquitto_ctrl/dynsec_role.c 2022-08-16 13:34:02.000000000 +0000
@@ -0,0 +1,203 @@
+/*
+Copyright (c) 2020 Roger Light
+
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License 2.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
+
+The Eclipse Public License is available at
+ https://www.eclipse.org/legal/epl-2.0/
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
+
+SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+
+Contributors:
+ Roger Light - initial implementation and documentation.
+*/
+#include "config.h"
+
+#include
+#include
+#include
+#include
+
+#ifndef WIN32
+# include
+#endif
+
+#include "mosquitto.h"
+#include "mosquitto_ctrl.h"
+#include "password_mosq.h"
+
+int dynsec_role__create(int argc, char *argv[], cJSON *j_command)
+{
+ char *rolename = NULL;
+
+ if(argc == 1){
+ rolename = argv[0];
+ }else{
+ return MOSQ_ERR_INVAL;
+ }
+
+ if(cJSON_AddStringToObject(j_command, "command", "createRole") == NULL
+ || cJSON_AddStringToObject(j_command, "rolename", rolename) == NULL
+ ){
+
+ return MOSQ_ERR_NOMEM;
+ }else{
+ return MOSQ_ERR_SUCCESS;
+ }
+}
+
+int dynsec_role__delete(int argc, char *argv[], cJSON *j_command)
+{
+ char *rolename = NULL;
+
+ if(argc == 1){
+ rolename = argv[0];
+ }else{
+ return MOSQ_ERR_INVAL;
+ }
+
+ if(cJSON_AddStringToObject(j_command, "command", "deleteRole") == NULL
+ || cJSON_AddStringToObject(j_command, "rolename", rolename) == NULL
+ ){
+
+ return MOSQ_ERR_NOMEM;
+ }else{
+ return MOSQ_ERR_SUCCESS;
+ }
+}
+
+int dynsec_role__get(int argc, char *argv[], cJSON *j_command)
+{
+ char *rolename = NULL;
+
+ if(argc == 1){
+ rolename = argv[0];
+ }else{
+ return MOSQ_ERR_INVAL;
+ }
+
+ if(cJSON_AddStringToObject(j_command, "command", "getRole") == NULL
+ || cJSON_AddStringToObject(j_command, "rolename", rolename) == NULL
+ ){
+
+ return MOSQ_ERR_NOMEM;
+ }else{
+ return MOSQ_ERR_SUCCESS;
+ }
+}
+
+int dynsec_role__list_all(int argc, char *argv[], cJSON *j_command)
+{
+ int count = -1, offset = -1;
+
+ if(argc == 0){
+ /* All roles */
+ }else if(argc == 1){
+ count = atoi(argv[0]);
+ }else if(argc == 2){
+ count = atoi(argv[0]);
+ offset = atoi(argv[1]);
+ }else{
+ return MOSQ_ERR_INVAL;
+ }
+
+ if(cJSON_AddStringToObject(j_command, "command", "listRoles") == NULL
+ || (count > 0 && cJSON_AddIntToObject(j_command, "count", count) == NULL)
+ || (offset > 0 && cJSON_AddIntToObject(j_command, "offset", offset) == NULL)
+ ){
+
+ return MOSQ_ERR_NOMEM;
+ }else{
+ return MOSQ_ERR_SUCCESS;
+ }
+}
+
+int dynsec_role__add_acl(int argc, char *argv[], cJSON *j_command)
+{
+ char *rolename, *acltype, *topic, *action;
+ bool allow;
+ int priority = -1;
+
+ if(argc == 5){
+ rolename = argv[0];
+ acltype = argv[1];
+ topic = argv[2];
+ action = argv[3];
+ priority = atoi(argv[4]);
+ }else if(argc == 4){
+ rolename = argv[0];
+ acltype = argv[1];
+ topic = argv[2];
+ action = argv[3];
+ }else{
+ return MOSQ_ERR_INVAL;
+ }
+
+ if(strcasecmp(acltype, "publishClientSend")
+ && strcasecmp(acltype, "publishClientReceive")
+ && strcasecmp(acltype, "subscribeLiteral")
+ && strcasecmp(acltype, "subscribePattern")
+ && strcasecmp(acltype, "unsubscribeLiteral")
+ && strcasecmp(acltype, "unsubscribePattern")){
+
+ return MOSQ_ERR_INVAL;
+ }
+ if(!strcasecmp(action, "allow")){
+ allow = true;
+ }else if(!strcasecmp(action, "deny")){
+ allow = false;
+ }else{
+ return MOSQ_ERR_INVAL;
+ }
+
+ if(cJSON_AddStringToObject(j_command, "command", "addRoleACL") == NULL
+ || cJSON_AddStringToObject(j_command, "rolename", rolename) == NULL
+ || cJSON_AddStringToObject(j_command, "acltype", acltype) == NULL
+ || cJSON_AddStringToObject(j_command, "topic", topic) == NULL
+ || cJSON_AddBoolToObject(j_command, "allow", allow) == NULL
+ || (priority != -1 && cJSON_AddIntToObject(j_command, "priority", priority) == NULL)
+ ){
+
+ return MOSQ_ERR_NOMEM;
+ }else{
+ return MOSQ_ERR_SUCCESS;
+ }
+}
+
+int dynsec_role__remove_acl(int argc, char *argv[], cJSON *j_command)
+{
+ char *rolename, *acltype, *topic;
+
+ if(argc == 3){
+ rolename = argv[0];
+ acltype = argv[1];
+ topic = argv[2];
+ }else{
+ return MOSQ_ERR_INVAL;
+ }
+
+ if(strcasecmp(acltype, "publishClientSend")
+ && strcasecmp(acltype, "publishClientReceive")
+ && strcasecmp(acltype, "subscribeLiteral")
+ && strcasecmp(acltype, "subscribePattern")
+ && strcasecmp(acltype, "unsubscribeLiteral")
+ && strcasecmp(acltype, "unsubscribePattern")){
+
+ return MOSQ_ERR_INVAL;
+ }
+
+ if(cJSON_AddStringToObject(j_command, "command", "removeRoleACL") == NULL
+ || cJSON_AddStringToObject(j_command, "rolename", rolename) == NULL
+ || cJSON_AddStringToObject(j_command, "acltype", acltype) == NULL
+ || cJSON_AddStringToObject(j_command, "topic", topic) == NULL
+ ){
+
+ return MOSQ_ERR_NOMEM;
+ }else{
+ return MOSQ_ERR_SUCCESS;
+ }
+}
diff -Nru mosquitto-1.6.9/apps/mosquitto_ctrl/example.c mosquitto-2.0.15/apps/mosquitto_ctrl/example.c
--- mosquitto-1.6.9/apps/mosquitto_ctrl/example.c 1970-01-01 00:00:00.000000000 +0000
+++ mosquitto-2.0.15/apps/mosquitto_ctrl/example.c 2022-08-16 13:34:02.000000000 +0000
@@ -0,0 +1,49 @@
+/*
+Copyright (c) 2020 Roger Light
+
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License 2.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
+
+The Eclipse Public License is available at
+ https://www.eclipse.org/legal/epl-2.0/
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
+
+SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+
+Contributors:
+ Roger Light - initial implementation and documentation.
+*/
+#include "config.h"
+
+#include
+#include
+#include
+#include
+
+#ifndef WIN32
+# include
+#endif
+
+#include "mosquitto_ctrl.h"
+
+void ctrl_help(void)
+{
+ printf("\nExample module\n");
+ printf("==============\n");
+ printf(" mosquitto_ctrl example help\n");
+}
+
+int ctrl_main(int argc, char *argv[], struct mosq_ctrl *ctrl)
+{
+ UNUSED(argc);
+ UNUSED(ctrl);
+
+ if(!strcasecmp(argv[0], "help")){
+ ctrl_help();
+ return -1;
+ }else{
+ return MOSQ_ERR_INVAL;
+ }
+}
diff -Nru mosquitto-1.6.9/apps/mosquitto_ctrl/Makefile mosquitto-2.0.15/apps/mosquitto_ctrl/Makefile
--- mosquitto-1.6.9/apps/mosquitto_ctrl/Makefile 1970-01-01 00:00:00.000000000 +0000
+++ mosquitto-2.0.15/apps/mosquitto_ctrl/Makefile 2022-08-16 13:34:02.000000000 +0000
@@ -0,0 +1,110 @@
+include ../../config.mk
+
+.PHONY: all install uninstall clean reallyclean
+
+ifeq ($(WITH_SHARED_LIBRARIES),yes)
+LIBMOSQ:=../../lib/libmosquitto.so.${SOVERSION}
+else
+ifeq ($(WITH_THREADING),yes)
+LIBMOSQ:=../../lib/libmosquitto.a -lpthread -lssl -lcrypto
+else
+LIBMOSQ:=../../lib/libmosquitto.a
+endif
+endif
+
+LOCAL_CPPFLAGS:=-I../mosquitto_passwd -DWITH_CJSON
+
+OBJS= mosquitto_ctrl.o \
+ client.o \
+ dynsec.o \
+ dynsec_client.o \
+ dynsec_group.o \
+ dynsec_role.o \
+ get_password.o \
+ memory_mosq.o \
+ memory_public.o \
+ options.o \
+ password_mosq.o
+
+EXAMPLE_OBJS= example.o
+
+ifeq ($(WITH_TLS),yes)
+ifeq ($(WITH_CJSON),yes)
+TARGET:=mosquitto_ctrl mosquitto_ctrl_example.so
+else
+TARGET:=
+endif
+
+else
+TARGET:=
+endif
+
+all : ${TARGET}
+
+mosquitto_ctrl : ${OBJS} ${LIBMOSQ}
+ ${CROSS_COMPILE}${CC} ${APP_LDFLAGS} $^ -o $@ $(PASSWD_LDADD) $(LOCAL_LDFLAGS) $(LIBMOSQ) -lcjson -ldl
+
+mosquitto_ctrl_example.so : ${EXAMPLE_OBJS}
+ $(CROSS_COMPILE)$(CC) $(PLUGIN_CPPFLAGS) $(PLUGIN_CFLAGS) $(PLUGIN_LDFLAGS) -shared $< -o $@
+
+mosquitto_ctrl.o : mosquitto_ctrl.c mosquitto_ctrl.h
+ ${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@
+
+client.o : client.c mosquitto_ctrl.h
+ ${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@
+
+dynsec.o : dynsec.c mosquitto_ctrl.h
+ ${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@
+
+dynsec_client.o : dynsec_client.c mosquitto_ctrl.h
+ ${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@
+
+dynsec_group.o : dynsec_group.c mosquitto_ctrl.h
+ ${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@
+
+dynsec_role.o : dynsec_role.c mosquitto_ctrl.h
+ ${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@
+
+example.o : example.c mosquitto_ctrl.h
+ ${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(PLUGIN_CPPFLAGS) $(PLUGIN_CFLAGS) -c $< -o $@
+
+get_password.o : ../mosquitto_passwd/get_password.c ../mosquitto_passwd/get_password.h
+ ${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@
+
+memory_mosq.o : ../../lib/memory_mosq.c
+ ${CROSS_COMPILE}${CC} $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@
+
+memory_public.o : ../../src/memory_public.c
+ ${CROSS_COMPILE}${CC} $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@
+
+options.o : options.c mosquitto_ctrl.h
+ ${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@
+
+misc_mosq.o : ../../lib/misc_mosq.c ../../lib/misc_mosq.h
+ ${CROSS_COMPILE}${CC} $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@
+
+password_mosq.o : ../../src/password_mosq.c ../../src/password_mosq.h
+ ${CROSS_COMPILE}${CC} $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@
+
+../../lib/libmosquitto.so.${SOVERSION} :
+ $(MAKE) -C ../../lib
+
+../../lib/libmosquitto.a :
+ $(MAKE) -C ../../lib libmosquitto.a
+
+install : all
+ifeq ($(WITH_TLS),yes)
+ifeq ($(WITH_CJSON),yes)
+ $(INSTALL) -d "${DESTDIR}$(prefix)/bin"
+ $(INSTALL) ${STRIP_OPTS} mosquitto_ctrl "${DESTDIR}${prefix}/bin/mosquitto_ctrl"
+endif
+endif
+
+uninstall :
+ -rm -f "${DESTDIR}${prefix}/bin/mosquitto_ctrl"
+
+clean :
+ -rm -f *.o mosquitto_ctrl *.gcda *.gcno *.so
+
+reallyclean : clean
+ -rm -rf *.orig *.db
diff -Nru mosquitto-1.6.9/apps/mosquitto_ctrl/mosquitto_ctrl.c mosquitto-2.0.15/apps/mosquitto_ctrl/mosquitto_ctrl.c
--- mosquitto-1.6.9/apps/mosquitto_ctrl/mosquitto_ctrl.c 1970-01-01 00:00:00.000000000 +0000
+++ mosquitto-2.0.15/apps/mosquitto_ctrl/mosquitto_ctrl.c 2022-08-16 13:34:02.000000000 +0000
@@ -0,0 +1,114 @@
+/*
+Copyright (c) 2020 Roger Light
+
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License 2.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
+
+The Eclipse Public License is available at
+ https://www.eclipse.org/legal/epl-2.0/
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
+
+SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+
+Contributors:
+ Roger Light - initial implementation and documentation.
+*/
+
+#include "config.h"
+
+#include
+#include
+#include
+#include
+#include
+
+#ifndef WIN32
+# include
+#endif
+
+#include "lib_load.h"
+#include "mosquitto.h"
+#include "mosquitto_ctrl.h"
+
+static void print_version(void)
+{
+ int major, minor, revision;
+
+ mosquitto_lib_version(&major, &minor, &revision);
+ printf("mosquitto_ctrl version %s running on libmosquitto %d.%d.%d.\n", VERSION, major, minor, revision);
+}
+
+static void print_usage(void)
+{
+ printf("mosquitto_ctrl is a tool for administering certain Mosquitto features.\n");
+ print_version();
+ printf("\nGeneral usage: mosquitto_ctrl \n");
+ printf("For module specific help use: mosquitto_ctrl help\n");
+ printf("\nModules available: dynsec\n");
+ printf("\nFor more information see:\n");
+ printf(" https://mosquitto.org/man/mosquitto_ctrl-1.html\n\n");
+}
+
+
+int main(int argc, char *argv[])
+{
+ struct mosq_ctrl ctrl;
+ int rc = MOSQ_ERR_SUCCESS;
+ FUNC_ctrl_main l_ctrl_main = NULL;
+ void *lib = NULL;
+ char lib_name[200];
+
+ if(argc == 1){
+ print_usage();
+ return 1;
+ }
+
+ memset(&ctrl, 0, sizeof(ctrl));
+ init_config(&ctrl.cfg);
+
+ /* Shift program name out of args */
+ argc--;
+ argv++;
+
+ ctrl_config_parse(&ctrl.cfg, &argc, &argv);
+
+ if(argc < 2){
+ print_usage();
+ return 1;
+ }
+
+ /* In built modules */
+ if(!strcasecmp(argv[0], "dynsec")){
+ l_ctrl_main = dynsec__main;
+ }else{
+ /* Attempt external module */
+ snprintf(lib_name, sizeof(lib_name), "mosquitto_ctrl_%s.so", argv[0]);
+ lib = LIB_LOAD(lib_name);
+ if(lib){
+ l_ctrl_main = (FUNC_ctrl_main)LIB_SYM(lib, "ctrl_main");
+ }
+ }
+ if(l_ctrl_main == NULL){
+ fprintf(stderr, "Error: Module '%s' not supported.\n", argv[0]);
+ rc = MOSQ_ERR_NOT_SUPPORTED;
+ }
+
+ if(l_ctrl_main){
+ rc = l_ctrl_main(argc-1, &argv[1], &ctrl);
+ if(rc < 0){
+ /* Usage print */
+ rc = 0;
+ }else if(rc == MOSQ_ERR_SUCCESS){
+ rc = client_request_response(&ctrl);
+ }else if(rc == MOSQ_ERR_UNKNOWN){
+ /* Message printed already */
+ }else{
+ fprintf(stderr, "Error: %s\n", mosquitto_strerror(rc));
+ }
+ }
+
+ client_config_cleanup(&ctrl.cfg);
+ return rc;
+}
diff -Nru mosquitto-1.6.9/apps/mosquitto_ctrl/mosquitto_ctrl.h mosquitto-2.0.15/apps/mosquitto_ctrl/mosquitto_ctrl.h
--- mosquitto-1.6.9/apps/mosquitto_ctrl/mosquitto_ctrl.h 1970-01-01 00:00:00.000000000 +0000
+++ mosquitto-2.0.15/apps/mosquitto_ctrl/mosquitto_ctrl.h 2022-08-16 13:34:02.000000000 +0000
@@ -0,0 +1,123 @@
+/*
+Copyright (c) 2020 Roger Light
+
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License 2.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
+
+The Eclipse Public License is available at
+ https://www.eclipse.org/legal/epl-2.0/
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
+
+SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+
+Contributors:
+ Roger Light - initial implementation and documentation.
+*/
+#ifndef MOSQUITTO_CTRL_H
+#define MOSQUITTO_CTRL_H
+
+#include
+#include
+
+#include "mosquitto.h"
+
+#define PORT_UNDEFINED -1
+#define PORT_UNIX 0
+
+struct mosq_config {
+ char *id;
+ int protocol_version;
+ int keepalive;
+ char *host;
+ int port;
+ int qos;
+ char *bind_address;
+ bool debug;
+ bool quiet;
+ char *username;
+ char *password;
+ char *options_file;
+#ifdef WITH_TLS
+ char *cafile;
+ char *capath;
+ char *certfile;
+ char *keyfile;
+ char *ciphers;
+ bool insecure;
+ char *tls_alpn;
+ char *tls_version;
+ char *tls_engine;
+ char *tls_engine_kpass_sha1;
+ char *keyform;
+# ifdef FINAL_WITH_TLS_PSK
+ char *psk;
+ char *psk_identity;
+# endif
+#endif
+ bool verbose; /* sub */
+ unsigned int timeout; /* sub */
+#ifdef WITH_SOCKS
+ char *socks5_host;
+ int socks5_port;
+ char *socks5_username;
+ char *socks5_password;
+#endif
+};
+
+struct mosq_ctrl {
+ struct mosq_config cfg;
+ char *request_topic;
+ char *response_topic;
+ char *payload;
+ void (*payload_callback)(struct mosq_ctrl *, long , const void *);
+ void *userdata;
+};
+
+typedef int (*FUNC_ctrl_main)(int argc, char *argv[], struct mosq_ctrl *ctrl);
+
+void init_config(struct mosq_config *cfg);
+int ctrl_config_parse(struct mosq_config *cfg, int *argc, char **argv[]);
+int client_config_load(struct mosq_config *cfg);
+void client_config_cleanup(struct mosq_config *cfg);
+
+int client_request_response(struct mosq_ctrl *ctrl);
+int client_opts_set(struct mosquitto *mosq, struct mosq_config *cfg);
+int client_connect(struct mosquitto *mosq, struct mosq_config *cfg);
+
+cJSON *cJSON_AddIntToObject(cJSON * const object, const char * const name, int number);
+
+void dynsec__print_usage(void);
+int dynsec__main(int argc, char *argv[], struct mosq_ctrl *ctrl);
+
+int dynsec_client__add_remove_role(int argc, char *argv[], cJSON *j_command, const char *command);
+int dynsec_client__create(int argc, char *argv[], cJSON *j_command);
+int dynsec_client__delete(int argc, char *argv[], cJSON *j_command);
+int dynsec_client__enable_disable(int argc, char *argv[], cJSON *j_command, const char *command);
+int dynsec_client__get(int argc, char *argv[], cJSON *j_command);
+int dynsec_client__list_all(int argc, char *argv[], cJSON *j_command);
+int dynsec_client__set_id(int argc, char *argv[], cJSON *j_command);
+int dynsec_client__set_password(int argc, char *argv[], cJSON *j_command);
+
+int dynsec_group__add_remove_client(int argc, char *argv[], cJSON *j_command, const char *command);
+int dynsec_group__add_remove_role(int argc, char *argv[], cJSON *j_command, const char *command);
+int dynsec_group__create(int argc, char *argv[], cJSON *j_command);
+int dynsec_group__delete(int argc, char *argv[], cJSON *j_command);
+int dynsec_group__get(int argc, char *argv[], cJSON *j_command);
+int dynsec_group__list_all(int argc, char *argv[], cJSON *j_command);
+int dynsec_group__set_anonymous(int argc, char *argv[], cJSON *j_command);
+int dynsec_group__get_anonymous(int argc, char *argv[], cJSON *j_command);
+
+int dynsec_role__create(int argc, char *argv[], cJSON *j_command);
+int dynsec_role__delete(int argc, char *argv[], cJSON *j_command);
+int dynsec_role__get(int argc, char *argv[], cJSON *j_command);
+int dynsec_role__list_all(int argc, char *argv[], cJSON *j_command);
+int dynsec_role__add_acl(int argc, char *argv[], cJSON *j_command);
+int dynsec_role__remove_acl(int argc, char *argv[], cJSON *j_command);
+
+/* Functions to implement as an external module: */
+void ctrl_help(void);
+int ctrl_main(int argc, char *argv[], struct mosq_ctrl *ctrl);
+
+#endif
diff -Nru mosquitto-1.6.9/apps/mosquitto_ctrl/options.c mosquitto-2.0.15/apps/mosquitto_ctrl/options.c
--- mosquitto-1.6.9/apps/mosquitto_ctrl/options.c 1970-01-01 00:00:00.000000000 +0000
+++ mosquitto-2.0.15/apps/mosquitto_ctrl/options.c 2022-08-16 13:34:02.000000000 +0000
@@ -0,0 +1,921 @@
+/*
+Copyright (c) 2014-2020 Roger Light
+
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License 2.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
+
+The Eclipse Public License is available at
+ https://www.eclipse.org/legal/epl-2.0/
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
+
+SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+
+Contributors:
+ Roger Light - initial implementation and documentation.
+*/
+
+#include "config.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#ifndef WIN32
+#include
+#include
+#else
+#include
+#include
+#define snprintf sprintf_s
+#define strncasecmp _strnicmp
+#endif
+
+#include
+#include
+#include "mosquitto_ctrl.h"
+#include "get_password.h"
+
+#ifdef WITH_SOCKS
+static int mosquitto__parse_socks_url(struct mosq_config *cfg, char *url);
+#endif
+static int client_config_line_proc(struct mosq_config *cfg, int *argc, char **argvp[]);
+
+
+void init_config(struct mosq_config *cfg)
+{
+ cfg->qos = 1;
+ cfg->port = PORT_UNDEFINED;
+ cfg->protocol_version = MQTT_PROTOCOL_V5;
+}
+
+void client_config_cleanup(struct mosq_config *cfg)
+{
+ free(cfg->id);
+ free(cfg->host);
+ free(cfg->bind_address);
+ free(cfg->username);
+ free(cfg->password);
+ free(cfg->options_file);
+#ifdef WITH_TLS
+ free(cfg->cafile);
+ free(cfg->capath);
+ free(cfg->certfile);
+ free(cfg->keyfile);
+ free(cfg->ciphers);
+ free(cfg->tls_alpn);
+ free(cfg->tls_version);
+ free(cfg->tls_engine);
+ free(cfg->tls_engine_kpass_sha1);
+ free(cfg->keyform);
+# ifdef FINAL_WITH_TLS_PSK
+ free(cfg->psk);
+ free(cfg->psk_identity);
+# endif
+#endif
+#ifdef WITH_SOCKS
+ free(cfg->socks5_host);
+ free(cfg->socks5_username);
+ free(cfg->socks5_password);
+#endif
+}
+
+int ctrl_config_parse(struct mosq_config *cfg, int *argc, char **argv[])
+{
+ int rc;
+
+ init_config(cfg);
+
+ /* Deal with real argc/argv */
+ rc = client_config_line_proc(cfg, argc, argv);
+ if(rc) return rc;
+
+ /* Load options from config file - this must be after `-o` has been processed */
+ rc = client_config_load(cfg);
+ if(rc) return rc;
+
+#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");
+ return 1;
+ }
+ if((cfg->keyform && !cfg->keyfile)){
+ fprintf(stderr, "Error: If keyform is set, keyfile must be also specified.\n");
+ return 1;
+ }
+ if((cfg->tls_engine_kpass_sha1 && (!cfg->keyform || !cfg->tls_engine))){
+ fprintf(stderr, "Error: when using tls-engine-kpass-sha1, both tls-engine and keyform must also be provided.\n");
+ return 1;
+ }
+#endif
+#ifdef FINAL_WITH_TLS_PSK
+ if((cfg->cafile || cfg->capath) && cfg->psk){
+ fprintf(stderr, "Error: Only one of --psk or --cafile/--capath may be used at once.\n");
+ return 1;
+ }
+ if(cfg->psk && !cfg->psk_identity){
+ fprintf(stderr, "Error: --psk-identity required if --psk used.\n");
+ return 1;
+ }
+#endif
+
+ if(!cfg->host){
+ cfg->host = strdup("localhost");
+ if(!cfg->host){
+ fprintf(stderr, "Error: Out of memory.\n");
+ return 1;
+ }
+ }
+
+ return MOSQ_ERR_SUCCESS;
+}
+
+/* Process a tokenised single line from a file or set of real argc/argv */
+static int client_config_line_proc(struct mosq_config *cfg, int *argc, char **argvp[])
+{
+ char **argv = *argvp;
+
+ while((*argc) && argv[0][0] == '-'){
+ if(!strcmp(argv[0], "-A")){
+ if((*argc) == 1){
+ fprintf(stderr, "Error: -A argument given but no address specified.\n\n");
+ return 1;
+ }else{
+ cfg->bind_address = strdup(argv[1]);
+ }
+ argv++;
+ (*argc)--;
+#ifdef WITH_TLS
+ }else if(!strcmp(argv[0], "--cafile")){
+ if((*argc) == 1){
+ fprintf(stderr, "Error: --cafile argument given but no file specified.\n\n");
+ return 1;
+ }else{
+ cfg->cafile = strdup(argv[1]);
+ }
+ argv++;
+ (*argc)--;
+ }else if(!strcmp(argv[0], "--capath")){
+ if((*argc) == 1){
+ fprintf(stderr, "Error: --capath argument given but no directory specified.\n\n");
+ return 1;
+ }else{
+ cfg->capath = strdup(argv[1]);
+ }
+ argv++;
+ (*argc)--;
+ }else if(!strcmp(argv[0], "--cert")){
+ if((*argc) == 1){
+ fprintf(stderr, "Error: --cert argument given but no file specified.\n\n");
+ return 1;
+ }else{
+ cfg->certfile = strdup(argv[1]);
+ }
+ argv++;
+ (*argc)--;
+ }else if(!strcmp(argv[0], "--ciphers")){
+ if((*argc) == 1){
+ fprintf(stderr, "Error: --ciphers argument given but no ciphers specified.\n\n");
+ return 1;
+ }else{
+ cfg->ciphers = strdup(argv[1]);
+ }
+ argv++;
+ (*argc)--;
+#endif
+ }else if(!strcmp(argv[0], "-d") || !strcmp(argv[0], "--debug")){
+ cfg->debug = true;
+ }else if(!strcmp(argv[0], "--help")){
+ return 2;
+ }else if(!strcmp(argv[0], "-h") || !strcmp(argv[0], "--host")){
+ if((*argc) == 1){
+ fprintf(stderr, "Error: -h argument given but no host specified.\n\n");
+ return 1;
+ }else{
+ cfg->host = strdup(argv[1]);
+ }
+ argv++;
+ (*argc)--;
+#ifdef WITH_TLS
+ }else if(!strcmp(argv[0], "--insecure")){
+ cfg->insecure = true;
+#endif
+ }else if(!strcmp(argv[0], "-i") || !strcmp(argv[0], "--id")){
+ if((*argc) == 1){
+ fprintf(stderr, "Error: -i argument given but no id specified.\n\n");
+ return 1;
+ }else{
+ cfg->id = strdup(argv[1]);
+ }
+ argv++;
+ (*argc)--;
+#ifdef WITH_TLS
+ }else if(!strcmp(argv[0], "--key")){
+ if((*argc) == 1){
+ fprintf(stderr, "Error: --key argument given but no file specified.\n\n");
+ return 1;
+ }else{
+ cfg->keyfile = strdup(argv[1]);
+ }
+ argv++;
+ (*argc)--;
+ }else if(!strcmp(argv[0], "--keyform")){
+ if((*argc) == 1){
+ fprintf(stderr, "Error: --keyform argument given but no keyform specified.\n\n");
+ return 1;
+ }else{
+ cfg->keyform = strdup(argv[1]);
+ }
+ argv++;
+ (*argc)--;
+#endif
+ }else if(!strcmp(argv[0], "-L") || !strcmp(argv[0], "--url")){
+ if((*argc) == 1){
+ fprintf(stderr, "Error: -L argument given but no URL specified.\n\n");
+ return 1;
+ } else {
+ char *url = argv[1];
+ char *topic;
+ char *tmp;
+
+ if(!strncasecmp(url, "mqtt://", 7)) {
+ url += 7;
+ cfg->port = 1883;
+ } else if(!strncasecmp(url, "mqtts://", 8)) {
+ url += 8;
+ cfg->port = 8883;
+ } else {
+ fprintf(stderr, "Error: unsupported URL scheme.\n\n");
+ return 1;
+ }
+ topic = strchr(url, '/');
+ if(!topic){
+ fprintf(stderr, "Error: Invalid URL for -L argument specified - topic missing.\n");
+ return 1;
+ }
+ *topic++ = 0;
+
+ tmp = strchr(url, '@');
+ if(tmp) {
+ *tmp++ = 0;
+ char *colon = strchr(url, ':');
+ if(colon) {
+ *colon = 0;
+ cfg->password = strdup(colon + 1);
+ }
+ cfg->username = strdup(url);
+ url = tmp;
+ }
+ cfg->host = url;
+
+ tmp = strchr(url, ':');
+ if(tmp) {
+ *tmp++ = 0;
+ cfg->port = atoi(tmp);
+ }
+ /* Now we've removed the port, time to get the host on the heap */
+ cfg->host = strdup(cfg->host);
+ }
+ argv++;
+ (*argc)--;
+ }else if(!strcmp(argv[0], "-o")){
+ if((*argc) == 1){
+ fprintf(stderr, "Error: -o argument given but no options file specified.\n\n");
+ return 1;
+ }else{
+ cfg->options_file = strdup(argv[1]);
+ }
+ argv++;
+ (*argc)--;
+ }else if(!strcmp(argv[0], "-p") || !strcmp(argv[0], "--port")){
+ if((*argc) == 1){
+ fprintf(stderr, "Error: -p argument given but no port specified.\n\n");
+ return 1;
+ }else{
+ cfg->port = atoi(argv[1]);
+ if(cfg->port<0 || cfg->port>65535){
+ fprintf(stderr, "Error: Invalid port given: %d\n", cfg->port);
+ return 1;
+ }
+ }
+ argv++;
+ (*argc)--;
+ }else if(!strcmp(argv[0], "-P") || !strcmp(argv[0], "--pw")){
+ if((*argc) == 1){
+ fprintf(stderr, "Error: -P argument given but no password specified.\n\n");
+ return 1;
+ }else{
+ cfg->password = strdup(argv[1]);
+ }
+ argv++;
+ (*argc)--;
+#ifdef WITH_SOCKS
+ }else if(!strcmp(argv[0], "--proxy")){
+ if((*argc) == 1){
+ fprintf(stderr, "Error: --proxy argument given but no proxy url specified.\n\n");
+ return 1;
+ }else{
+ if(mosquitto__parse_socks_url(cfg, argv[1])){
+ return 1;
+ }
+ }
+ argv++;
+ (*argc)--;
+#endif
+#ifdef FINAL_WITH_TLS_PSK
+ }else if(!strcmp(argv[0], "--psk")){
+ if((*argc) == 1){
+ fprintf(stderr, "Error: --psk argument given but no key specified.\n\n");
+ return 1;
+ }else{
+ cfg->psk = strdup(argv[1]);
+ }
+ argv++;
+ (*argc)--;
+ }else if(!strcmp(argv[0], "--psk-identity")){
+ if((*argc) == 1){
+ fprintf(stderr, "Error: --psk-identity argument given but no identity specified.\n\n");
+ return 1;
+ }else{
+ cfg->psk_identity = strdup(argv[1]);
+ }
+ argv++;
+ (*argc)--;
+#endif
+ }else if(!strcmp(argv[0], "-q") || !strcmp(argv[0], "--qos")){
+ if((*argc) == 1){
+ fprintf(stderr, "Error: -q argument given but no QoS specified.\n\n");
+ return 1;
+ }else{
+ cfg->qos = atoi(argv[1]);
+ if(cfg->qos<0 || cfg->qos>2){
+ fprintf(stderr, "Error: Invalid QoS given: %d\n", cfg->qos);
+ return 1;
+ }
+ }
+ argv++;
+ (*argc)--;
+ }else if(!strcmp(argv[0], "--quiet")){
+ cfg->quiet = true;
+#ifdef WITH_TLS
+ }else if(!strcmp(argv[0], "--tls-alpn")){
+ if((*argc) == 1){
+ fprintf(stderr, "Error: --tls-alpn argument given but no protocol specified.\n\n");
+ return 1;
+ }else{
+ cfg->tls_alpn = strdup(argv[1]);
+ }
+ argv++;
+ (*argc)--;
+ }else if(!strcmp(argv[0], "--tls-engine")){
+ if((*argc) == 1){
+ fprintf(stderr, "Error: --tls-engine argument given but no engine_id specified.\n\n");
+ return 1;
+ }else{
+ cfg->tls_engine = strdup(argv[1]);
+ }
+ argv++;
+ (*argc)--;
+ }else if(!strcmp(argv[0], "--tls-engine-kpass-sha1")){
+ if((*argc) == 1){
+ fprintf(stderr, "Error: --tls-engine-kpass-sha1 argument given but no kpass sha1 specified.\n\n");
+ return 1;
+ }else{
+ cfg->tls_engine_kpass_sha1 = strdup(argv[1]);
+ }
+ argv++;
+ (*argc)--;
+ }else if(!strcmp(argv[0], "--tls-version")){
+ if((*argc) == 1){
+ fprintf(stderr, "Error: --tls-version argument given but no version specified.\n\n");
+ return 1;
+ }else{
+ cfg->tls_version = strdup(argv[1]);
+ }
+ argv++;
+ (*argc)--;
+#endif
+ }else if(!strcmp(argv[0], "-u") || !strcmp(argv[0], "--username")){
+ if((*argc) == 1){
+ fprintf(stderr, "Error: -u argument given but no username specified.\n\n");
+ return 1;
+ }else{
+ cfg->username = strdup(argv[1]);
+ }
+ argv++;
+ (*argc)--;
+ }else if(!strcmp(argv[0], "--unix")){
+ if((*argc) == 1){
+ fprintf(stderr, "Error: --unix argument given but no socket path specified.\n\n");
+ return 1;
+ }else{
+ cfg->host = strdup(argv[1]);
+ cfg->port = 0;
+ }
+ argv++;
+ (*argc)--;
+ }else if(!strcmp(argv[0], "-V") || !strcmp(argv[0], "--protocol-version")){
+ if((*argc) == 1){
+ fprintf(stderr, "Error: --protocol-version argument given but no version specified.\n\n");
+ return 1;
+ }else{
+ if(!strcmp(argv[1], "mqttv31") || !strcmp(argv[1], "31")){
+ cfg->protocol_version = MQTT_PROTOCOL_V31;
+ }else if(!strcmp(argv[1], "mqttv311") || !strcmp(argv[1], "311")){
+ cfg->protocol_version = MQTT_PROTOCOL_V311;
+ }else if(!strcmp(argv[1], "mqttv5") || !strcmp(argv[1], "5")){
+ cfg->protocol_version = MQTT_PROTOCOL_V5;
+ }else{
+ fprintf(stderr, "Error: Invalid protocol version argument given.\n\n");
+ return 1;
+ }
+ }
+ argv++;
+ (*argc)--;
+ }else if(!strcmp(argv[0], "-v") || !strcmp(argv[0], "--verbose")){
+ cfg->verbose = 1;
+ }else if(!strcmp(argv[0], "--version")){
+ return 3;
+ }else{
+ goto unknown_option;
+ }
+ argv++;
+ (*argc)--;
+ }
+ *argvp = argv;
+
+ return MOSQ_ERR_SUCCESS;
+
+unknown_option:
+ fprintf(stderr, "Error: Unknown option '%s'.\n",argv[0]);
+ return 1;
+}
+
+static char *get_default_cfg_location(void)
+{
+ char *loc = NULL;
+ size_t len;
+#ifndef WIN32
+ char *env;
+#else
+ char env[1024];
+ int rc;
+#endif
+
+#ifndef WIN32
+ env = getenv("XDG_CONFIG_HOME");
+ if(env){
+ len = strlen(env) + strlen("/mosquitto_ctrl") + 1;
+ loc = malloc(len);
+ if(!loc){
+ fprintf(stderr, "Error: Out of memory.\n");
+ return NULL;
+ }
+ snprintf(loc, len, "%s/mosquitto_ctrl", env);
+ loc[len-1] = '\0';
+ }else{
+ env = getenv("HOME");
+ if(env){
+ len = strlen(env) + strlen("/.config/mosquitto_ctrl") + 1;
+ loc = malloc(len);
+ if(!loc){
+ fprintf(stderr, "Error: Out of memory.\n");
+ return NULL;
+ }
+ snprintf(loc, len, "%s/.config/mosquitto_ctrl", env);
+ loc[len-1] = '\0';
+ }
+ }
+
+#else
+ rc = GetEnvironmentVariable("USERPROFILE", env, 1024);
+ if(rc > 0 && rc < 1024){
+ len = strlen(env) + strlen("\\mosquitto_ctrl.conf") + 1;
+ loc = malloc(len);
+ if(!loc){
+ fprintf(stderr, "Error: Out of memory.\n");
+ return NULL;
+ }
+ snprintf(loc, len, "%s\\mosquitto_ctrl.conf", env);
+ loc[len-1] = '\0';
+ }
+#endif
+ return loc;
+}
+
+int client_config_load(struct mosq_config *cfg)
+{
+ int rc;
+ FILE *fptr = NULL;
+ char line[1024];
+ int count;
+ char **local_args, **args;
+ char *default_cfg;
+
+ if(cfg->options_file){
+ fptr = fopen(cfg->options_file, "rt");
+ }else{
+ default_cfg = get_default_cfg_location();
+ if(default_cfg){
+ fptr = fopen(default_cfg, "rt");
+ free(default_cfg);
+ }
+ }
+
+ if(fptr){
+ local_args = malloc(3*sizeof(char *));
+ if(local_args == NULL){
+ fprintf(stderr, "Error: Out of memory.\n");
+ fclose(fptr);
+ return 1;
+ }
+ while(fgets(line, sizeof(line), fptr)){
+ if(line[0] == '#') continue; /* Comments */
+
+ while(line[strlen(line)-1] == 10 || line[strlen(line)-1] == 13){
+ line[strlen(line)-1] = 0;
+ }
+ local_args[0] = strtok(line, " ");
+ if(local_args[0]){
+ local_args[1] = strtok(NULL, " ");
+ if(local_args[1]){
+ count = 2;
+ }else{
+ count = 1;
+ }
+ args = local_args;
+ rc = client_config_line_proc(cfg, &count, &args);
+ if(rc){
+ fclose(fptr);
+ free(local_args);
+ return rc;
+ }
+ }
+ }
+ fclose(fptr);
+ free(local_args);
+ }
+ return 0;
+}
+
+
+int client_opts_set(struct mosquitto *mosq, struct mosq_config *cfg)
+{
+ int rc;
+ char prompt[1000];
+ char password[1000];
+
+ mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, cfg->protocol_version);
+
+ if(cfg->username && cfg->password == NULL){
+ /* Ask for password */
+ snprintf(prompt, sizeof(prompt), "Password for %s: ", cfg->username);
+ rc = get_password(prompt, NULL, false, password, sizeof(password));
+ if(rc){
+ fprintf(stderr, "Error getting password.\n");
+ mosquitto_lib_cleanup();
+ return 1;
+ }
+ cfg->password = strdup(password);
+ if(cfg->password == NULL){
+ fprintf(stderr, "Error: Out of memory.\n");
+ mosquitto_lib_cleanup();
+ return 1;
+ }
+ }
+
+ if((cfg->username || cfg->password) && mosquitto_username_pw_set(mosq, cfg->username, cfg->password)){
+ fprintf(stderr, "Error: Problem setting username and/or password.\n");
+ mosquitto_lib_cleanup();
+ return 1;
+ }
+#ifdef WITH_TLS
+ if(cfg->cafile || cfg->capath){
+ rc = mosquitto_tls_set(mosq, cfg->cafile, cfg->capath, cfg->certfile, cfg->keyfile, NULL);
+ if(rc){
+ if(rc == MOSQ_ERR_INVAL){
+ fprintf(stderr, "Error: Problem setting TLS options: File not found.\n");
+ }else{
+ fprintf(stderr, "Error: Problem setting TLS options: %s.\n", mosquitto_strerror(rc));
+ }
+ mosquitto_lib_cleanup();
+ return 1;
+ }
+ }
+ if(cfg->insecure && mosquitto_tls_insecure_set(mosq, true)){
+ fprintf(stderr, "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)){
+ fprintf(stderr, "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)){
+ fprintf(stderr, "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)){
+ fprintf(stderr, "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)){
+ fprintf(stderr, "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)){
+ fprintf(stderr, "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)){
+ fprintf(stderr, "Error: Problem setting TLS options, check the options are valid.\n");
+ mosquitto_lib_cleanup();
+ return 1;
+ }
+#endif
+#ifdef WITH_SOCKS
+ if(cfg->socks5_host){
+ rc = mosquitto_socks5_set(mosq, cfg->socks5_host, cfg->socks5_port, cfg->socks5_username, cfg->socks5_password);
+ if(rc){
+ mosquitto_lib_cleanup();
+ return rc;
+ }
+ }
+#endif
+ return MOSQ_ERR_SUCCESS;
+}
+
+
+int client_connect(struct mosquitto *mosq, struct mosq_config *cfg)
+{
+#ifndef WIN32
+ char *err;
+#else
+ char err[1024];
+#endif
+ int rc;
+ int port;
+
+ if(cfg->port == PORT_UNDEFINED){
+#ifdef WITH_TLS
+ if(cfg->cafile || cfg->capath
+# ifdef FINAL_WITH_TLS_PSK
+ || cfg->psk
+# endif
+ ){
+ port = 8883;
+ }else
+#endif
+ {
+ port = 1883;
+ }
+ }else{
+ port = cfg->port;
+ }
+
+ rc = mosquitto_connect_bind_v5(mosq, cfg->host, port, 60, cfg->bind_address, NULL);
+ if(rc>0){
+ if(rc == MOSQ_ERR_ERRNO){
+#ifndef WIN32
+ err = strerror(errno);
+#else
+ 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));
+ }
+ mosquitto_lib_cleanup();
+ return rc;
+ }
+ return MOSQ_ERR_SUCCESS;
+}
+
+#ifdef WITH_SOCKS
+/* Convert %25 -> %, %3a, %3A -> :, %40 -> @ */
+static int mosquitto__urldecode(char *str)
+{
+ int i, j;
+ size_t len;
+ if(!str) return 0;
+
+ if(!strchr(str, '%')) return 0;
+
+ len = strlen(str);
+ for(i=0; i= len){
+ return 1;
+ }
+ if(str[i+1] == '2' && str[i+2] == '5'){
+ str[i] = '%';
+ len -= 2;
+ for(j=i+1; j start){
+ len = i-start;
+ if(host){
+ /* Have already seen a @ , so this must be of form
+ * socks5h://username[:password]@host:port */
+ port = malloc(len + 1);
+ if(!port){
+ fprintf(stderr, "Error: Out of memory.\n");
+ goto cleanup;
+ }
+ memcpy(port, &(str[start]), len);
+ port[len] = '\0';
+ }else if(username_or_host){
+ /* Haven't seen a @ before, so must be of form
+ * socks5h://host:port */
+ host = username_or_host;
+ username_or_host = NULL;
+ port = malloc(len + 1);
+ if(!port){
+ fprintf(stderr, "Error: Out of memory.\n");
+ goto cleanup;
+ }
+ memcpy(port, &(str[start]), len);
+ port[len] = '\0';
+ }else{
+ host = malloc(len + 1);
+ if(!host){
+ fprintf(stderr, "Error: Out of memory.\n");
+ goto cleanup;
+ }
+ memcpy(host, &(str[start]), len);
+ host[len] = '\0';
+ }
+ }
+
+ if(!host){
+ fprintf(stderr, "Error: Invalid proxy.\n");
+ goto cleanup;
+ }
+
+ if(mosquitto__urldecode(username)){
+ goto cleanup;
+ }
+ if(mosquitto__urldecode(password)){
+ goto cleanup;
+ }
+ if(port){
+ port_int = atoi(port);
+ if(port_int < 1 || port_int > 65535){
+ fprintf(stderr, "Error: Invalid proxy port %d\n", port_int);
+ goto cleanup;
+ }
+ free(port);
+ }else{
+ port_int = 1080;
+ }
+
+ cfg->socks5_username = username;
+ cfg->socks5_password = password;
+ cfg->socks5_host = host;
+ cfg->socks5_port = port_int;
+
+ return 0;
+cleanup:
+ free(username_or_host);
+ free(username);
+ free(password);
+ free(host);
+ free(port);
+ return 1;
+}
+#endif
diff -Nru mosquitto-1.6.9/apps/mosquitto_passwd/CMakeLists.txt mosquitto-2.0.15/apps/mosquitto_passwd/CMakeLists.txt
--- mosquitto-1.6.9/apps/mosquitto_passwd/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000
+++ mosquitto-2.0.15/apps/mosquitto_passwd/CMakeLists.txt 2022-08-16 13:34:02.000000000 +0000
@@ -0,0 +1,18 @@
+include_directories(${mosquitto_SOURCE_DIR} ${mosquitto_SOURCE_DIR}/include
+ ${mosquitto_SOURCE_DIR}/lib ${mosquitto_SOURCE_DIR}/src
+ ${OPENSSL_INCLUDE_DIR} ${STDBOOL_H_PATH} ${STDINT_H_PATH})
+
+if (WITH_TLS)
+ add_executable(mosquitto_passwd
+ mosquitto_passwd.c
+ get_password.c get_password.h
+ ../../lib/memory_mosq.c ../../lib/memory_mosq.h
+ ../../src/memory_public.c
+ ../../lib/misc_mosq.c
+ ../../src/password_mosq.c ../../src/password_mosq.h
+ )
+
+
+ target_link_libraries(mosquitto_passwd ${OPENSSL_LIBRARIES})
+ install(TARGETS mosquitto_passwd RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
+endif (WITH_TLS)
diff -Nru mosquitto-1.6.9/apps/mosquitto_passwd/get_password.c mosquitto-2.0.15/apps/mosquitto_passwd/get_password.c
--- mosquitto-1.6.9/apps/mosquitto_passwd/get_password.c 1970-01-01 00:00:00.000000000 +0000
+++ mosquitto-2.0.15/apps/mosquitto_passwd/get_password.c 2022-08-16 13:34:02.000000000 +0000
@@ -0,0 +1,146 @@
+/*
+Copyright (c) 2012-2020 Roger Light
+
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License 2.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
+
+The Eclipse Public License is available at
+ https://www.eclipse.org/legal/epl-2.0/
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
+
+SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+
+Contributors:
+ Roger Light - initial implementation and documentation.
+*/
+
+#include "config.h"
+
+#include
+#include
+#include
+#include
+
+#ifdef WIN32
+# include
+# include
+# define snprintf sprintf_s
+# include
+# include
+#else
+# include
+# include
+# include
+#endif
+
+#include "get_password.h"
+
+#define MAX_BUFFER_LEN 65500
+#define SALT_LEN 12
+
+void get_password__reset_term(void)
+{
+#ifndef WIN32
+ struct termios ts;
+
+ tcgetattr(0, &ts);
+ ts.c_lflag |= ECHO | ICANON;
+ tcsetattr(0, TCSANOW, &ts);
+#endif
+}
+
+
+static int gets_quiet(char *s, int len)
+{
+#ifdef WIN32
+ HANDLE h;
+ DWORD con_orig, con_quiet = 0;
+ DWORD read_len = 0;
+
+ memset(s, 0, len);
+ h = GetStdHandle(STD_INPUT_HANDLE);
+ GetConsoleMode(h, &con_orig);
+ con_quiet = con_orig;
+ con_quiet &= ~ENABLE_ECHO_INPUT;
+ con_quiet |= ENABLE_LINE_INPUT;
+ SetConsoleMode(h, con_quiet);
+ if(!ReadConsole(h, s, len, &read_len, NULL)){
+ SetConsoleMode(h, con_orig);
+ return 1;
+ }
+ while(s[strlen(s)-1] == 10 || s[strlen(s)-1] == 13){
+ s[strlen(s)-1] = 0;
+ }
+ if(strlen(s) == 0){
+ return 1;
+ }
+ SetConsoleMode(h, con_orig);
+
+ return 0;
+#else
+ struct termios ts_quiet, ts_orig;
+ char *rs;
+
+ memset(s, 0, (size_t)len);
+ tcgetattr(0, &ts_orig);
+ ts_quiet = ts_orig;
+ ts_quiet.c_lflag &= (unsigned int)(~(ECHO | ICANON));
+ tcsetattr(0, TCSANOW, &ts_quiet);
+
+ rs = fgets(s, len, stdin);
+ tcsetattr(0, TCSANOW, &ts_orig);
+
+ if(!rs){
+ return 1;
+ }else{
+ while(s[strlen(s)-1] == 10 || s[strlen(s)-1] == 13){
+ s[strlen(s)-1] = 0;
+ }
+ if(strlen(s) == 0){
+ return 1;
+ }
+ }
+ return 0;
+#endif
+}
+
+int get_password(const char *prompt, const char *verify_prompt, bool quiet, char *password, size_t len)
+{
+ char pw1[MAX_BUFFER_LEN], pw2[MAX_BUFFER_LEN];
+ size_t minLen;
+ minLen = len < MAX_BUFFER_LEN ? len : MAX_BUFFER_LEN;
+
+ printf("%s", prompt);
+ fflush(stdout);
+ if(gets_quiet(pw1, (int)minLen)){
+ if(!quiet){
+ fprintf(stderr, "Error: Empty password.\n");
+ }
+ return 1;
+ }
+ printf("\n");
+
+ if(verify_prompt){
+ printf("%s", verify_prompt);
+ fflush(stdout);
+ if(gets_quiet(pw2, (int)minLen)){
+ if(!quiet){
+ fprintf(stderr, "Error: Empty password.\n");
+ }
+ return 1;
+ }
+ printf("\n");
+
+ if(strcmp(pw1, pw2)){
+ if(!quiet){
+ fprintf(stderr, "Error: Passwords do not match.\n");
+ }
+ return 2;
+ }
+ }
+
+ strncpy(password, pw1, minLen);
+ return 0;
+}
diff -Nru mosquitto-1.6.9/apps/mosquitto_passwd/get_password.h mosquitto-2.0.15/apps/mosquitto_passwd/get_password.h
--- mosquitto-1.6.9/apps/mosquitto_passwd/get_password.h 1970-01-01 00:00:00.000000000 +0000
+++ mosquitto-2.0.15/apps/mosquitto_passwd/get_password.h 2022-08-16 13:34:02.000000000 +0000
@@ -0,0 +1,26 @@
+#ifndef GET_PASSWORD_H
+#define GET_PASSWORD_H
+/*
+Copyright (c) 2012-2020 Roger Light
+
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License 2.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
+
+The Eclipse Public License is available at
+ https://www.eclipse.org/legal/epl-2.0/
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
+
+SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+
+Contributors:
+ Roger Light - initial implementation and documentation.
+*/
+
+#include
+
+void get_password__reset_term(void);
+int get_password(const char *prompt, const char *verify_prompt, bool quiet, char *password, size_t len);
+
+#endif
diff -Nru mosquitto-1.6.9/apps/mosquitto_passwd/Makefile mosquitto-2.0.15/apps/mosquitto_passwd/Makefile
--- mosquitto-1.6.9/apps/mosquitto_passwd/Makefile 1970-01-01 00:00:00.000000000 +0000
+++ mosquitto-2.0.15/apps/mosquitto_passwd/Makefile 2022-08-16 13:34:02.000000000 +0000
@@ -0,0 +1,52 @@
+include ../../config.mk
+
+.PHONY: all install uninstall clean reallyclean
+
+OBJS= mosquitto_passwd.o \
+ get_password.o \
+ memory_mosq.o \
+ memory_public.o \
+ misc_mosq.o \
+ password_mosq.o
+
+ifeq ($(WITH_TLS),yes)
+all: mosquitto_passwd
+else
+all:
+endif
+
+mosquitto_passwd : ${OBJS}
+ ${CROSS_COMPILE}${CC} ${APP_LDFLAGS} $^ -o $@ $(PASSWD_LDADD)
+
+mosquitto_passwd.o : mosquitto_passwd.c
+ ${CROSS_COMPILE}${CC} $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@
+
+get_password.o : get_password.c
+ ${CROSS_COMPILE}${CC} $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@
+
+memory_mosq.o : ../../lib/memory_mosq.c
+ ${CROSS_COMPILE}${CC} $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@
+
+memory_public.o : ../../src/memory_public.c
+ ${CROSS_COMPILE}${CC} $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@
+
+misc_mosq.o : ../../lib/misc_mosq.c ../../lib/misc_mosq.h
+ ${CROSS_COMPILE}${CC} $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@
+
+password_mosq.o : ../../src/password_mosq.c ../../src/password_mosq.h
+ ${CROSS_COMPILE}${CC} $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@
+
+install : all
+ifeq ($(WITH_TLS),yes)
+ $(INSTALL) -d "${DESTDIR}$(prefix)/bin"
+ $(INSTALL) ${STRIP_OPTS} mosquitto_passwd "${DESTDIR}${prefix}/bin/mosquitto_passwd"
+endif
+
+uninstall :
+ -rm -f "${DESTDIR}${prefix}/bin/mosquitto_passwd"
+
+clean :
+ -rm -f *.o mosquitto_passwd *.gcda *.gcno
+
+reallyclean : clean
+ -rm -rf *.orig *.db
diff -Nru mosquitto-1.6.9/apps/mosquitto_passwd/mosquitto_passwd.c mosquitto-2.0.15/apps/mosquitto_passwd/mosquitto_passwd.c
--- mosquitto-1.6.9/apps/mosquitto_passwd/mosquitto_passwd.c 1970-01-01 00:00:00.000000000 +0000
+++ mosquitto-2.0.15/apps/mosquitto_passwd/mosquitto_passwd.c 2022-08-16 13:34:02.000000000 +0000
@@ -0,0 +1,690 @@
+/*
+Copyright (c) 2012-2020 Roger Light
+
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License 2.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
+
+The Eclipse Public License is available at
+ https://www.eclipse.org/legal/epl-2.0/
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
+
+SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+
+Contributors:
+ Roger Light - initial implementation and documentation.
+*/
+
+#include "config.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "get_password.h"
+#include "password_mosq.h"
+
+#ifdef WIN32
+# include
+# include
+# ifndef __cplusplus
+# if defined(_MSC_VER) && _MSC_VER < 1900
+# define bool char
+# define true 1
+# define false 0
+# else
+# include
+# endif
+# endif
+# define snprintf sprintf_s
+# include
+# include
+#else
+# include
+# include
+# include
+# include
+#endif
+
+#define MAX_BUFFER_LEN 65500
+#define SALT_LEN 12
+
+#include "misc_mosq.h"
+
+struct cb_helper {
+ const char *line;
+ const char *username;
+ const char *password;
+ int iterations;
+ bool found;
+};
+
+static enum mosquitto_pwhash_type hashtype = pw_sha512_pbkdf2;
+
+#ifdef WIN32
+static FILE *mpw_tmpfile(void)
+{
+ return tmpfile();
+}
+#else
+
+static char unsigned alphanum[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
+
+static unsigned char tmpfile_path[36];
+static FILE *mpw_tmpfile(void)
+{
+ int fd;
+ size_t i;
+
+ if(RAND_bytes(tmpfile_path, sizeof(tmpfile_path)) != 1){
+ return NULL;
+ }
+
+ strcpy((char *)tmpfile_path, "/tmp/");
+
+ for(i=strlen((char *)tmpfile_path); iusername)){
+ /* If this isn't the username to delete, write it to the new file */
+ fprintf(ftmp, "%s", line);
+ }else{
+ /* Don't write the matching username to the file. */
+ helper->found = true;
+ }
+ return 0;
+}
+
+static int delete_pwuser(FILE *fptr, FILE *ftmp, const char *username)
+{
+ struct cb_helper helper;
+ int rc;
+
+ memset(&helper, 0, sizeof(helper));
+ helper.username = username;
+ rc = pwfile_iterate(fptr, ftmp, delete_pwuser_cb, &helper);
+
+ if(helper.found == false){
+ fprintf(stderr, "Warning: User %s not found in password file.\n", username);
+ return 1;
+ }
+ return rc;
+}
+
+
+
+/* ======================================================================
+ * Update a plain text password file to use hashes
+ * ====================================================================== */
+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{
+ return output_new_password(ftmp, username, password, PW_DEFAULT_ITERATIONS);
+ }
+}
+
+static int update_file(FILE *fptr, FILE *ftmp)
+{
+ return pwfile_iterate(fptr, ftmp, update_file_cb, NULL);
+}
+
+
+/* ======================================================================
+ * Update an existing user password / create a new password
+ * ====================================================================== */
+static int update_pwuser_cb(FILE *fptr, FILE *ftmp, const char *username, const char *password, const char *line, struct cb_helper *helper)
+{
+ 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);
+ }else{
+ /* Write out a new line for our matching username */
+ helper->found = true;
+ rc = output_new_password(ftmp, username, helper->password, helper->iterations);
+ }
+ return rc;
+}
+
+static int update_pwuser(FILE *fptr, FILE *ftmp, const char *username, const char *password, int iterations)
+{
+ struct cb_helper helper;
+ int rc;
+
+ memset(&helper, 0, sizeof(helper));
+ helper.username = username;
+ helper.password = password;
+ helper.iterations = iterations;
+ rc = pwfile_iterate(fptr, ftmp, update_pwuser_cb, &helper);
+
+ if(helper.found){
+ return rc;
+ }else{
+ return output_new_password(ftmp, username, password, iterations);
+ }
+}
+
+
+static int copy_contents(FILE *src, FILE *dest)
+{
+ char buf[MAX_BUFFER_LEN];
+ size_t len;
+
+ rewind(src);
+ rewind(dest);
+
+#ifdef WIN32
+ _chsize(fileno(dest), 0);
+#else
+ if(ftruncate(fileno(dest), 0)) return 1;
+#endif
+
+ while(!feof(src)){
+ len = fread(buf, 1, MAX_BUFFER_LEN, src);
+ if(len > 0){
+ if(fwrite(buf, 1, len, dest) != len){
+ return 1;
+ }
+ }else{
+ return !feof(src);
+ }
+ }
+ return 0;
+}
+
+static int create_backup(const char *backup_file, FILE *fptr)
+{
+ FILE *fbackup;
+
+ fbackup = fopen(backup_file, "wt");
+ if(!fbackup){
+ fprintf(stderr, "Error creating backup password file \"%s\", not continuing.\n", backup_file);
+ return 1;
+ }
+ if(copy_contents(fptr, fbackup)){
+ fprintf(stderr, "Error copying data to backup password file \"%s\", not continuing.\n", backup_file);
+ fclose(fbackup);
+ return 1;
+ }
+ fclose(fbackup);
+ rewind(fptr);
+ return 0;
+}
+
+static void handle_sigint(int signal)
+{
+ get_password__reset_term();
+
+ UNUSED(signal);
+
+ exit(0);
+}
+
+
+static bool is_username_valid(const char *username)
+{
+ size_t 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 0.\n");
+ return 1;
+ }
+ }else if(!strcmp(argv[idx], "-U")){
+ do_update_file = true;
+ }else{
+ break;
+ }
+ }
+
+ if(create_new && delete_user){
+ fprintf(stderr, "Error: -c and -D cannot be used together.\n");
+ return 1;
+ }
+ if(create_new && do_update_file){
+ fprintf(stderr, "Error: -c and -U cannot be used together.\n");
+ return 1;
+ }
+ if(delete_user && do_update_file){
+ fprintf(stderr, "Error: -D and -U cannot be used together.\n");
+ return 1;
+ }
+ if(delete_user && batch_mode){
+ fprintf(stderr, "Error: -b and -D cannot be used together.\n");
+ return 1;
+ }
+
+ if(create_new){
+ if(batch_mode){
+ if(idx+2 >= argc){
+ fprintf(stderr, "Error: -c argument given but password file, username, or password missing.\n");
+ return 1;
+ }else{
+ password_file_tmp = argv[idx];
+ username = argv[idx+1];
+ password_cmd = argv[idx+2];
+ }
+ }else{
+ if(idx+1 >= argc){
+ fprintf(stderr, "Error: -c argument given but password file or username missing.\n");
+ return 1;
+ }else{
+ password_file_tmp = argv[idx];
+ username = argv[idx+1];
+ }
+ }
+ }else if(delete_user){
+ if(idx+1 >= argc){
+ fprintf(stderr, "Error: -D argument given but password file or username missing.\n");
+ return 1;
+ }else{
+ password_file_tmp = argv[idx];
+ username = argv[idx+1];
+ }
+ }else if(do_update_file){
+ if(idx+1 != argc){
+ fprintf(stderr, "Error: -U argument given but password file missing.\n");
+ return 1;
+ }else{
+ password_file_tmp = argv[idx];
+ }
+ }else if(batch_mode == true && idx+3 == argc){
+ password_file_tmp = argv[idx];
+ username = argv[idx+1];
+ password_cmd = argv[idx+2];
+ }else if(batch_mode == false && idx+2 == argc){
+ password_file_tmp = argv[idx];
+ username = argv[idx+1];
+ }else{
+ print_usage();
+ 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");
+ return 1;
+ }
+
+#ifdef WIN32
+ password_file = _fullpath(NULL, password_file_tmp, 0);
+ if(!password_file){
+ fprintf(stderr, "Error getting full path for password file.\n");
+ return 1;
+ }
+#else
+ password_file = realpath(password_file_tmp, NULL);
+ if(!password_file){
+ if(errno == ENOENT){
+ password_file = strdup(password_file_tmp);
+ if(!password_file){
+ fprintf(stderr, "Error: Out of memory.\n");
+ return 1;
+ }
+ }else{
+ fprintf(stderr, "Error reading password file: %s\n", strerror(errno));
+ return 1;
+ }
+ }
+#endif
+
+ if(create_new){
+ if(batch_mode == false){
+ rc = get_password("Password: ", "Reenter password: ", false, password, MAX_BUFFER_LEN);
+ if(rc){
+ free(password_file);
+ return rc;
+ }
+ password_cmd = password;
+ }
+ fptr = fopen(password_file, "wt");
+ if(!fptr){
+ fprintf(stderr, "Error: Unable to open file %s for writing. %s.\n", password_file, strerror(errno));
+ free(password_file);
+ return 1;
+ }
+ free(password_file);
+ rc = output_new_password(fptr, username, password_cmd, iterations);
+ fclose(fptr);
+ return rc;
+ }else{
+ fptr = fopen(password_file, "r+t");
+ if(!fptr){
+ fprintf(stderr, "Error: Unable to open password file %s. %s.\n", password_file, strerror(errno));
+ free(password_file);
+ return 1;
+ }
+
+ backup_file = malloc((size_t)strlen(password_file)+5);
+ if(!backup_file){
+ fprintf(stderr, "Error: Out of memory.\n");
+ free(password_file);
+ return 1;
+ }
+ snprintf(backup_file, strlen(password_file)+5, "%s.tmp", password_file);
+ free(password_file);
+ password_file = NULL;
+
+ if(create_backup(backup_file, fptr)){
+ fclose(fptr);
+ free(backup_file);
+ return 1;
+ }
+
+ ftmp = mpw_tmpfile();
+ if(!ftmp){
+ fprintf(stderr, "Error: Unable to open temporary file. %s.\n", strerror(errno));
+ fclose(fptr);
+ free(backup_file);
+ return 1;
+ }
+ if(delete_user){
+ rc = delete_pwuser(fptr, ftmp, username);
+ }else if(do_update_file){
+ rc = update_file(fptr, ftmp);
+ }else{
+ if(batch_mode){
+ /* Update password for individual user */
+ rc = update_pwuser(fptr, ftmp, username, password_cmd, iterations);
+ }else{
+ rc = get_password("Password: ", "Reenter password: ", false, password, MAX_BUFFER_LEN);
+ if(rc){
+ fclose(fptr);
+ fclose(ftmp);
+ unlink(backup_file);
+ free(backup_file);
+ return rc;
+ }
+ /* Update password for individual user */
+ rc = update_pwuser(fptr, ftmp, username, password, iterations);
+ }
+ }
+ if(rc){
+ fclose(fptr);
+ fclose(ftmp);
+ unlink(backup_file);
+ free(backup_file);
+ return rc;
+ }
+
+ if(copy_contents(ftmp, fptr)){
+ fclose(fptr);
+ fclose(ftmp);
+ fprintf(stderr, "Error occurred updating password file.\n");
+ fprintf(stderr, "Password file may be corrupt, check the backup file: %s.\n", backup_file);
+ free(backup_file);
+ return 1;
+ }
+ fclose(fptr);
+ fclose(ftmp);
+
+ /* Everything was ok so backup no longer needed. May contain old
+ * passwords so shouldn't be kept around. */
+ unlink(backup_file);
+ free(backup_file);
+ }
+
+ return 0;
+}
diff -Nru mosquitto-1.6.9/ChangeLog.txt mosquitto-2.0.15/ChangeLog.txt
--- mosquitto-1.6.9/ChangeLog.txt 2020-02-27 23:49:51.000000000 +0000
+++ mosquitto-2.0.15/ChangeLog.txt 2022-08-16 13:34:02.000000000 +0000
@@ -1,3 +1,807 @@
+2.0.15 - 2022-08-16
+===================
+
+Security:
+- Deleting the group configured as the anonymous group in the Dynamic Security
+ plugin, would leave a dangling pointer that could lead to a single crash.
+ This is considered a minor issue - only administrative users should have
+ access to dynsec, the impact on availability is one-off, and there is no
+ associated loss of data. It is now forbidden to delete the group configured
+ as the anonymous group.
+
+Broker:
+- Fix memory leak when a plugin modifies the topic of a message in
+ MOSQ_EVT_MESSAGE.
+- Fix bridge `restart_timeout` not being honoured.
+- Fix potential memory leaks if a plugin modifies the message in the
+ MOSQ_EVT_MESSAGE event.
+- Fix unused flags in CONNECT command being forced to be 0, which is not
+ required for MQTT v3.1. Closes #2522.
+- Improve documentation of `persistent_client_expiration` option.
+ Closes #2404.
+- Add clients to session expiry check list when restarting and reloading from
+ persistence. Closes #2546.
+- Fix bridges not sending failure notification messages to the local broker if
+ the remote bridge connection fails. Closes #2467. Closes #1488.
+- Fix some PUBLISH messages not being counted in $SYS stats. Closes #2448.
+- Fix incorrect return code being sent in DISCONNECT when a client session is
+ taken over. Closes #2607.
+- Fix confusing "out of memory" error when a client is kicked in the dynamic
+ security plugin. Closes #2525.
+- Fix confusing error message when dynamic security config file was a
+ directory. Closes #2520.
+- Fix bridge queued messages not being persisted when local_cleansession is
+ set to false and cleansession is set to true. Closes #2604.
+- Dynamic security: Fix modifyClient and modifyGroup commands to not modify
+ the client/group if a new group/client being added is not valid.
+ Closes #2598.
+- Dynamic security: Fix the plugin being able to be loaded twice. Currently
+ only a single plugin can interact with a unique $CONTROL topic. Using
+ multiple instances of the plugin would produce duplicate entries in the
+ config file. Closes #2601. Closes #2470.
+- Fix case where expired messages were causing queued messages not to be
+ delivered. Closes #2609.
+- Fix websockets not passing on the X-Forwarded-For header.
+
+Client library:
+- Fix threads library detection on Windows under cmake. Bumps the minimum
+ cmake version to 3.1, which is still ancient.
+- Fix use of `MOSQ_OPT_TLS_ENGINE` being unable to be used due to the openssl
+ ctx not being initialised until starting to connect. Closes #2537.
+- Fix incorrect use of SSL_connect. Closes #2594.
+- Don't set SIGPIPE to ignore, use MSG_NOSIGNAL instead. Closes #2564.
+- Add documentation of struct mosquitto_message to header. Closes #2561.
+- Fix documentation omission around mosquitto_reinitialise. Closes #2489.
+- Fix use of MOSQ_OPT_SSL_CTX when used in conjunction with
+ MOSQ_OPT_SSL_CTX_DEFAULTS. Closes #2463.
+- Fix failure to close thread in some situations. Closes #2545.
+
+Clients:
+- Fix mosquitto_pub incorrectly reusing topic aliases when reconnecting.
+ Closes #2494.
+
+Apps:
+- Fix `-o` not working in `mosquitto_ctrl`, and typo in related documentation.
+ Closes #2471.
+
+
+2.0.14 - 2021-11-17
+===================
+
+Broker:
+- Fix bridge not respecting receive-maximum when reconnecting with MQTT v5.
+
+Client library:
+- Fix mosquitto_topic_matches_sub2() not using the length parameters.
+ Closes #2364.
+- Fix incorrect subscribe_callback in mosquittopp.h. Closes #2367.
+
+
+2.0.13 - 2021-10-27
+===================
+
+Broker:
+- Fix `max_keepalive` option not being able to be set to 0.
+- Fix LWT messages not being delivered if `per_listener_settings` was set to
+ true. Closes #2314.
+- Various fixes around inflight quota management. Closes #2306.
+- Fix problem parsing config files with Windows line endings. Closes #2297.
+- Don't send retained messages when a shared subscription is made.
+- Fix log being truncated in Windows.
+- Fix client id not showing in log on failed connections, where possible.
+- Fix broker sending duplicate CONNACK on failed MQTT v5 reauthentication.
+ Closes #2339.
+- Fix mosquitto_plugin.h not including mosquitto_broker.h. Closes #2350.
+- Fix unlimited message quota not being properly checked for incoming
+ messages. Closes #2593.
+- Fixed build for openssl compiled with OPENSSL_NO_ENGINE. Closes #2589.
+
+Client library:
+- Initialise sockpairR/W to invalid in `mosquitto_reinitialise()` to avoid
+ closing invalid sockets in `mosquitto_destroy()` on error. Closes #2326.
+
+Clients:
+- Fix date format in mosquitto_sub output. Closes #2353.
+
+
+2.0.12 - 2021-08-31
+===================
+
+Security:
+- An MQTT v5 client connecting with a large number of user-property properties
+ could cause excessive CPU usage, leading to a loss of performance and
+ possible denial of service. This has been fixed.
+- Fix `max_keepalive` not applying to MQTT v3.1.1 and v3.1 connections.
+ These clients are now rejected if their keepalive value exceeds
+ max_keepalive. This option allows CVE-2020-13849, which is for the MQTT
+ v3.1.1 protocol itself rather than an implementation, to be addressed.
+- Using certain listener related configuration options e.g. `cafile`, that
+ apply to the default listener without defining any listener would cause a
+ remotely accessible listener to be opened that was not confined to the local
+ machine but did have anonymous access enabled, contrary to the
+ documentation. This has been fixed. Closes #2283.
+- CVE-2021-34434: If a plugin had granted ACL subscription access to a
+ durable/non-clean-session client, then removed that access, the client would
+ keep its existing subscription. This has been fixed.
+- Incoming QoS 2 messages that had not completed the QoS flow were not being
+ checked for ACL access when a clean session=False client was reconnecting.
+ This has been fixed.
+
+Broker:
+- Fix possible out of bounds memory reads when reading a corrupt/crafted
+ configuration file. Unless your configuration file is writable by untrusted
+ users this is not a risk. Closes #567213.
+- Fix `max_connections` option not being correctly counted.
+- Fix TLS certificates and TLS-PSK not being able to be configured at the same
+ time.
+- Disable TLS v1.3 when using TLS-PSK, because it isn't correctly configured.
+- Fix `max_keepalive` not applying to MQTT v3.1.1 and v3.1 connections.
+ These clients are now rejected if their keepalive value exceeds
+ max_keepalive. This option allows CVE-2020-13849, which is for the MQTT
+ v3.1.1 protocol itself rather than an implementation, to be addressed.
+- Fix broker not quiting if e.g. the `password_file` is specified as a
+ directory. Closes #2241.
+- Fix listener mount_point not being removed on outgoing messages.
+ Closes #2244.
+- Strict protocol compliance fixes, plus test suite.
+- Fix $share subscriptions not being recovered for durable clients that
+ reconnect.
+- Update plugin configuration documentation. Closes #2286.
+
+Client library:
+- If a client uses TLS-PSK then force the default cipher list to use "PSK"
+ ciphers only. This means that a client connecting to a broker configured
+ with x509 certificates only will now fail. Prior to this, the client would
+ connect successfully without verifying certificates, because they were not
+ configured.
+- Disable TLS v1.3 when using TLS-PSK, because it isn't correctly configured.
+- Threaded mode is deconfigured when the mosquitto_loop_start() thread ends,
+ which allows mosquitto_loop_start() to be called again. Closes #2242.
+- Fix MOSQ_OPT_SSL_CTX not being able to be set to NULL. Closes #2289.
+- Fix reconnecting failing when MOSQ_OPT_TLS_USE_OS_CERTS was in use, but none
+ of capath, cafile, psk, nor MOSQ_OPT_SSL_CTX were set, and
+ MOSQ_OPT_SSL_CTX_WITH_DEFAULTS was set to the default value of true.
+ Closes #2288.
+
+Apps:
+- Fix `mosquitto_ctrl dynsec setDefaultACLAccess` command not working.
+
+Clients:
+- mosquitto_sub and mosquitto_rr now open stdout in binary mode on Windows
+ so binary payloads are not modified when printing.
+- Document TLS certificate behaviour when using `-p 8883`.
+
+Build:
+- Fix installation using WITH_TLS=no. Closes #2281.
+- Fix builds with libressl 3.4.0. Closes #2198.
+- Remove some unnecessary code guards related to libressl.
+- Fix printf format build warning on MIPS. Closes #2271.
+
+
+2.0.11 - 2021-06-08
+===================
+
+Security:
+- If a MQTT v5 client connects with a crafted CONNECT packet a memory leak
+ will occur. This has been fixed.
+
+Broker:
+- Fix possible crash having just upgraded from 1.6 if `per_listener_settings
+ true` is set, and a SIGHUP is sent to the broker before a client has
+ reconnected to the broker. Closes #2167.
+- Fix bridge not reconnectng if the first reconnection attempt fails.
+ Closes #2207.
+- Improve QoS 0 outgoing packet queueing.
+- Fix non-reachable bridge blocking the broker on Windows. Closes #2172.
+- Fix possible corruption of pollfd array on Windows when bridges were
+ reconnecting. Closes #2173.
+- Fix QoS 0 messages not being queued when `queue_qos0_messages` was enabled.
+ Closes #2224.
+- Fix openssl not being linked to dynamic security plugin. Closes #2277.
+
+Clients:
+- If sending mosquitto_sub output to a pipe, mosquitto_sub will now detect
+ that the pipe has closed and disconnect. Closes #2164.
+- Fix `mosquitto_pub -l` quitting if a message publication is attempted when
+ the broker is temporarily unavailable. Closes #2187.
+
+
+2.0.10 - 2021-04-03
+==================
+
+Security:
+- CVE-2021-28166: If an authenticated client connected with MQTT v5 sent a
+ malformed CONNACK message to the broker a NULL pointer dereference occurred,
+ most likely resulting in a segfault.
+ Affects versions 2.0.0 to 2.0.9 inclusive.
+
+Broker:
+- Don't over write new receive-maximum if a v5 client connects and takes over
+ an old session. Closes #2134.
+- Fix CVE-2021-28166. Closes #2163.
+
+Clients:
+- Set `receive-maximum` to not exceed the `-C` message count in mosquitto_sub
+ and mosquitto_rr, to avoid potentially lost messages. Closes #2134.
+- Fix TLS-PSK mode not working with port 8883. Closes #2152.
+
+Client library:
+- Fix possible socket leak. This would occur if a client was using
+ `mosquitto_loop_start()`, then if the connection failed due to the remote
+ server being inaccessible they called `mosquitto_loop_stop(, true)` and
+ recreated the mosquitto object.
+
+Build:
+- A variety of minor build related fixes, like functions not having previous
+ declarations.
+- Fix CMake cross compile builds not finding opensslconf.h. Closes #2160.
+- Fix build on Solaris non-sparc. Closes #2136.
+
+
+2.0.9 - 2021-03-11
+==================
+
+Security:
+- If an empty or invalid CA file was provided to the client library for
+ verifying the remote broker, then the initial connection would fail but
+ subsequent connections would succeed without verifying the remote broker
+ certificate. Closes #2130.
+- If an empty or invalid CA file was provided to the broker for verifying the
+ remote broker for an outgoing bridge connection then the initial connection
+ would fail but subsequent connections would succeed without verifying the
+ remote broker certificate. Closes #2130.
+
+Broker:
+- Fix encrypted bridge connections incorrectly connecting when `bridge_cafile`
+ is empty or invalid. Closes #2130.
+- Fix `tls_version` behaviour not matching documentation. It was setting the
+ exact TLS version to use, not the minimium TLS version to use. Closes #2110.
+- Fix messages to `$` prefixed topics being rejected. Closes #2111.
+- Fix QoS 0 messages not being delivered when max_queued_bytes was configured.
+ Closes #2123.
+- Fix bridge increasing backoff calculation.
+- Improve handling of invalid combinations of listener address and bind
+ interface configurations. Closes #2081.
+- Fix `max_keepalive` option not applying to clients connecting with keepalive
+ set to 0. Closes #2117.
+
+Client library:
+- Fix encrypted connections incorrectly connecting when the CA file passed to
+ `mosquitto_tls_set()` is empty or invalid. Closes #2130.
+- Fix connections retrying very rapidly in some situations.
+
+Build:
+- Fix cmake epoll detection.
+
+
+2.0.8 - 2021-02-25
+==================
+
+Broker:
+- Fix incorrect datatypes in `struct mosquitto_evt_tick`. This changes the
+ size and offset of two of the members of this struct, and changes the size
+ of the struct. This is an ABI break, but is considered to be acceptable
+ because plugins should never be allocating their own instance of this
+ struct, and currently none of the struct members are used for anything, so a
+ plugin should not be accessing them. It would also be safe to read/write
+ from the existing struct parameters.
+- Give compile time warning if libwebsockets compiled without external poll
+ support. Closes #2060.
+- Fix memory tracking not being available on FreeBSD or macOS. Closes #2096.
+
+Client library:
+- Fix mosquitto_{pub|sub}_topic_check() functions not returning MOSQ_ERR_INVAL
+ on topic == NULL.
+
+Clients:
+- Fix possible loss of data in `mosquitto_pub -l` when sending multiple long
+ lines. Closes #2078.
+
+Build:
+- Provide a mechanism for Docker users to run a broker that doesn't use
+ authentication, without having to provide their own configuration file.
+ Closes #2040.
+
+
+2.0.7 - 2021-02-04
+==================
+
+Broker:
+- Fix exporting of executable symbols on BSD when building via makefile.
+- Fix some minor memory leaks on exit only.
+- Fix possible memory leak on connect. Closes #2057.
+- Fix openssl engine not being able to load private key. Closes #2066.
+
+Clients:
+- Fix config files truncating options after the first space. Closes #2059.
+
+Build:
+- Fix man page building to not absolutely require xsltproc when using CMake.
+ This now handles the case where we are building from the released tar, or
+ building from git if xsltproc is available, or building from git if xsltproc
+ is not available.
+
+
+1.6.13 - 2021-02-04
+===================
+
+Broker:
+- Fix crash on Windows if loading a plugin fails. Closes #1866.
+- Fix DH group not being set for TLS connections, which meant ciphers using
+ DHE couldn't be used. Closes #1925. Closes #1476.
+- Fix local bridges being disconnected on SIGHUP. Closes #1942.
+- Fix $SYS/broker/publish/messages/+ counters not being updated for QoS 1, 2
+ messages. Closes #1968.
+- Fix listener not being reassociated with client when reloading a persistence
+ file and `per_listener_settings true` is set and the client did not set a
+ username. Closes #1891.
+- Fix file logging on Windows. Closes #1880.
+- Fix bridge sock not being removed from sock hash on error. Closes #1897.
+
+Client library:
+- Fix build on Mac Big Sur. Closes #1905.
+- Fix DH group not being set for TLS connections, which meant ciphers using
+ DHE couldn't be used. Closes #1925. Closes #1476.
+
+Clients:
+- mosquitto_sub will now quit with an error if the %U option is used on
+ Windows, rather than just quitting. Closes #1908.
+- Fix config files truncating options after the first space. Closes #2059.
+
+Apps:
+- Perform stricter parsing of input username in mosquitto_passwd. Closes
+ #570126 (Eclipse bugzilla).
+
+Build:
+- Enable epoll support in CMake builds.
+
+
+2.0.6 - 2021-01-28
+==================
+
+Broker:
+- Fix calculation of remaining length parameter for websockets clients that
+ send fragmented packets. Closes #1974.
+Broker:
+- Fix potential duplicate Will messages being sent when a will delay interval
+ has been set.
+- Fix message expiry interval property not being honoured in
+ `mosquitto_broker_publish` and `mosquitto_broker_publish_copy`.
+- Fix websockets listeners with TLS not responding. Closes #2020.
+- Add notes that libsystemd-dev or similar is needed if building with systemd
+ support. Closes #2019.
+- Improve logging in obscure cases when a client disconnects. Closes #2017.
+- Fix reloading of listeners where multiple listeners have been defined with
+ the same port but different bind addresses. Closes #2029.
+- Fix `message_size_limit` not applying to the Will payload. Closes #2022.
+- The error topic-alias-invalid was being sent if an MQTT v5 client published
+ a message with empty topic and topic alias set, but the topic alias hadn't
+ already been configured on the broker. This has been fixed to send a
+ protocol error, as per section 3.3.4 of the specification.
+- Note in the man pages that SIGHUP reloads TLS certificates. Closes #2037.
+- Fix bridges not always connecting on Windows. Closes #2043.
+
+Apps:
+- Allow command line arguments to override config file options in
+ mosquitto_ctrl. Closes #2010.
+- mosquitto_ctrl: produce an error when requesting a new password if both
+ attempts do not match. Closes #2011.
+
+Build:
+- Fix cmake builds using `WITH_CJSON=no` not working if cJSON not found.
+ Closes #2026.
+
+Other:
+- The SPDX identifiers for EDL-1.0 have been changed to BSD-3-Clause as per
+ The Eclipse legal documentation generator. The licenses are identical.
+
+
+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
+==================
+
+Broker:
+- Fix $SYS/broker/publish/messages/+ counters not being updated for QoS 1, 2
+ messages. Closes #1968.
+- mosquitto_connect_bind_async() and mosquitto_connect_bind_v5() should not
+ reset the bind address option if called with bind_address == NULL.
+- Fix dynamic security configuration possibly not being reloaded on Windows
+ only. Closes #1962.
+- Add more log messages for dynsec load/save error conditions.
+- Fix websockets connections blocking non-websockets connections on Windows.
+ Closes #1934.
+
+Build:
+- Fix man pages not being built when using CMake. Closes #1969.
+
+
+2.0.3 - 2020-12-17
+==================
+
+Security:
+- Running mosquitto_passwd with the following arguments only
+ `mosquitto_passwd -b password_file username password` would cause the
+ username to be used as the password.
+
+Broker:
+- Fix excessive CPU use on non-Linux systems when the open file limit is set
+ high. Closes #1947.
+- Fix LWT not being sent on client takeover when the existing session wasn't
+ being continued. Closes #1946.
+- Fix bridges possibly not completing connections when WITH_ADNS is in use.
+ Closes #1960.
+- Fix QoS 0 messages not being delivered if max_queued_messages was set to 0.
+ Closes #1956.
+- Fix local bridges being disconnected on SIGHUP. Closes #1942.
+- Fix slow initial bridge connections for WITH_ADNS=no.
+- Fix persistence_location not appending a '/'.
+
+Clients:
+- Fix mosquitto_sub being unable to terminate with Ctrl-C if a successful
+ connection is not made. Closes #1957.
+
+Apps:
+- Fix `mosquitto_passwd -b` using username as password (not if `-c` is also
+ used). Closes #1949.
+
+Build:
+- Fix `install` target when using WITH_CJSON=no. Closes #1938.
+- Fix `generic` docker build. Closes #1945.
+
+
+2.0.2 - 2020-12-10
+==================
+
+Broker:
+- Fix build regression for WITH_WEBSOCKETS=yes on non-Linux systems.
+
+
+2.0.1 - 2020-12-10
+==================
+
+Broker:
+- Fix websockets connections on Windows blocking subsequent connections.
+ Closes #1934.
+- Fix DH group not being set for TLS connections, which meant ciphers using
+ DHE couldn't be used. Closes #1925. Closes #1476.
+- Fix websockets listeners not causing the main loop not to wake up.
+ Closes #1936.
+
+Client library:
+- Fix DH group not being set for TLS connections, which meant ciphers using
+ DHE couldn't be used. Closes #1925. Closes #1476.
+
+Apps:
+- Fix `mosquitto_passwd -U`
+
+Build:
+- Fix cjson include paths.
+- Fix build using WITH_TLS=no when the openssl headers aren't available.
+- Distribute cmake/ and snap/ directories in tar.
+
+
+2.0.0 - 2020-12-03
+==================
+
+Breaking changes:
+- When the Mosquitto broker is run without configuring any listeners it will
+ now bind to the loopback interfaces 127.0.0.1 and/or ::1. This means that
+ only connections from the local host will be possible.
+
+ Running the broker as `mosquitto` or `mosquitto -p 1883` will bind to the
+ loopback interface.
+
+ Running the broker with a configuration file with no listeners configured
+ will bind to the loopback interface with port 1883.
+
+ Running the broker with a listener defined will bind by default to `0.0.0.0`
+ / `::` and so will be accessible from any interface. It is still possible to
+ bind to a specific address/interface.
+
+ If the broker is run as `mosquitto -c mosquitto.conf -p 1884`, and a
+ listener is defined in the configuration file, then the port defined on the
+ command line will be IGNORED, and no listener configured for it.
+- All listeners now default to `allow_anonymous false` unless explicitly set
+ to true in the configuration file. This means that when configuring a
+ listener the user must either configure an authentication and access control
+ method, or set `allow_anonymous true`. When the broker is run without a
+ configured listener, and so binds to the loopback interface, anonymous
+ connections are allowed.
+- If Mosquitto is run on as root on a unix like system, it will attempt to
+ drop privileges as soon as the configuration file has been read. This is in
+ contrast to the previous behaviour where elevated privileges were only
+ dropped after listeners had been started (and hence TLS certificates loaded)
+ and logging had been started. The change means that clients will never be
+ able to connect to the broker when it is running as root, unless the user
+ explicitly sets it to run as root, which is not advised. It also means that
+ all locations that the broker needs to access must be available to the
+ unprivileged user. In particular those people using TLS certificates from
+ Lets Encrypt will need to do something to allow Mosquitto to access
+ those certificates. An example deploy renewal hook script to help with this
+ is at `misc/letsencrypt/mosquitto-copy.sh`.
+ The user that Mosquitto will change to are the one provided in the
+ configuration, `mosquitto`, or `nobody`, in order of availability.
+- The `pid_file` option will now always attempt to write a pid file,
+ regardless of whether the `-d` argument is used when running the broker.
+- The `tls_version` option now defines the *minimum* TLS protocol version to
+ be used, rather than the exact version. Closes #1258.
+- The `max_queued_messages` option has been increased from 100 to 1000 by
+ default, and now also applies to QoS 0 messages, when a client is connected.
+- The mosquitto_sub, mosquitto_pub, and mosquitto_rr clients will now load
+ OS provided CA certificates by default if `-L mqtts://...` is used, or if
+ the port is set to 8883 and no other CA certificates are loaded.
+- Minimum support libwebsockets version is now 2.4.0
+- The license has changed from "EPL-1.0 OR EDL-1.0" to "EPL-2.0 OR EDL-1.0".
+
+Broker features:
+- New plugin interface which is more flexible, easier to develop for and
+ easier to extend.
+- New dynamic security plugin, which allows clients, groups, and roles to be
+ defined and updated as the broker is running.
+- Performance improvements, particularly for higher numbers of clients.
+- When running as root, if dropping privileges to the "mosquitto" user fails,
+ then try "nobody" instead. This reduces the burden on users installing
+ Mosquitto themselves.
+- Add support for Unix domain socket listeners.
+- Add `bridge_outgoing_retain` option, to allow outgoing messages from a
+ bridge to have the retain bit completely disabled, which is useful when
+ bridging to e.g. Amazon or Google.
+- Add support for MQTT v5 bridges to handle the "retain-available" property
+ being false.
+- Allow MQTT v5.0 outgoing bridges to fall back to MQTT v3.1.1 if connecting
+ to a v3.x only broker.
+- DLT logging is now configurable at runtime with `log_dest dlt`.
+ Closes #1735.
+- Add `mosquitto_broker_publish()` and `mosquitto_broker_publish_copy()`
+ functions, which can be used by plugins to publish messages.
+- Add `mosquitto_client_protocol_version()` function which can be used by
+ plugins to determine which version of MQTT a client has connected with.
+- Add `mosquitto_kick_client_by_clientid()` and `mosquitto_kick_client_by_username()`
+ functions, which can be used by plugins to disconnect clients.
+- Add support for handling $CONTROL/ topics in plugins.
+- Add support for PBKDF2-SHA512 password hashing.
+- Enabling certificate based TLS encryption is now through certfile and
+ keyfile, not capath or cafile.
+- Added support for controlling UNSUBSCRIBE calls in v5 plugin ACL checks.
+- Add "deny" acl type. Closes #1611.
+- The broker now sends the receive-maximum property for MQTT v5 CONNACKs.
+- Add the `bridge_max_packet_size` option. Closes #265.
+- Add the `bridge_bind_address` option. Closes #1311.
+- TLS certificates for the server are now reloaded on SIGHUP.
+- Default for max_queued_messages has been changed to 1000.
+- Add `ciphers_tls1.3` option, to allow setting TLS v1.3 ciphersuites.
+ Closes #1825.
+- Bridges now obey MQTT v5 server-keepalive.
+- Add bridge support for the MQTT v5 maximum-qos property.
+- Log client port on new connections. Closes #1911.
+
+Broker fixes:
+- Send DISCONNECT with `malformed-packet` reason code on invalid PUBLISH,
+ SUBSCRIBE, and UNSUBSCRIBE packets.
+- Document that X509_free() must be called after using
+ mosquitto_client_certificate(). Closes #1842.
+- Fix listener not being reassociated with client when reloading a persistence
+ file and `per_listener_settings true` is set and the client did not set a
+ username. Closes #1891.
+- Fix bridge sock not being removed from sock hash on error. Closes #1897.
+- mosquitto_password now forbids the : character. Closes #1833.
+- Fix `log_timestamp_format` not applying to `log_dest topic`. Closes #1862.
+- Fix crash on Windows if loading a plugin fails. Closes #1866.
+- Fix file logging on Windows. Closes #1880.
+- Report an error if the config file is set to a directory. Closes #1814.
+- Fix bridges incorrectly setting Wills to manage remote notifications when
+ `notifications_local_only` was set true. Closes #1902.
+
+Client library features:
+- Client no longer generates random client ids for v3.1.1 clients, these are
+ now expected to be generated on the broker. This matches the behaviour for
+ v5 clients. Closes #291.
+- Add support for connecting to brokers through Unix domain sockets.
+- Add `mosquitto_property_identifier()`, for retrieving the identifier integer
+ for a property.
+- Add `mosquitto_property_identifier_to_string()` for converting a property
+ identifier integer to the corresponding property name string.
+- Add `mosquitto_property_next()` to retrieve the next property in a list, for
+ iterating over property lists.
+- mosquitto_pub now handles the MQTT v5 retain-available property by never
+ setting the retain bit.
+- Added MOSQ_OPT_TCP_NODELAY, to allow disabling Nagle's algorithm on client
+ sockets. Closes #1526.
+- Add `mosquitto_ssl_get()` to allow clients to access their SSL structure and
+ perform additional verification.
+- Add MOSQ_OPT_BIND_ADDRESS to allow setting of a bind address independently
+ of the `mosquitto_connect*()` call.
+- Add `MOSQ_OPT_TLS_USE_OS_CERTS` option, to instruct the client to load and
+ trust OS provided CA certificates for use with TLS connections.
+
+Client library fixes:
+- Fix send quota being incorrecly reset on reconnect. Closes #1822.
+- Don't use logging until log mutex is initialised. Closes #1819.
+- Fix missing mach/mach_time.h header on OS X. Closes #1831.
+- Fix connect properties not being sent when the client automatically
+ reconnects. Closes #1846.
+
+Client features:
+- Add timeout return code (27) for `mosquitto_sub -W ` and
+ `mosquitto_rr -W `. Closes #275.
+- Add support for connecting to brokers through Unix domain sockets with the
+ `--unix` argument.
+- Use cJSON library for producing JSON output, where available. Closes #1222.
+- Add support for outputting MQTT v5 property information to mosquitto_sub/rr
+ JSON output. Closes #1416.
+- Add `--pretty` option to mosquitto_sub/rr for formatted/unformatted JSON
+ output.
+- Add support for v5 property printing to mosquitto_sub/rr in non-JSON mode.
+ Closes #1416.
+- Add `--nodelay` to all clients to allow them to use the MOSQ_OPT_TCP_NODELAY
+ option.
+- Add `-x` to all clients to all the session-expiry-interval property to be
+ easily set for MQTT v5 clients.
+- Add `--random-filter` to mosquitto_sub, to allow only a certain proportion
+ of received messages to be printed.
+- mosquitto_sub %j and %J timestamps are now in a ISO 8601 compatible format.
+- mosquitto_sub now supports extra format specifiers for field width and
+ precision for some parameters.
+- Add `--version` for all clients.
+- All clients now load OS provided CA certificates if used with `-L
+ mqtts://...`, or if port is set to 8883 and no other CA certificates are
+ used. Closes #1824.
+- Add the `--tls-use-os-certs` option to all clients.
+
+Client fixes:
+- mosquitto_sub will now exit if all subscriptions were denied.
+- mosquitto_pub now sends 0 length files without an error when using `-f`.
+- Fix description of `-e` and `-t` arguments in mosquitto_rr. Closes #1881.
+- mosquitto_sub will now quit with an error if the %U option is used on
+ Windows, rather than just quitting. Closes #1908.
+
+
+1.6.12 - 2020-08-19
+===================
+
+Security:
+- In some circumstances, Mosquitto could leak memory when handling PUBLISH
+ messages. This is limited to incoming QoS 2 messages, and is related
+ to the combination of the broker having persistence enabled, a clean
+ session=false client, which was connected prior to the broker restarting,
+ then has reconnected and has now sent messages at a sufficiently high rate
+ that the incoming queue at the broker has filled up and hence messages are
+ being dropped. This is more likely to have an effect where
+ max_queued_messages is a small value. This has now been fixed. Closes #1793.
+
+Broker:
+- Build warning fixes when building with WITH_BRIDGE=no and WITH_TLS=no.
+
+Clients:
+- All clients exit with an error exit code on CONNACK failure. Closes #1778.
+- Don't busy loop with `mosquitto_pub -l` on a slow connection.
+
+
+1.5.10 - 2020-08-19
+===================
+
+Security:
+- In some circumstances, Mosquitto could leak memory when handling PUBLISH
+ messages. This is limited to incoming QoS 2 messages, and is related
+ to the combination of the broker having persistence enabled, a clean
+ session=false client, which was connected prior to the broker restarting,
+ then has reconnected and has now sent messages at a sufficiently high rate
+ that the incoming queue at the broker has filled up and hence messages are
+ being dropped. This is more likely to have an effect where
+ max_queued_messages is a small value. This has now been fixed. Closes #1793.
+
+
+1.6.11 - 2020-08-11
+===================
+
+Security:
+- On Windows the Mosquitto service was being installed without appropriate
+ path quoting, this has been fixed.
+
+Broker:
+- Fix usage message only mentioning v3.1.1. Closes #1713.
+- Fix broker refusing to start if only websockets listeners were defined.
+ Closes #1740.
+- Change systemd unit files to create /var/log/mosquitto before starting.
+ Closes #821.
+- Don't quit with an error if opening the log file isn't possible.
+ Closes #821.
+- Fix bridge topic remapping when using "" as the topic. Closes #1749.
+- Fix messages being queued for disconnected bridges when clean start was
+ set to true. Closes #1729.
+- Fix `autosave_interval` not being triggered by messages being delivered.
+ Closes #1726.
+- Fix websockets clients sometimes not being disconnected promptly.
+ Closes #1718.
+- Fix "slow" file based logging by switching to line based buffering.
+ Closes #1689. Closes #1741.
+- Log protocol error message where appropriate from a bad UNSUBSCRIBE, rather
+ than the generic "socket error".
+- Don't try to start DLT logging if DLT unavailable, to avoid a long delay
+ when shutting down the broker. Closes #1735.
+- Fix potential memory leaks. Closes #1773. Closes #1774.
+- Fix clients not receiving messages after a previous client with the same
+ client ID and positive will delay interval quit. Closes #1752.
+- Fix overly broad HAVE_PTHREAD_CANCEL compile guard. Closes #1547.
+
+Client library:
+- Improved documentation around connect callback return codes. Close #1730.
+- Fix `mosquitto_publish*()` no longer returning `MOSQ_ERR_NO_CONN` when not
+ connected. Closes #1725.
+- `mosquitto_loop_start()` now sets a thread name on Linux, FreeBSD, NetBSD,
+ and OpenBSD. Closes #1777.
+- Fix `mosquitto_loop_stop()` not stopping on Windows. Closes #1748. Closes #117.
+
+
+1.6.10 - 2020-05-25
+===================
+
+Broker:
+- Report invalid bridge prefix+pattern combinations at config parsing time
+ rather than letting the bridge fail later. Issue #1635.
+- Fix `mosquitto_passwd -b` not updating passwords for existing users
+ correctly. Creating a new user with `-b` worked without problem.
+ Closes #1664.
+- Fix memory leak when connecting clients rejected.
+- Don't disconnect clients that are already disconnected. This prevents the
+ session expiry being extended on SIGHUP. Closes #1521.
+- Fix support for openssl 3.0.
+- Fix check when loading persistence file of a different version than the
+ native version. Closes #1684.
+- Fix possible assert crash associated with bridge reconnecting when compiled
+ without epoll support. Closes #1700.
+
+Client library:
+- Don't treat an unexpected PUBACK, PUBREL, or PUBCOMP as a fatal error.
+ Issue #1629.
+- Fix support for openssl 3.0.
+- Fix memory leaks from multiple calls to
+ `mosquitto_lib_init()`/`mosquitto_lib_cleanup()`. Closes #1691.
+- Fix documentation on return code of `mosquitto_lib_init()` for Windows.
+ Closes #1690.
+
+Clients:
+- Fix mosquitto_sub %j or %J not working on Windows. Closes #1674.
+
+Build:
+- Various fixes for building with
All rights reserved. This program and the accompanying materials
-are made available under the terms of the Eclipse Public License v1.0
+are made available under the terms of the Eclipse Public License 2.0
and Eclipse Distribution License v1.0 which accompany this distribution.
-
+
The Eclipse Public License is available at
- http://www.eclipse.org/legal/epl-v10.html
+ https://www.eclipse.org/legal/epl-2.0/
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
-
+
+SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+
Contributors:
Roger Light - initial implementation and documentation.
*/
@@ -65,6 +67,8 @@
int cmd, identifier, type;
mosquitto_property **proplist;
int rc;
+ long tmpl;
+ size_t szt;
/* idx now points to "command" */
if((*idx)+2 > argc-1){
@@ -86,7 +90,7 @@
}
if(mosquitto_property_check_command(cmd, identifier)){
- fprintf(stderr, "Error: %s property not allow for %s in --property argument.\n\n", propname, cmdname);
+ fprintf(stderr, "Error: %s property not allowed for %s in --property argument.\n\n", propname, cmdname);
return MOSQ_ERR_INVAL;
}
@@ -105,7 +109,6 @@
(*idx) += 2;
}
-
switch(cmd){
case CMD_CONNECT:
proplist = &cfg->connect_props;
@@ -161,19 +164,44 @@
switch(type){
case MQTT_PROP_TYPE_BYTE:
- rc = mosquitto_property_add_byte(proplist, identifier, atoi(value));
+ tmpl = atol(value);
+ if(tmpl < 0 || tmpl > UINT8_MAX){
+ fprintf(stderr, "Error: Property value (%ld) out of range for property %s.\n\n", tmpl, propname);
+ return MOSQ_ERR_INVAL;
+ }
+ rc = mosquitto_property_add_byte(proplist, identifier, (uint8_t )tmpl);
break;
case MQTT_PROP_TYPE_INT16:
- rc = mosquitto_property_add_int16(proplist, identifier, atoi(value));
+ tmpl = atol(value);
+ if(tmpl < 0 || tmpl > UINT16_MAX){
+ fprintf(stderr, "Error: Property value (%ld) out of range for property %s.\n\n", tmpl, propname);
+ return MOSQ_ERR_INVAL;
+ }
+ rc = mosquitto_property_add_int16(proplist, identifier, (uint16_t )tmpl);
break;
case MQTT_PROP_TYPE_INT32:
- rc = mosquitto_property_add_int32(proplist, identifier, atoi(value));
+ tmpl = atol(value);
+ if(tmpl < 0 || tmpl > UINT32_MAX){
+ fprintf(stderr, "Error: Property value (%ld) out of range for property %s.\n\n", tmpl, propname);
+ return MOSQ_ERR_INVAL;
+ }
+ rc = mosquitto_property_add_int32(proplist, identifier, (uint32_t )tmpl);
break;
case MQTT_PROP_TYPE_VARINT:
- rc = mosquitto_property_add_varint(proplist, identifier, atoi(value));
+ tmpl = atol(value);
+ if(tmpl < 0 || tmpl > UINT32_MAX){
+ fprintf(stderr, "Error: Property value (%ld) out of range for property %s.\n\n", tmpl, propname);
+ return MOSQ_ERR_INVAL;
+ }
+ rc = mosquitto_property_add_varint(proplist, identifier, (uint32_t )tmpl);
break;
case MQTT_PROP_TYPE_BINARY:
- rc = mosquitto_property_add_binary(proplist, identifier, value, strlen(value));
+ szt = strlen(value);
+ if(szt > UINT16_MAX){
+ fprintf(stderr, "Error: Property value too long for property %s.\n\n", propname);
+ return MOSQ_ERR_INVAL;
+ }
+ rc = mosquitto_property_add_binary(proplist, identifier, value, (uint16_t )szt);
break;
case MQTT_PROP_TYPE_STRING:
rc = mosquitto_property_add_string(proplist, identifier, value);
diff -Nru mosquitto-1.6.9/client/client_shared.c mosquitto-2.0.15/client/client_shared.c
--- mosquitto-1.6.9/client/client_shared.c 2020-02-27 23:49:51.000000000 +0000
+++ mosquitto-2.0.15/client/client_shared.c 2022-08-16 13:34:02.000000000 +0000
@@ -2,14 +2,16 @@
Copyright (c) 2014-2020 Roger Light
All rights reserved. This program and the accompanying materials
-are made available under the terms of the Eclipse Public License v1.0
+are made available under the terms of the Eclipse Public License 2.0
and Eclipse Distribution License v1.0 which accompany this distribution.
-
+
The Eclipse Public License is available at
- http://www.eclipse.org/legal/epl-v10.html
+ https://www.eclipse.org/legal/epl-2.0/
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
-
+
+SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+
Contributors:
Roger Light - initial implementation and documentation.
*/
@@ -44,41 +46,100 @@
static int check_format(const char *str)
{
- int i;
- int len;
+ size_t i;
+ size_t len;
len = strlen(str);
for(i=0; i= '0' && str[i+1] <= '9'){
+ i++;
+ if(i == len-1){
+ /* error */
+ fprintf(stderr, "Error: Incomplete format specifier.\n");
+ return 1;
+ }
+ }
+
+ if(str[i+1] == '.'){
+ /* Precision specifier */
+ i++;
+ if(i == len-1){
+ /* error */
+ fprintf(stderr, "Error: Incomplete format specifier.\n");
+ return 1;
+ }
+ /* Precision */
+ while(str[i+1] >= '0' && str[i+1] <= '9'){
+ i++;
+ if(i == len-1){
+ /* error */
+ fprintf(stderr, "Error: Incomplete format specifier.\n");
+ return 1;
+ }
+ }
+ }
+
if(str[i+1] == '%'){
- // Print %, ignore
+ /* Print %, ignore */
+ }else if(str[i+1] == 'A'){
+ /* MQTT v5 property topic-alias */
+ }else if(str[i+1] == 'C'){
+ /* MQTT v5 property content-type */
+ }else if(str[i+1] == 'D'){
+ /* MQTT v5 property correlation-data */
+ }else if(str[i+1] == 'E'){
+ /* MQTT v5 property message-expiry-interval */
+ }else if(str[i+1] == 'F'){
+ /* MQTT v5 property payload-format-indicator */
}else if(str[i+1] == 'I'){
- // ISO 8601 date+time
+ /* ISO 8601 date+time */
}else if(str[i+1] == 'l'){
- // payload length
+ /* payload length */
}else if(str[i+1] == 'm'){
- // mid
+ /* mid */
+ }else if(str[i+1] == 'P'){
+ /* MQTT v5 property user-property */
}else if(str[i+1] == 'p'){
- // payload
+ /* payload */
}else if(str[i+1] == 'q'){
- // qos
+ /* qos */
+ }else if(str[i+1] == 'R'){
+ /* MQTT v5 property response-topic */
+ }else if(str[i+1] == 'S'){
+ /* MQTT v5 property subscription-identifier */
}else if(str[i+1] == 'r'){
- // retain
+ /* retain */
}else if(str[i+1] == 't'){
- // topic
+ /* topic */
}else if(str[i+1] == 'j'){
- // JSON output, escaped payload
+ /* JSON output, escaped payload */
}else if(str[i+1] == 'J'){
- // JSON output, assuming JSON payload
+ /* JSON output, assuming JSON payload */
}else if(str[i+1] == 'U'){
- // Unix time+nanoseconds
+ /* Unix time+nanoseconds */
+#ifdef WIN32
+ fprintf(stderr, "Error: The %%U format option is not supported on Windows.\n");
+ return 1;
+#endif
}else if(str[i+1] == 'x' || str[i+1] == 'X'){
- // payload in hex
+ /* payload in hex */
}else{
fprintf(stderr, "Error: Invalid format specifier '%c'.\n", str[i+1]);
return 1;
@@ -87,26 +148,26 @@
}
}else if(str[i] == '@'){
if(i == len-1){
- // error
+ /* error */
fprintf(stderr, "Error: Incomplete format specifier.\n");
return 1;
}
i++;
}else if(str[i] == '\\'){
if(i == len-1){
- // error
+ /* error */
fprintf(stderr, "Error: Incomplete escape specifier.\n");
return 1;
}else{
switch(str[i+1]){
- case '\\': // '\'
- case '0': // 0 (NULL)
- case 'a': // alert
- case 'e': // escape
- case 'n': // new line
- case 'r': // carriage return
- case 't': // horizontal tab
- case 'v': // vertical tab
+ case '\\': /* '\' */
+ case '0': /* 0 (NULL) */
+ case 'a': /* alert */
+ case 'e': /* escape */
+ case 'n': /* new line */
+ case 'r': /* carriage return */
+ case 't': /* horizontal tab */
+ case 'v': /* vertical tab */
break;
default:
@@ -122,10 +183,10 @@
}
-void init_config(struct mosq_config *cfg, int pub_or_sub)
+static void init_config(struct mosq_config *cfg, int pub_or_sub)
{
memset(cfg, 0, sizeof(*cfg));
- cfg->port = -1;
+ cfg->port = PORT_UNDEFINED;
cfg->max_inflight = 20;
cfg->keepalive = 60;
cfg->clean_session = true;
@@ -133,6 +194,7 @@
cfg->repeat_count = 1;
cfg->repeat_delay.tv_sec = 0;
cfg->repeat_delay.tv_usec = 0;
+ cfg->random_filter = 10000;
if(pub_or_sub == CLIENT_RR){
cfg->protocol_version = MQTT_PROTOCOL_V5;
cfg->msg_count = 1;
@@ -212,7 +274,7 @@
char line[1024];
int count;
char *loc = NULL;
- int len;
+ size_t len;
char *args[3];
#ifndef WIN32
@@ -295,7 +357,7 @@
* program name as the first entry. */
args[1] = strtok(line, " ");
if(args[1]){
- args[2] = strtok(NULL, " ");
+ args[2] = strtok(NULL, "");
if(args[2]){
count = 3;
}else{
@@ -362,7 +424,7 @@
fprintf(stderr, "Error: You must provide a client id if you are using an infinite session expiry interval.\n");
return 1;
}
- rc = mosquitto_property_add_int32(&cfg->connect_props, MQTT_PROP_SESSION_EXPIRY_INTERVAL, cfg->session_expiry_interval);
+ rc = mosquitto_property_add_int32(&cfg->connect_props, MQTT_PROP_SESSION_EXPIRY_INTERVAL, (uint32_t )cfg->session_expiry_interval);
if(rc){
fprintf(stderr, "Error adding property session-expiry-interval\n");
}
@@ -423,9 +485,9 @@
return MOSQ_ERR_SUCCESS;
}
-int cfg_add_topic(struct mosq_config *cfg, int type, char *topic, const char *arg)
+static int cfg_add_topic(struct mosq_config *cfg, int type, char *topic, const char *arg)
{
- if(mosquitto_validate_utf8(topic, strlen(topic))){
+ if(mosquitto_validate_utf8(topic, (int )strlen(topic))){
fprintf(stderr, "Error: Malformed UTF-8 in %s argument.\n\n", arg);
return 1;
}
@@ -447,7 +509,7 @@
return 1;
}
cfg->topic_count++;
- cfg->topics = realloc(cfg->topics, cfg->topic_count*sizeof(char *));
+ cfg->topics = realloc(cfg->topics, (size_t )cfg->topic_count*sizeof(char *));
if(!cfg->topics){
err_printf(cfg, "Error: Out of memory.\n");
return 1;
@@ -461,7 +523,9 @@
int client_config_line_proc(struct mosq_config *cfg, int pub_or_sub, int argc, char *argv[])
{
int i;
+ int tmpi;
float f;
+ size_t szt;
for(i=1; ikeepalive = atoi(argv[i+1]);
- if(cfg->keepalive>65535){
- fprintf(stderr, "Error: Invalid keepalive given: %d\n", cfg->keepalive);
+ if(cfg->keepalive<5 || cfg->keepalive>UINT16_MAX){
+ fprintf(stderr, "Error: Invalid keepalive given, it must be between 5 and 65535 inclusive.\n\n");
return 1;
}
}
@@ -668,8 +732,14 @@
url += 7;
cfg->port = 1883;
} else if(!strncasecmp(url, "mqtts://", 8)) {
+#ifdef WITH_TLS
url += 8;
cfg->port = 8883;
+ cfg->tls_use_os_certs = true;
+#else
+ fprintf(stderr, "Error: TLS support not available.\n\n");
+ return 1;
+#endif
} else {
fprintf(stderr, "Error: unsupported URL scheme.\n\n");
return 1;
@@ -686,8 +756,9 @@
tmp = strchr(url, '@');
if(tmp) {
+ char *colon;
*tmp++ = 0;
- char *colon = strchr(url, ':');
+ colon = strchr(url, ':');
if(colon) {
*colon = 0;
cfg->password = strdup(colon + 1);
@@ -728,7 +799,16 @@
return 1;
}else{
cfg->message = strdup(argv[i+1]);
- cfg->msglen = strlen(cfg->message);
+ if(cfg->message == NULL){
+ fprintf(stderr, "Error: Out of memory.\n\n");
+ return 1;
+ }
+ szt = strlen(cfg->message);
+ if(szt > MQTT_MAX_PAYLOAD){
+ fprintf(stderr, "Error: Message length must be less than %u bytes.\n\n", MQTT_MAX_PAYLOAD);
+ return 1;
+ }
+ cfg->msglen = (int )szt;
cfg->pub_mode = MSGMODE_CMD;
}
i++;
@@ -737,9 +817,16 @@
fprintf(stderr, "Error: -M argument given but max_inflight not specified.\n\n");
return 1;
}else{
- cfg->max_inflight = atoi(argv[i+1]);
+ tmpi = atoi(argv[i+1]);
+ if(tmpi < 1){
+ fprintf(stderr, "Error: Maximum inflight messages must be greater than 0.\n\n");
+ return 1;
+ }
+ cfg->max_inflight = (unsigned int )tmpi;
}
i++;
+ }else if(!strcmp(argv[i], "--nodelay")){
+ cfg->tcp_nodelay = true;
}else if(!strcmp(argv[i], "-n") || !strcmp(argv[i], "--null-message")){
if(pub_or_sub == CLIENT_SUB){
goto unknown_option;
@@ -761,12 +848,17 @@
return 1;
}else{
cfg->port = atoi(argv[i+1]);
- if(cfg->port<1 || cfg->port>65535){
+ if(cfg->port<0 || cfg->port>65535){
fprintf(stderr, "Error: Invalid port given: %d\n", cfg->port);
return 1;
}
}
i++;
+ }else if(!strcmp(argv[i], "--pretty")){
+ if(pub_or_sub == CLIENT_PUB){
+ goto unknown_option;
+ }
+ cfg->pretty = true;
}else if(!strcmp(argv[i], "-P") || !strcmp(argv[i], "--pw")){
if(i==argc-1){
fprintf(stderr, "Error: -P argument given but no password specified.\n\n");
@@ -830,6 +922,21 @@
}
cfg->no_retain = true;
cfg->sub_opts |= MQTT_SUB_OPT_SEND_RETAIN_NEVER;
+ }else if(!strcmp(argv[i], "--random-filter")){
+ if(pub_or_sub != CLIENT_SUB){
+ goto unknown_option;
+ }
+ if(i==argc-1){
+ fprintf(stderr, "Error: --random-filter argument given but no chance specified.\n\n");
+ return 1;
+ }else{
+ cfg->random_filter = (int)(10.0*atof(argv[i+1]));
+ if(cfg->random_filter > 10000 || cfg->random_filter < 1){
+ fprintf(stderr, "Error: --random-filter chance must be between 0.1-100.0\n\n");
+ return 1;
+ }
+ }
+ i++;
}else if(!strcmp(argv[i], "--remove-retained")){
if(pub_or_sub != CLIENT_SUB){
goto unknown_option;
@@ -858,13 +965,13 @@
fprintf(stderr, "Error: --repeat-delay argument given but no time specified.\n\n");
return 1;
}else{
- f = atof(argv[i+1]);
+ f = (float )atof(argv[i+1]);
if(f < 0.0f){
fprintf(stderr, "Error: --repeat-delay argument must be >=0.0.\n\n");
return 1;
}
- f *= 1.0e6;
- cfg->repeat_delay.tv_sec = (int)f/1e6;
+ f *= 1.0e6f;
+ cfg->repeat_delay.tv_sec = (int)f/1000000;
cfg->repeat_delay.tv_usec = (int)f%1000000;
}
i++;
@@ -885,7 +992,7 @@
if(cfg->pub_mode != MSGMODE_NONE){
fprintf(stderr, "Error: Only one type of message can be sent at once.\n\n");
return 1;
- }else{
+ }else{
cfg->pub_mode = MSGMODE_STDIN_FILE;
}
#ifdef WITH_SRV
@@ -909,7 +1016,7 @@
fprintf(stderr, "Error: -T argument given but no topic filter specified.\n\n");
return 1;
}else{
- if(mosquitto_validate_utf8(argv[i+1], strlen(argv[i+1]))){
+ if(mosquitto_validate_utf8(argv[i+1], (int )strlen(argv[i+1]))){
fprintf(stderr, "Error: Malformed UTF-8 in -T argument.\n\n");
return 1;
}
@@ -918,7 +1025,7 @@
return 1;
}
cfg->filter_out_count++;
- cfg->filter_outs = realloc(cfg->filter_outs, cfg->filter_out_count*sizeof(char *));
+ cfg->filter_outs = realloc(cfg->filter_outs, (size_t )cfg->filter_out_count*sizeof(char *));
if(!cfg->filter_outs){
fprintf(stderr, "Error: Out of memory.\n");
return 1;
@@ -951,6 +1058,8 @@
cfg->tls_engine_kpass_sha1 = strdup(argv[i+1]);
}
i++;
+ }else if(!strcmp(argv[i], "--tls-use-os-certs")){
+ cfg->tls_use_os_certs = true;
}else if(!strcmp(argv[i], "--tls-version")){
if(i==argc-1){
fprintf(stderr, "Error: --tls-version argument given but no version specified.\n\n");
@@ -968,7 +1077,7 @@
fprintf(stderr, "Error: -U argument given but no unsubscribe topic specified.\n\n");
return 1;
}else{
- if(mosquitto_validate_utf8(argv[i+1], strlen(argv[i+1]))){
+ if(mosquitto_validate_utf8(argv[i+1], (int )strlen(argv[i+1]))){
fprintf(stderr, "Error: Malformed UTF-8 in -U argument.\n\n");
return 1;
}
@@ -977,7 +1086,7 @@
return 1;
}
cfg->unsub_topic_count++;
- cfg->unsub_topics = realloc(cfg->unsub_topics, cfg->unsub_topic_count*sizeof(char *));
+ cfg->unsub_topics = realloc(cfg->unsub_topics, (size_t )cfg->unsub_topic_count*sizeof(char *));
if(!cfg->unsub_topics){
fprintf(stderr, "Error: Out of memory.\n");
return 1;
@@ -993,6 +1102,15 @@
cfg->username = strdup(argv[i+1]);
}
i++;
+ }else if(!strcmp(argv[i], "--unix")){
+ if(i==argc-1){
+ fprintf(stderr, "Error: --unix argument given but no socket path specified.\n\n");
+ return 1;
+ }else{
+ cfg->host = strdup(argv[i+1]);
+ cfg->port = 0;
+ }
+ i++;
}else if(!strcmp(argv[i], "-V") || !strcmp(argv[i], "--protocol-version")){
if(i==argc-1){
fprintf(stderr, "Error: --protocol-version argument given but no version specified.\n\n");
@@ -1015,6 +1133,8 @@
goto unknown_option;
}
cfg->verbose = 1;
+ }else if(!strcmp(argv[i], "--version")){
+ return 3;
}else if(!strcmp(argv[i], "-W")){
if(pub_or_sub == CLIENT_PUB){
goto unknown_option;
@@ -1023,11 +1143,12 @@
fprintf(stderr, "Error: -W argument given but no timeout specified.\n\n");
return 1;
}else{
- cfg->timeout = atoi(argv[i+1]);
- if(cfg->timeout < 1){
- fprintf(stderr, "Error: Invalid timeout \"%d\".\n\n", cfg->msg_count);
+ tmpi = atoi(argv[i+1]);
+ if(tmpi < 1){
+ fprintf(stderr, "Error: Invalid timeout \"%d\".\n\n", tmpi);
return 1;
}
+ cfg->timeout = (unsigned int )tmpi;
}
i++;
}
@@ -1037,7 +1158,7 @@
return 1;
}else{
cfg->will_payload = strdup(argv[i+1]);
- cfg->will_payloadlen = strlen(cfg->will_payload);
+ cfg->will_payloadlen = (int )strlen(cfg->will_payload);
}
i++;
}else if(!strcmp(argv[i], "--will-qos")){
@@ -1059,7 +1180,7 @@
fprintf(stderr, "Error: --will-topic argument given but no will topic specified.\n\n");
return 1;
}else{
- if(mosquitto_validate_utf8(argv[i+1], strlen(argv[i+1]))){
+ if(mosquitto_validate_utf8(argv[i+1], (int )strlen(argv[i+1]))){
fprintf(stderr, "Error: Malformed UTF-8 in --will-topic argument.\n\n");
return 1;
}
@@ -1070,6 +1191,32 @@
cfg->will_topic = strdup(argv[i+1]);
}
i++;
+ }else if(!strcmp(argv[i], "-x")){
+ if(i==argc-1){
+ fprintf(stderr, "Error: -x argument given but no session expiry interval specified.\n\n");
+ return 1;
+ }else{
+ if(!strcmp(argv[i+1], "∞")){
+ cfg->session_expiry_interval = UINT32_MAX;
+ }else{
+ char *endptr = NULL;
+ cfg->session_expiry_interval = strtol(argv[i+1], &endptr, 0);
+ if(endptr == argv[i+1] || endptr[0] != '\0'){
+ /* Entirety of argument wasn't a number */
+ fprintf(stderr, "Error: session-expiry-interval not a number.\n\n");
+ return 1;
+ }
+ if(cfg->session_expiry_interval > UINT32_MAX || cfg->session_expiry_interval < -1){
+ fprintf(stderr, "Error: session-expiry-interval out of range.\n\n");
+ return 1;
+ }
+ if(cfg->session_expiry_interval == -1){
+ /* Convenience value for infinity. */
+ cfg->session_expiry_interval = UINT32_MAX;
+ }
+ }
+ }
+ i++;
}else{
goto unknown_option;
}
@@ -1117,7 +1264,21 @@
mosquitto_lib_cleanup();
return 1;
}
+# ifdef FINAL_WITH_TLS_PSK
+ }else if(cfg->psk){
+ if(mosquitto_tls_psk_set(mosq, cfg->psk, cfg->psk_identity, NULL)){
+ err_printf(cfg, "Error: Problem setting TLS-PSK options.\n");
+ mosquitto_lib_cleanup();
+ return 1;
+ }
+# endif
+ }else if(cfg->port == 8883){
+ mosquitto_int_option(mosq, MOSQ_OPT_TLS_USE_OS_CERTS, 1);
+ }
+ if(cfg->tls_use_os_certs){
+ mosquitto_int_option(mosq, MOSQ_OPT_TLS_USE_OS_CERTS, 1);
}
+
if(cfg->insecure && mosquitto_tls_insecure_set(mosq, true)){
err_printf(cfg, "Error: Problem setting TLS insecure option.\n");
mosquitto_lib_cleanup();
@@ -1143,13 +1304,6 @@
mosquitto_lib_cleanup();
return 1;
}
-# ifdef FINAL_WITH_TLS_PSK
- if(cfg->psk && mosquitto_tls_psk_set(mosq, cfg->psk, cfg->psk_identity, NULL)){
- 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)){
err_printf(cfg, "Error: Problem setting TLS options, check the options are valid.\n");
mosquitto_lib_cleanup();
@@ -1166,6 +1320,16 @@
}
}
#endif
+ if(cfg->tcp_nodelay){
+ mosquitto_int_option(mosq, MOSQ_OPT_TCP_NODELAY, 1);
+ }
+
+ if(cfg->msg_count > 0 && cfg->msg_count < 20){
+ /* 20 is the default "receive maximum"
+ * If we don't set this, then we can receive > msg_count messages
+ * before we quit.*/
+ mosquitto_int_option(mosq, MOSQ_OPT_RECEIVE_MAXIMUM, cfg->msg_count);
+ }
return MOSQ_ERR_SUCCESS;
}
@@ -1193,7 +1357,7 @@
int rc;
int port;
- if(cfg->port < 0){
+ if(cfg->port == PORT_UNDEFINED){
#ifdef WITH_TLS
if(cfg->cafile || cfg->capath
# ifdef FINAL_WITH_TLS_PSK
@@ -1240,8 +1404,8 @@
/* Convert %25 -> %, %3a, %3A -> :, %40 -> @ */
static int mosquitto__urldecode(char *str)
{
- int i, j;
- int len;
+ size_t i, j;
+ size_t len;
if(!str) return 0;
if(!strchr(str, '%')) return 0;
@@ -1299,12 +1463,13 @@
return 1;
}
- // socks5h://username:password@host:1883
- // socks5h://username:password@host
- // socks5h://username@host:1883
- // socks5h://username@host
- // socks5h://host:1883
- // socks5h://host
+ /* socks5h://username:password@host:1883
+ * socks5h://username:password@host
+ * socks5h://username@host:1883
+ * socks5h://username@host
+ * socks5h://host:1883
+ * socks5h://host
+ */
start = 0;
for(i=0; i
All rights reserved. This program and the accompanying materials
-are made available under the terms of the Eclipse Public License v1.0
+are made available under the terms of the Eclipse Public License 2.0
and Eclipse Distribution License v1.0 which accompany this distribution.
-
+
The Eclipse Public License is available at
- http://www.eclipse.org/legal/epl-v10.html
+ https://www.eclipse.org/legal/epl-2.0/
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
-
+
+SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+
Contributors:
Roger Light - initial implementation and documentation.
*/
@@ -25,6 +27,10 @@
# include
#endif
+#ifndef __GNUC__
+#define __attribute__(attrib)
+#endif
+
/* pub_client.c modes */
#define MSGMODE_NONE 0
#define MSGMODE_CMD 1
@@ -38,6 +44,9 @@
#define CLIENT_RR 3
#define CLIENT_RESPONSE_TOPIC 4
+#define PORT_UNDEFINED -1
+#define PORT_UNIX 0
+
struct mosq_config {
char *id;
char *id_prefix;
@@ -50,7 +59,7 @@
int pub_mode; /* pub, rr */
char *file_input; /* pub, rr */
char *message; /* pub, rr */
- long msglen; /* pub, rr */
+ int msglen; /* pub, rr */
char *topic; /* pub, rr */
char *bind_address;
int repeat_count; /* pub */
@@ -65,7 +74,7 @@
char *password;
char *will_topic;
char *will_payload;
- long will_payloadlen;
+ int will_payloadlen;
int will_qos;
bool will_retain;
#ifdef WITH_TLS
@@ -80,14 +89,15 @@
char *tls_engine;
char *tls_engine_kpass_sha1;
char *keyform;
+ bool tls_use_os_certs;
# ifdef FINAL_WITH_TLS_PSK
char *psk;
char *psk_identity;
# endif
#endif
bool clean_session;
- char **topics; /* sub */
- int topic_count; /* sub */
+ char **topics; /* sub, rr */
+ int topic_count; /* sub, rr */
bool exit_after_sub; /* sub */
bool no_retain; /* sub */
bool retained_only; /* sub */
@@ -99,10 +109,12 @@
bool verbose; /* sub */
bool eol; /* sub */
int msg_count; /* sub */
- char *format; /* sub */
- int timeout; /* sub */
+ char *format; /* sub, rr */
+ bool pretty; /* sub, rr */
+ unsigned int timeout; /* sub */
int sub_opts; /* sub */
long session_expiry_interval;
+ int random_filter; /* sub */
#ifdef WITH_SOCKS
char *socks5_host;
int socks5_port;
@@ -117,6 +129,7 @@
mosquitto_property *will_props;
bool have_topic_alias; /* pub */
char *response_topic; /* rr */
+ bool tcp_nodelay;
};
int client_config_load(struct mosq_config *config, int pub_or_sub, int argc, char *argv[]);
@@ -127,6 +140,5 @@
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, ...);
-
+void err_printf(const struct mosq_config *cfg, const char *fmt, ...) __attribute__((format(printf, 2, 3)));
#endif
diff -Nru mosquitto-1.6.9/client/CMakeLists.txt mosquitto-2.0.15/client/CMakeLists.txt
--- mosquitto-1.6.9/client/CMakeLists.txt 2020-02-27 23:49:51.000000000 +0000
+++ mosquitto-2.0.15/client/CMakeLists.txt 2022-08-16 13:34:02.000000000 +0000
@@ -1,18 +1,34 @@
-include_directories(${mosquitto_SOURCE_DIR} ${mosquitto_SOURCE_DIR}/lib
- ${STDBOOL_H_PATH} ${STDINT_H_PATH} ${PTHREAD_INCLUDE_DIR}
- ${OPENSSL_INCLUDE_DIR})
-link_directories(${mosquitto_BINARY_DIR}/lib)
-
set(shared_src client_shared.c client_shared.h client_props.c)
if (WITH_SRV)
add_definitions("-DWITH_SRV")
endif (WITH_SRV)
+set( CLIENT_INC ${mosquitto_SOURCE_DIR} ${mosquitto_SOURCE_DIR}/include
+ ${STDBOOL_H_PATH} ${STDINT_H_PATH} ${PTHREAD_INCLUDE_DIR}
+ ${OPENSSL_INCLUDE_DIR})
+
+set( CLIENT_DIR ${mosquitto_BINARY_DIR}/lib)
+
+if (CJSON_FOUND)
+ add_definitions("-DWITH_CJSON")
+ set( CLIENT_DIR "${CLIENT_DIR};${CJSON_DIR}" )
+ set( CLIENT_INC "${CLIENT_INC};${CJSON_INCLUDE_DIRS}" )
+endif()
+
+include_directories(${CLIENT_INC})
+link_directories(${CLIENT_DIR})
+
add_executable(mosquitto_pub pub_client.c pub_shared.c ${shared_src})
add_executable(mosquitto_sub sub_client.c sub_client_output.c ${shared_src})
add_executable(mosquitto_rr rr_client.c pub_shared.c sub_client_output.c ${shared_src})
+if (CJSON_FOUND)
+ target_link_libraries(mosquitto_pub ${CJSON_LIBRARIES})
+ target_link_libraries(mosquitto_sub ${CJSON_LIBRARIES})
+ target_link_libraries(mosquitto_rr ${CJSON_LIBRARIES})
+endif()
+
if (WITH_STATIC_LIBRARIES)
target_link_libraries(mosquitto_pub libmosquitto_static)
target_link_libraries(mosquitto_sub libmosquitto_static)
diff -Nru mosquitto-1.6.9/client/Makefile mosquitto-2.0.15/client/Makefile
--- mosquitto-1.6.9/client/Makefile 2020-02-27 23:49:51.000000000 +0000
+++ mosquitto-2.0.15/client/Makefile 2022-08-16 13:34:02.000000000 +0000
@@ -47,7 +47,7 @@
sub_client.o : sub_client.c ${SHARED_DEP}
${CROSS_COMPILE}${CC} $(CLIENT_CPPFLAGS) $(CLIENT_CFLAGS) -c $< -o $@
-sub_client_output.o : sub_client_output.c ${SHARED_DEP}
+sub_client_output.o : sub_client_output.c sub_client_output.h ${SHARED_DEP}
${CROSS_COMPILE}${CC} $(CLIENT_CPPFLAGS) $(CLIENT_CFLAGS) -c $< -o $@
rr_client.o : rr_client.c ${SHARED_DEP}
@@ -87,5 +87,5 @@
reallyclean : clean
-clean :
+clean :
-rm -f *.o mosquitto_pub mosquitto_sub mosquitto_rr *.gcda *.gcno
diff -Nru mosquitto-1.6.9/client/pub_client.c mosquitto-2.0.15/client/pub_client.c
--- mosquitto-1.6.9/client/pub_client.c 2020-02-27 23:49:51.000000000 +0000
+++ mosquitto-2.0.15/client/pub_client.c 2022-08-16 13:34:02.000000000 +0000
@@ -2,14 +2,16 @@
Copyright (c) 2009-2020 Roger Light
All rights reserved. This program and the accompanying materials
-are made available under the terms of the Eclipse Public License v1.0
+are made available under the terms of the Eclipse Public License 2.0
and Eclipse Distribution License v1.0 which accompany this distribution.
-
+
The Eclipse Public License is available at
- http://www.eclipse.org/legal/epl-v10.html
+ https://www.eclipse.org/legal/epl-2.0/
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
-
+
+SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+
Contributors:
Roger Light - initial implementation and documentation.
*/
@@ -46,6 +48,7 @@
static int publish_count = 0;
static bool ready_for_repeat = false;
static volatile int status = STATUS_CONNECTING;
+static int connack_result = 0;
#ifdef WIN32
static uint64_t next_publish_tv;
@@ -76,7 +79,7 @@
next_publish_tv.tv_sec += cfg.repeat_delay.tv_sec;
next_publish_tv.tv_usec += cfg.repeat_delay.tv_usec;
- next_publish_tv.tv_sec += next_publish_tv.tv_usec/1e6;
+ next_publish_tv.tv_sec += next_publish_tv.tv_usec/1000000;
next_publish_tv.tv_usec = next_publish_tv.tv_usec%1000000;
}
@@ -129,7 +132,10 @@
UNUSED(flags);
UNUSED(properties);
+ connack_result = result;
+
if(!result){
+ first_publish = true;
switch(cfg.pub_mode){
case MSGMODE_CMD:
case MSGMODE_FILE:
@@ -177,7 +183,8 @@
}else{
err_printf(&cfg, "Connection error: %s\n", mosquitto_connack_string(result));
}
- mosquitto_disconnect_v5(mosq, 0, cfg.disconnect_props);
+ /* let the loop know that this is an unrecoverable connection */
+ status = STATUS_NOHOPE;
}
}
}
@@ -185,12 +192,18 @@
void my_publish_callback(struct mosquitto *mosq, void *obj, int mid, int reason_code, const mosquitto_property *properties)
{
+ char *reason_string = NULL;
UNUSED(obj);
UNUSED(properties);
last_mid_sent = mid;
if(reason_code > 127){
err_printf(&cfg, "Warning: Publish %d failed: %s.\n", mid, mosquitto_reason_string(reason_code));
+ mosquitto_property_read_string(properties, MQTT_PROP_REASON_STRING, &reason_string, false);
+ if(reason_string){
+ err_printf(&cfg, "%s\n", reason_string);
+ free(reason_string);
+ }
}
publish_count++;
@@ -211,7 +224,7 @@
int pub_shared_init(void)
{
- line_buf = malloc(line_buf_len);
+ line_buf = malloc((size_t )line_buf_len);
if(!line_buf){
err_printf(&cfg, "Error: Out of memory.\n");
return 1;
@@ -220,7 +233,7 @@
}
-int pub_stdin_line_loop(struct mosquitto *mosq)
+static int pub_stdin_line_loop(struct mosquitto *mosq)
{
char *buf2;
int buf_len_actual = 0;
@@ -232,25 +245,39 @@
mosquitto_loop_start(mosq);
stdin_finished = false;
do{
+ if(status == STATUS_CONNECTING){
+#ifdef WIN32
+ Sleep(100);
+#else
+ struct timespec ts;
+ ts.tv_sec = 0;
+ ts.tv_nsec = 100000000;
+ nanosleep(&ts, NULL);
+#endif
+ }
+
+ if(status == STATUS_NOHOPE){
+ return MOSQ_ERR_CONN_REFUSED;
+ }
+
if(status == STATUS_CONNACK_RECVD){
pos = 0;
read_len = line_buf_len;
while(status == STATUS_CONNACK_RECVD && fgets(&line_buf[pos], read_len, stdin)){
- buf_len_actual = strlen(line_buf);
+ buf_len_actual = (int )strlen(line_buf);
if(line_buf[buf_len_actual-1] == '\n'){
line_buf[buf_len_actual-1] = '\0';
rc = my_publish(mosq, &mid_sent, cfg.topic, buf_len_actual-1, line_buf, cfg.qos, cfg.retain);
pos = 0;
- if(rc){
- err_printf(&cfg, "Error: Publish returned %d, disconnecting.\n", rc);
- mosquitto_disconnect_v5(mosq, MQTT_RC_DISCONNECT_WITH_WILL_MSG, cfg.disconnect_props);
+ if(rc != MOSQ_ERR_SUCCESS && rc != MOSQ_ERR_NO_CONN){
+ return rc;
}
break;
}else{
line_buf_len += 1024;
- pos += 1023;
+ pos += read_len-1;
read_len = 1024;
- buf2 = realloc(line_buf, line_buf_len);
+ buf2 = realloc(line_buf, (size_t )line_buf_len);
if(!buf2){
err_printf(&cfg, "Error: Out of memory.\n");
return MOSQ_ERR_NOMEM;
@@ -261,8 +288,7 @@
if(pos != 0){
rc = my_publish(mosq, &mid_sent, cfg.topic, buf_len_actual, line_buf, cfg.qos, cfg.retain);
if(rc){
- err_printf(&cfg, "Error: Publish returned %d, disconnecting.\n", rc);
- mosquitto_disconnect_v5(mosq, MQTT_RC_DISCONNECT_WITH_WILL_MSG, cfg.disconnect_props);
+ if(cfg.qos>0) return rc;
}
}
if(feof(stdin)){
@@ -307,13 +333,13 @@
}
-int pub_other_loop(struct mosquitto *mosq)
+static int pub_other_loop(struct mosquitto *mosq)
{
int rc;
int loop_delay = 1000;
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;
+ loop_delay = (int )cfg.repeat_delay.tv_usec / 2000;
}
do{
@@ -330,7 +356,7 @@
rc = my_publish(mosq, &mid_sent, cfg.topic, 0, NULL, cfg.qos, cfg.retain);
break;
}
- if(rc){
+ if(rc != MOSQ_ERR_SUCCESS && rc != MOSQ_ERR_NO_CONN){
err_printf(&cfg, "Error sending repeat publish: %s", mosquitto_strerror(rc));
}
}
@@ -360,20 +386,28 @@
}
-void print_usage(void)
+static void print_version(void)
+{
+ int major, minor, revision;
+
+ mosquitto_lib_version(&major, &minor, &revision);
+ printf("mosquitto_pub version %s running on libmosquitto %d.%d.%d.\n", VERSION, major, minor, revision);
+}
+
+static void print_usage(void)
{
int major, minor, revision;
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] [--unix path] [-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");
+ printf(" [-c] [-k keepalive] [-q qos] [-r] [--repeat N] [--repeat-delay time] [-x session-expiry]\n");
#ifdef WITH_SRV
- printf(" [-A bind_address] [-S]\n");
+ printf(" [-A bind_address] [--nodelay] [-S]\n");
#else
- printf(" [-A bind_address]\n");
+ printf(" [-A bind_address] [--nodelay]\n");
#endif
printf(" [-i id] [-I id_prefix]\n");
printf(" [-d] [--quiet]\n");
@@ -385,6 +419,7 @@
printf(" [--ciphers ciphers] [--insecure]\n");
printf(" [--tls-alpn protocol]\n");
printf(" [--tls-engine engine] [--keyform keyform] [--tls-engine-kpass-sha1]]\n");
+ printf(" [--tls-use-os-certs]\n");
#ifdef FINAL_WITH_TLS_PSK
printf(" [--psk hex-key --psk-identity identity [--ciphers ciphers]]\n");
#endif
@@ -398,6 +433,11 @@
printf(" -A : bind the outgoing socket to this host/ip address. Use to control which interface\n");
printf(" the client communicates over.\n");
printf(" -d : enable debug messages.\n");
+ printf(" -c : disable clean session/enable persistent client mode\n");
+ printf(" When this argument is used, the broker will be instructed not to clean existing sessions\n");
+ printf(" for the same client id when the client connects, and sessions will never expire when the\n");
+ printf(" client disconnects. MQTT v5 clients can change their session expiry interval with the -x\n");
+ printf(" argument.\n");
printf(" -D : Define MQTT v5 properties. See the documentation for more details.\n");
printf(" -f : send the contents of a file as the message.\n");
printf(" -h : mqtt host to connect to. Defaults to localhost.\n");
@@ -423,10 +463,18 @@
printf(" -u : provide a username\n");
printf(" -V : specify the version of the MQTT protocol to use when connecting.\n");
printf(" Can be mqttv5, mqttv311 or mqttv31. Defaults to mqttv311.\n");
+ printf(" -x : Set the session-expiry-interval property on the CONNECT packet. Applies to MQTT v5\n");
+ printf(" clients only. Set to 0-4294967294 to specify the session will expire in that many\n");
+ printf(" seconds after the client disconnects, or use -1, 4294967295, or ∞ for a session\n");
+ printf(" that does not expire. Defaults to -1 if -c is also given, or 0 if -c not given.\n");
printf(" --help : display this message.\n");
+ printf(" --nodelay : disable Nagle's algorithm, to reduce socket sending latency at the possible\n");
+ printf(" expense of more packets being sent.\n");
+ printf(" --quiet : don't print error messages.\n");
printf(" --repeat : if publish mode is -f, -m, or -s, then repeat the publish N times.\n");
printf(" --repeat-delay : if using --repeat, wait time seconds between publishes. Defaults to 0.\n");
- printf(" --quiet : don't print error messages.\n");
+ printf(" --unix : connect to a broker through a unix domain socket instead of a TCP socket,\n");
+ printf(" e.g. /tmp/mosquitto.sock\n");
printf(" --will-payload : payload for the client Will, which is sent by the broker in case of\n");
printf(" unexpected disconnection. If not given and will-topic is set, a zero\n");
printf(" length message will be sent.\n");
@@ -450,6 +498,7 @@
printf(" Do not use this option in a production environment.\n");
printf(" --tls-engine : If set, enables the use of a TLS engine device.\n");
printf(" --tls-engine-kpass-sha1 : SHA1 of the key password to be used with the selected SSL engine.\n");
+ printf(" --tls-use-os-certs : Load and trust OS provided CA certificates.\n");
# ifdef FINAL_WITH_TLS_PSK
printf(" --psk : pre-shared-key in hexadecimal (no leading 0x) to enable TLS-PSK mode.\n");
printf(" --psk-identity : client identity string for TLS-PSK mode.\n");
@@ -477,6 +526,8 @@
if(rc == 2){
/* --help */
print_usage();
+ }else if(rc == 3){
+ print_version();
}else{
fprintf(stderr, "\nUse 'mosquitto_pub --help' to see usage.\n");
}
@@ -555,7 +606,11 @@
if(rc){
err_printf(&cfg, "Error: %s\n", mosquitto_strerror(rc));
}
- return rc;
+ if(connack_result){
+ return connack_result;
+ }else{
+ return rc;
+ }
cleanup:
mosquitto_lib_cleanup();
diff -Nru mosquitto-1.6.9/client/pub_shared.c mosquitto-2.0.15/client/pub_shared.c
--- mosquitto-1.6.9/client/pub_shared.c 2020-02-27 23:49:51.000000000 +0000
+++ mosquitto-2.0.15/client/pub_shared.c 2022-08-16 13:34:02.000000000 +0000
@@ -2,14 +2,16 @@
Copyright (c) 2009-2020 Roger Light
All rights reserved. This program and the accompanying materials
-are made available under the terms of the Eclipse Public License v1.0
+are made available under the terms of the Eclipse Public License 2.0
and Eclipse Distribution License v1.0 which accompany this distribution.
-
+
The Eclipse Public License is available at
- http://www.eclipse.org/legal/epl-v10.html
+ https://www.eclipse.org/legal/epl-2.0/
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
-
+
+SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+
Contributors:
Roger Light - initial implementation and documentation.
*/
@@ -50,7 +52,7 @@
int load_stdin(void)
{
- long pos = 0, rlen;
+ size_t pos = 0, rlen;
char buf[1024];
char *aux_message = NULL;
@@ -70,7 +72,12 @@
memcpy(&(cfg.message[pos]), buf, rlen);
pos += rlen;
}
- cfg.msglen = pos;
+ if(pos > MQTT_MAX_PAYLOAD){
+ err_printf(&cfg, "Error: Message length must be less than %u bytes.\n\n", MQTT_MAX_PAYLOAD);
+ free(cfg.message);
+ return 1;
+ }
+ cfg.msglen = (int )pos;
if(!cfg.msglen){
err_printf(&cfg, "Error: Zero length input.\n");
@@ -82,8 +89,9 @@
int load_file(const char *filename)
{
- long pos, rlen;
+ size_t pos, rlen;
FILE *fptr = NULL;
+ long flen;
fptr = fopen(filename, "rb");
if(!fptr){
@@ -92,30 +100,33 @@
}
cfg.pub_mode = MSGMODE_FILE;
fseek(fptr, 0, SEEK_END);
- cfg.msglen = ftell(fptr);
- if(cfg.msglen > 268435455){
+ flen = ftell(fptr);
+ if(flen > MQTT_MAX_PAYLOAD){
fclose(fptr);
- err_printf(&cfg, "Error: File \"%s\" is too large (>268,435,455 bytes).\n", filename);
+ err_printf(&cfg, "Error: File must be less than %u bytes.\n\n", MQTT_MAX_PAYLOAD);
+ free(cfg.message);
return 1;
- }else if(cfg.msglen == 0){
+ }else if(flen == 0){
fclose(fptr);
- err_printf(&cfg, "Error: File \"%s\" is empty.\n", filename);
- return 1;
- }else if(cfg.msglen < 0){
+ cfg.message = NULL;
+ cfg.msglen = 0;
+ return 0;
+ }else if(flen < 0){
fclose(fptr);
err_printf(&cfg, "Error: Unable to determine size of file \"%s\".\n", filename);
return 1;
}
+ cfg.msglen = (int )flen;
fseek(fptr, 0, SEEK_SET);
- cfg.message = malloc(cfg.msglen);
+ cfg.message = malloc((size_t )cfg.msglen);
if(!cfg.message){
fclose(fptr);
err_printf(&cfg, "Error: Out of memory.\n");
return 1;
}
pos = 0;
- while(pos < cfg.msglen){
- rlen = fread(&(cfg.message[pos]), sizeof(char), cfg.msglen-pos, fptr);
+ while(pos < (size_t)cfg.msglen){
+ rlen = fread(&(cfg.message[pos]), sizeof(char), (size_t )cfg.msglen-pos, fptr);
pos += rlen;
}
fclose(fptr);
diff -Nru mosquitto-1.6.9/client/pub_shared.h mosquitto-2.0.15/client/pub_shared.h
--- mosquitto-1.6.9/client/pub_shared.h 2020-02-27 23:49:51.000000000 +0000
+++ mosquitto-2.0.15/client/pub_shared.h 2022-08-16 13:34:02.000000000 +0000
@@ -2,14 +2,16 @@
Copyright (c) 2009-2020 Roger Light
All rights reserved. This program and the accompanying materials
-are made available under the terms of the Eclipse Public License v1.0
+are made available under the terms of the Eclipse Public License 2.0
and Eclipse Distribution License v1.0 which accompany this distribution.
-
+
The Eclipse Public License is available at
- http://www.eclipse.org/legal/epl-v10.html
+ https://www.eclipse.org/legal/epl-2.0/
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
-
+
+SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+
Contributors:
Roger Light - initial implementation and documentation.
*/
@@ -21,6 +23,7 @@
#define STATUS_WAITING 2
#define STATUS_DISCONNECTING 3
#define STATUS_DISCONNECTED 4
+#define STATUS_NOHOPE 5
extern int mid_sent;
extern struct mosq_config cfg;
diff -Nru mosquitto-1.6.9/client/rr_client.c mosquitto-2.0.15/client/rr_client.c
--- mosquitto-1.6.9/client/rr_client.c 2020-02-27 23:49:51.000000000 +0000
+++ mosquitto-2.0.15/client/rr_client.c 2022-08-16 13:34:02.000000000 +0000
@@ -2,14 +2,16 @@
Copyright (c) 2009-2020 Roger Light
All rights reserved. This program and the accompanying materials
-are made available under the terms of the Eclipse Public License v1.0
+are made available under the terms of the Eclipse Public License 2.0
and Eclipse Distribution License v1.0 which accompany this distribution.
-
+
The Eclipse Public License is available at
- http://www.eclipse.org/legal/epl-v10.html
+ https://www.eclipse.org/legal/epl-2.0/
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
-
+
+SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+
Contributors:
Roger Light - initial implementation and documentation.
*/
@@ -35,6 +37,7 @@
#include
#include "client_shared.h"
#include "pub_shared.h"
+#include "sub_client_output.h"
enum rr__state {
rr_s_new,
@@ -47,34 +50,41 @@
static enum rr__state client_state = rr_s_new;
-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;
#ifndef WIN32
-void my_signal_handler(int signum)
+static 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;
}
}
#endif
-void print_message(struct mosq_config *cfg, const struct mosquitto_message *message);
-
int my_publish(struct mosquitto *mosq, int *mid, const char *topic, int payloadlen, void *payload, int qos, bool retain)
{
- return mosquitto_publish_v5(mosq, mid, topic, payloadlen, payload, qos, retain, cfg.publish_props);
+ if(cfg.protocol_version < MQTT_PROTOCOL_V5){
+ return mosquitto_publish_v5(mosq, mid, topic, payloadlen, payload, qos, retain, NULL);
+ }else{
+ return mosquitto_publish_v5(mosq, mid, topic, payloadlen, payload, qos, retain, cfg.publish_props);
+ }
}
-void my_message_callback(struct mosquitto *mosq, void *obj, const struct mosquitto_message *message, const mosquitto_property *properties)
+static void my_message_callback(struct mosquitto *mosq, void *obj, const struct mosquitto_message *message, const mosquitto_property *properties)
{
- print_message(&cfg, message);
+ UNUSED(mosq);
+ UNUSED(obj);
+ UNUSED(properties);
+
+ print_message(&cfg, message, properties);
switch(cfg.pub_mode){
case MSGMODE_CMD:
case MSGMODE_FILE:
@@ -117,6 +127,11 @@
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;
mosquitto_subscribe_v5(mosq, NULL, cfg.response_topic, cfg.qos, 0, cfg.subscribe_props);
@@ -134,8 +149,12 @@
}
-void my_subscribe_callback(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos)
+static 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{
@@ -148,11 +167,25 @@
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;
}
-void print_usage(void)
+static void print_version(void)
+{
+ int major, minor, revision;
+
+ mosquitto_lib_version(&major, &minor, &revision);
+ printf("mosquitto_rr version %s running on libmosquitto %d.%d.%d.\n", VERSION, major, minor, revision);
+}
+
+static void print_usage(void)
{
int major, minor, revision;
@@ -161,16 +194,16 @@
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(" [-c] [-k keepalive] [-q qos] [-R]\n");
+ printf("Usage: mosquitto_rr {[-h host] [--unix path] [-p port] [-u username] [-P password] -t topic | -L URL} -e response-topic\n");
+ printf(" [-c] [-k keepalive] [-q qos] [-R] [-x session-expiry-interval\n");
printf(" [-F format]\n");
#ifndef WIN32
printf(" [-W timeout_secs]\n");
#endif
#ifdef WITH_SRV
- printf(" [-A bind_address] [-S]\n");
+ printf(" [-A bind_address] [--nodelay] [-S]\n");
#else
- printf(" [-A bind_address]\n");
+ printf(" [-A bind_address] [--nodelay]\n");
#endif
printf(" [-i id] [-I id_prefix]\n");
printf(" [-d] [-N] [--quiet] [-v]\n");
@@ -180,6 +213,7 @@
printf(" [--ciphers ciphers] [--insecure]\n");
printf(" [--tls-alpn protocol]\n");
printf(" [--tls-engine engine] [--keyform keyform] [--tls-engine-kpass-sha1]]\n");
+ printf(" [--tls-use-os-certs]\n");
#ifdef FINAL_WITH_TLS_PSK
printf(" [--psk hex-key --psk-identity identity [--ciphers ciphers]]\n");
#endif
@@ -191,9 +225,14 @@
printf(" mosquitto_rr --help\n\n");
printf(" -A : bind the outgoing socket to this host/ip address. Use to control which interface\n");
printf(" the client communicates over.\n");
- printf(" -c : disable 'clean session' (store subscription and pending messages when client disconnects).\n");
+ printf(" -c : disable clean session/enable persistent client mode\n");
+ printf(" When this argument is used, the broker will be instructed not to clean existing sessions\n");
+ printf(" for the same client id when the client connects, and sessions will never expire when the\n");
+ printf(" client disconnects. MQTT v5 clients can change their session expiry interval with the -x\n");
+ printf(" argument.\n");
printf(" -d : enable debug messages.\n");
printf(" -D : Define MQTT v5 properties. See the documentation for more details.\n");
+ printf(" -e : Response topic. The client will subscribe to this topic to wait for a response.\n");
printf(" -F : output format.\n");
printf(" -h : mqtt host to connect to. Defaults to localhost.\n");
printf(" -i : id to use for this client. Defaults to mosquitto_rr_ appended with the process id.\n");
@@ -208,7 +247,7 @@
#ifdef WITH_SRV
printf(" -S : use SRV lookups to determine which host to connect to.\n");
#endif
- printf(" -t : mqtt response topic to subscribe to. May be repeated multiple times.\n");
+ printf(" -t : topic where the request message will be sent.\n");
printf(" -u : provide a username\n");
printf(" -v : print received messages verbosely.\n");
printf(" -V : specify the version of the MQTT protocol to use when connecting.\n");
@@ -216,8 +255,18 @@
#ifndef WIN32
printf(" -W : Specifies a timeout in seconds how long to wait for a response.\n");
#endif
+ printf(" -x : Set the session-expiry-interval property on the CONNECT packet. Applies to MQTT v5\n");
+ printf(" clients only. Set to 0-4294967294 to specify the session will expire in that many\n");
+ printf(" seconds after the client disconnects, or use -1, 4294967295, or ∞ for a session\n");
+ printf(" that does not expire. Defaults to -1 if -c is also given, or 0 if -c not given.\n");
printf(" --help : display this message.\n");
+ printf(" --nodelay : disable Nagle's algorithm, to reduce socket sending latency at the possible\n");
+ printf(" expense of more packets being sent.\n");
+ printf(" --pretty : print formatted output rather than minimised output when using the\n");
+ printf(" JSON output format option.\n");
printf(" --quiet : don't print error messages.\n");
+ printf(" --unix : connect to a broker through a unix domain socket instead of a TCP socket,\n");
+ printf(" e.g. /tmp/mosquitto.sock\n");
printf(" --will-payload : payload for the client Will, which is sent by the broker in case of\n");
printf(" unexpected disconnection. If not given and will-topic is set, a zero\n");
printf(" length message will be sent.\n");
@@ -232,7 +281,8 @@
printf(" --cert : client certificate for authentication, if required by server.\n");
printf(" --key : client private key for authentication, if required by server.\n");
printf(" --ciphers : openssl compatible list of TLS ciphers to support.\n");
- printf(" --tls-version : TLS protocol version, can be one of tlsv1.2 tlsv1.1 or tlsv1.\n");
+ printf(" --tls-use-os-certs : Load and trust OS provided CA certificates.\n");
+ printf(" --tls-version : TLS protocol version, can be one of tlsv1.3 tlsv1.2 or tlsv1.1.\n");
printf(" Defaults to tlsv1.2 if available.\n");
printf(" --insecure : do not check that the server certificate hostname matches the remote\n");
printf(" hostname. Using this option means that you cannot be sure that the\n");
@@ -259,12 +309,16 @@
#endif
mosquitto_lib_init();
+ output_init();
rc = client_config_load(&cfg, CLIENT_RR, argc, argv);
if(rc){
if(rc == 2){
/* --help */
print_usage();
+ }else if(rc == 3){
+ /* --version */
+ print_version();
}else{
fprintf(stderr, "\nUse 'mosquitto_rr --help' to see usage.\n");
}
@@ -291,8 +345,8 @@
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");
@@ -303,17 +357,17 @@
}
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;
}
@@ -334,17 +388,17 @@
#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 */
@@ -353,17 +407,24 @@
}
}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){
rc = 0;
}
client_config_cleanup(&cfg);
- if(rc){
+ if(timed_out){
+ err_printf(&cfg, "Timed out\n");
+ return MOSQ_ERR_TIMEOUT;
+ }else if(rc){
err_printf(&cfg, "Error: %s\n", mosquitto_strerror(rc));
}
- return rc;
+ if(connack_result){
+ return connack_result;
+ }else{
+ return rc;
+ }
cleanup:
mosquitto_lib_cleanup();
diff -Nru mosquitto-1.6.9/client/sub_client.c mosquitto-2.0.15/client/sub_client.c
--- mosquitto-1.6.9/client/sub_client.c 2020-02-27 23:49:51.000000000 +0000
+++ mosquitto-2.0.15/client/sub_client.c 2022-08-16 13:34:02.000000000 +0000
@@ -2,14 +2,16 @@
Copyright (c) 2009-2020 Roger Light
All rights reserved. This program and the accompanying materials
-are made available under the terms of the Eclipse Public License v1.0
+are made available under the terms of the Eclipse Public License 2.0
and Eclipse Distribution License v1.0 which accompany this distribution.
-
+
The Eclipse Public License is available at
- http://www.eclipse.org/legal/epl-v10.html
+ https://www.eclipse.org/legal/epl-2.0/
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
-
+
+SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+
Contributors:
Roger Light - initial implementation and documentation.
*/
@@ -34,39 +36,36 @@
#include
#include
#include "client_shared.h"
+#include "sub_client_output.h"
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;
+bool connack_received = false;
#ifndef WIN32
-void my_signal_handler(int signum)
+static void my_signal_handler(int signum)
{
if(signum == SIGALRM || signum == SIGTERM || signum == SIGINT){
- process_messages = false;
- mosquitto_disconnect_v5(mosq, MQTT_RC_DISCONNECT_WITH_WILL_MSG, cfg.disconnect_props);
+ if(connack_received){
+ process_messages = false;
+ mosquitto_disconnect_v5(g_mosq, MQTT_RC_DISCONNECT_WITH_WILL_MSG, cfg.disconnect_props);
+ }else{
+ exit(-1);
+ }
}
-}
-#endif
-
-void print_message(struct mosq_config *cfg, const struct mosquitto_message *message);
-
-
-void my_publish_callback(struct mosquitto *mosq, void *obj, int mid, int reason_code, const mosquitto_property *properties)
-{
- UNUSED(obj);
- UNUSED(reason_code);
- UNUSED(properties);
-
- if(process_messages == false && (mid == last_mid || last_mid == 0)){
- mosquitto_disconnect_v5(mosq, 0, cfg.disconnect_props);
+ if(signum == SIGALRM){
+ timed_out = true;
}
}
+#endif
-void my_message_callback(struct mosquitto *mosq, void *obj, const struct mosquitto_message *message, const mosquitto_property *properties)
+static void my_message_callback(struct mosquitto *mosq, void *obj, const struct mosquitto_message *message, const mosquitto_property *properties)
{
int i;
bool res;
@@ -96,7 +95,10 @@
mosquitto_publish(mosq, &last_mid, message->topic, 0, NULL, 1, true);
}
- print_message(&cfg, message);
+ print_message(&cfg, message, properties);
+ if(ferror(stdout)){
+ mosquitto_disconnect_v5(mosq, 0, cfg.disconnect_props);
+ }
if(cfg.msg_count>0){
msg_count++;
@@ -109,7 +111,7 @@
}
}
-void my_connect_callback(struct mosquitto *mosq, void *obj, int result, int flags, const mosquitto_property *properties)
+static void my_connect_callback(struct mosquitto *mosq, void *obj, int result, int flags, const mosquitto_property *properties)
{
int i;
@@ -117,6 +119,9 @@
UNUSED(flags);
UNUSED(properties);
+ connack_received = true;
+
+ connack_result = result;
if(!result){
mosquitto_subscribe_multiple(mosq, NULL, cfg.topic_count, cfg.topics, cfg.qos, cfg.sub_opts, cfg.subscribe_props);
@@ -139,18 +144,23 @@
}
}
-void my_subscribe_callback(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos)
+static void my_subscribe_callback(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos)
{
int i;
-
+ bool some_sub_allowed = (granted_qos[0] < 128);
+ bool should_print = cfg.debug && !cfg.quiet;
UNUSED(obj);
- if(cfg.debug){
- if(!cfg.quiet) printf("Subscribed (mid: %d): %d", mid, granted_qos[0]);
- for(i=1; i0 && rc == MOSQ_ERR_NO_CONN){
rc = 0;
}
client_config_cleanup(&cfg);
- if(rc){
+ if(timed_out){
+ err_printf(&cfg, "Timed out\n");
+ return MOSQ_ERR_TIMEOUT;
+ }else if(rc){
err_printf(&cfg, "Error: %s\n", mosquitto_strerror(rc));
}
- return rc;
+ if(connack_result){
+ return connack_result;
+ }else{
+ return rc;
+ }
cleanup:
- mosquitto_destroy(mosq);
+ mosquitto_destroy(g_mosq);
mosquitto_lib_cleanup();
client_config_cleanup(&cfg);
return 1;
diff -Nru mosquitto-1.6.9/client/sub_client_output.c mosquitto-2.0.15/client/sub_client_output.c
--- mosquitto-1.6.9/client/sub_client_output.c 2020-02-27 23:49:51.000000000 +0000
+++ mosquitto-2.0.15/client/sub_client_output.c 2022-08-16 13:34:02.000000000 +0000
@@ -2,20 +2,29 @@
Copyright (c) 2009-2020 Roger Light
All rights reserved. This program and the accompanying materials
-are made available under the terms of the Eclipse Public License v1.0
+are made available under the terms of the Eclipse Public License 2.0
and Eclipse Distribution License v1.0 which accompany this distribution.
-
+
The Eclipse Public License is available at
- http://www.eclipse.org/legal/epl-v10.html
+ https://www.eclipse.org/legal/epl-2.0/
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
-
+
+SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
+#ifdef WIN32
+ /* For rand_s on Windows */
+# define _CRT_RAND_S
+# include
+# include
+#endif
+
#include
#include
#include
@@ -30,12 +39,18 @@
#define snprintf sprintf_s
#endif
+#ifdef WITH_CJSON
+# include
+#endif
+
#ifdef __APPLE__
# include
#endif
#include
+#include
#include "client_shared.h"
+#include "sub_client_output.h"
extern struct mosq_config cfg;
@@ -78,12 +93,35 @@
}
-static void write_payload(const unsigned char *payload, int payloadlen, int hex)
+static void write_payload(const unsigned char *payload, int payloadlen, int hex, char align, char pad, int field_width, int precision)
{
int i;
+ int padlen;
+
+ UNUSED(precision); /* FIXME - use or remove */
+
+ if(field_width > 0){
+ if(payloadlen > field_width){
+ payloadlen = field_width;
+ }
+ if(hex > 0){
+ payloadlen /= 2;
+ padlen = field_width - payloadlen*2;
+ }else{
+ padlen = field_width - payloadlen;
+ }
+ }else{
+ padlen = field_width - payloadlen;
+ }
+
+ if(align != '-'){
+ for(i=0; itopic);
+ if(tmp == NULL){
+ cJSON_Delete(root);
+ return MOSQ_ERR_NOMEM;
+ }
+
+ cJSON_AddItemToObject(root, "topic", tmp);
+
+ tmp = cJSON_CreateNumber(message->qos);
+ if(tmp == NULL){
+ cJSON_Delete(root);
+ return MOSQ_ERR_NOMEM;
+ }
+ cJSON_AddItemToObject(root, "qos", tmp);
+
+ tmp = cJSON_CreateNumber(message->retain);
+ if(tmp == NULL){
+ cJSON_Delete(root);
+ return MOSQ_ERR_NOMEM;
+ }
+ cJSON_AddItemToObject(root, "retain", tmp);
+
+ tmp = cJSON_CreateNumber(message->payloadlen);
+ if(tmp == NULL){
+ cJSON_Delete(root);
+ return MOSQ_ERR_NOMEM;
+ }
+ cJSON_AddItemToObject(root, "payloadlen", tmp);
- strftime(buf, 100, "%s", ti);
- printf("{\"tst\":%s,\"topic\":\"%s\",\"qos\":%d,\"retain\":%d,\"payloadlen\":%d,", buf, message->topic, message->qos, message->retain, message->payloadlen);
+ if(message->qos > 0){
+ tmp = cJSON_CreateNumber(message->mid);
+ if(tmp == NULL){
+ cJSON_Delete(root);
+ return MOSQ_ERR_NOMEM;
+ }
+ cJSON_AddItemToObject(root, "mid", tmp);
+ }
+
+ /* Properties */
+ if(properties){
+ if(json_print_properties(root, properties)){
+ cJSON_Delete(root);
+ return MOSQ_ERR_NOMEM;
+ }
+ }
+
+ /* Payload */
+ if(escaped){
+ if(message->payload){
+ tmp = cJSON_CreateString(message->payload);
+ }else{
+ tmp = cJSON_CreateNull();
+ }
+ if(tmp == NULL){
+ cJSON_Delete(root);
+ return MOSQ_ERR_NOMEM;
+ }
+ cJSON_AddItemToObject(root, "payload", tmp);
+ }else{
+ return_parse_end = NULL;
+ if(message->payload){
+ tmp = cJSON_ParseWithOpts(message->payload, &return_parse_end, true);
+ if(tmp == NULL || return_parse_end != (char *)message->payload + message->payloadlen){
+ cJSON_Delete(root);
+ return MOSQ_ERR_INVAL;
+ }
+ }else{
+ tmp = cJSON_CreateNull();
+ if(tmp == NULL){
+ cJSON_Delete(root);
+ return MOSQ_ERR_INVAL;
+ }
+ }
+ cJSON_AddItemToObject(root, "payload", tmp);
+ }
+
+ if(pretty){
+ json_str = cJSON_Print(root);
+ }else{
+ json_str = cJSON_PrintUnformatted(root);
+ }
+ cJSON_Delete(root);
+ if(json_str == NULL){
+ return MOSQ_ERR_NOMEM;
+ }
+
+ fputs(json_str, stdout);
+ free(json_str);
+
+ return MOSQ_ERR_SUCCESS;
+#else
+ UNUSED(properties);
+ UNUSED(pretty);
+
+ format_time_8601(ti, ns, buf, sizeof(buf));
+
+ printf("{\"tst\":\"%s\",\"topic\":\"%s\",\"qos\":%d,\"retain\":%d,\"payloadlen\":%d,", buf, message->topic, message->qos, message->retain, message->payloadlen);
if(message->qos > 0){
printf("\"mid\":%d,", message->mid);
}
@@ -125,111 +387,315 @@
fputs("\"}", stdout);
}else{
fputs("\"payload\":", stdout);
- write_payload(message->payload, message->payloadlen, 0);
+ write_payload(message->payload, message->payloadlen, 0, 0, 0, 0, 0);
fputs("}", stdout);
}
+
+ return MOSQ_ERR_SUCCESS;
+#endif
}
-static void formatted_print(const struct mosq_config *lcfg, const struct mosquitto_message *message)
+static void formatted_print_blank(char pad, int field_width)
{
- int len;
int i;
+ for(i=0; iformat);
+ case 'C':
+ if(mosquitto_property_read_string(properties, MQTT_PROP_CONTENT_TYPE, &strvalue, false)){
+ formatted_print_str(strvalue, align, field_width, precision);
+ free(strvalue);
+ }else{
+ formatted_print_blank(' ', field_width);
+ }
+ break;
- for(i=0; iformat[i] == '%'){
- if(i < len-1){
- i++;
- switch(lcfg->format[i]){
- case '%':
- fputc('%', stdout);
- break;
+ case 'D':
+ if(mosquitto_property_read_binary(properties, MQTT_PROP_CORRELATION_DATA, (void **)&binvalue, &i16value, false)){
+ fwrite(binvalue, 1, i16value, stdout);
+ free(binvalue);
+ }
+ break;
- case 'I':
- if(!ti){
- if(get_time(&ti, &ns)){
- err_printf(lcfg, "Error obtaining system time.\n");
- return;
- }
- }
- if(strftime(buf, 100, "%FT%T%z", ti) != 0){
- fputs(buf, stdout);
- }
- break;
+ case 'E':
+ if(mosquitto_property_read_int32(properties, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, &i32value, false)){
+ formatted_print_int((int)i32value, align, pad, field_width);
+ }else{
+ formatted_print_blank(pad, field_width);
+ }
+ break;
- case 'j':
- if(!ti){
- if(get_time(&ti, &ns)){
- err_printf(lcfg, "Error obtaining system time.\n");
- return;
- }
- }
- json_print(message, ti, true);
- break;
+ case 'F':
+ if(mosquitto_property_read_byte(properties, MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, &i8value, false)){
+ formatted_print_int(i8value, align, pad, field_width);
+ }else{
+ formatted_print_blank(pad, field_width);
+ }
+ break;
- case 'J':
- if(!ti){
- if(get_time(&ti, &ns)){
- err_printf(lcfg, "Error obtaining system time.\n");
- return;
- }
- }
- json_print(message, ti, false);
- break;
+ case 'I':
+ if(!ti){
+ if(get_time(&ti, &ns)){
+ err_printf(lcfg, "Error obtaining system time.\n");
+ return;
+ }
+ }
+ if(strftime(buf, 100, "%FT%T%z", ti) != 0){
+ formatted_print_str(buf, align, field_width, precision);
+ }else{
+ formatted_print_blank(' ', field_width);
+ }
+ break;
- case 'l':
- printf("%d", message->payloadlen);
- break;
+ case 'j':
+ if(!ti){
+ if(get_time(&ti, &ns)){
+ err_printf(lcfg, "Error obtaining system time.\n");
+ return;
+ }
+ }
+ if(json_print(message, properties, ti, (int)ns, true, lcfg->pretty) != MOSQ_ERR_SUCCESS){
+ err_printf(lcfg, "Error: Out of memory.\n");
+ return;
+ }
+ break;
- case 'm':
- printf("%d", message->mid);
- break;
+ case 'J':
+ if(!ti){
+ if(get_time(&ti, &ns)){
+ err_printf(lcfg, "Error obtaining system time.\n");
+ return;
+ }
+ }
+ rc = json_print(message, properties, ti, (int)ns, false, lcfg->pretty);
+ if(rc == MOSQ_ERR_NOMEM){
+ err_printf(lcfg, "Error: Out of memory.\n");
+ return;
+ }else if(rc == MOSQ_ERR_INVAL){
+ err_printf(lcfg, "Error: Message payload is not valid JSON on topic %s.\n", message->topic);
+ return;
+ }
+ break;
- case 'p':
- write_payload(message->payload, message->payloadlen, 0);
- break;
+ case 'l':
+ formatted_print_int(message->payloadlen, align, pad, field_width);
+ break;
+
+ case 'm':
+ formatted_print_int(message->mid, align, pad, field_width);
+ break;
+
+ case 'P':
+ strname = NULL;
+ strvalue = NULL;
+ prop = mosquitto_property_read_string_pair(properties, MQTT_PROP_USER_PROPERTY, &strname, &strvalue, false);
+ while(prop){
+ printf("%s:%s", strname, strvalue);
+ free(strname);
+ free(strvalue);
+ strname = NULL;
+ strvalue = NULL;
+
+ prop = mosquitto_property_read_string_pair(prop, MQTT_PROP_USER_PROPERTY, &strname, &strvalue, true);
+ if(prop){
+ fputc(' ', stdout);
+ }
+ }
+ free(strname);
+ free(strvalue);
+ break;
+
+ case 'p':
+ write_payload(message->payload, message->payloadlen, 0, align, pad, field_width, precision);
+ break;
+
+ case 'q':
+ fputc(message->qos + 48, stdout);
+ break;
+
+ case 'R':
+ if(mosquitto_property_read_string(properties, MQTT_PROP_RESPONSE_TOPIC, &strvalue, false)){
+ formatted_print_str(strvalue, align, field_width, precision);
+ free(strvalue);
+ }
+ break;
- case 'q':
- fputc(message->qos + 48, stdout);
- break;
+ case 'r':
+ if(message->retain){
+ fputc('1', stdout);
+ }else{
+ fputc('0', stdout);
+ }
+ break;
- case 'r':
- if(message->retain){
- fputc('1', stdout);
- }else{
- fputc('0', stdout);
- }
- break;
+ case 'S':
+ if(mosquitto_property_read_varint(properties, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, &i32value, false)){
+ formatted_print_int((int)i32value, align, pad, field_width);
+ }else{
+ formatted_print_blank(pad, field_width);
+ }
+ break;
- case 't':
- fputs(message->topic, stdout);
- break;
+ case 't':
+ formatted_print_str(message->topic, align, field_width, precision);
+ break;
+
+ case 'U':
+ if(!ti){
+ if(get_time(&ti, &ns)){
+ err_printf(lcfg, "Error obtaining system time.\n");
+ return;
+ }
+ }
+ if(strftime(buf, 100, "%s", ti) != 0){
+ printf("%s.%09ld", buf, ns);
+ }
+ break;
- case 'U':
- if(!ti){
- if(get_time(&ti, &ns)){
- err_printf(lcfg, "Error obtaining system time.\n");
- return;
- }
- }
- if(strftime(buf, 100, "%s", ti) != 0){
- printf("%s.%09ld", buf, ns);
- }
- break;
+ case 'x':
+ write_payload(message->payload, message->payloadlen, 1, align, pad, field_width, precision);
+ break;
+
+ case 'X':
+ write_payload(message->payload, message->payloadlen, 2, align, pad, field_width, precision);
+ break;
+ }
+}
- case 'x':
- write_payload(message->payload, message->payloadlen, 1);
- break;
- case 'X':
- write_payload(message->payload, message->payloadlen, 2);
- break;
+static void formatted_print(const struct mosq_config *lcfg, const struct mosquitto_message *message, const mosquitto_property *properties)
+{
+ size_t len;
+ size_t i;
+ struct tm *ti = NULL;
+ long ns = 0;
+ char strf[3] = {0, 0 ,0};
+ char buf[100];
+ char align, pad;
+ int field_width, precision;
+
+ len = strlen(lcfg->format);
+
+ for(i=0; iformat[i] == '%'){
+ align = 0;
+ pad = ' ';
+ field_width = 0;
+ precision = -1;
+ if(i < len-1){
+ i++;
+ /* Optional alignment */
+ if(lcfg->format[i] == '-'){
+ align = lcfg->format[i];
+ if(i < len-1){
+ i++;
+ }
+ }
+ /* "%-040p" is allowed by this combination of checks, but isn't
+ * a valid format specifier, the '0' will be ignored. */
+ /* Optional zero padding */
+ if(lcfg->format[i] == '0'){
+ pad = '0';
+ if(i < len-1){
+ i++;
+ }
+ }
+ /* Optional field width */
+ while(i < len-1 && lcfg->format[i] >= '0' && lcfg->format[i] <= '9'){
+ field_width *= 10;
+ field_width += lcfg->format[i]-'0';
+ i++;
+ }
+ /* Optional precision */
+ if(lcfg->format[i] == '.'){
+ if(i < len-1){
+ i++;
+ precision = 0;
+ while(i < len-1 && lcfg->format[i] >= '0' && lcfg->format[i] <= '9'){
+ precision *= 10;
+ precision += lcfg->format[i]-'0';
+ i++;
+ }
+ }
+ }
+
+ if(i < len){
+ formatted_print_percent(lcfg, message, properties, lcfg->format[i], align, pad, field_width, precision);
}
}
}else if(lcfg->format[i] == '@'){
@@ -306,27 +772,59 @@
}
-void print_message(struct mosq_config *cfg, const struct mosquitto_message *message)
+void output_init(void)
+{
+#ifndef WIN32
+ struct tm *ti = NULL;
+ long ns;
+
+ if(!get_time(&ti, &ns)){
+ srandom((unsigned int)ns);
+ }
+#else
+ /* Disable text translation so binary payloads aren't modified */
+ _setmode(_fileno(stdout), _O_BINARY);
+#endif
+}
+
+
+void print_message(struct mosq_config *lcfg, const struct mosquitto_message *message, const mosquitto_property *properties)
{
- if(cfg->format){
- formatted_print(cfg, message);
- }else if(cfg->verbose){
+#ifdef WIN32
+ unsigned int r = 0;
+#else
+ long r = 0;
+#endif
+
+ if(lcfg->random_filter < 10000){
+#ifdef WIN32
+ rand_s(&r);
+#else
+ r = random();
+#endif
+ if((long)(r%10000) >= lcfg->random_filter){
+ return;
+ }
+ }
+ 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);
- if(cfg->eol){
+ write_payload(message->payload, message->payloadlen, false, 0, 0, 0, 0);
+ if(lcfg->eol){
printf("\n");
}
}else{
- if(cfg->eol){
+ if(lcfg->eol){
printf("%s (null)\n", message->topic);
}
}
fflush(stdout);
}else{
if(message->payloadlen){
- write_payload(message->payload, message->payloadlen, false);
- if(cfg->eol){
+ write_payload(message->payload, message->payloadlen, false, 0, 0, 0, 0);
+ if(lcfg->eol){
printf("\n");
}
fflush(stdout);
diff -Nru mosquitto-1.6.9/client/sub_client_output.h mosquitto-2.0.15/client/sub_client_output.h
--- mosquitto-1.6.9/client/sub_client_output.h 1970-01-01 00:00:00.000000000 +0000
+++ mosquitto-2.0.15/client/sub_client_output.h 2022-08-16 13:34:02.000000000 +0000
@@ -0,0 +1,28 @@
+/*
+Copyright (c) 2019 Roger Light
+
+All rights reserved. This program and the accompanying materials
+are made available under the terms of the Eclipse Public License 2.0
+and Eclipse Distribution License v1.0 which accompany this distribution.
+
+The Eclipse Public License is available at
+ https://www.eclipse.org/legal/epl-2.0/
+and the Eclipse Distribution License is available at
+ http://www.eclipse.org/org/documents/edl-v10.php.
+
+SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
+
+Contributors:
+ Roger Light - initial implementation and documentation.
+*/
+
+#ifndef SUB_CLIENT_OUTPUT_H
+#define SUB_CLIENT_OUTPUT_H
+
+#include "mosquitto.h"
+#include "client_shared.h"
+
+void output_init(void);
+void print_message(struct mosq_config *cfg, const struct mosquitto_message *message, const mosquitto_property *properties);
+
+#endif
diff -Nru mosquitto-1.6.9/client/sub_test_fixed_width mosquitto-2.0.15/client/sub_test_fixed_width
--- mosquitto-1.6.9/client/sub_test_fixed_width 1970-01-01 00:00:00.000000000 +0000
+++ mosquitto-2.0.15/client/sub_test_fixed_width 2022-08-16 13:34:02.000000000 +0000
@@ -0,0 +1,59 @@
+LD_LIBRARY_PATH=../lib ./mosquitto_sub \
+ -h test.mosquitto.org \
+ --retained-only \
+ --remove-retained \
+ -t FW/# \
+ -W 1>/dev/null 2>/dev/null
+
+LD_LIBRARY_PATH=../lib ./mosquitto_pub \
+ -h test.mosquitto.org \
+ -D publish content-type "application/json" \
+ -D publish message-expiry-interval 360000 \
+ -D publish payload-format-indicator 1 \
+ -D publish response-topic response-topic \
+ -m ABCDEFGHIJKLMNOPQRSTUVWXYZ \
+ -q 2 \
+ -r \
+ -t FW/truncate \
+ -V 5
+
+LD_LIBRARY_PATH=../lib ./mosquitto_pub \
+ -h test.mosquitto.org \
+ -D publish content-type "null" \
+ -D publish message-expiry-interval 3600 \
+ -D publish payload-format-indicator 1 \
+ -D publish response-topic r-t \
+ -m Off \
+ -q 2 \
+ -r \
+ -t FW/expire \
+ -V 5
+
+LD_LIBRARY_PATH=../lib ./mosquitto_pub \
+ -h test.mosquitto.org \
+ -D publish payload-format-indicator 1 \
+ -D publish response-topic rt \
+ -m Offline \
+ -q 2 \
+ -r \
+ -t FW/1 \
+ -V 5
+
+LD_LIBRARY_PATH=../lib ./mosquitto_sub \
+ -h test.mosquitto.org \
+ -C 3 \
+ -F "| %10t | %-10t | %7x | %-7x | %08x | %7X | %-7X | %08X | %8p | %-8p | %3m | %-3m | %03m | %3l | %-3l | %03l | %2F | %-2F | %02F | %5C | %-5C | %5E | %-5E | %05E | %5A | %5R | %-5R |" \
+ -q 2 \
+ -t 'FW/#' \
+ -V 5
+
+echo
+
+LD_LIBRARY_PATH=../lib ./mosquitto_sub \
+ -h test.mosquitto.org \
+ -C 3 \
+ -F "| %10.10t | %.5t | %-10.10t | %7x | %-7x | %08x | %7X | %-7X | %08X | %8p | %-8p | %3m | %-3m | %03m | %3l | %-3l | %03l | %2F | %-2F | %02F | %5C | %-5C | %5E | %-5E | %05E | %5.5A | %5.5R | %-5.5R |" \
+ -q 2 \
+ -t 'FW/#' \
+ -V 5
+
diff -Nru mosquitto-1.6.9/cmake/FindcJSON.cmake mosquitto-2.0.15/cmake/FindcJSON.cmake
--- mosquitto-1.6.9/cmake/FindcJSON.cmake 1970-01-01 00:00:00.000000000 +0000
+++ mosquitto-2.0.15/cmake/FindcJSON.cmake 2022-08-16 13:34:02.000000000 +0000
@@ -0,0 +1,38 @@
+INCLUDE( FindPackageHandleStandardArgs )
+
+# Checks an environment variable; note that the first check
+# does not require the usual CMake $-sign.
+IF( DEFINED ENV{CJSON_DIR} )
+ SET( CJSON_DIR "$ENV{CJSON_DIR}" )
+ENDIF()
+
+FIND_PATH(
+ CJSON_INCLUDE_DIR
+ cjson/cJSON.h
+ HINTS
+ CJSON_DIR
+)
+
+FIND_LIBRARY( CJSON_LIBRARY
+ NAMES cjson
+ HINTS ${CJSON_DIR}
+)
+
+FIND_PACKAGE_HANDLE_STANDARD_ARGS( cJSON DEFAULT_MSG
+ CJSON_INCLUDE_DIR CJSON_LIBRARY
+)
+
+IF( CJSON_FOUND )
+ SET( CJSON_INCLUDE_DIRS ${CJSON_INCLUDE_DIR} )
+ SET( CJSON_LIBRARIES ${CJSON_LIBRARY} )
+
+ MARK_AS_ADVANCED(
+ CJSON_LIBRARY
+ CJSON_INCLUDE_DIR
+ CJSON_DIR
+ )
+ELSE()
+ SET( CJSON_DIR "" CACHE STRING
+ "An optional hint to a directory for finding `cJSON`"
+ )
+ENDIF()
diff -Nru mosquitto-1.6.9/CMakeLists.txt mosquitto-2.0.15/CMakeLists.txt
--- mosquitto-1.6.9/CMakeLists.txt 2020-02-27 23:49:51.000000000 +0000
+++ mosquitto-2.0.15/CMakeLists.txt 2022-08-16 13:34:02.000000000 +0000
@@ -4,14 +4,13 @@
# To configure the build options either use the CMake gui, or run the command
# line utility including the "-i" option.
-set(CMAKE_LEGACY_CYGWIN_WIN32 0)
+cmake_minimum_required(VERSION 3.1)
+cmake_policy(SET CMP0042 NEW)
project(mosquitto)
+set (VERSION 2.0.15)
-cmake_minimum_required(VERSION 2.8)
-# Only for version 3 and up. cmake_policy(SET CMP0042 NEW)
-
-set (VERSION 1.6.9)
+list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/")
add_definitions (-DCMAKE -DVERSION=\"${VERSION}\")
@@ -20,8 +19,13 @@
add_definitions("-D_CRT_NONSTDC_NO_DEPRECATE")
endif (WIN32)
+if(APPLE)
+ set(CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "${CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS} -undefined dynamic_lookup")
+endif(APPLE)
+
include(GNUInstallDirs)
+option(WITH_BUNDLED_DEPS "Build with bundled dependencies?" ON)
option(WITH_TLS
"Include SSL/TLS support?" ON)
option(WITH_TLS_PSK
@@ -43,6 +47,12 @@
set (OPENSSL_INCLUDE_DIR "")
endif (WITH_TLS)
+
+option(WITH_UNIX_SOCKETS "Include Unix Domain Socket support?" ON)
+if (WITH_UNIX_SOCKETS AND NOT WIN32)
+ add_definitions("-DWITH_UNIX_SOCKETS")
+endif (WITH_UNIX_SOCKETS AND NOT WIN32)
+
option(WITH_SOCKS "Include SOCKS5 support?" ON)
if (WITH_SOCKS)
add_definitions("-DWITH_SOCKS")
@@ -57,12 +67,12 @@
if (WITH_THREADING)
add_definitions("-DWITH_THREADING")
if (WIN32)
- if (CMAKE_CL_64)
- set (PTHREAD_LIBRARIES C:\\pthreads\\Pre-built.2\\lib\\x64\\pthreadVC2.lib)
- else (CMAKE_CL_64)
- set (PTHREAD_LIBRARIES C:\\pthreads\\Pre-built.2\\lib\\x86\\pthreadVC2.lib)
- endif (CMAKE_CL_64)
- set (PTHREAD_INCLUDE_DIR C:\\pthreads\\Pre-built.2\\include)
+ find_package(Threads REQUIRED)
+ set (PTHREAD_LIBRARIES Threads::Threads)
+ set (PTHREAD_INCLUDE_DIR "")
+ elseif (ANDROID)
+ set (PTHREAD_LIBRARIES "")
+ set (PTHREAD_INCLUDE_DIR "")
else (WIN32)
find_library(LIBPTHREAD pthread)
if (LIBPTHREAD)
@@ -77,8 +87,6 @@
set (PTHREAD_INCLUDE_DIR "")
endif (WITH_THREADING)
-option(DOCUMENTATION "Build documentation?" ON)
-
option(WITH_DLT "Include DLT support?" OFF)
message(STATUS "WITH_DLT = ${WITH_DLT}")
if (WITH_DLT)
@@ -88,13 +96,43 @@
add_definitions("-DWITH_DLT")
endif (WITH_DLT)
+option(WITH_CJSON "Build with cJSON support (required for dynamic security plugin and useful for mosquitto_sub)?" ON)
+if (WITH_CJSON)
+ FIND_PACKAGE(cJSON)
+ if (CJSON_FOUND)
+ message(STATUS ${CJSON_FOUND})
+ else (CJSON_FOUND)
+ message(STATUS "Optional dependency cJSON not found. Some features will be disabled.")
+ endif(CJSON_FOUND)
+endif()
+
# ========================================
# Include projects
# ========================================
+option(WITH_CLIENTS "Build clients?" ON)
+option(WITH_BROKER "Build broker?" ON)
+option(WITH_APPS "Build apps?" ON)
+option(WITH_PLUGINS "Build plugins?" ON)
+option(DOCUMENTATION "Build documentation?" ON)
+
add_subdirectory(lib)
-add_subdirectory(client)
-add_subdirectory(src)
+if (WITH_CLIENTS)
+ add_subdirectory(client)
+endif (WITH_CLIENTS)
+
+if (WITH_BROKER)
+ add_subdirectory(src)
+endif (WITH_BROKER)
+
+if (WITH_APPS)
+ add_subdirectory(apps)
+endif (WITH_APPS)
+
+if (WITH_PLUGINS)
+ add_subdirectory(plugins)
+endif (WITH_PLUGINS)
+
if (DOCUMENTATION)
add_subdirectory(man)
endif (DOCUMENTATION)
@@ -103,8 +141,9 @@
# Install config file
# ========================================
-install(FILES mosquitto.conf aclfile.example pskfile.example pwfile.example DESTINATION "${CMAKE_INSTALL_SYSCONFDIR}/mosquitto")
-
+if (WITH_BROKER)
+ install(FILES mosquitto.conf aclfile.example pskfile.example pwfile.example DESTINATION "${CMAKE_INSTALL_SYSCONFDIR}/mosquitto")
+endif (WITH_BROKER)
# ========================================
# Install pkg-config files
diff -Nru mosquitto-1.6.9/compiling.txt mosquitto-2.0.15/compiling.txt
--- mosquitto-1.6.9/compiling.txt 2020-02-27 23:49:51.000000000 +0000
+++ mosquitto-2.0.15/compiling.txt 1970-01-01 00:00:00.000000000 +0000
@@ -1,20 +0,0 @@
-The following packages can be used to add features to mosquitto. All of them
-are optional.
-
-* openssl
-* c-ares (for DNS-SRV support, disabled by default)
-* tcp-wrappers (optional, package name libwrap0-dev)
-* libwebsockets (optional, disabled by default, version 1.3 and above)
-* On Windows, a pthreads library is required if threading support is to be
- included.
-
-To compile, run "make", but also see the file config.mk for more details on the
-various options that can be compiled in.
-
-Where possible use the Makefiles to compile. This is particularly relevant for
-the client libraries as symbol information will be included. Use cmake to
-compile on Windows or Mac.
-
-If you have any questions, problems or suggestions (particularly related to
-installing on a more unusual device like a plug-computer) then please get in
-touch using the details in readme.txt.
diff -Nru mosquitto-1.6.9/config.h mosquitto-2.0.15/config.h
--- mosquitto-1.6.9/config.h 2020-02-27 23:49:51.000000000 +0000
+++ mosquitto-2.0.15/config.h 2022-08-16 13:34:02.000000000 +0000
@@ -6,10 +6,14 @@
#ifdef __APPLE__
# define __DARWIN_C_SOURCE
-#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__SYMBIAN32__) || defined(__QNX__)
+#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__SYMBIAN32__)
# define _XOPEN_SOURCE 700
# define __BSD_VISIBLE 1
# define HAVE_NETINET_IN_H
+#elif defined(__QNX__)
+# define _XOPEN_SOURCE 600
+# define __BSD_VISIBLE 1
+# define HAVE_NETINET_IN_H
#else
# define _XOPEN_SOURCE 700
# define _DEFAULT_SOURCE 1
@@ -29,6 +33,15 @@
#if defined(_MSC_VER) && _MSC_VER < 1900
# define snprintf sprintf_s
# define EPROTO ECONNABORTED
+# ifndef ECONNABORTED
+# define ECONNABORTED WSAECONNABORTED
+# endif
+# ifndef ENOTCONN
+# define ENOTCONN WSAENOTCONN
+# endif
+# ifndef ECONNREFUSED
+# define ECONNREFUSED WSAECONNREFUSED
+# endif
#endif
#ifdef WIN32
@@ -40,8 +53,8 @@
#endif
-#define uthash_malloc(sz) mosquitto__malloc(sz)
-#define uthash_free(ptr,sz) mosquitto__free(ptr)
+#define uthash_malloc(sz) mosquitto_malloc(sz)
+#define uthash_free(ptr,sz) mosquitto_free(ptr)
#ifdef WITH_TLS
@@ -69,4 +82,9 @@
# define HAVE_PTHREAD_CANCEL
#endif
+#ifdef WITH_CJSON
+# include
+# define CJSON_VERSION_FULL (CJSON_VERSION_MAJOR*1000000+CJSON_VERSION_MINOR*1000+CJSON_VERSION_PATCH)
+#endif
+
#endif
diff -Nru mosquitto-1.6.9/config.mk mosquitto-2.0.15/config.mk
--- mosquitto-1.6.9/config.mk 2020-02-27 23:49:51.000000000 +0000
+++ mosquitto-2.0.15/config.mk 2022-08-16 13:34:02.000000000 +0000
@@ -59,6 +59,8 @@
# Build with systemd support. If enabled, mosquitto will notify systemd after
# initialization. See README in service/systemd/ for more information.
+# Setting to yes means the libsystemd-dev or similar package will need to be
+# installed.
WITH_SYSTEMD:=no
# Build with SRV lookup support.
@@ -102,6 +104,22 @@
# Build with coverage options
WITH_COVERAGE:=no
+# Build with unix domain socket support
+WITH_UNIX_SOCKETS:=yes
+
+# Build mosquitto_sub with cJSON support
+WITH_CJSON:=yes
+
+# Build mosquitto with support for the $CONTROL topics.
+WITH_CONTROL:=yes
+
+# Build the broker with the jemalloc allocator
+WITH_JEMALLOC:=no
+
+# Build with xtreport capability. This is for debugging purposes and is
+# probably of no particular interest to end users.
+WITH_XTREPORT=no
+
# =============================================================================
# End of user configuration
# =============================================================================
@@ -109,7 +127,7 @@
# Also bump lib/mosquitto.h, CMakeLists.txt,
# installer/mosquitto.nsi, installer/mosquitto64.nsi
-VERSION=1.6.9
+VERSION=2.0.15
# Client library SO version. Bump if incompatible API/ABI changes are made.
SOVERSION=1
@@ -122,6 +140,7 @@
#MANCOUNTRIES=en_GB
UNAME:=$(shell uname -s)
+ARCH:=$(shell uname -p)
ifeq ($(UNAME),SunOS)
ifeq ($(CC),cc)
@@ -130,36 +149,44 @@
CFLAGS?=-Wall -ggdb -O2
endif
else
- CFLAGS?=-Wall -ggdb -O2
+ CFLAGS?=-Wall -ggdb -O2 -Wconversion -Wextra
endif
STATIC_LIB_DEPS:=
-LIB_CPPFLAGS=$(CPPFLAGS) -I. -I.. -I../lib
-ifeq ($(WITH_BUNDLED_DEPS),yes)
- LIB_CPPFLAGS:=$(LIB_CPPFLAGS) -I../src/deps
-endif
+APP_CPPFLAGS=$(CPPFLAGS) -I. -I../../ -I../../include -I../../src -I../../lib
+APP_CFLAGS=$(CFLAGS) -DVERSION=\""${VERSION}\""
+APP_LDFLAGS:=$(LDFLAGS)
+
+LIB_CPPFLAGS=$(CPPFLAGS) -I. -I.. -I../include -I../../include
LIB_CFLAGS:=$(CFLAGS)
LIB_CXXFLAGS:=$(CXXFLAGS)
LIB_LDFLAGS:=$(LDFLAGS)
LIB_LIBADD:=$(LIBADD)
-BROKER_CPPFLAGS:=$(LIB_CPPFLAGS)
+BROKER_CPPFLAGS:=$(LIB_CPPFLAGS) -I../lib
BROKER_CFLAGS:=${CFLAGS} -DVERSION="\"${VERSION}\"" -DWITH_BROKER
BROKER_LDFLAGS:=${LDFLAGS}
BROKER_LDADD:=
-CLIENT_CPPFLAGS:=$(CPPFLAGS) -I.. -I../lib
+CLIENT_CPPFLAGS:=$(CPPFLAGS) -I.. -I../include
CLIENT_CFLAGS:=${CFLAGS} -DVERSION="\"${VERSION}\""
CLIENT_LDFLAGS:=$(LDFLAGS) -L../lib
CLIENT_LDADD:=
PASSWD_LDADD:=
+PLUGIN_CPPFLAGS:=$(CPPFLAGS) -I../.. -I../../include
+PLUGIN_CFLAGS:=$(CFLAGS) -fPIC
+PLUGIN_LDFLAGS:=$(LDFLAGS)
+
ifneq ($(or $(findstring $(UNAME),FreeBSD), $(findstring $(UNAME),OpenBSD), $(findstring $(UNAME),NetBSD)),)
BROKER_LDADD:=$(BROKER_LDADD) -lm
+ BROKER_LDFLAGS:=$(BROKER_LDFLAGS) -Wl,--dynamic-list=linker.syms
+ SEDINPLACE:=-i ""
else
BROKER_LDADD:=$(BROKER_LDADD) -ldl -lm
+ SEDINPLACE:=-i
endif
ifeq ($(UNAME),Linux)
@@ -173,9 +200,15 @@
endif
ifeq ($(UNAME),SunOS)
- ifeq ($(CC),cc)
- LIB_CFLAGS:=$(LIB_CFLAGS) -xc99 -KPIC
- else
+ SEDINPLACE:=
+ ifeq ($(ARCH),sparc)
+ ifeq ($(CC),cc)
+ LIB_CFLAGS:=$(LIB_CFLAGS) -xc99 -KPIC
+ else
+ LIB_CFLAGS:=$(LIB_CFLAGS) -fPIC
+ endif
+ endif
+ ifeq ($(ARCH),i386)
LIB_CFLAGS:=$(LIB_CFLAGS) -fPIC
endif
@@ -204,12 +237,13 @@
endif
ifeq ($(WITH_TLS),yes)
- BROKER_LDADD:=$(BROKER_LDADD) -lssl -lcrypto
- LIB_LIBADD:=$(LIB_LIBADD) -lssl -lcrypto
+ APP_CPPFLAGS:=$(APP_CPPFLAGS) -DWITH_TLS
BROKER_CPPFLAGS:=$(BROKER_CPPFLAGS) -DWITH_TLS
+ BROKER_LDADD:=$(BROKER_LDADD) -lssl -lcrypto
+ CLIENT_CPPFLAGS:=$(CLIENT_CPPFLAGS) -DWITH_TLS
LIB_CPPFLAGS:=$(LIB_CPPFLAGS) -DWITH_TLS
+ LIB_LIBADD:=$(LIB_LIBADD) -lssl -lcrypto
PASSWD_LDADD:=$(PASSWD_LDADD) -lcrypto
- CLIENT_CPPFLAGS:=$(CLIENT_CPPFLAGS) -DWITH_TLS
STATIC_LIB_DEPS:=$(STATIC_LIB_DEPS) -lssl -lcrypto
ifeq ($(WITH_TLS_PSK),yes)
@@ -220,10 +254,10 @@
endif
ifeq ($(WITH_THREADING),yes)
- LIB_LIBADD:=$(LIB_LIBADD) -lpthread
+ LIB_LDFLAGS:=$(LIB_LDFLAGS) -pthread
LIB_CPPFLAGS:=$(LIB_CPPFLAGS) -DWITH_THREADING
CLIENT_CPPFLAGS:=$(CLIENT_CPPFLAGS) -DWITH_THREADING
- STATIC_LIB_DEPS:=$(STATIC_LIB_DEPS) -lpthread
+ STATIC_LIB_DEPS:=$(STATIC_LIB_DEPS) -pthread
endif
ifeq ($(WITH_SOCKS),yes)
@@ -275,19 +309,28 @@
BROKER_CPPFLAGS:=$(BROKER_CPPFLAGS) -DWITH_ADNS
endif
+ifeq ($(WITH_CONTROL),yes)
+ BROKER_CPPFLAGS:=$(BROKER_CPPFLAGS) -DWITH_CONTROL
+endif
+
MAKE_ALL:=mosquitto
ifeq ($(WITH_DOCS),yes)
MAKE_ALL:=$(MAKE_ALL) docs
endif
-ifeq ($(WITH_WEBSOCKETS),yes)
- BROKER_CPPFLAGS:=$(BROKER_CPPFLAGS) -DWITH_WEBSOCKETS
- BROKER_LDADD:=$(BROKER_LDADD) -lwebsockets
+ifeq ($(WITH_JEMALLOC),yes)
+ BROKER_LDADD:=$(BROKER_LDADD) -ljemalloc
endif
-ifeq ($(WITH_WEBSOCKETS),static)
+ifeq ($(WITH_UNIX_SOCKETS),yes)
+ BROKER_CPPFLAGS:=$(BROKER_CPPFLAGS) -DWITH_UNIX_SOCKETS
+ LIB_CPPFLAGS:=$(LIB_CPPFLAGS) -DWITH_UNIX_SOCKETS
+ CLIENT_CPPFLAGS:=$(CLIENT_CPPFLAGS) -DWITH_UNIX_SOCKETS
+endif
+
+ifeq ($(WITH_WEBSOCKETS),yes)
BROKER_CPPFLAGS:=$(BROKER_CPPFLAGS) -DWITH_WEBSOCKETS
- BROKER_LDADD:=$(BROKER_LDADD) -static -lwebsockets
+ BROKER_LDADD:=$(BROKER_LDADD) -lwebsockets
endif
INSTALL?=install
@@ -309,18 +352,33 @@
endif
ifeq ($(WITH_BUNDLED_DEPS),yes)
- BROKER_CPPFLAGS:=$(BROKER_CPPFLAGS) -Ideps
+ BROKER_CPPFLAGS:=$(BROKER_CPPFLAGS) -I../deps
+ LIB_CPPFLAGS:=$(LIB_CPPFLAGS) -I../deps
+ PLUGIN_CPPFLAGS:=$(PLUGIN_CPPFLAGS) -I../../deps
endif
ifeq ($(WITH_COVERAGE),yes)
BROKER_CFLAGS:=$(BROKER_CFLAGS) -coverage
BROKER_LDFLAGS:=$(BROKER_LDFLAGS) -coverage
+ PLUGIN_CFLAGS:=$(PLUGIN_CFLAGS) -coverage
+ PLUGIN_LDFLAGS:=$(PLUGIN_LDFLAGS) -coverage
LIB_CFLAGS:=$(LIB_CFLAGS) -coverage
LIB_LDFLAGS:=$(LIB_LDFLAGS) -coverage
CLIENT_CFLAGS:=$(CLIENT_CFLAGS) -coverage
CLIENT_LDFLAGS:=$(CLIENT_LDFLAGS) -coverage
endif
+ifeq ($(WITH_CJSON),yes)
+ CLIENT_CFLAGS:=$(CLIENT_CFLAGS) -DWITH_CJSON
+ CLIENT_LDADD:=$(CLIENT_LDADD) -lcjson
+ CLIENT_STATIC_LDADD:=$(CLIENT_STATIC_LDADD) -lcjson
+ CLIENT_LDFLAGS:=$(CLIENT_LDFLAGS)
+endif
+
+ifeq ($(WITH_XTREPORT),yes)
+ BROKER_CFLAGS:=$(BROKER_CFLAGS) -DWITH_XTREPORT
+endif
+
BROKER_LDADD:=${BROKER_LDADD} ${LDADD}
CLIENT_LDADD:=${CLIENT_LDADD} ${LDADD}
PASSWD_LDADD:=${PASSWD_LDADD} ${LDADD}
diff -Nru mosquitto-1.6.9/debian/changelog mosquitto-2.0.15/debian/changelog
--- mosquitto-1.6.9/debian/changelog 2020-03-03 15:16:15.000000000 +0000
+++ mosquitto-2.0.15/debian/changelog 2023-07-24 11:47:19.000000000 +0000
@@ -1,3 +1,191 @@
+mosquitto (2.0.15-2~bpo20.04.1~ppa1) focal; urgency=medium
+
+ * Change backport to focal.
+ - drop sysv-utils runtime dependency
+
+ -- Gianfranco Costamagna Mon, 24 Jul 2023 13:47:19 +0200
+
+mosquitto (2.0.15-2) unstable; urgency=medium
+
+ [ Philippe Coval ]
+ * debian/tests/control: Fix tests
+ * debian/patches: Refresh missing-test.patch bypass 06 test
+
+ [ Gianfranco Costamagna ]
+ * Add manpages to clean target, they are autogenerated
+
+ -- Gianfranco Costamagna Fri, 21 Jul 2023 11:17:58 +0200
+
+mosquitto (2.0.15-1) unstable; urgency=medium
+
+ [ Philippe Coval ]
+ * New upstream release (Closes: #993400)
+ * debian/patches: Drop Fix-CONNECT...patch
+ * debian/patches: Drop ssl-sslcontext-wrap_socket.patch
+ * debian/patches: Refresh 1571.patch
+ * debian/patches: Refresh deb-test.patch
+ * debian/control: Transfer maintenance to team
+ * debian/gbp.conf: Build on tag
+ * debian/watch: Fix Lintian by scanning from git
+ * debian/control: Bump standards
+ * debian/control: Add Rules-Requires-Root Field
+ * debian/mosquitto.lintian-overrides: Ignore lws spelling
+ * debian/mosquitto.lintian-overrides: Ignore upstream spelling
+ * debian/control: Fix lintian d-on-obsolete-package : lsb to sysV
+ * d/mosquitto.lintian-overrides: Hide h-in-library-directory-missing-soname
+ * d/libmosquittopp1.lintian-overrides: Silent library-not-linked-against-libc
+ * debian/control: Add missing Pre-depends for systemd
+ * debian/rules: Add hardening flags
+ * debian/mosquitto.lintian-overrides: Relocate groff-message warning
+ * debian/libmosquitto*.symbols: Fix Lintian symbols-file-m-b-d-p-field
+ * debian/rules: Fix lintian debug-symbol-migration-possibly-complete
+ * debian/mosquitto.triggers: Remove ldconfig step
+ * debian/control: Fix cme lint libssl-dev dep
+ * debian/control: Fix cme lint Multi-Arch
+
+ [ наб ]
+ * debian/mosquitto.postrm: Purge user (Closes: #1032200)
+
+ [ Gianfranco Costamagna ]
+ * upload to sid
+
+ -- Gianfranco Costamagna Thu, 20 Jul 2023 12:10:52 +0200
+
+mosquitto (2.0.11-1.2) unstable; urgency=medium
+
+ * Non-maintainer upload.
+ * Fix CONNECT performance with many user-properties (CVE-2021-41039)
+ (Closes: #1001028)
+ * debian/tests/broker: Make all test python scripts executable
+
+ -- Salvatore Bonaccorso Thu, 29 Dec 2022 13:38:30 +0100
+
+mosquitto (2.0.11-1.1) unstable; urgency=medium
+
+ * Non-maintainer upload
+
+ [ Olivier Gayot ]
+ * Fix autopkgtest failure when running against Python 3.10 (Closes:
+ #1009096) (LP: #1960214)
+
+ -- Sebastian Ramacher Sat, 16 Apr 2022 17:17:54 +0200
+
+mosquitto (2.0.11-1) unstable; urgency=medium
+
+ * SECURITY UPDATE: In Eclipse Mosquitto 1.6 to 2.0.10, if an authenticated
+ client that had connected with MQTT v5 sent a crafted CONNECT message to
+ the broker, a memory leak would occur.
+ * New upstream release.
+ * Removed systemd-run.patch, applied upstream.
+ * Removed signed-unsigned.patch, applied upstream.
+ * missing-test.patch: Fix missing upstream test.
+ * Update copyright years and paths
+
+ -- Roger A. Light Wed, 09 Jun 2021 13:54:36 +0100
+
+mosquitto (2.0.10-6) unstable; urgency=medium
+
+ * Don't chown /run/mosquitto in mosquitto.postinst, this is done in the
+ systemd unit file at run time. (closes: #983429).
+ * systemd-run.patch: use /run/mosquitto instead of /var/run/mosquitto in
+ systemd unit file.
+
+ -- Roger A. Light Mon, 26 Apr 2021 22:07:57 +0100
+
+mosquitto (2.0.10-5) unstable; urgency=medium
+
+ * Don't use `pkill` in tests. (closes: #987467)
+ * Lintian fixes:
+ - dir-or-file-in-run
+ - extended-description-line-too-long
+ - lacks-ldconfig-trigger
+ - package-contains-empty-directory
+ - renamed-tag
+ - shared-library-is-multi-arch-foreign
+ - spelling-in-override-comment
+ - typo-in-manual-page
+
+ -- Roger A. Light Thu, 22 Apr 2021 14:38:23 +0100
+
+mosquitto (2.0.10-4) unstable; urgency=medium
+
+ * Fix autopkgtest test build dependencies.
+
+ -- Roger A. Light Wed, 21 Apr 2021 12:10:45 +0100
+
+mosquitto (2.0.10-3) unstable; urgency=medium
+
+ * signed-unsigned.patch: Fix signed/unsigned conversion warnings.
+
+ -- Roger A. Light Mon, 19 Apr 2021 09:41:00 +0100
+
+mosquitto (2.0.10-2) unstable; urgency=medium
+
+ * Fix autopkgtests.
+ * deb-test.patch: Fix paths to allow autopkgtest to work in the Debian
+ environment.
+
+ -- Roger A. Light Sun, 18 Apr 2021 21:42:48 +0100
+
+mosquitto (2.0.10-1) unstable; urgency=high
+
+ * SECURITY UPDATE: In Eclipse Mosquitto version 2.0.0 to 2.0.9, if an
+ authenticated client that had connected with MQTT v5 sent a crafted
+ CONNACK message to the broker, a NULL pointer dereference would occur.
+ (Closes: #986701)
+ - CVE-2021-28166
+ * New upstream release.
+
+ -- Roger A. Light Sat, 10 Apr 2021 00:41:35 +0100
+
+mosquitto (2.0.9-1) unstable; urgency=medium
+
+ * New upstream release.
+
+ -- Roger A. Light Thu, 11 Mar 2021 22:53:34 +0000
+
+mosquitto (2.0.8-1) unstable; urgency=medium
+
+ * New upstream release.
+
+ -- Roger A. Light Thu, 25 Feb 2021 18:56:57 +0000
+
+mosquitto (2.0.7-3) unstable; urgency=medium
+
+ * Change all paths `/var/run` to `/run` to avoid installing through a
+ symlink.
+
+ -- Roger A. Light Tue, 09 Feb 2021 09:31:09 +0000
+
+mosquitto (2.0.7-2) unstable; urgency=medium
+
+ * Add new xsltproc and docbook-xsl dependencies needed to build manpages.
+
+ -- Gianfranco Costamagna Mon, 08 Feb 2021 21:55:11 +0100
+
+mosquitto (2.0.7-1) unstable; urgency=medium
+
+ * New upstream release.
+ * License has changed from EPL-1.0 OR EDL-1.0 to EPL-2.0 OR EDL-1.0.
+ * New dependency, libcjson
+ * Remove install-protocol.patch, this has been fixed upstreamed.
+ * pid file moved to /var/run/mosquitto/mosquitto.pid because mosquitto is no
+ longer root when it tries to create that file.
+
+ -- Roger A. Light Thu, 4 Feb 2021 23:27:31 +0000
+
+mosquitto (1.6.12-1) unstable; urgency=medium
+
+ * New upstream release.
+
+ -- Roger A. Light Wed, 19 Aug 2020 15:24:26 +0100
+
+mosquitto (1.6.11-1) unstable; urgency=medium
+
+ * New upstream release.
+
+ -- Roger A. Light Tue, 11 Aug 2020 16:53:20 +0100
+
mosquitto (1.6.9-1) unstable; urgency=medium
* New upstream release.
diff -Nru mosquitto-1.6.9/debian/clean mosquitto-2.0.15/debian/clean
--- mosquitto-1.6.9/debian/clean 1970-01-01 00:00:00.000000000 +0000
+++ mosquitto-2.0.15/debian/clean 2023-07-21 08:54:02.000000000 +0000
@@ -0,0 +1,11 @@
+man/libmosquitto.3
+man/mosquitto-tls.7
+man/mosquitto.8
+man/mosquitto.conf.5
+man/mosquitto_ctrl.1
+man/mosquitto_ctrl_dynsec.1
+man/mosquitto_passwd.1
+man/mosquitto_pub.1
+man/mosquitto_rr.1
+man/mosquitto_sub.1
+man/mqtt.7
diff -Nru mosquitto-1.6.9/debian/control mosquitto-2.0.15/debian/control
--- mosquitto-1.6.9/debian/control 2020-03-03 15:16:15.000000000 +0000
+++ mosquitto-2.0.15/debian/control 2023-07-24 11:47:19.000000000 +0000
@@ -1,28 +1,34 @@
Source: mosquitto
Section: net
Priority: optional
-Maintainer: Roger A. Light
-Build-Depends: debhelper-compat (= 12),
+Maintainer: Debian IoT Maintainers
+Uploaders:
+ Philippe Coval ,
+ Roger A. Light
+Build-Depends: debhelper-compat (= 13),
cmake,
- libssl-dev (>=1.0.0),
+ libcjson-dev,
+ libdlt-dev,
+ libssl-dev,
libsystemd-dev,
libwebsockets-dev,
- libdlt-dev,
libwrap0-dev,
pkg-config,
- uthash-dev (>=2.1.0),
- uuid-dev
-Standards-Version: 4.5.0
+ uthash-dev,
+ xsltproc,
+ docbook-xsl
+Standards-Version: 4.6.2
Homepage: https://mosquitto.org/
-Vcs-Git: https://github.com/eclipse/mosquitto
-Vcs-Browser: https://github.com/eclipse/mosquitto/tree/debian
+Vcs-Git: https://salsa.debian.org/debian-iot-team/mosquitto/ -b debian/master
+Vcs-Browser: https://salsa.debian.org/debian-iot-team/mosquitto/debian/master
+Rules-Requires-Root: no
Package: mosquitto
Architecture: any
-Multi-Arch: foreign
+Pre-Depends: ${misc:Pre-Depends}
Depends: adduser (>= 3.10),
- libuuid1,
- lsb-base (>=4.1+Debian3),
+ libcjson1,
+ libmosquitto1 (= ${binary:Version}),
${misc:Depends},
${shlibs:Depends}
Suggests: apparmor
@@ -116,8 +122,8 @@
${misc:Depends},
${shlibs:Depends}
Description: Mosquitto command line MQTT clients
- This is two MQTT version 5.0/3.1.1/3.1 command line clients. mosquitto_pub can be
- used to publish messages to a broker and mosquitto_sub can be used to
+ This is two MQTT version 5.0/3.1.1/3.1 command line clients. mosquitto_pub can
+ be used to publish messages to a broker and mosquitto_sub can be used to
subscribe to a topic to receive messages.
.
MQTT provides a method of carrying out messaging using a publish/subscribe
diff -Nru mosquitto-1.6.9/debian/copyright mosquitto-2.0.15/debian/copyright
--- mosquitto-1.6.9/debian/copyright 2020-03-03 15:16:15.000000000 +0000
+++ mosquitto-2.0.15/debian/copyright 2023-07-20 14:31:58.000000000 +0000
@@ -4,8 +4,8 @@
Source: https://mosquitto.org/files/source/
Files: *
-Copyright: 2009-2018 Roger A. Light
-License: EPL-1.0 or EDL-1.0
+Copyright: 2009-2021 Roger A. Light
+License: EPL-2.0 or EDL-1.0
License: EDL-1.0
Eclipse Distribution License - v 1.0
@@ -39,224 +39,287 @@
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-License: EPL-1.0
- Eclipse Public License - v 1.0
+License: EPL-2.0
+ Eclipse Public License - v 2.0
.
- THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC
- LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM
- CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
+ THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE
+ PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION
+ OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
.
1. DEFINITIONS
.
"Contribution" means:
.
- a) in the case of the initial Contributor, the initial code and documentation
- distributed under this Agreement, and
+ a) in the case of the initial Contributor, the initial content
+ Distributed under this Agreement, and
.
- b) in the case of each subsequent Contributor:
- .
- i) changes to the Program, and
- .
- ii) additions to the Program;
- .
- where such changes and/or additions to the Program originate from and are
- distributed by that particular Contributor. A Contribution 'originates' from a
- Contributor if it was added to the Program by such Contributor itself or
- anyone acting on such Contributor's behalf. Contributions do not include
- additions to the Program which: (i) are separate modules of software
- distributed in conjunction with the Program under their own license agreement,
- and (ii) are not derivative works of the Program.
- .
- "Contributor" means any person or entity that distributes the Program.
- .
- "Licensed Patents " mean patent claims licensable by a Contributor which are
- necessarily infringed by the use or sale of its Contribution alone or when
- combined with the Program.
- .
- "Program" means the Contributions distributed in accordance with this Agreement.
- .
- "Recipient" means anyone who receives the Program under this Agreement,
- including all Contributors.
+ b) in the case of each subsequent Contributor:
+ i) changes to the Program, and
+ ii) additions to the Program;
+ where such changes and/or additions to the Program originate from
+ and are Distributed by that particular Contributor. A Contribution
+ "originates" from a Contributor if it was added to the Program by
+ such Contributor itself or anyone acting on such Contributor's behalf.
+ Contributions do not include changes or additions to the Program that
+ are not Modified Works.
+ .
+ "Contributor" means any person or entity that Distributes the Program.
+ .
+ "Licensed Patents" mean patent claims licensable by a Contributor which
+ are necessarily infringed by the use or sale of its Contribution alone
+ or when combined with the Program.
+ .
+ "Program" means the Contributions Distributed in accordance with this
+ Agreement.
+ .
+ "Recipient" means anyone who receives the Program under this Agreement
+ or any Secondary License (as applicable), including Contributors.
+ .
+ "Derivative Works" shall mean any work, whether in Source Code or other
+ form, that is based on (or derived from) the Program and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship.
+ .
+ "Modified Works" shall mean any work in Source Code or other form that
+ results from an addition to, deletion from, or modification of the
+ contents of the Program, including, for purposes of clarity any new file
+ in Source Code form that contains any contents of the Program. Modified
+ Works shall not include works that contain only declarations,
+ interfaces, types, classes, structures, or files of the Program solely
+ in each case in order to link to, bind by name, or subclass the Program
+ or Modified Works thereof.
+ .
+ "Distribute" means the acts of a) distributing or b) making available
+ in any manner that enables the transfer of a copy.
+ .
+ "Source Code" means the form of a Program preferred for making
+ modifications, including but not limited to software source code,
+ documentation source, and configuration files.
+ .
+ "Secondary License" means either the GNU General Public License,
+ Version 2.0, or any later versions of that license, including any
+ exceptions or additional permissions as identified by the initial
+ Contributor.
.
2. GRANT OF RIGHTS
.
- a) Subject to the terms of this Agreement, each Contributor hereby grants
- Recipient a non-exclusive, worldwide, royalty-free copyright license to
- reproduce, prepare derivative works of, publicly display, publicly perform,
- distribute and sublicense the Contribution of such Contributor, if any, and
- such derivative works, in source code and object code form.
- .
- b) Subject to the terms of this Agreement, each Contributor hereby grants
- Recipient a non-exclusive, worldwide, royalty-free patent license under
- Licensed Patents to make, use, sell, offer to sell, import and otherwise
- transfer the Contribution of such Contributor, if any, in source code and
- object code form. This patent license shall apply to the combination of the
- Contribution and the Program if, at the time the Contribution is added by the
- Contributor, such addition of the Contribution causes such combination to be
- covered by the Licensed Patents. The patent license shall not apply to any
- other combinations which include the Contribution. No hardware per se is
- licensed hereunder.
- .
- c) Recipient understands that although each Contributor grants the licenses to
- its Contributions set forth herein, no assurances are provided by any
- Contributor that the Program does not infringe the patent or other
- intellectual property rights of any other entity. Each Contributor disclaims
- any liability to Recipient for claims brought by any other entity based on
- infringement of intellectual property rights or otherwise. As a condition to
- exercising the rights and licenses granted hereunder, each Recipient hereby
- assumes sole responsibility to secure any other intellectual property rights
- needed, if any. For example, if a third party patent license is required to
- allow Recipient to distribute the Program, it is Recipient's responsibility to
- acquire that license before distributing the Program.
- .
- d) Each Contributor represents that to its knowledge it has sufficient
- copyright rights in its Contribution, if any, to grant the copyright license
- set forth in this Agreement.
+ a) Subject to the terms of this Agreement, each Contributor hereby
+ grants Recipient a non-exclusive, worldwide, royalty-free copyright
+ license to reproduce, prepare Derivative Works of, publicly display,
+ publicly perform, Distribute and sublicense the Contribution of such
+ Contributor, if any, and such Derivative Works.
+ .
+ b) Subject to the terms of this Agreement, each Contributor hereby
+ grants Recipient a non-exclusive, worldwide, royalty-free patent
+ license under Licensed Patents to make, use, sell, offer to sell,
+ import and otherwise transfer the Contribution of such Contributor,
+ if any, in Source Code or other form. This patent license shall
+ apply to the combination of the Contribution and the Program if, at
+ the time the Contribution is added by the Contributor, such addition
+ of the Contribution causes such combination to be covered by the
+ Licensed Patents. The patent license shall not apply to any other
+ combinations which include the Contribution. No hardware per se is
+ licensed hereunder.
+ .
+ c) Recipient understands that although each Contributor grants the
+ licenses to its Contributions set forth herein, no assurances are
+ provided by any Contributor that the Program does not infringe the
+ patent or other intellectual property rights of any other entity.
+ Each Contributor disclaims any liability to Recipient for claims
+ brought by any other entity based on infringement of intellectual
+ property rights or otherwise. As a condition to exercising the
+ rights and licenses granted hereunder, each Recipient hereby
+ assumes sole responsibility to secure any other intellectual
+ property rights needed, if any. For example, if a third party
+ patent license is required to allow Recipient to Distribute the
+ Program, it is Recipient's responsibility to acquire that license
+ before distributing the Program.
+ .
+ d) Each Contributor represents that to its knowledge it has
+ sufficient copyright rights in its Contribution, if any, to grant
+ the copyright license set forth in this Agreement.
+ .
+ e) Notwithstanding the terms of any Secondary License, no
+ Contributor makes additional grants to any Recipient (other than
+ those set forth in this Agreement) as a result of such Recipient's
+ receipt of the Program under the terms of a Secondary License
+ (if permitted under the terms of Section 3).
.
3. REQUIREMENTS
.
- A Contributor may choose to distribute the Program in object code form under
- its own license agreement, provided that:
- .
- a) it complies with the terms and conditions of this Agreement; and
- .
- b) its license agreement:
+ 3.1 If a Contributor Distributes the Program in any form, then:
.
- i) effectively disclaims on behalf of all Contributors all warranties and
- conditions, express and implied, including warranties or conditions of title
- and non-infringement, and implied warranties or conditions of merchantability
- and fitness for a particular purpose;
- .
- ii) effectively excludes on behalf of all Contributors all liability for
- damages, including direct, indirect, special, incidental and consequential
- damages, such as lost profits;
- .
- iii) states that any provisions which differ from this Agreement are offered
- by that Contributor alone and not by any other party; and
- .
- iv) states that source code for the Program is available from such
- Contributor, and informs licensees how to obtain it in a reasonable manner on
- or through a medium customarily used for software exchange.
- .
- When the Program is made available in source code form:
- .
- a) it must be made available under this Agreement; and
- .
- b) a copy of this Agreement must be included with each copy of the Program.
- .
- Contributors may not remove or alter any copyright notices contained within
- the Program.
- .
- Each Contributor must identify itself as the originator of its Contribution,
- if any, in a manner that reasonably allows subsequent Recipients to identify
- the originator of the Contribution.
+ a) the Program must also be made available as Source Code, in
+ accordance with section 3.2, and the Contributor must accompany
+ the Program with a statement that the Source Code for the Program
+ is available under this Agreement, and informs Recipients how to
+ obtain it in a reasonable manner on or through a medium customarily
+ used for software exchange; and
+ .
+ b) the Contributor may Distribute the Program under a license
+ different than this Agreement, provided that such license:
+ i) effectively disclaims on behalf of all other Contributors all
+ warranties and conditions, express and implied, including
+ warranties or conditions of title and non-infringement, and
+ implied warranties or conditions of merchantability and fitness
+ for a particular purpose;
+ .
+ ii) effectively excludes on behalf of all other Contributors all
+ liability for damages, including direct, indirect, special,
+ incidental and consequential damages, such as lost profits;
+ .
+ iii) does not attempt to limit or alter the recipients' rights
+ in the Source Code under section 3.2; and
+ .
+ iv) requires any subsequent distribution of the Program by any
+ party to be under a license that satisfies the requirements
+ of this section 3.
+ .
+ 3.2 When the Program is Distributed as Source Code:
+ .
+ a) it must be made available under this Agreement, or if the
+ Program (i) is combined with other material in a separate file or
+ files made available under a Secondary License, and (ii) the initial
+ Contributor attached to the Source Code the notice described in
+ Exhibit A of this Agreement, then the Program may be made available
+ under the terms of such Secondary Licenses, and
+ .
+ b) a copy of this Agreement must be included with each copy of
+ the Program.
+ .
+ 3.3 Contributors may not remove or alter any copyright, patent,
+ trademark, attribution notices, disclaimers of warranty, or limitations
+ of liability ("notices") contained within the Program from any copy of
+ the Program which they Distribute, provided that Contributors may add
+ their own appropriate notices.
.
4. COMMERCIAL DISTRIBUTION
.
- Commercial distributors of software may accept certain responsibilities with
- respect to end users, business partners and the like. While this license is
- intended to facilitate the commercial use of the Program, the Contributor who
- includes the Program in a commercial product offering should do so in a manner
- which does not create potential liability for other Contributors. Therefore,
- if a Contributor includes the Program in a commercial product offering, such
- Contributor ("Commercial Contributor") hereby agrees to defend and indemnify
- every other Contributor ("Indemnified Contributor") against any losses,
- damages and costs (collectively "Losses") arising from claims, lawsuits and
- other legal actions brought by a third party against the Indemnified
- Contributor to the extent caused by the acts or omissions of such Commercial
- Contributor in connection with its distribution of the Program in a commercial
- product offering. The obligations in this section do not apply to any claims
- or Losses relating to any actual or alleged intellectual property
- infringement. In order to qualify, an Indemnified Contributor must:
- a) promptly notify the Commercial Contributor in writing of such claim, and
- b) allow the Commercial Contributor to control, and cooperate with the
- Commercial Contributor in, the defense and any related settlement
- negotiations. The Indemnified Contributor may participate in any such claim at
- its own expense.
- .
- For example, a Contributor might include the Program in a commercial product
- offering, Product X. That Contributor is then a Commercial Contributor. If
- that Commercial Contributor then makes performance claims, or offers
- warranties related to Product X, those performance claims and warranties are
- such Commercial Contributor's responsibility alone. Under this section, the
- Commercial Contributor would have to defend claims against the other
- Contributors related to those performance claims and warranties, and if a
- court requires any other Contributor to pay any damages as a result, the
- Commercial Contributor must pay those damages.
+ Commercial distributors of software may accept certain responsibilities
+ with respect to end users, business partners and the like. While this
+ license is intended to facilitate the commercial use of the Program,
+ the Contributor who includes the Program in a commercial product
+ offering should do so in a manner which does not create potential
+ liability for other Contributors. Therefore, if a Contributor includes
+ the Program in a commercial product offering, such Contributor
+ ("Commercial Contributor") hereby agrees to defend and indemnify every
+ other Contributor ("Indemnified Contributor") against any losses,
+ damages and costs (collectively "Losses") arising from claims, lawsuits
+ and other legal actions brought by a third party against the Indemnified
+ Contributor to the extent caused by the acts or omissions of such
+ Commercial Contributor in connection with its distribution of the Program
+ in a commercial product offering. The obligations in this section do not
+ apply to any claims or Losses relating to any actual or alleged
+ intellectual property infringement. In order to qualify, an Indemnified
+ Contributor must: a) promptly notify the Commercial Contributor in
+ writing of such claim, and b) allow the Commercial Contributor to control,
+ and cooperate with the Commercial Contributor in, the defense and any
+ related settlement negotiations. The Indemnified Contributor may
+ participate in any such claim at its own expense.
+ .
+ For example, a Contributor might include the Program in a commercial
+ product offering, Product X. That Contributor is then a Commercial
+ Contributor. If that Commercial Contributor then makes performance
+ claims, or offers warranties related to Product X, those performance
+ claims and warranties are such Commercial Contributor's responsibility
+ alone. Under this section, the Commercial Contributor would have to
+ defend claims against the other Contributors related to those performance
+ claims and warranties, and if a court requires any other Contributor to
+ pay any damages as a result, the Commercial Contributor must pay
+ those damages.
.
5. NO WARRANTY
.
- EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN
- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR
- IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE,
- NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each
- Recipient is solely responsible for determining the appropriateness of using
- and distributing the Program and assumes all risks associated with its
- exercise of rights under this Agreement , including but not limited to the
- risks and costs of program errors, compliance with applicable laws, damage to
- or loss of data, programs or equipment, and unavailability or interruption of
- operations.
+ EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT
+ PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN "AS IS"
+ BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR
+ IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF
+ TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR
+ PURPOSE. Each Recipient is solely responsible for determining the
+ appropriateness of using and distributing the Program and assumes all
+ risks associated with its exercise of rights under this Agreement,
+ including but not limited to the risks and costs of program errors,
+ compliance with applicable laws, damage to or loss of data, programs
+ or equipment, and unavailability or interruption of operations.
.
6. DISCLAIMER OF LIABILITY
.
- EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY
- CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION
- LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT
+ PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS
+ SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST
+ PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE
- EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY
- OF SUCH DAMAGES.
+ EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGES.
.
7. GENERAL
.
If any provision of this Agreement is invalid or unenforceable under
- applicable law, it shall not affect the validity or enforceability of the
- remainder of the terms of this Agreement, and without further action by the
- parties hereto, such provision shall be reformed to the minimum extent
- necessary to make such provision valid and enforceable.
- .
- If Recipient institutes patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Program itself
- (excluding combinations of the Program with other software or hardware)
- infringes such Recipient's patent(s), then such Recipient's rights granted
- under Section 2(b) shall terminate as of the date such litigation is filed.
- .
- All Recipient's rights under this Agreement shall terminate if it fails to
- comply with any of the material terms or conditions of this Agreement and does
- not cure such failure in a reasonable period of time after becoming aware of
- such noncompliance. If all Recipient's rights under this Agreement terminate,
- Recipient agrees to cease use and distribution of the Program as soon as
- reasonably practicable. However, Recipient's obligations under this Agreement
- and any licenses granted by Recipient relating to the Program shall continue
- and survive.
- .
- Everyone is permitted to copy and distribute copies of this Agreement, but in
- order to avoid inconsistency the Agreement is copyrighted and may only be
- modified in the following manner. The Agreement Steward reserves the right to
- publish new versions (including revisions) of this Agreement from time to
- time. No one other than the Agreement Steward has the right to modify this
- Agreement. The Eclipse Foundation is the initial Agreement Steward. The
- Eclipse Foundation may assign the responsibility to serve as the Agreement
- Steward to a suitable separate entity. Each new version of the Agreement will
- be given a distinguishing version number. The Program (including
- Contributions) may always be distributed subject to the version of the
- Agreement under which it was received. In addition, after a new version of the
- Agreement is published, Contributor may elect to distribute the Program
- (including its Contributions) under the new version. Except as expressly
- stated in Sections 2(a) and 2(b) above, Recipient receives no rights or
- licenses to the intellectual property of any Contributor under this Agreement,
- whether expressly, by implication, estoppel or otherwise. All rights in the
- Program not expressly granted under this Agreement are reserved.
- .
- This Agreement is governed by the laws of the State of New York and the
- intellectual property laws of the United States of America. No party to this
- Agreement will bring a legal action under this Agreement more than one year
- after the cause of action arose. Each party waives its rights to a jury trial
- in any resulting litigation.
+ applicable law, it shall not affect the validity or enforceability of
+ the remainder of the terms of this Agreement, and without further
+ action by the parties hereto, such provision shall be reformed to the
+ minimum extent necessary to make such provision valid and enforceable.
+ .
+ If Recipient institutes patent litigation against any entity
+ (including a cross-claim or counterclaim in a lawsuit) alleging that the
+ Program itself (excluding combinations of the Program with other software
+ or hardware) infringes such Recipient's patent(s), then such Recipient's
+ rights granted under Section 2(b) shall terminate as of the date such
+ litigation is filed.
+ .
+ All Recipient's rights under this Agreement shall terminate if it
+ fails to comply with any of the material terms or conditions of this
+ Agreement and does not cure such failure in a reasonable period of
+ time after becoming aware of such noncompliance. If all Recipient's
+ rights under this Agreement terminate, Recipient agrees to cease use
+ and distribution of the Program as soon as reasonably practicable.
+ However, Recipient's obligations under this Agreement and any licenses
+ granted by Recipient relating to the Program shall continue and survive.
+ .
+ Everyone is permitted to copy and distribute copies of this Agreement,
+ but in order to avoid inconsistency the Agreement is copyrighted and
+ may only be modified in the following manner. The Agreement Steward
+ reserves the right to publish new versions (including revisions) of
+ this Agreement from time to time. No one other than the Agreement
+ Steward has the right to modify this Agreement. The Eclipse Foundation
+ is the initial Agreement Steward. The Eclipse Foundation may assign the
+ responsibility to serve as the Agreement Steward to a suitable separate
+ entity. Each new version of the Agreement will be given a distinguishing
+ version number. The Program (including Contributions) may always be
+ Distributed subject to the version of the Agreement under which it was
+ received. In addition, after a new version of the Agreement is published,
+ Contributor may elect to Distribute the Program (including its
+ Contributions) under the new version.
+ .
+ Except as expressly stated in Sections 2(a) and 2(b) above, Recipient
+ receives no rights or licenses to the intellectual property of any
+ Contributor under this Agreement, whether expressly, by implication,
+ estoppel or otherwise. All rights in the Program not expressly granted
+ under this Agreement are reserved. Nothing in this Agreement is intended
+ to be enforceable by any entity that is not a Contributor or Recipient.
+ No third-party beneficiary rights are created under this Agreement.
+ .
+ Exhibit A - Form of Secondary Licenses Notice
+ .
+ "This Source Code may also be made available under the following
+ Secondary Licenses when the conditions for such availability set forth
+ in the Eclipse Public License, v. 2.0 are satisfied: {name license(s),
+ version(s), and exceptions or additional permissions here}."
+ .
+ Simply including a copy of this Agreement, including this Exhibit A
+ is not sufficient to license the Source Code under Secondary Licenses.
+ .
+ If it is not possible or desirable to put the notice in a particular
+ file, then You may include the notice in a location (such as a LICENSE
+ file in a relevant directory) where a recipient would be likely to
+ look for such a notice.
+ .
+ You may add additional accurate notices of copyright ownership.
-Files: src/deps/uthash.h
-Copyright: 2003-2013, Troy D. Hanson http://uthash.sourceforge.net
+Files: deps/uthash.h
+Copyright: 2003-2018 Troy D. Hanson http://uthash.sourceforge.net
License: BSD-1-clause
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
diff -Nru mosquitto-1.6.9/debian/gbp.conf mosquitto-2.0.15/debian/gbp.conf
--- mosquitto-1.6.9/debian/gbp.conf 1970-01-01 00:00:00.000000000 +0000
+++ mosquitto-2.0.15/debian/gbp.conf 2023-07-20 14:31:58.000000000 +0000
@@ -0,0 +1,5 @@
+[DEFAULT]
+debian-branch=debian/master
+upstream-branch=master
+filter=*/.git
+upstream-tag=v%(version)s
diff -Nru mosquitto-1.6.9/debian/libmosquitto1.symbols mosquitto-2.0.15/debian/libmosquitto1.symbols
--- mosquitto-1.6.9/debian/libmosquitto1.symbols 2020-03-03 15:16:15.000000000 +0000
+++ mosquitto-2.0.15/debian/libmosquitto1.symbols 2023-07-20 14:31:58.000000000 +0000
@@ -1,4 +1,5 @@
libmosquitto.so.1 libmosquitto1 #MINVER#
+* Build-Depends-Package: libmosquitto-dev
(symver)MOSQ_1.0 1.0
(symver)MOSQ_1.1 1.1
(symver)MOSQ_1.2 1.2
@@ -6,3 +7,6 @@
(symver)MOSQ_1.4 1.4
(symver)MOSQ_1.5 1.5
(symver)MOSQ_1.6 1.6
+ (symver)MOSQ_1.6 1.6
+ (symver)MOSQ_1.6 1.6
+ (symver)MOSQ_1.7 2.0
diff -Nru mosquitto-1.6.9/debian/libmosquitto-dev.lintian-overrides mosquitto-2.0.15/debian/libmosquitto-dev.lintian-overrides
--- mosquitto-1.6.9/debian/libmosquitto-dev.lintian-overrides 2020-03-03 15:16:15.000000000 +0000
+++ mosquitto-2.0.15/debian/libmosquitto-dev.lintian-overrides 1970-01-01 00:00:00.000000000 +0000
@@ -1,3 +0,0 @@
-# xsltproc generated man pages have long lines but look ok. Patching the
-# generated files to use hypenation results in very odd looking files.
-libmosquitto-dev binary: manpage-has-errors-from-man
diff -Nru mosquitto-1.6.9/debian/libmosquittopp1.lintian-overrides mosquitto-2.0.15/debian/libmosquittopp1.lintian-overrides
--- mosquitto-1.6.9/debian/libmosquittopp1.lintian-overrides 1970-01-01 00:00:00.000000000 +0000
+++ mosquitto-2.0.15/debian/libmosquittopp1.lintian-overrides 2023-07-20 14:31:58.000000000 +0000
@@ -0,0 +1,2 @@
+# C++ library
+libmosquittopp1: library-not-linked-against-libc [usr/lib/x86_64-linux-gnu/libmosquittopp.so.2.0.15]
diff -Nru mosquitto-1.6.9/debian/libmosquittopp1.symbols mosquitto-2.0.15/debian/libmosquittopp1.symbols
--- mosquitto-1.6.9/debian/libmosquittopp1.symbols 2020-03-03 15:16:15.000000000 +0000
+++ mosquitto-2.0.15/debian/libmosquittopp1.symbols 2023-07-20 14:31:58.000000000 +0000
@@ -1,4 +1,5 @@
libmosquittopp.so.1 libmosquittopp1 #MINVER#
+* Build-Depends-Package: libmosquitto-dev
(c++)"mosqpp::subscribe_simple(mosquitto_message**, int, bool, char const*, int, char const*, int, char const*, int, bool, char const*, char const*, libmosquitto_will const*, libmosquitto_tls const*)@Base" 1.5
(c++)"mosqpp::subscribe_callback(int (*)(mosquitto*, void*, mosquitto_message const*), void*, char const*, int, char const*, int, char const*, int, bool, char const*, char const*, libmosquitto_will const*, libmosquitto_tls const*)@Base" 1.5
(c++)"mosqpp::lib_cleanup()@Base" 1.0
diff -Nru mosquitto-1.6.9/debian/mosquitto-clients.lintian-overrides mosquitto-2.0.15/debian/mosquitto-clients.lintian-overrides
--- mosquitto-1.6.9/debian/mosquitto-clients.lintian-overrides 2020-03-03 15:16:15.000000000 +0000
+++ mosquitto-2.0.15/debian/mosquitto-clients.lintian-overrides 2023-07-20 14:31:58.000000000 +0000
@@ -1,3 +1,3 @@
# xsltproc generated man pages have long lines but look ok. Patching the
-# generated files to use hypenation results in very odd looking files.
-mosquitto-clients binary: manpage-has-errors-from-man
+# generated files to use hyphenation results in very odd looking files.
+mosquitto-clients binary: groff-message
diff -Nru mosquitto-1.6.9/debian/mosquitto.conf mosquitto-2.0.15/debian/mosquitto.conf
--- mosquitto-1.6.9/debian/mosquitto.conf 2020-03-03 15:16:15.000000000 +0000
+++ mosquitto-2.0.15/debian/mosquitto.conf 2023-07-20 14:31:58.000000000 +0000
@@ -3,7 +3,7 @@
# A full description of the configuration file is at
# /usr/share/doc/mosquitto/examples/mosquitto.conf.example
-pid_file /var/run/mosquitto.pid
+pid_file /run/mosquitto/mosquitto.pid
persistence true
persistence_location /var/lib/mosquitto/
diff -Nru mosquitto-1.6.9/debian/mosquitto.docs mosquitto-2.0.15/debian/mosquitto.docs
--- mosquitto-1.6.9/debian/mosquitto.docs 2020-03-03 15:16:15.000000000 +0000
+++ mosquitto-2.0.15/debian/mosquitto.docs 2023-07-20 14:31:58.000000000 +0000
@@ -1 +1,2 @@
-readme.md
+README.md
+README-letsencrypt.md
diff -Nru mosquitto-1.6.9/debian/mosquitto.init mosquitto-2.0.15/debian/mosquitto.init
--- mosquitto-1.6.9/debian/mosquitto.init 2020-03-03 15:16:15.000000000 +0000
+++ mosquitto-2.0.15/debian/mosquitto.init 2023-07-20 14:31:58.000000000 +0000
@@ -20,7 +20,7 @@
set -e
-PIDFILE=/var/run/mosquitto.pid
+PIDFILE=/run/mosquitto/mosquitto.pid
DAEMON=/usr/sbin/mosquitto
# /etc/init.d/mosquitto: start and stop the mosquitto MQTT message broker
diff -Nru mosquitto-1.6.9/debian/mosquitto.install mosquitto-2.0.15/debian/mosquitto.install
--- mosquitto-1.6.9/debian/mosquitto.install 2020-03-03 15:16:15.000000000 +0000
+++ mosquitto-2.0.15/debian/mosquitto.install 2023-07-20 14:31:58.000000000 +0000
@@ -5,4 +5,6 @@
etc/mosquitto/*.example
lib/systemd/system/mosquitto.service
usr/bin/mosquitto_passwd
+usr/bin/mosquitto_ctrl
+usr/lib/*/mosquitto_dynamic_security.so*
usr/sbin/mosquitto
diff -Nru mosquitto-1.6.9/debian/mosquitto.lintian-overrides mosquitto-2.0.15/debian/mosquitto.lintian-overrides
--- mosquitto-1.6.9/debian/mosquitto.lintian-overrides 1970-01-01 00:00:00.000000000 +0000
+++ mosquitto-2.0.15/debian/mosquitto.lintian-overrides 2023-07-20 14:31:58.000000000 +0000
@@ -0,0 +1,12 @@
+# Forwarded: https://github.com/eclipse/mosquitto/pull/2846#Open
+mosquitto: typo-in-manual-page noticable noticeable [usr/share/man/man1/mosquitto_ctrl.1.gz:225]
+
+# Forwarded: https://github.com/warmcat/libwebsockets/pull/2927#Open
+mosquitto: spelling-error-in-binary Inital Initial [usr/sbin/mosquitto]
+mosquitto: spelling-error-in-binary witholding withholding [usr/sbin/mosquitto]
+
+# It looks like a dynamic Plugin, doesn't it?
+mosquitto: sharedobject-in-library-directory-missing-soname [usr/lib/x86_64-linux-gnu/mosquitto_dynamic_security.so]
+
+# May be fixed in XML source
+mosquitto: groff-message 36: warning [p 1, 2.2i]: can't break line [usr/share/man/man1/mosquitto_ctrl.1.gz:1]
diff -Nru mosquitto-1.6.9/debian/mosquitto.manpages mosquitto-2.0.15/debian/mosquitto.manpages
--- mosquitto-1.6.9/debian/mosquitto.manpages 2020-03-03 15:16:15.000000000 +0000
+++ mosquitto-2.0.15/debian/mosquitto.manpages 2023-07-20 14:31:58.000000000 +0000
@@ -1,3 +1,5 @@
+usr/share/man/man1/mosquitto_ctrl.1
+usr/share/man/man1/mosquitto_ctrl_dynsec.1
usr/share/man/man1/mosquitto_passwd.1
usr/share/man/man5/mosquitto.conf.5
usr/share/man/man7/mosquitto-tls.7
diff -Nru mosquitto-1.6.9/debian/mosquitto.postrm mosquitto-2.0.15/debian/mosquitto.postrm
--- mosquitto-1.6.9/debian/mosquitto.postrm 2020-03-03 15:16:15.000000000 +0000
+++ mosquitto-2.0.15/debian/mosquitto.postrm 2023-07-20 14:31:58.000000000 +0000
@@ -15,6 +15,12 @@
if [ -d /var/log/mosquitto ]; then
rmdir --ignore-fail-on-non-empty /var/log/mosquitto
fi
+ rm -f /run/mosquitto/mosquitto.pid
+ if [ -d /run/mosquitto ]; then
+ rmdir --ignore-fail-on-non-empty /run/mosquitto
+ fi
+ deluser --quiet --system mosquitto || :
+ delgroup --quiet --system mosquitto || :
APP_PROFILE="usr.sbin.mosquitto"
rm -f /etc/apparmor.d/disable/$APP_PROFILE >/dev/null 2>&1 || true
;;
diff -Nru mosquitto-1.6.9/debian/patches/1571.patch mosquitto-2.0.15/debian/patches/1571.patch
--- mosquitto-1.6.9/debian/patches/1571.patch 2020-03-03 15:16:15.000000000 +0000
+++ mosquitto-2.0.15/debian/patches/1571.patch 2023-07-20 14:31:58.000000000 +0000
@@ -1,17 +1,23 @@
-Upstream-Status: Submitted [https://github.com/eclipse/mosquitto/pull/1571]
From 3fe5468f1bdca1bff1d18cf43c9e338f41aa9e32 Mon Sep 17 00:00:00 2001
From: Gianfranco Costamagna
Date: Wed, 22 Jan 2020 12:39:49 +0100
Subject: [PATCH] Add dynamic symbols linking with cmake too
+Upstream-Status: Submitted [https://github.com/eclipse/mosquitto/pull/1571]
+Signed-off-by: Gianfranco Costamagna
+Last-Update: 2023-07-09
+-
+
Signed-off-by: Gianfranco Costamagna
---
lib/CMakeLists.txt | 2 ++
1 file changed, 2 insertions(+)
+diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
+index 31cc35e3..f0f71132 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
-@@ -89,6 +89,8 @@
+@@ -94,6 +94,8 @@ set_target_properties(libmosquitto PROPERTIES
OUTPUT_NAME mosquitto
VERSION ${VERSION}
SOVERSION 1
@@ -19,4 +25,7 @@
+ LINK_FLAGS "-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/linker.version"
)
- install(TARGETS libmosquitto RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}")
+ install(TARGETS libmosquitto
+--
+2.39.2
+
diff -Nru mosquitto-1.6.9/debian/patches/debian-config.patch mosquitto-2.0.15/debian/patches/debian-config.patch
--- mosquitto-1.6.9/debian/patches/debian-config.patch 2020-03-03 15:16:15.000000000 +0000
+++ mosquitto-2.0.15/debian/patches/debian-config.patch 2023-07-20 14:31:58.000000000 +0000
@@ -3,7 +3,7 @@
Forwarded: not-needed
--- a/Makefile
+++ b/Makefile
-@@ -87,6 +87,7 @@
+@@ -94,6 +94,7 @@
endif
$(INSTALL) -d "${DESTDIR}/etc/mosquitto"
$(INSTALL) -m 644 mosquitto.conf "${DESTDIR}/etc/mosquitto/mosquitto.conf.example"
diff -Nru mosquitto-1.6.9/debian/patches/deb-test.patch mosquitto-2.0.15/debian/patches/deb-test.patch
--- mosquitto-1.6.9/debian/patches/deb-test.patch 1970-01-01 00:00:00.000000000 +0000
+++ mosquitto-2.0.15/debian/patches/deb-test.patch 2023-07-20 21:48:32.000000000 +0000
@@ -0,0 +1,757 @@
+From a17b03b7de01e44cf07f17e5ddee14fc8fd79fd8 Mon Sep 17 00:00:00 2001
+From: Philippe Coval
+Date: Sun, 9 Jul 2023 10:05:32 +0200
+Subject: [PATCH] Fix test paths for Debian.
+Description: Fix test paths for Debian.
+Author: Roger Light
+Forwarded: in-progress
+Last-Update: 2023-07-09
+Signed-off-by: Philippe Coval
+---
+ ...4-retain-check-source-persist-diff-port.py | 10 ++---
+ test/broker/04-retain-check-source-persist.py | 10 ++---
+ test/broker/04-retain-check-source.py | 6 +--
+ test/broker/04-retain-upgrade-outgoing-qos.py | 4 +-
+ .../06-bridge-b2br-late-connection-retain.py | 8 ++--
+ test/broker/06-bridge-clean-session-core.py | 10 ++---
+ test/broker/06-bridge-reconnect-local-out.py | 4 +-
+ test/broker/08-tls-psk-bridge.py | 10 ++---
+ test/broker/11-message-expiry.py | 15 ++++----
+ .../11-persistent-subscription-no-local.py | 15 ++++----
+ test/broker/11-persistent-subscription-v5.py | 15 ++++----
+ test/broker/11-persistent-subscription.py | 15 ++++----
+ test/broker/11-pub-props.py | 15 ++++----
+ test/broker/11-subscription-id.py | 17 ++++-----
+ test/broker/Makefile | 38 +++++++++----------
+ test/broker/c/Makefile | 2 +-
+ test/client/test.sh | 16 ++++----
+ test/lib/Makefile | 4 +-
+ test/lib/c/Makefile | 2 +-
+ test/lib/cpp/Makefile | 2 +-
+ test/mosq_test.py | 6 +--
+ 21 files changed, 114 insertions(+), 110 deletions(-)
+
+diff --git a/test/mosq_test.py b/test/mosq_test.py
+index d9fe6970..faf16ea0 100644
+--- a/test/mosq_test.py
++++ b/test/mosq_test.py
+@@ -26,16 +26,16 @@ def start_broker(filename, cmd=None, port=0, use_conf=False, expect_fail=False,
+ delay = 0.1
+
+ if use_conf == True:
+- cmd = ['../../src/mosquitto', '-v', '-c', filename.replace('.py', '.conf')]
++ cmd = ['/usr/sbin/mosquitto', '-v', '-c', filename.replace('.py', '.conf')]
+
+ if port == 0:
+ port = 1888
+ else:
+ if cmd is None and port != 0:
+- cmd = ['../../src/mosquitto', '-v', '-p', str(port)]
++ cmd = ['/usr/sbin/mosquitto', '-v', '-p', str(port)]
+ elif cmd is None and port == 0:
+ port = 1888
+- cmd = ['../../src/mosquitto', '-v', '-c', filename.replace('.py', '.conf')]
++ cmd = ['/usr/sbin/mosquitto', '-v', '-c', filename.replace('.py', '.conf')]
+ elif cmd is not None and port == 0:
+ port = 1888
+
+diff --git a/test/broker/c/Makefile b/test/broker/c/Makefile
+index e897e34d..472537cd 100644
+--- a/test/broker/c/Makefile
++++ b/test/broker/c/Makefile
+@@ -37,7 +37,7 @@ ${PLUGINS} : %.so: %.c
+
+
+ ${TESTS} : %.test: %.c
+- $(CC) ${CFLAGS} $< -o $@ ../../../lib/libmosquitto.so.1
++ $(CC) ${CFLAGS} $< -o $@ -lmosquitto
+
+
+ reallyclean : clean
+diff --git a/test/lib/c/Makefile b/test/lib/c/Makefile
+index 40cb7d15..6d180a36 100644
+--- a/test/lib/c/Makefile
++++ b/test/lib/c/Makefile
+@@ -3,7 +3,7 @@ include ../../../config.mk
+ .PHONY: all clean reallyclean
+
+ CFLAGS=-I../../../include -Werror
+-LIBS=../../../lib/libmosquitto.so.1
++LIBS=-lmosquitto
+
+ SRC = \
+ 01-con-discon-success.c \
+diff --git a/test/lib/cpp/Makefile b/test/lib/cpp/Makefile
+index c4ae14ca..022d1033 100644
+--- a/test/lib/cpp/Makefile
++++ b/test/lib/cpp/Makefile
+@@ -1,7 +1,7 @@
+ .PHONY: all test 01 02 03 04 08 09 clean reallyclean
+
+ CFLAGS=-I../../../include -I../../../lib/cpp -DDEBUG
+-LIBS=../../../lib/libmosquitto.so.1 ../../../lib/cpp/libmosquittopp.so.1
++LIBS=-lmosquitto -lmosquittopp
+
+ all : 01 02 03 04 08 09
+
+
+
+diff --git a/test/client/test.sh b/test/client/test.sh
+index 53ee84b9..ed97cad3 100755
+--- a/test/client/test.sh
++++ b/test/client/test.sh
+@@ -11,7 +11,7 @@ export PORT=1888
+ export SUB_TIMEOUT=1
+
+ # Start broker
+-../../src/mosquitto -p ${PORT} 2>/dev/null &
++/usr/sbin/mosquitto -p ${PORT} 2>/dev/null &
+ export MOSQ_PID=$!
+ sleep 0.5
+
+@@ -20,28 +20,28 @@ trap "kill $MOSQ_PID" EXIT
+
+
+ # Simple subscribe test - single message from $SYS
+-${BASE_PATH}/client/mosquitto_sub -p ${PORT} -W ${SUB_TIMEOUT} -C 1 -t '$SYS/broker/uptime' >/dev/null
++/usr/bin/mosquitto_sub -p ${PORT} -W ${SUB_TIMEOUT} -C 1 -t '$SYS/broker/uptime' >/dev/null
+ echo "Simple subscribe ok"
+
+ # Simple publish/subscribe test - single message from mosquitto_pub
+-${BASE_PATH}/client/mosquitto_sub -p ${PORT} -W ${SUB_TIMEOUT} -C 1 -t 'single/test' >/dev/null &
++/usr/bin/mosquitto_sub -p ${PORT} -W ${SUB_TIMEOUT} -C 1 -t 'single/test' >/dev/null &
+ export SUB_PID=$!
+-${BASE_PATH}/client/mosquitto_pub -p ${PORT} -t 'single/test' -m 'single-test'
++/usr/bin/mosquitto_pub -p ${PORT} -t 'single/test' -m 'single-test'
+ kill ${SUB_PID} 2>/dev/null || true
+ echo "Simple publish/subscribe ok"
+
+ # Publish a file and subscribe, do we get at least that many lines?
+ export TEST_LINES=$(wc -l test.sh | cut -d' ' -f1)
+-${BASE_PATH}/client/mosquitto_sub -p ${PORT} -W ${SUB_TIMEOUT} -C ${TEST_LINES} -t 'file-publish' >/dev/null &
++/usr/bin/mosquitto_sub -p ${PORT} -W ${SUB_TIMEOUT} -C ${TEST_LINES} -t 'file-publish' >/dev/null &
+ export SUB_PID=$!
+-${BASE_PATH}/client/mosquitto_pub -p ${PORT} -t 'file-publish' -f ./test.sh
++/usr/bin/mosquitto_pub -p ${PORT} -t 'file-publish' -f ./test.sh
+ kill ${SUB_PID} 2>/dev/null || true
+ echo "File publish ok"
+
+ # Publish a file from stdin and subscribe, do we get at least that many lines?
+ export TEST_LINES=$(wc -l test.sh | cut -d' ' -f1)
+-${BASE_PATH}/client/mosquitto_sub -p ${PORT} -W ${SUB_TIMEOUT} -C ${TEST_LINES} -t 'file-publish' >/dev/null &
++/usr/bin/mosquitto_sub -p ${PORT} -W ${SUB_TIMEOUT} -C ${TEST_LINES} -t 'file-publish' >/dev/null &
+ export SUB_PID=$!
+-${BASE_PATH}/client/mosquitto_pub -p ${PORT} -t 'file-publish' -l < ./test.sh
++/usr/bin/mosquitto_pub -p ${PORT} -t 'file-publish' -l < ./test.sh
+ kill ${SUB_PID} 2>/dev/null || true
+ echo "stdin publish ok"
+
+--
+2.39.2
+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 efe1cfba..dacc617f 100755
+--- a/test/broker/04-retain-check-source-persist-diff-port.py
++++ b/test/broker/04-retain-check-source-persist-diff-port.py
+@@ -32,16 +32,16 @@ def write_acl_2(filename, username):
+
+
+ def do_test(proto_ver, per_listener, username):
+- conf_file = os.path.basename(__file__).replace('.py', '.conf')
++ conf_file = "/tmp/"+os.path.basename(__file__).replace('.py', '.conf')
+ write_config(conf_file, port1, port2, per_listener)
+
+- persistence_file = os.path.basename(__file__).replace('.py', '.db')
++ persistence_file = "/tmp/"+os.path.basename(__file__).replace('.py', '.db')
+ try:
+ os.remove(persistence_file)
+ except OSError:
+ pass
+
+- acl_file = os.path.basename(__file__).replace('.py', '.acl')
++ acl_file = "/tmp/"+os.path.basename(__file__).replace('.py', '.acl')
+ write_acl_1(acl_file, username)
+
+
+@@ -65,7 +65,7 @@ def do_test(proto_ver, per_listener, username):
+ subscribe_packet = mosq_test.gen_subscribe(mid, "test/topic", 0, proto_ver=proto_ver)
+ suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver)
+
+- broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port1)
++ broker = mosq_test.start_broker(filename=conf_file, use_conf=True, port=port1)
+
+ try:
+ sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port1)
+@@ -85,7 +85,7 @@ def do_test(proto_ver, per_listener, username):
+ if os.path.isfile(persistence_file) == False:
+ raise FileNotFoundError("Persistence file not written")
+
+- broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port1)
++ broker = mosq_test.start_broker(filename=conf_file, use_conf=True, port=port1)
+
+ sock = mosq_test.do_client_connect(connect2_packet, connack2_packet, port=port2)
+ mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback 2")
+diff --git a/test/broker/04-retain-check-source-persist.py b/test/broker/04-retain-check-source-persist.py
+index 9e594415..21d6ab94 100755
+--- a/test/broker/04-retain-check-source-persist.py
++++ b/test/broker/04-retain-check-source-persist.py
+@@ -29,16 +29,16 @@ def write_acl_2(filename, username):
+
+
+ def do_test(proto_ver, per_listener, username):
+- conf_file = os.path.basename(__file__).replace('.py', '.conf')
++ conf_file = "/tmp/"+os.path.basename(__file__).replace('.py', '.conf')
+ write_config(conf_file, port, per_listener)
+
+- persistence_file = os.path.basename(__file__).replace('.py', '.db')
++ persistence_file = "/tmp/"+os.path.basename(__file__).replace('.py', '.db')
+ try:
+ os.remove(persistence_file)
+ except OSError:
+ pass
+
+- acl_file = os.path.basename(__file__).replace('.py', '.acl')
++ acl_file = "/tmp/"+os.path.basename(__file__).replace('.py', '.acl')
+ write_acl_1(acl_file, username)
+
+
+@@ -52,7 +52,7 @@ def do_test(proto_ver, per_listener, username):
+ subscribe_packet = mosq_test.gen_subscribe(mid, "test/topic", 0, proto_ver=proto_ver)
+ suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver)
+
+- broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)
++ broker = mosq_test.start_broker(filename=conf_file, use_conf=True, port=port)
+
+ try:
+ sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)
+@@ -70,7 +70,7 @@ def do_test(proto_ver, per_listener, username):
+ broker.terminate()
+ broker.wait()
+
+- broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)
++ broker = mosq_test.start_broker(filename=conf_file, use_conf=True, port=port)
+
+ sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)
+ mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback 2")
+diff --git a/test/broker/04-retain-check-source.py b/test/broker/04-retain-check-source.py
+index 5a7ed298..70a0cc1b 100755
+--- a/test/broker/04-retain-check-source.py
++++ b/test/broker/04-retain-check-source.py
+@@ -23,10 +23,10 @@ def write_acl_2(filename):
+
+
+ def do_test(proto_ver, per_listener):
+- conf_file = os.path.basename(__file__).replace('.py', '.conf')
++ conf_file = "/tmp/"+os.path.basename(__file__).replace('.py', '.conf')
+ write_config(conf_file, port, per_listener)
+
+- acl_file = os.path.basename(__file__).replace('.py', '.acl')
++ acl_file = "/tmp/"+os.path.basename(__file__).replace('.py', '.acl')
+ write_acl_1(acl_file)
+
+
+@@ -40,7 +40,7 @@ def do_test(proto_ver, per_listener):
+ subscribe_packet = mosq_test.gen_subscribe(mid, "test/topic", 0, proto_ver=proto_ver)
+ suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver)
+
+- broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)
++ broker = mosq_test.start_broker(filename=conf_file, use_conf=True, port=port)
+
+ try:
+ sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)
+diff --git a/test/broker/04-retain-upgrade-outgoing-qos.py b/test/broker/04-retain-upgrade-outgoing-qos.py
+index e2720bcf..942507f6 100755
+--- a/test/broker/04-retain-upgrade-outgoing-qos.py
++++ b/test/broker/04-retain-upgrade-outgoing-qos.py
+@@ -14,7 +14,7 @@ def write_config(filename, port):
+
+ def do_test(proto_ver):
+ port = mosq_test.get_port()
+- conf_file = os.path.basename(__file__).replace('.py', '.conf')
++ conf_file = "/tmp/"+os.path.basename(__file__).replace('.py', '.conf')
+ write_config(conf_file, port)
+
+ rc = 1
+@@ -29,7 +29,7 @@ def do_test(proto_ver):
+
+ publish_packet2 = mosq_test.gen_publish("retain/qos0/test", mid=1, qos=1, payload="retained message", retain=True, proto_ver=proto_ver)
+
+- broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)
++ broker = mosq_test.start_broker(filename=conf_file, use_conf=True, port=port)
+
+ try:
+ sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)
+diff --git a/test/broker/06-bridge-b2br-late-connection-retain.py b/test/broker/06-bridge-b2br-late-connection-retain.py
+index 4beeb7c2..5126abfd 100755
+--- a/test/broker/06-bridge-b2br-late-connection-retain.py
++++ b/test/broker/06-bridge-b2br-late-connection-retain.py
+@@ -35,8 +35,8 @@ def do_test(proto_ver):
+ proto_ver_connect = 5
+
+ (port1, port2) = mosq_test.get_port(2)
+- conf_file = os.path.basename(__file__).replace('.py', '.conf')
+- persistence_file = os.path.basename(__file__).replace('.py', '.db')
++ conf_file = "/tmp/"+os.path.basename(__file__).replace('.py', '.conf')
++ persistence_file = "/tmp/"+os.path.basename(__file__).replace('.py', '.db')
+
+ rc = 1
+ keepalive = 60
+@@ -64,7 +64,7 @@ def do_test(proto_ver):
+ write_config1(conf_file, persistence_file, port1, port2)
+
+ try:
+- broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True)
++ broker = mosq_test.start_broker(filename=conf_file, port=port2, use_conf=True)
+ client = mosq_test.do_client_connect(c_connect_packet, c_connack_packet, timeout=20, port=port2)
+ mosq_test.do_send_receive(client, publish_packet, puback_packet, "puback")
+ client.close()
+@@ -74,7 +74,7 @@ def do_test(proto_ver):
+
+ # Restart, with retained message in place
+ write_config2(conf_file, persistence_file, port1, port2, bridge_protocol)
+- broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True)
++ broker = mosq_test.start_broker(filename=conf_file, port=port2, use_conf=True)
+
+ (bridge, address) = ssock.accept()
+ bridge.settimeout(20)
+diff --git a/test/broker/06-bridge-clean-session-core.py b/test/broker/06-bridge-clean-session-core.py
+index 89237d23..b9f91a61 100755
+--- a/test/broker/06-bridge-clean-session-core.py
++++ b/test/broker/06-bridge-clean-session-core.py
+@@ -84,12 +84,12 @@ def do_test(proto_ver, cs, lcs=None):
+
+
+ (port_a_listen, port_b_listen) = mosq_test.get_port(2)
+- conf_file_a = os.path.basename(__file__).replace('.py', '%d_a_edge.conf'%(port_a_listen))
+- persistence_file_a = os.path.basename(__file__).replace('.py', '%d_a_edge.db'%(port_a_listen))
++ conf_file_a = "/tmp/"+os.path.basename(__file__).replace('.py', '%d_a_edge.conf'%(port_a_listen))
++ persistence_file_a = "/tmp/"+os.path.basename(__file__).replace('.py', '%d_a_edge.db'%(port_a_listen))
+ write_config_edge(conf_file_a, persistence_file_a, port_b_listen, port_a_listen, bridge_protocol, cs=cs, lcs=lcs)
+-
+- conf_file_b = os.path.basename(__file__).replace('.py', '%d_b_core.conf'%(port_b_listen))
+- persistence_file_b = os.path.basename(__file__).replace('.py', '%d_b_core.db'%(port_b_listen))
++
++ conf_file_b = "/tmp/"+os.path.basename(__file__).replace('.py', '%d_b_core.conf'%(port_b_listen))
++ persistence_file_b = "/tmp/"+os.path.basename(__file__).replace('.py', '%d_b_core.db'%(port_b_listen))
+ write_config_core(conf_file_b, port_b_listen, persistence_file_b)
+
+ AckedPair = namedtuple("AckedPair", "p ack")
+diff --git a/test/broker/06-bridge-reconnect-local-out.py b/test/broker/06-bridge-reconnect-local-out.py
+index 887470b6..2a198e88 100755
+--- a/test/broker/06-bridge-reconnect-local-out.py
++++ b/test/broker/06-bridge-reconnect-local-out.py
+@@ -11,7 +11,7 @@ def write_config(filename, port1, port2, protocol_version):
+ f.write("allow_anonymous true\n")
+ f.write("\n")
+ f.write("persistence true\n")
+- f.write("persistence_file mosquitto-%d.db" % (port1))
++ f.write("persistence_file %s\n" % (filename.replace('.conf', '.db')))
+ f.write("\n")
+ f.write("connection bridge_sample\n")
+ f.write("address 127.0.0.1:%d\n" % (port1))
+@@ -48,7 +48,7 @@ def do_test(proto_ver):
+
+ broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port1, use_conf=False)
+
+- local_cmd = ['../../src/mosquitto', '-c', '06-bridge-reconnect-local-out.conf']
++ local_cmd = ['/usr/sbin/mosquitto', '-c', '06-bridge-reconnect-local-out.conf']
+ local_broker = mosq_test.start_broker(cmd=local_cmd, filename=os.path.basename(__file__)+'_local1', use_conf=False, port=port2)
+ if os.environ.get('MOSQ_USE_VALGRIND') is not None:
+ time.sleep(5)
+
+diff --git a/test/broker/11-message-expiry.py b/test/broker/11-message-expiry.py
+index de109eb0..1e963f2d 100755
+--- a/test/broker/11-message-expiry.py
++++ b/test/broker/11-message-expiry.py
+@@ -11,16 +11,17 @@
+
+ from mosq_test_helper import *
+
+-def write_config(filename, port):
++def write_config(filename, port, persist_file):
+ with open(filename, 'w') as f:
+ f.write("port %d\n" % (port))
+ f.write("allow_anonymous true\n")
+ f.write("persistence true\n")
+- f.write("persistence_file mosquitto-%d.db\n" % (port))
++ f.write("persistence_file %s\n" % (persist_file))
+
+ port = mosq_test.get_port()
+ conf_file = os.path.basename(__file__).replace('.py', '.conf')
+-write_config(conf_file, port)
++persist_file = '/tmp/' + conf_file.replace('.conf', '.db')
++write_config(conf_file, port, persist_file)
+
+
+ rc = 1
+@@ -53,8 +54,8 @@ publish3_packet = mosq_test.gen_publish("subpub/qos1", mid=mid, qos=1, payload="
+ puback3_packet = mosq_test.gen_puback(mid)
+
+
+-if os.path.exists('mosquitto-%d.db' % (port)):
+- os.unlink('mosquitto-%d.db' % (port))
++if os.path.exists(persist_file):
++ os.unlink(persist_file)
+
+ port = mosq_test.get_port()
+ broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)
+@@ -97,8 +98,8 @@ finally:
+ (stdo, stde) = broker.communicate()
+ if rc:
+ print(stde.decode('utf-8'))
+- if os.path.exists('mosquitto-%d.db' % (port)):
+- os.unlink('mosquitto-%d.db' % (port))
++ if os.path.exists(persist_file):
++ os.unlink(persist_file)
+
+ exit(rc)
+
+diff --git a/test/broker/11-persistent-subscription-no-local.py b/test/broker/11-persistent-subscription-no-local.py
+index 350ba5ca..63059ade 100755
+--- a/test/broker/11-persistent-subscription-no-local.py
++++ b/test/broker/11-persistent-subscription-no-local.py
+@@ -5,16 +5,17 @@
+
+ from mosq_test_helper import *
+
+-def write_config(filename, port):
++def write_config(filename, port, persist_file):
+ with open(filename, 'w') as f:
+ f.write("port %d\n" % (port))
+ f.write("allow_anonymous true\n")
+ f.write("persistence true\n")
+- f.write("persistence_file mosquitto-%d.db\n" % (port))
++ f.write("persistence_file %s\n" % (persist_file))
+
+ port = mosq_test.get_port()
+ conf_file = os.path.basename(__file__).replace('.py', '.conf')
+-write_config(conf_file, port)
++persist_file = '/tmp/' + conf_file.replace('.conf', '.db')
++write_config(conf_file, port, persist_file)
+
+ rc = 1
+ keepalive = 60
+@@ -48,8 +49,8 @@ mid = 2
+ publish2b_packet = mosq_test.gen_publish("subpub/local", qos=1, mid=mid, payload="message", proto_ver=5)
+ puback2b_packet = mosq_test.gen_puback(mid, proto_ver=5)
+
+-if os.path.exists('mosquitto-%d.db' % (port)):
+- os.unlink('mosquitto-%d.db' % (port))
++if os.path.exists(persist_file):
++ os.unlink(persist_file)
+
+ broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)
+
+@@ -90,8 +91,8 @@ finally:
+ (stdo, stde) = broker.communicate()
+ if rc:
+ print(stde.decode('utf-8'))
+- if os.path.exists('mosquitto-%d.db' % (port)):
+- os.unlink('mosquitto-%d.db' % (port))
++ if os.path.exists(persist_file):
++ os.unlink(persist_file)
+
+
+ exit(rc)
+diff --git a/test/broker/11-persistent-subscription-v5.py b/test/broker/11-persistent-subscription-v5.py
+index 7cd9ae6a..8bc4d8e3 100755
+--- a/test/broker/11-persistent-subscription-v5.py
++++ b/test/broker/11-persistent-subscription-v5.py
+@@ -4,16 +4,17 @@
+
+ from mosq_test_helper import *
+
+-def write_config(filename, port):
++def write_config(filename, port, persist_file):
+ with open(filename, 'w') as f:
+ f.write("port %d\n" % (port))
+ f.write("allow_anonymous true\n")
+ f.write("persistence true\n")
+- f.write("persistence_file mosquitto-%d.db\n" % (port))
++ f.write("persistence_file %s\n" % (persist_file))
+
+ port = mosq_test.get_port()
+ conf_file = os.path.basename(__file__).replace('.py', '.conf')
+-write_config(conf_file, port)
++persist_file = '/tmp/' + conf_file.replace('.conf', '.db')
++write_config(conf_file, port, persist_file)
+
+ rc = 1
+ mid = 530
+@@ -35,8 +36,8 @@ puback_packet = mosq_test.gen_puback(mid, proto_ver=5)
+ mid = 1
+ publish_packet2 = mosq_test.gen_publish("subpub/qos1", qos=1, mid=mid, payload="message", proto_ver=5)
+
+-if os.path.exists('mosquitto-%d.db' % (port)):
+- os.unlink('mosquitto-%d.db' % (port))
++if os.path.exists(persist_file):
++ os.unlink(persist_file)
+
+ broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)
+
+@@ -67,8 +68,8 @@ finally:
+ (stdo, stde) = broker.communicate()
+ if rc:
+ print(stde.decode('utf-8'))
+- if os.path.exists('mosquitto-%d.db' % (port)):
+- os.unlink('mosquitto-%d.db' % (port))
++ if os.path.exists(persist_file):
++ os.unlink(persist_file)
+
+
+ exit(rc)
+diff --git a/test/broker/11-persistent-subscription.py b/test/broker/11-persistent-subscription.py
+index 2ec2871c..a54b5b97 100755
+--- a/test/broker/11-persistent-subscription.py
++++ b/test/broker/11-persistent-subscription.py
+@@ -4,17 +4,18 @@
+
+ from mosq_test_helper import *
+
+-def write_config(filename, port):
++def write_config(filename, port, persist_file):
+ with open(filename, 'w') as f:
+ f.write("port %d\n" % (port))
+ f.write("allow_anonymous true\n")
+ f.write("persistence true\n")
+- f.write("persistence_file mosquitto-%d.db\n" % (port))
++ f.write("persistence_file %s\n" % (persist_file))
+
+ def do_test(proto_ver):
+ port = mosq_test.get_port()
+ conf_file = os.path.basename(__file__).replace('.py', '.conf')
+- write_config(conf_file, port)
++ persist_file = '/tmp/' + conf_file.replace('.conf', '.db')
++ write_config(conf_file, port, persist_file)
+
+ rc = 1
+ mid = 530
+@@ -35,8 +36,8 @@ def do_test(proto_ver):
+ mid = 1
+ publish_packet2 = mosq_test.gen_publish("subpub/qos1", qos=1, mid=mid, payload="message", proto_ver=proto_ver)
+
+- if os.path.exists('mosquitto-%d.db' % (port)):
+- os.unlink('mosquitto-%d.db' % (port))
++ if os.path.exists(persist_file):
++ os.unlink(persist_file)
+
+ broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)
+
+@@ -65,8 +66,8 @@ def do_test(proto_ver):
+ broker.terminate()
+ broker.wait()
+ (stdo, stde) = broker.communicate()
+- if os.path.exists('mosquitto-%d.db' % (port)):
+- os.unlink('mosquitto-%d.db' % (port))
++ if os.path.exists(persist_file):
++ os.unlink(persist_file)
+ if rc:
+ print(stde.decode('utf-8'))
+ print("proto_ver=%d" % (proto_ver))
+diff --git a/test/broker/11-pub-props.py b/test/broker/11-pub-props.py
+index 1b76fa25..541719fb 100755
+--- a/test/broker/11-pub-props.py
++++ b/test/broker/11-pub-props.py
+@@ -4,16 +4,17 @@
+
+ from mosq_test_helper import *
+
+-def write_config(filename, port):
++def write_config(filename, port, persist_file):
+ with open(filename, 'w') as f:
+ f.write("port %d\n" % (port))
+ f.write("allow_anonymous true\n")
+ f.write("persistence true\n")
+- f.write("persistence_file mosquitto-%d.db\n" % (port))
++ f.write("persistence_file %s\n" % (persist_file))
+
+ port = mosq_test.get_port()
+ conf_file = os.path.basename(__file__).replace('.py', '.conf')
+-write_config(conf_file, port)
++persist_file = '/tmp/' + conf_file.replace('.conf', '.db')
++write_config(conf_file, port, persist_file)
+
+ rc = 1
+ keepalive = 60
+@@ -37,8 +38,8 @@ mid = 1
+ subscribe_packet = mosq_test.gen_subscribe(mid, "subpub/qos1", 0, proto_ver=5)
+ suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=5)
+
+-if os.path.exists('mosquitto-%d.db' % (port)):
+- os.unlink('mosquitto-%d.db' % (port))
++if os.path.exists(persist_file):
++ os.unlink(persist_file)
+
+ broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)
+
+@@ -72,8 +73,8 @@ finally:
+ (stdo, stde) = broker.communicate()
+ if rc:
+ print(stde.decode('utf-8'))
+- if os.path.exists('mosquitto-%d.db' % (port)):
+- os.unlink('mosquitto-%d.db' % (port))
++ if os.path.exists(persist_file):
++ os.unlink(persist_file)
+
+
+ exit(rc)
+diff --git a/test/broker/11-subscription-id.py b/test/broker/11-subscription-id.py
+index ed178425..4b9ad16a 100755
+--- a/test/broker/11-subscription-id.py
++++ b/test/broker/11-subscription-id.py
+@@ -4,16 +4,17 @@
+
+ from mosq_test_helper import *
+
+-def write_config(filename, port):
++def write_config(filename, port, persist_file):
+ with open(filename, 'w') as f:
+ f.write("port %d\n" % (port))
+ f.write("allow_anonymous true\n")
+ f.write("persistence true\n")
+- f.write("persistence_file mosquitto-%d.db\n" % (port))
++ f.write("persistence_file %s\n" % (persist_file))
+
+ port = mosq_test.get_port()
+ conf_file = os.path.basename(__file__).replace('.py', '.conf')
+-write_config(conf_file, port)
++persist_file = '/tmp/' + conf_file.replace('.conf', '.db')
++write_config(conf_file, port, persist_file)
+
+ rc = 1
+ keepalive = 60
+@@ -42,8 +43,8 @@ helper_publish_packet = mosq_test.gen_publish("subpub/qos1", qos=1, mid=mid, pay
+ helper_puback_packet = mosq_test.gen_puback(mid, proto_ver=5)
+
+
+-if os.path.exists('mosquitto-%d.db' % (port)):
+- os.unlink('mosquitto-%d.db' % (port))
++if os.path.exists(persist_file):
++ os.unlink(persist_file)
+
+ broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port)
+
+@@ -77,10 +78,8 @@ finally:
+ (stdo, stde) = broker.communicate()
+ if rc:
+ print(stde.decode('utf-8'))
+- if os.path.exists('mosquitto-%d.db' % (port)):
+- os.unlink('mosquitto-%d.db' % (port))
+- pass
++ if os.path.exists(persist_file):
++ os.unlink(persist_file)
+
+
+ exit(rc)
+-
+diff --git a/test/broker/Makefile b/test/broker/Makefile
+index 63b9ae8f..99f90220 100644
+--- a/test/broker/Makefile
++++ b/test/broker/Makefile
+@@ -104,7 +104,7 @@ msg_sequence_test:
+ 06 :
+ ./06-bridge-b2br-disconnect-qos1.py
+ ./06-bridge-b2br-disconnect-qos2.py
+- ./06-bridge-b2br-late-connection-retain.py
++ #./06-bridge-b2br-late-connection-retain.py
+ ./06-bridge-b2br-late-connection.py
+ ./06-bridge-b2br-remapping.py
+ ./06-bridge-br2b-disconnect-qos1.py
+@@ -146,9 +146,9 @@ msg_sequence_test:
+ ifeq ($(WITH_TLS),yes)
+ ./08-ssl-bridge.py
+ ./08-ssl-connect-cert-auth-crl.py
+- ./08-ssl-connect-cert-auth-expired.py
+- ./08-ssl-connect-cert-auth-revoked.py
+- ./08-ssl-connect-cert-auth-without.py
++ #./08-ssl-connect-cert-auth-expired.py
++ #./08-ssl-connect-cert-auth-revoked.py
++ #./08-ssl-connect-cert-auth-without.py
+ ./08-ssl-connect-cert-auth.py
+ ./08-ssl-connect-identity.py
+ ./08-ssl-connect-no-auth-wrong-ca.py
+@@ -217,20 +217,20 @@ endif
+ 14 :
+ ifeq ($(WITH_TLS),yes)
+ ifeq ($(WITH_CJSON),yes)
+- ./14-dynsec-acl.py
+- ./14-dynsec-anon-group.py
+- ./14-dynsec-auth.py
+- ./14-dynsec-client.py
+- ./14-dynsec-client-invalid.py
+- ./14-dynsec-default-access.py
+- ./14-dynsec-disable-client.py
+- ./14-dynsec-group.py
+- ./14-dynsec-group-invalid.py
+- ./14-dynsec-modify-client.py
+- ./14-dynsec-modify-group.py
+- ./14-dynsec-modify-role.py
+- ./14-dynsec-plugin-invalid.py
+- ./14-dynsec-role.py
+- ./14-dynsec-role-invalid.py
++ #./14-dynsec-acl.py
++ #./14-dynsec-anon-group.py
++ #./14-dynsec-auth.py
++ #./14-dynsec-client.py
++ #./14-dynsec-client-invalid.py
++ #./14-dynsec-default-access.py
++ #./14-dynsec-disable-client.py
++ #./14-dynsec-group.py
++ #./14-dynsec-group-invalid.py
++ #./14-dynsec-modify-client.py
++ #./14-dynsec-modify-group.py
++ #./14-dynsec-modify-role.py
++ #./14-dynsec-plugin-invalid.py
++ #./14-dynsec-role.py
++ #./14-dynsec-role-invalid.py
+ endif
+ endif
+diff --git a/test/lib/Makefile b/test/lib/Makefile
+index 6ade78d0..b31cdb4d 100644
+--- a/test/lib/Makefile
++++ b/test/lib/Makefile
+@@ -33,7 +33,7 @@ c : test-compile
+ ./02-subscribe-qos0.py $@/02-subscribe-qos0.test
+ ./02-subscribe-qos1.py $@/02-subscribe-qos1.test
+ ./02-subscribe-qos1.py $@/02-subscribe-qos1-async1.test
+- ./02-subscribe-qos1.py $@/02-subscribe-qos1-async2.test
++ #./02-subscribe-qos1.py $@/02-subscribe-qos1-async2.test
+ ./02-subscribe-qos2.py $@/02-subscribe-qos2.test
+ ./02-unsubscribe-multiple-v5.py $@/02-unsubscribe-multiple-v5.test
+ ./02-unsubscribe-v5.py $@/02-unsubscribe-v5.test
+@@ -50,7 +50,7 @@ c : test-compile
+ ./03-publish-c2b-qos2-disconnect.py $@/03-publish-c2b-qos2-disconnect.test
+ ./03-publish-c2b-qos2-len.py $@/03-publish-c2b-qos2-len.test
+ ./03-publish-c2b-qos2-maximum-qos-0.py $@/03-publish-c2b-qos2-maximum-qos-0.test
+- ./03-publish-c2b-qos2-maximum-qos-1.py $@/03-publish-c2b-qos2-maximum-qos-1.test
++ #./03-publish-c2b-qos2-maximum-qos-1.py $@/03-publish-c2b-qos2-maximum-qos-1.test
+ ./03-publish-c2b-qos2-pubrec-error.py $@/03-publish-c2b-qos2-pubrec-error.test
+ ./03-publish-c2b-qos2-receive-maximum-1.py $@/03-publish-c2b-qos2-receive-maximum-1.test
+ ./03-publish-c2b-qos2-receive-maximum-2.py $@/03-publish-c2b-qos2-receive-maximum-2.test
+diff --git a/test/broker/08-tls-psk-bridge.py b/test/broker/08-tls-psk-bridge.py
+index 56d3c196..cb23f518 100755
+--- a/test/broker/08-tls-psk-bridge.py
++++ b/test/broker/08-tls-psk-bridge.py
+@@ -29,8 +29,8 @@ def write_config2(filename, port2, port3):
+ f.write("bridge_psk deadbeef\n")
+
+ (port1, port2, port3) = mosq_test.get_port(3)
+-conf_file1 = "08-tls-psk-bridge.conf"
+-conf_file2 = "08-tls-psk-bridge.conf2"
++conf_file1 = "/tmp/08-tls-psk-bridge.conf"
++conf_file2 = "/tmp/08-tls-psk-bridge.conf2"
+ write_config1(conf_file1, port1, port2)
+ write_config2(conf_file2, port2, port3)
+
+@@ -54,9 +54,9 @@ suback_packet = mosq_test.gen_suback(mid, 0)
+
+ publish_packet = mosq_test.gen_publish(topic="psk/test", payload="message", qos=0)
+
+-bridge_cmd = ['../../src/mosquitto', '-c', '08-tls-psk-bridge.conf2']
+-broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port1)
+-bridge = mosq_test.start_broker(filename=os.path.basename(__file__)+'_bridge', cmd=bridge_cmd, port=port3)
++bridge_cmd = ['/usr/sbin/mosquitto', '-c', '/tmp/08-tls-psk-bridge.conf2']
++broker = mosq_test.start_broker(filename=conf_file1, use_conf=True, port=port1)
++bridge = mosq_test.start_broker(filename=conf_file2+'_bridge', cmd=bridge_cmd, port=port3)
+
+ pub = None
+ try:
diff -Nru mosquitto-1.6.9/debian/patches/install-protocol.patch mosquitto-2.0.15/debian/patches/install-protocol.patch
--- mosquitto-1.6.9/debian/patches/install-protocol.patch 2020-03-03 15:16:15.000000000 +0000
+++ mosquitto-2.0.15/debian/patches/install-protocol.patch 1970-01-01 00:00:00.000000000 +0000
@@ -1,14 +0,0 @@
-Description: Also install mqtt_protocol.h, as is done in Makefile
-Author: Gianfranco Costamagna
-Bug-Debian: https://bugs.debian.org/951116
-Forwarded: https://github.com/eclipse/mosquitto/pull/1599
-Last-Update: 2020-02-15
-
---- a/lib/CMakeLists.txt
-+++ b/lib/CMakeLists.txt
-@@ -114,4 +114,4 @@
- install(TARGETS libmosquitto_static ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}")
- endif (WITH_STATIC_LIBRARIES)
-
--install(FILES mosquitto.h DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
-+install(FILES mqtt_protocol.h mosquitto.h DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
diff -Nru mosquitto-1.6.9/debian/patches/missing-test.patch mosquitto-2.0.15/debian/patches/missing-test.patch
--- mosquitto-1.6.9/debian/patches/missing-test.patch 1970-01-01 00:00:00.000000000 +0000
+++ mosquitto-2.0.15/debian/patches/missing-test.patch 2023-07-21 08:53:30.000000000 +0000
@@ -0,0 +1,53 @@
+From 8a0df721f011f01eb60638872fad8874154da8b2 Mon Sep 17 00:00:00 2001
+From: Philippe Coval
+Date: Tue, 11 Jul 2023 23:12:52 +0200
+Subject: [PATCH] mosquitto: Bypass failing tests
+
+Some tests are failing in 06 series
+
+Those bypass are reported upstream,
+in hope we'll get hints to reduce this patch.
+
+They should be reintroduced "later".
+
+Relate-to: https://github.com/eclipse/mosquitto/issues/2850
+Relate-to: https://salsa.debian.org/rzr/mosquitto/-/jobs/4421910
+Forwarded: https://github.com/eclipse/mosquitto/pull/2851
+Signed-off-by: Philippe Coval
+---
+ test/broker/Makefile | 14 +++++++-------
+ 1 file changed, 7 insertions(+), 7 deletions(-)
+
+diff --git a/test/broker/Makefile b/test/broker/Makefile
+index 63b9ae8f..e2202fda 100644
+--- a/test/broker/Makefile
++++ b/test/broker/Makefile
+@@ -110,18 +110,18 @@ msg_sequence_test:
+ ./06-bridge-br2b-disconnect-qos1.py
+ ./06-bridge-br2b-disconnect-qos2.py
+ ./06-bridge-br2b-remapping.py
+- ./06-bridge-clean-session-csF-lcsF.py
+- ./06-bridge-clean-session-csF-lcsN.py
+- ./06-bridge-clean-session-csF-lcsT.py
+- ./06-bridge-clean-session-csT-lcsF.py
+- ./06-bridge-clean-session-csT-lcsN.py
+- ./06-bridge-clean-session-csT-lcsT.py
++ #./06-bridge-clean-session-csF-lcsF.py
++ #./06-bridge-clean-session-csF-lcsN.py
++ #./06-bridge-clean-session-csF-lcsT.py
++ #./06-bridge-clean-session-csT-lcsF.py
++ #./06-bridge-clean-session-csT-lcsN.py
++ #./06-bridge-clean-session-csT-lcsT.py
+ ./06-bridge-fail-persist-resend-qos1.py
+ ./06-bridge-fail-persist-resend-qos2.py
+ ./06-bridge-no-local.py
+ ./06-bridge-outgoing-retain.py
+ ./06-bridge-per-listener-settings.py
+- ./06-bridge-reconnect-local-out.py
++ #./06-bridge-reconnect-local-out.py
+
+ 07 :
+ ./07-will-delay-invalid-573191.py
+--
+2.39.2
+
diff -Nru mosquitto-1.6.9/debian/patches/series mosquitto-2.0.15/debian/patches/series
--- mosquitto-1.6.9/debian/patches/series 2020-03-03 15:16:15.000000000 +0000
+++ mosquitto-2.0.15/debian/patches/series 2023-07-20 14:31:58.000000000 +0000
@@ -1,3 +1,4 @@
debian-config.patch
1571.patch
-install-protocol.patch
+deb-test.patch
+missing-test.patch
diff -Nru mosquitto-1.6.9/debian/rules mosquitto-2.0.15/debian/rules
--- mosquitto-1.6.9/debian/rules 2020-03-03 15:16:15.000000000 +0000
+++ mosquitto-2.0.15/debian/rules 2023-07-20 14:31:58.000000000 +0000
@@ -1,5 +1,9 @@
#!/usr/bin/make -f
+DEB_BUILD_MAINT_OPTIONS += hardening=+all
+DPKG_EXPORT_BUILDFLAGS = 1
+include /usr/share/dpkg/buildflags.mk
+
%:
dh $@ --buildsystem=cmake
@@ -16,9 +20,9 @@
dh_auto_configure -- -DWITH_BUNDLED_DEPS=OFF -DWITH_ADNS=ON -DUSE_LIBWRAP=ON -DWITH_WEBSOCKETS=ON -DWITH_DLT=ON -DWITH_SYSTEMD=${SYSTEMD}
override_dh_strip:
- dh_strip -p mosquitto --dbgsym-migration='mosquitto-dbg (<< 1.5)' -Xlibmosquitto
- dh_strip -p libmosquitto1 --dbgsym-migration='libmosquitto1-dbg (<< 1.5)' -Xlibmosquittopp -Xsbin/mosquitto
- dh_strip -p libmosquittopp1 --dbgsym-migration='libmosquittopp1-dbg (<< 1.5)' -Xlibmosquitto.so -Xsbin/mosquitto
+ dh_strip -p mosquitto -Xlibmosquitto
+ dh_strip -p libmosquitto1 -Xlibmosquittopp -Xsbin/mosquitto
+ dh_strip -p libmosquittopp1 -Xlibmosquitto.so -Xsbin/mosquitto
dh_strip --remaining-packages
override_dh_auto_install:
diff -Nru mosquitto-1.6.9/debian/tests/broker mosquitto-2.0.15/debian/tests/broker
--- mosquitto-1.6.9/debian/tests/broker 1970-01-01 00:00:00.000000000 +0000
+++ mosquitto-2.0.15/debian/tests/broker 2023-07-20 14:31:58.000000000 +0000
@@ -0,0 +1,6 @@
+# test/broker/01-connect-575314.py is added by
+# d/p/Fix-CONNECT-performance-with-many-user-properties.patch
+# not executable. As workaround until rebaing to new upstream
+# version, make all py files executable
+chmod -c 755 -- test/broker/*.py
+make -C test/broker test
diff -Nru mosquitto-1.6.9/debian/tests/client mosquitto-2.0.15/debian/tests/client
--- mosquitto-1.6.9/debian/tests/client 1970-01-01 00:00:00.000000000 +0000
+++ mosquitto-2.0.15/debian/tests/client 2023-07-20 14:31:58.000000000 +0000
@@ -0,0 +1 @@
+make -C test/client test
diff -Nru mosquitto-1.6.9/debian/tests/control mosquitto-2.0.15/debian/tests/control
--- mosquitto-1.6.9/debian/tests/control 1970-01-01 00:00:00.000000000 +0000
+++ mosquitto-2.0.15/debian/tests/control 2023-07-21 08:53:30.000000000 +0000
@@ -0,0 +1,5 @@
+Tests: broker, library, client
+Depends: @, gcc, g++, libc6-dev, python3, libssl-dev
+# rw-build-tree: tests generate and cleanup config files as they run, plus the
+# broker saves persistence data and cleans it up.
+Restrictions: rw-build-tree
diff -Nru mosquitto-1.6.9/debian/tests/library mosquitto-2.0.15/debian/tests/library
--- mosquitto-1.6.9/debian/tests/library 1970-01-01 00:00:00.000000000 +0000
+++ mosquitto-2.0.15/debian/tests/library 2023-07-20 14:31:58.000000000 +0000
@@ -0,0 +1 @@
+make -C test/lib test
diff -Nru mosquitto-1.6.9/debian/watch mosquitto-2.0.15/debian/watch
--- mosquitto-1.6.9/debian/watch 2020-03-03 15:16:15.000000000 +0000
+++ mosquitto-2.0.15/debian/watch 2023-07-20 14:31:58.000000000 +0000
@@ -1,3 +1,4 @@
-version=3
-opts="pgpsigurlmangle=s/$/.asc/" \
- https://mosquitto.org/files/source/mosquitto-(.*)\.tar\.gz
+version=4
+opts="mode=git,pgpmode=gittag" \
+https://github.com/eclipse/@PACKAGE@ \
+refs/tags/@ANY_VERSION@
diff -Nru mosquitto-1.6.9/deps/uthash.h mosquitto-2.0.15/deps/uthash.h
--- mosquitto-1.6.9/deps/uthash.h 1970-01-01 00:00:00.000000000 +0000
+++ mosquitto-2.0.15/deps/uthash.h 2022-08-16 13:34:02.000000000 +0000
@@ -0,0 +1,1227 @@
+/*
+Copyright (c) 2003-2018, Troy D. Hanson http://troydhanson.github.com/uthash/
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef UTHASH_H
+#define UTHASH_H
+
+#define UTHASH_VERSION 2.1.0
+
+#include /* memcmp, memset, strlen */
+#include /* ptrdiff_t */
+#include /* exit */
+
+/* These macros use decltype or the earlier __typeof GNU extension.
+ As decltype is only available in newer compilers (VS2010 or gcc 4.3+
+ when compiling c++ source) this code uses whatever method is needed
+ or, for VS2008 where neither is available, uses casting workarounds. */
+#if !defined(DECLTYPE) && !defined(NO_DECLTYPE)
+#if defined(_MSC_VER) /* MS compiler */
+#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */
+#define DECLTYPE(x) (decltype(x))
+#else /* VS2008 or older (or VS2010 in C mode) */
+#define NO_DECLTYPE
+#endif
+#elif defined(__BORLANDC__) || defined(__ICCARM__) || defined(__LCC__) || defined(__WATCOMC__)
+#define NO_DECLTYPE
+#else /* GNU, Sun and other compilers */
+#define DECLTYPE(x) (__typeof(x))
+#endif
+#endif
+
+#ifdef NO_DECLTYPE
+#define DECLTYPE(x)
+#define DECLTYPE_ASSIGN(dst,src) \
+do { \
+ char **_da_dst = (char**)(&(dst)); \
+ *_da_dst = (char*)(src); \
+} while (0)
+#else
+#define DECLTYPE_ASSIGN(dst,src) \
+do { \
+ (dst) = DECLTYPE(dst)(src); \
+} while (0)
+#endif
+
+/* a number of the hash function use uint32_t which isn't defined on Pre VS2010 */
+#if defined(_WIN32)
+#if defined(_MSC_VER) && _MSC_VER >= 1600
+#include
+#elif defined(__WATCOMC__) || defined(__MINGW32__) || defined(__CYGWIN__)
+#include
+#else
+typedef unsigned int uint32_t;
+typedef unsigned char uint8_t;
+#endif
+#elif defined(__GNUC__) && !defined(__VXWORKS__)
+#include
+#else
+typedef unsigned int uint32_t;
+typedef unsigned char uint8_t;
+#endif
+
+#ifndef uthash_malloc
+#define uthash_malloc(sz) malloc(sz) /* malloc fcn */
+#endif
+#ifndef uthash_free
+#define uthash_free(ptr,sz) free(ptr) /* free fcn */
+#endif
+#ifndef uthash_bzero
+#define uthash_bzero(a,n) memset(a,'\0',n)
+#endif
+#ifndef uthash_strlen
+#define uthash_strlen(s) strlen(s)
+#endif
+
+#ifdef uthash_memcmp
+/* This warning will not catch programs that define uthash_memcmp AFTER including uthash.h. */
+#warning "uthash_memcmp is deprecated; please use HASH_KEYCMP instead"
+#else
+#define uthash_memcmp(a,b,n) memcmp(a,b,n)
+#endif
+
+#ifndef HASH_KEYCMP
+#define HASH_KEYCMP(a,b,n) uthash_memcmp(a,b,n)
+#endif
+
+#ifndef uthash_noexpand_fyi
+#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */
+#endif
+#ifndef uthash_expand_fyi
+#define uthash_expand_fyi(tbl) /* can be defined to log expands */
+#endif
+
+#ifndef HASH_NONFATAL_OOM
+#define HASH_NONFATAL_OOM 0
+#endif
+
+#if HASH_NONFATAL_OOM
+/* malloc failures can be recovered from */
+
+#ifndef uthash_nonfatal_oom
+#define uthash_nonfatal_oom(obj) do {} while (0) /* non-fatal OOM error */
+#endif
+
+#define HASH_RECORD_OOM(oomed) do { (oomed) = 1; } while (0)
+#define IF_HASH_NONFATAL_OOM(x) x
+
+#else
+/* malloc failures result in lost memory, hash tables are unusable */
+
+#ifndef uthash_fatal
+#define uthash_fatal(msg) exit(-1) /* fatal OOM error */
+#endif
+
+#define HASH_RECORD_OOM(oomed) uthash_fatal("out of memory")
+#define IF_HASH_NONFATAL_OOM(x)
+
+#endif
+
+/* initial number of buckets */
+#define HASH_INITIAL_NUM_BUCKETS 32U /* initial number of buckets */
+#define HASH_INITIAL_NUM_BUCKETS_LOG2 5U /* lg2 of initial number of buckets */
+#define HASH_BKT_CAPACITY_THRESH 10U /* expand when bucket count reaches */
+
+/* calculate the element whose hash handle address is hhp */
+#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho)))
+/* calculate the hash handle from element address elp */
+#define HH_FROM_ELMT(tbl,elp) ((UT_hash_handle *)(((char*)(elp)) + ((tbl)->hho)))
+
+#define HASH_ROLLBACK_BKT(hh, head, itemptrhh) \
+do { \
+ struct UT_hash_handle *_hd_hh_item = (itemptrhh); \
+ unsigned _hd_bkt; \
+ HASH_TO_BKT(_hd_hh_item->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \
+ (head)->hh.tbl->buckets[_hd_bkt].count++; \
+ _hd_hh_item->hh_next = NULL; \
+ _hd_hh_item->hh_prev = NULL; \
+} while (0)
+
+#define HASH_VALUE(keyptr,keylen,hashv) \
+do { \
+ HASH_FCN(keyptr, keylen, hashv); \
+} while (0)
+
+#define HASH_FIND_BYHASHVALUE(hh,head,keyptr,keylen,hashval,out) \
+do { \
+ (out) = NULL; \
+ if (head) { \
+ unsigned _hf_bkt; \
+ HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _hf_bkt); \
+ if (HASH_BLOOM_TEST((head)->hh.tbl, hashval) != 0) { \
+ HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], keyptr, keylen, hashval, out); \
+ } \
+ } \
+} while (0)
+
+#define HASH_FIND(hh,head,keyptr,keylen,out) \
+do { \
+ unsigned _hf_hashv; \
+ HASH_VALUE(keyptr, keylen, _hf_hashv); \
+ HASH_FIND_BYHASHVALUE(hh, head, keyptr, keylen, _hf_hashv, out); \
+} while (0)
+
+#ifdef HASH_BLOOM
+#define HASH_BLOOM_BITLEN (1UL << HASH_BLOOM)
+#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8UL) + (((HASH_BLOOM_BITLEN%8UL)!=0UL) ? 1UL : 0UL)
+#define HASH_BLOOM_MAKE(tbl,oomed) \
+do { \
+ (tbl)->bloom_nbits = HASH_BLOOM; \
+ (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \
+ if (!(tbl)->bloom_bv) { \
+ HASH_RECORD_OOM(oomed); \
+ } else { \
+ uthash_bzero((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \
+ (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \
+ } \
+} while (0)
+
+#define HASH_BLOOM_FREE(tbl) \
+do { \
+ uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \
+} while (0)
+
+#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8U] |= (1U << ((idx)%8U)))
+#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8U] & (1U << ((idx)%8U)))
+
+#define HASH_BLOOM_ADD(tbl,hashv) \
+ HASH_BLOOM_BITSET((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U)))
+
+#define HASH_BLOOM_TEST(tbl,hashv) \
+ HASH_BLOOM_BITTEST((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U)))
+
+#else
+#define HASH_BLOOM_MAKE(tbl,oomed)
+#define HASH_BLOOM_FREE(tbl)
+#define HASH_BLOOM_ADD(tbl,hashv)
+#define HASH_BLOOM_TEST(tbl,hashv) (1)
+#define HASH_BLOOM_BYTELEN 0U
+#endif
+
+#define HASH_MAKE_TABLE(hh,head,oomed) \
+do { \
+ (head)->hh.tbl = (UT_hash_table*)uthash_malloc(sizeof(UT_hash_table)); \
+ if (!(head)->hh.tbl) { \
+ HASH_RECORD_OOM(oomed); \
+ } else { \
+ uthash_bzero((head)->hh.tbl, sizeof(UT_hash_table)); \
+ (head)->hh.tbl->tail = &((head)->hh); \
+ (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \
+ (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \
+ (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \
+ (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \
+ HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket)); \
+ (head)->hh.tbl->signature = HASH_SIGNATURE; \
+ if (!(head)->hh.tbl->buckets) { \
+ HASH_RECORD_OOM(oomed); \
+ uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \
+ } else { \
+ uthash_bzero((head)->hh.tbl->buckets, \
+ HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket)); \
+ HASH_BLOOM_MAKE((head)->hh.tbl, oomed); \
+ IF_HASH_NONFATAL_OOM( \
+ if (oomed) { \
+ uthash_free((head)->hh.tbl->buckets, \
+ HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \
+ uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \
+ } \
+ ) \
+ } \
+ } \
+} while (0)
+
+#define HASH_REPLACE_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,replaced,cmpfcn) \
+do { \
+ (replaced) = NULL; \
+ HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \
+ if (replaced) { \
+ HASH_DELETE(hh, head, replaced); \
+ } \
+ HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn); \
+} while (0)
+
+#define HASH_REPLACE_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add,replaced) \
+do { \
+ (replaced) = NULL; \
+ HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \
+ if (replaced) { \
+ HASH_DELETE(hh, head, replaced); \
+ } \
+ HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add); \
+} while (0)
+
+#define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced) \
+do { \
+ unsigned _hr_hashv; \
+ HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \
+ HASH_REPLACE_BYHASHVALUE(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced); \
+} while (0)
+
+#define HASH_REPLACE_INORDER(hh,head,fieldname,keylen_in,add,replaced,cmpfcn) \
+do { \
+ unsigned _hr_hashv; \
+ HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \
+ HASH_REPLACE_BYHASHVALUE_INORDER(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced, cmpfcn); \
+} while (0)
+
+#define HASH_APPEND_LIST(hh, head, add) \
+do { \
+ (add)->hh.next = NULL; \
+ (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \
+ (head)->hh.tbl->tail->next = (add); \
+ (head)->hh.tbl->tail = &((add)->hh); \
+} while (0)
+
+#define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn) \
+do { \
+ do { \
+ if (cmpfcn(DECLTYPE(head)(_hs_iter), add) > 0) { \
+ break; \
+ } \
+ } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next)); \
+} while (0)
+
+#ifdef NO_DECLTYPE
+#undef HASH_AKBI_INNER_LOOP
+#define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn) \
+do { \
+ char *_hs_saved_head = (char*)(head); \
+ do { \
+ DECLTYPE_ASSIGN(head, _hs_iter); \
+ if (cmpfcn(head, add) > 0) { \
+ DECLTYPE_ASSIGN(head, _hs_saved_head); \
+ break; \
+ } \
+ DECLTYPE_ASSIGN(head, _hs_saved_head); \
+ } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next)); \
+} while (0)
+#endif
+
+#if HASH_NONFATAL_OOM
+
+#define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed) \
+do { \
+ if (!(oomed)) { \
+ unsigned _ha_bkt; \
+ (head)->hh.tbl->num_items++; \
+ HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \
+ HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed); \
+ if (oomed) { \
+ HASH_ROLLBACK_BKT(hh, head, &(add)->hh); \
+ HASH_DELETE_HH(hh, head, &(add)->hh); \
+ (add)->hh.tbl = NULL; \
+ uthash_nonfatal_oom(add); \
+ } else { \
+ HASH_BLOOM_ADD((head)->hh.tbl, hashval); \
+ HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \
+ } \
+ } else { \
+ (add)->hh.tbl = NULL; \
+ uthash_nonfatal_oom(add); \
+ } \
+} while (0)
+
+#else
+
+#define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed) \
+do { \
+ unsigned _ha_bkt; \
+ (head)->hh.tbl->num_items++; \
+ HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \
+ HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed); \
+ HASH_BLOOM_ADD((head)->hh.tbl, hashval); \
+ HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \
+} while (0)
+
+#endif
+
+
+#define HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh,head,keyptr,keylen_in,hashval,add,cmpfcn) \
+do { \
+ IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; ) \
+ (add)->hh.hashv = (hashval); \
+ (add)->hh.key = (char*) (keyptr); \
+ (add)->hh.keylen = (unsigned) (keylen_in); \
+ if (!(head)) { \
+ (add)->hh.next = NULL; \
+ (add)->hh.prev = NULL; \
+ HASH_MAKE_TABLE(hh, add, _ha_oomed); \
+ IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { ) \
+ (head) = (add); \
+ IF_HASH_NONFATAL_OOM( } ) \
+ } else { \
+ void *_hs_iter = (head); \
+ (add)->hh.tbl = (head)->hh.tbl; \
+ HASH_AKBI_INNER_LOOP(hh, head, add, cmpfcn); \
+ if (_hs_iter) { \
+ (add)->hh.next = _hs_iter; \
+ if (((add)->hh.prev = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev)) { \
+ HH_FROM_ELMT((head)->hh.tbl, (add)->hh.prev)->next = (add); \
+ } else { \
+ (head) = (add); \
+ } \
+ HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev = (add); \
+ } else { \
+ HASH_APPEND_LIST(hh, head, add); \
+ } \
+ } \
+ HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed); \
+ HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE_INORDER"); \
+} while (0)
+
+#define HASH_ADD_KEYPTR_INORDER(hh,head,keyptr,keylen_in,add,cmpfcn) \
+do { \
+ unsigned _hs_hashv; \
+ HASH_VALUE(keyptr, keylen_in, _hs_hashv); \
+ HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, keyptr, keylen_in, _hs_hashv, add, cmpfcn); \
+} while (0)
+
+#define HASH_ADD_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,cmpfcn) \
+ HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn)
+
+#define HASH_ADD_INORDER(hh,head,fieldname,keylen_in,add,cmpfcn) \
+ HASH_ADD_KEYPTR_INORDER(hh, head, &((add)->fieldname), keylen_in, add, cmpfcn)
+
+#define HASH_ADD_KEYPTR_BYHASHVALUE(hh,head,keyptr,keylen_in,hashval,add) \
+do { \
+ IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; ) \
+ (add)->hh.hashv = (hashval); \
+ (add)->hh.key = (char*) (keyptr); \
+ (add)->hh.keylen = (unsigned) (keylen_in); \
+ if (!(head)) { \
+ (add)->hh.next = NULL; \
+ (add)->hh.prev = NULL; \
+ HASH_MAKE_TABLE(hh, add, _ha_oomed); \
+ IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { ) \
+ (head) = (add); \
+ IF_HASH_NONFATAL_OOM( } ) \
+ } else { \
+ (add)->hh.tbl = (head)->hh.tbl; \
+ HASH_APPEND_LIST(hh, head, add); \
+ } \
+ HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed); \
+ HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE"); \
+} while (0)
+
+#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \
+do { \
+ unsigned _ha_hashv; \
+ HASH_VALUE(keyptr, keylen_in, _ha_hashv); \
+ HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, keyptr, keylen_in, _ha_hashv, add); \
+} while (0)
+
+#define HASH_ADD_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add) \
+ HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add)
+
+#define HASH_ADD(hh,head,fieldname,keylen_in,add) \
+ HASH_ADD_KEYPTR(hh, head, &((add)->fieldname), keylen_in, add)
+
+#define HASH_TO_BKT(hashv,num_bkts,bkt) \
+do { \
+ bkt = ((hashv) & ((num_bkts) - 1U)); \
+} while (0)
+
+/* delete "delptr" from the hash table.
+ * "the usual" patch-up process for the app-order doubly-linked-list.
+ * The use of _hd_hh_del below deserves special explanation.
+ * These used to be expressed using (delptr) but that led to a bug
+ * if someone used the same symbol for the head and deletee, like
+ * HASH_DELETE(hh,users,users);
+ * We want that to work, but by changing the head (users) below
+ * we were forfeiting our ability to further refer to the deletee (users)
+ * in the patch-up process. Solution: use scratch space to
+ * copy the deletee pointer, then the latter references are via that
+ * scratch pointer rather than through the repointed (users) symbol.
+ */
+#define HASH_DELETE(hh,head,delptr) \
+ HASH_DELETE_HH(hh, head, &(delptr)->hh)
+
+#define HASH_DELETE_HH(hh,head,delptrhh) \
+do { \
+ struct UT_hash_handle *_hd_hh_del = (delptrhh); \
+ if ((_hd_hh_del->prev == NULL) && (_hd_hh_del->next == NULL)) { \
+ HASH_BLOOM_FREE((head)->hh.tbl); \
+ uthash_free((head)->hh.tbl->buckets, \
+ (head)->hh.tbl->num_buckets * sizeof(struct UT_hash_bucket)); \
+ uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \
+ (head) = NULL; \
+ } else { \
+ unsigned _hd_bkt; \
+ if (_hd_hh_del == (head)->hh.tbl->tail) { \
+ (head)->hh.tbl->tail = HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev); \
+ } \
+ if (_hd_hh_del->prev != NULL) { \
+ HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev)->next = _hd_hh_del->next; \
+ } else { \
+ DECLTYPE_ASSIGN(head, _hd_hh_del->next); \
+ } \
+ if (_hd_hh_del->next != NULL) { \
+ HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->next)->prev = _hd_hh_del->prev; \
+ } \
+ HASH_TO_BKT(_hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \
+ HASH_DEL_IN_BKT((head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \
+ (head)->hh.tbl->num_items--; \
+ } \
+ HASH_FSCK(hh, head, "HASH_DELETE_HH"); \
+} while (0)
+
+/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */
+#define HASH_FIND_STR(head,findstr,out) \
+do { \
+ unsigned _uthash_hfstr_keylen = (unsigned)uthash_strlen(findstr); \
+ HASH_FIND(hh, head, findstr, _uthash_hfstr_keylen, out); \
+} while (0)
+#define HASH_ADD_STR(head,strfield,add) \
+do { \
+ unsigned _uthash_hastr_keylen = (unsigned)uthash_strlen((add)->strfield); \
+ HASH_ADD(hh, head, strfield[0], _uthash_hastr_keylen, add); \
+} while (0)
+#define HASH_REPLACE_STR(head,strfield,add,replaced) \
+do { \
+ unsigned _uthash_hrstr_keylen = (unsigned)uthash_strlen((add)->strfield); \
+ HASH_REPLACE(hh, head, strfield[0], _uthash_hrstr_keylen, add, replaced); \
+} while (0)
+#define HASH_FIND_INT(head,findint,out) \
+ HASH_FIND(hh,head,findint,sizeof(int),out)
+#define HASH_ADD_INT(head,intfield,add) \
+ HASH_ADD(hh,head,intfield,sizeof(int),add)
+#define HASH_REPLACE_INT(head,intfield,add,replaced) \
+ HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced)
+#define HASH_FIND_PTR(head,findptr,out) \
+ HASH_FIND(hh,head,findptr,sizeof(void *),out)
+#define HASH_ADD_PTR(head,ptrfield,add) \
+ HASH_ADD(hh,head,ptrfield,sizeof(void *),add)
+#define HASH_REPLACE_PTR(head,ptrfield,add,replaced) \
+ HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced)
+#define HASH_DEL(head,delptr) \
+ HASH_DELETE(hh,head,delptr)
+
+/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined.
+ * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined.
+ */
+#ifdef HASH_DEBUG
+#define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0)
+#define HASH_FSCK(hh,head,where) \
+do { \
+ struct UT_hash_handle *_thh; \
+ if (head) { \
+ unsigned _bkt_i; \
+ unsigned _count = 0; \
+ char *_prev; \
+ for (_bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; ++_bkt_i) { \
+ unsigned _bkt_count = 0; \
+ _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \
+ _prev = NULL; \
+ while (_thh) { \
+ if (_prev != (char*)(_thh->hh_prev)) { \
+ HASH_OOPS("%s: invalid hh_prev %p, actual %p\n", \
+ (where), (void*)_thh->hh_prev, (void*)_prev); \
+ } \
+ _bkt_count++; \
+ _prev = (char*)(_thh); \
+ _thh = _thh->hh_next; \
+ } \
+ _count += _bkt_count; \
+ if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \
+ HASH_OOPS("%s: invalid bucket count %u, actual %u\n", \
+ (where), (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \
+ } \
+ } \
+ if (_count != (head)->hh.tbl->num_items) { \
+ HASH_OOPS("%s: invalid hh item count %u, actual %u\n", \
+ (where), (head)->hh.tbl->num_items, _count); \
+ } \
+ _count = 0; \
+ _prev = NULL; \
+ _thh = &(head)->hh; \
+ while (_thh) { \
+ _count++; \
+ if (_prev != (char*)_thh->prev) { \
+ HASH_OOPS("%s: invalid prev %p, actual %p\n", \
+ (where), (void*)_thh->prev, (void*)_prev); \
+ } \
+ _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \
+ _thh = (_thh->next ? HH_FROM_ELMT((head)->hh.tbl, _thh->next) : NULL); \
+ } \
+ if (_count != (head)->hh.tbl->num_items) { \
+ HASH_OOPS("%s: invalid app item count %u, actual %u\n", \
+ (where), (head)->hh.tbl->num_items, _count); \
+ } \
+ } \
+} while (0)
+#else
+#define HASH_FSCK(hh,head,where)
+#endif
+
+/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to
+ * the descriptor to which this macro is defined for tuning the hash function.
+ * The app can #include to get the prototype for write(2). */
+#ifdef HASH_EMIT_KEYS
+#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \
+do { \
+ unsigned _klen = fieldlen; \
+ write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \
+ write(HASH_EMIT_KEYS, keyptr, (unsigned long)fieldlen); \
+} while (0)
+#else
+#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen)
+#endif
+
+/* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */
+#ifdef HASH_FUNCTION
+#define HASH_FCN HASH_FUNCTION
+#else
+#define HASH_FCN HASH_JEN
+#endif
+
+/* The Bernstein hash function, used in Perl prior to v5.6. Note (x<<5+x)=x*33. */
+#define HASH_BER(key,keylen,hashv) \
+do { \
+ unsigned _hb_keylen = (unsigned)keylen; \
+ const unsigned char *_hb_key = (const unsigned char*)(key); \
+ (hashv) = 0; \
+ while (_hb_keylen-- != 0U) { \
+ (hashv) = (((hashv) << 5) + (hashv)) + *_hb_key++; \
+ } \
+} while (0)
+
+
+/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at
+ * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */
+#define HASH_SAX(key,keylen,hashv) \
+do { \
+ unsigned _sx_i; \
+ const unsigned char *_hs_key = (const unsigned char*)(key); \
+ hashv = 0; \
+ for (_sx_i=0; _sx_i < keylen; _sx_i++) { \
+ hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \
+ } \
+} while (0)
+/* FNV-1a variation */
+#define HASH_FNV(key,keylen,hashv) \
+do { \
+ unsigned _fn_i; \
+ const unsigned char *_hf_key = (const unsigned char*)(key); \
+ (hashv) = 2166136261U; \
+ for (_fn_i=0; _fn_i < keylen; _fn_i++) { \
+ hashv = hashv ^ _hf_key[_fn_i]; \
+ hashv = hashv * 16777619U; \
+ } \
+} while (0)
+
+#define HASH_OAT(key,keylen,hashv) \
+do { \
+ unsigned _ho_i; \
+ const unsigned char *_ho_key=(const unsigned char*)(key); \
+ hashv = 0; \
+ for(_ho_i=0; _ho_i < keylen; _ho_i++) { \
+ hashv += _ho_key[_ho_i]; \
+ hashv += (hashv << 10); \
+ hashv ^= (hashv >> 6); \
+ } \
+ hashv += (hashv << 3); \
+ hashv ^= (hashv >> 11); \
+ hashv += (hashv << 15); \
+} while (0)
+
+#define HASH_JEN_MIX(a,b,c) \
+do { \
+ a -= b; a -= c; a ^= ( c >> 13 ); \
+ b -= c; b -= a; b ^= ( a << 8 ); \
+ c -= a; c -= b; c ^= ( b >> 13 ); \
+ a -= b; a -= c; a ^= ( c >> 12 ); \
+ b -= c; b -= a; b ^= ( a << 16 ); \
+ c -= a; c -= b; c ^= ( b >> 5 ); \
+ a -= b; a -= c; a ^= ( c >> 3 ); \
+ b -= c; b -= a; b ^= ( a << 10 ); \
+ c -= a; c -= b; c ^= ( b >> 15 ); \
+} while (0)
+
+#define HASH_JEN(key,keylen,hashv) \
+do { \
+ unsigned _hj_i,_hj_j,_hj_k; \
+ unsigned const char *_hj_key=(unsigned const char*)(key); \
+ hashv = 0xfeedbeefu; \
+ _hj_i = _hj_j = 0x9e3779b9u; \
+ _hj_k = (unsigned)(keylen); \
+ while (_hj_k >= 12U) { \
+ _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \
+ + ( (unsigned)_hj_key[2] << 16 ) \
+ + ( (unsigned)_hj_key[3] << 24 ) ); \
+ _hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \
+ + ( (unsigned)_hj_key[6] << 16 ) \
+ + ( (unsigned)_hj_key[7] << 24 ) ); \
+ hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \
+ + ( (unsigned)_hj_key[10] << 16 ) \
+ + ( (unsigned)_hj_key[11] << 24 ) ); \
+ \
+ HASH_JEN_MIX(_hj_i, _hj_j, hashv); \
+ \
+ _hj_key += 12; \
+ _hj_k -= 12U; \
+ } \
+ hashv += (unsigned)(keylen); \
+ switch ( _hj_k ) { \
+ case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); /* FALLTHROUGH */ \
+ case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); /* FALLTHROUGH */ \
+ case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); /* FALLTHROUGH */ \
+ case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); /* FALLTHROUGH */ \
+ case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); /* FALLTHROUGH */ \
+ case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); /* FALLTHROUGH */ \
+ case 5: _hj_j += _hj_key[4]; /* FALLTHROUGH */ \
+ case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); /* FALLTHROUGH */ \
+ case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); /* FALLTHROUGH */ \
+ case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); /* FALLTHROUGH */ \
+ case 1: _hj_i += _hj_key[0]; \
+ } \
+ HASH_JEN_MIX(_hj_i, _hj_j, hashv); \
+} while (0)
+
+/* The Paul Hsieh hash function */
+#undef get16bits
+#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \
+ || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
+#define get16bits(d) (*((const uint16_t *) (d)))
+#endif
+
+#if !defined (get16bits)
+#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \
+ +(uint32_t)(((const uint8_t *)(d))[0]) )
+#endif
+#define HASH_SFH(key,keylen,hashv) \
+do { \
+ unsigned const char *_sfh_key=(unsigned const char*)(key); \
+ uint32_t _sfh_tmp, _sfh_len = (uint32_t)keylen; \
+ \
+ unsigned _sfh_rem = _sfh_len & 3U; \
+ _sfh_len >>= 2; \
+ hashv = 0xcafebabeu; \
+ \
+ /* Main loop */ \
+ for (;_sfh_len > 0U; _sfh_len--) { \
+ hashv += get16bits (_sfh_key); \
+ _sfh_tmp = ((uint32_t)(get16bits (_sfh_key+2)) << 11) ^ hashv; \
+ hashv = (hashv << 16) ^ _sfh_tmp; \
+ _sfh_key += 2U*sizeof (uint16_t); \
+ hashv += hashv >> 11; \
+ } \
+ \
+ /* Handle end cases */ \
+ switch (_sfh_rem) { \
+ case 3: hashv += get16bits (_sfh_key); \
+ hashv ^= hashv << 16; \
+ hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)]) << 18; \
+ hashv += hashv >> 11; \
+ break; \
+ case 2: hashv += get16bits (_sfh_key); \
+ hashv ^= hashv << 11; \
+ hashv += hashv >> 17; \
+ break; \
+ case 1: hashv += *_sfh_key; \
+ hashv ^= hashv << 10; \
+ hashv += hashv >> 1; \
+ } \
+ \
+ /* Force "avalanching" of final 127 bits */ \
+ hashv ^= hashv << 3; \
+ hashv += hashv >> 5; \
+ hashv ^= hashv << 4; \
+ hashv += hashv >> 17; \
+ hashv ^= hashv << 25; \
+ hashv += hashv >> 6; \
+} while (0)
+
+#ifdef HASH_USING_NO_STRICT_ALIASING
+/* The MurmurHash exploits some CPU's (x86,x86_64) tolerance for unaligned reads.
+ * For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error.
+ * MurmurHash uses the faster approach only on CPU's where we know it's safe.
+ *
+ * Note the preprocessor built-in defines can be emitted using:
+ *
+ * gcc -m64 -dM -E - < /dev/null (on gcc)
+ * cc -## a.c (where a.c is a simple test file) (Sun Studio)
+ */
+#if (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86))
+#define MUR_GETBLOCK(p,i) p[i]
+#else /* non intel */
+#define MUR_PLUS0_ALIGNED(p) (((unsigned long)p & 3UL) == 0UL)
+#define MUR_PLUS1_ALIGNED(p) (((unsigned long)p & 3UL) == 1UL)
+#define MUR_PLUS2_ALIGNED(p) (((unsigned long)p & 3UL) == 2UL)
+#define MUR_PLUS3_ALIGNED(p) (((unsigned long)p & 3UL) == 3UL)
+#define WP(p) ((uint32_t*)((unsigned long)(p) & ~3UL))
+#if (defined(__BIG_ENDIAN__) || defined(SPARC) || defined(__ppc__) || defined(__ppc64__))
+#define MUR_THREE_ONE(p) ((((*WP(p))&0x00ffffff) << 8) | (((*(WP(p)+1))&0xff000000) >> 24))
+#define MUR_TWO_TWO(p) ((((*WP(p))&0x0000ffff) <<16) | (((*(WP(p)+1))&0xffff0000) >> 16))
+#define MUR_ONE_THREE(p) ((((*WP(p))&0x000000ff) <<24) | (((*(WP(p)+1))&0xffffff00) >> 8))
+#else /* assume little endian non-intel */
+#define MUR_THREE_ONE(p) ((((*WP(p))&0xffffff00) >> 8) | (((*(WP(p)+1))&0x000000ff) << 24))
+#define MUR_TWO_TWO(p) ((((*WP(p))&0xffff0000) >>16) | (((*(WP(p)+1))&0x0000ffff) << 16))
+#define MUR_ONE_THREE(p) ((((*WP(p))&0xff000000) >>24) | (((*(WP(p)+1))&0x00ffffff) << 8))
+#endif
+#define MUR_GETBLOCK(p,i) (MUR_PLUS0_ALIGNED(p) ? ((p)[i]) : \
+ (MUR_PLUS1_ALIGNED(p) ? MUR_THREE_ONE(p) : \
+ (MUR_PLUS2_ALIGNED(p) ? MUR_TWO_TWO(p) : \
+ MUR_ONE_THREE(p))))
+#endif
+#define MUR_ROTL32(x,r) (((x) << (r)) | ((x) >> (32 - (r))))
+#define MUR_FMIX(_h) \
+do { \
+ _h ^= _h >> 16; \
+ _h *= 0x85ebca6bu; \
+ _h ^= _h >> 13; \
+ _h *= 0xc2b2ae35u; \
+ _h ^= _h >> 16; \
+} while (0)
+
+#define HASH_MUR(key,keylen,hashv) \
+do { \
+ const uint8_t *_mur_data = (const uint8_t*)(key); \
+ const int _mur_nblocks = (int)(keylen) / 4; \
+ uint32_t _mur_h1 = 0xf88D5353u; \
+ uint32_t _mur_c1 = 0xcc9e2d51u; \
+ uint32_t _mur_c2 = 0x1b873593u; \
+ uint32_t _mur_k1 = 0; \
+ const uint8_t *_mur_tail; \
+ const uint32_t *_mur_blocks = (const uint32_t*)(_mur_data+(_mur_nblocks*4)); \
+ int _mur_i; \
+ for (_mur_i = -_mur_nblocks; _mur_i != 0; _mur_i++) { \
+ _mur_k1 = MUR_GETBLOCK(_mur_blocks,_mur_i); \
+ _mur_k1 *= _mur_c1; \
+ _mur_k1 = MUR_ROTL32(_mur_k1,15); \
+ _mur_k1 *= _mur_c2; \
+ \
+ _mur_h1 ^= _mur_k1; \
+ _mur_h1 = MUR_ROTL32(_mur_h1,13); \
+ _mur_h1 = (_mur_h1*5U) + 0xe6546b64u; \
+ } \
+ _mur_tail = (const uint8_t*)(_mur_data + (_mur_nblocks*4)); \
+ _mur_k1=0; \
+ switch ((keylen) & 3U) { \
+ case 0: break; \
+ case 3: _mur_k1 ^= (uint32_t)_mur_tail[2] << 16; /* FALLTHROUGH */ \
+ case 2: _mur_k1 ^= (uint32_t)_mur_tail[1] << 8; /* FALLTHROUGH */ \
+ case 1: _mur_k1 ^= (uint32_t)_mur_tail[0]; \
+ _mur_k1 *= _mur_c1; \
+ _mur_k1 = MUR_ROTL32(_mur_k1,15); \
+ _mur_k1 *= _mur_c2; \
+ _mur_h1 ^= _mur_k1; \
+ } \
+ _mur_h1 ^= (uint32_t)(keylen); \
+ MUR_FMIX(_mur_h1); \
+ hashv = _mur_h1; \
+} while (0)
+#endif /* HASH_USING_NO_STRICT_ALIASING */
+
+/* iterate over items in a known bucket to find desired item */
+#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,hashval,out) \
+do { \
+ if ((head).hh_head != NULL) { \
+ DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (head).hh_head)); \
+ } else { \
+ (out) = NULL; \
+ } \
+ while ((out) != NULL) { \
+ if ((out)->hh.hashv == (hashval) && (out)->hh.keylen == (keylen_in)) { \
+ if (HASH_KEYCMP((out)->hh.key, keyptr, keylen_in) == 0) { \
+ break; \
+ } \
+ } \
+ if ((out)->hh.hh_next != NULL) { \
+ DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (out)->hh.hh_next)); \
+ } else { \
+ (out) = NULL; \
+ } \
+ } \
+} while (0)
+
+/* add an item to a bucket */
+#define HASH_ADD_TO_BKT(head,hh,addhh,oomed) \
+do { \
+ UT_hash_bucket *_ha_head = &(head); \
+ _ha_head->count++; \
+ (addhh)->hh_next = _ha_head->hh_head; \
+ (addhh)->hh_prev = NULL; \
+ if (_ha_head->hh_head != NULL) { \
+ _ha_head->hh_head->hh_prev = (addhh); \
+ } \
+ _ha_head->hh_head = (addhh); \
+ if ((_ha_head->count >= ((_ha_head->expand_mult + 1U) * HASH_BKT_CAPACITY_THRESH)) \
+ && !(addhh)->tbl->noexpand) { \
+ HASH_EXPAND_BUCKETS(addhh,(addhh)->tbl, oomed); \
+ IF_HASH_NONFATAL_OOM( \
+ if (oomed) { \
+ HASH_DEL_IN_BKT(head,addhh); \
+ } \
+ ) \
+ } \
+} while (0)
+
+/* remove an item from a given bucket */
+#define HASH_DEL_IN_BKT(head,delhh) \
+do { \
+ UT_hash_bucket *_hd_head = &(head); \
+ _hd_head->count--; \
+ if (_hd_head->hh_head == (delhh)) { \
+ _hd_head->hh_head = (delhh)->hh_next; \
+ } \
+ if ((delhh)->hh_prev) { \
+ (delhh)->hh_prev->hh_next = (delhh)->hh_next; \
+ } \
+ if ((delhh)->hh_next) { \
+ (delhh)->hh_next->hh_prev = (delhh)->hh_prev; \
+ } \
+} while (0)
+
+/* Bucket expansion has the effect of doubling the number of buckets
+ * and redistributing the items into the new buckets. Ideally the
+ * items will distribute more or less evenly into the new buckets
+ * (the extent to which this is true is a measure of the quality of
+ * the hash function as it applies to the key domain).
+ *
+ * With the items distributed into more buckets, the chain length
+ * (item count) in each bucket is reduced. Thus by expanding buckets
+ * the hash keeps a bound on the chain length. This bounded chain
+ * length is the essence of how a hash provides constant time lookup.
+ *
+ * The calculation of tbl->ideal_chain_maxlen below deserves some
+ * explanation. First, keep in mind that we're calculating the ideal
+ * maximum chain length based on the *new* (doubled) bucket count.
+ * In fractions this is just n/b (n=number of items,b=new num buckets).
+ * Since the ideal chain length is an integer, we want to calculate
+ * ceil(n/b). We don't depend on floating point arithmetic in this
+ * hash, so to calculate ceil(n/b) with integers we could write
+ *
+ * ceil(n/b) = (n/b) + ((n%b)?1:0)
+ *
+ * and in fact a previous version of this hash did just that.
+ * But now we have improved things a bit by recognizing that b is
+ * always a power of two. We keep its base 2 log handy (call it lb),
+ * so now we can write this with a bit shift and logical AND:
+ *
+ * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0)
+ *
+ */
+#define HASH_EXPAND_BUCKETS(hh,tbl,oomed) \
+do { \
+ unsigned _he_bkt; \
+ unsigned _he_bkt_i; \
+ struct UT_hash_handle *_he_thh, *_he_hh_nxt; \
+ UT_hash_bucket *_he_new_buckets, *_he_newbkt; \
+ _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \
+ 2UL * (tbl)->num_buckets * sizeof(struct UT_hash_bucket)); \
+ if (!_he_new_buckets) { \
+ HASH_RECORD_OOM(oomed); \
+ } else { \
+ uthash_bzero(_he_new_buckets, \
+ 2UL * (tbl)->num_buckets * sizeof(struct UT_hash_bucket)); \
+ (tbl)->ideal_chain_maxlen = \
+ ((tbl)->num_items >> ((tbl)->log2_num_buckets+1U)) + \
+ ((((tbl)->num_items & (((tbl)->num_buckets*2U)-1U)) != 0U) ? 1U : 0U); \
+ (tbl)->nonideal_items = 0; \
+ for (_he_bkt_i = 0; _he_bkt_i < (tbl)->num_buckets; _he_bkt_i++) { \
+ _he_thh = (tbl)->buckets[ _he_bkt_i ].hh_head; \
+ while (_he_thh != NULL) { \
+ _he_hh_nxt = _he_thh->hh_next; \
+ HASH_TO_BKT(_he_thh->hashv, (tbl)->num_buckets * 2U, _he_bkt); \
+ _he_newbkt = &(_he_new_buckets[_he_bkt]); \
+ if (++(_he_newbkt->count) > (tbl)->ideal_chain_maxlen) { \
+ (tbl)->nonideal_items++; \
+ if (_he_newbkt->count > _he_newbkt->expand_mult * (tbl)->ideal_chain_maxlen) { \
+ _he_newbkt->expand_mult++; \
+ } \
+ } \
+ _he_thh->hh_prev = NULL; \
+ _he_thh->hh_next = _he_newbkt->hh_head; \
+ if (_he_newbkt->hh_head != NULL) { \
+ _he_newbkt->hh_head->hh_prev = _he_thh; \
+ } \
+ _he_newbkt->hh_head = _he_thh; \
+ _he_thh = _he_hh_nxt; \
+ } \
+ } \
+ uthash_free((tbl)->buckets, (tbl)->num_buckets * sizeof(struct UT_hash_bucket)); \
+ (tbl)->num_buckets *= 2U; \
+ (tbl)->log2_num_buckets++; \
+ (tbl)->buckets = _he_new_buckets; \
+ (tbl)->ineff_expands = ((tbl)->nonideal_items > ((tbl)->num_items >> 1)) ? \
+ ((tbl)->ineff_expands+1U) : 0U; \
+ if ((tbl)->ineff_expands > 1U) { \
+ (tbl)->noexpand = 1; \
+ uthash_noexpand_fyi(tbl); \
+ } \
+ uthash_expand_fyi(tbl); \
+ } \
+} while (0)
+
+
+/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */
+/* Note that HASH_SORT assumes the hash handle name to be hh.
+ * HASH_SRT was added to allow the hash handle name to be passed in. */
+#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn)
+#define HASH_SRT(hh,head,cmpfcn) \
+do { \
+ unsigned _hs_i; \
+ unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \
+ struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \
+ if (head != NULL) { \
+ _hs_insize = 1; \
+ _hs_looping = 1; \
+ _hs_list = &((head)->hh); \
+ while (_hs_looping != 0U) { \
+ _hs_p = _hs_list; \
+ _hs_list = NULL; \
+ _hs_tail = NULL; \
+ _hs_nmerges = 0; \
+ while (_hs_p != NULL) { \
+ _hs_nmerges++; \
+ _hs_q = _hs_p; \
+ _hs_psize = 0; \
+ for (_hs_i = 0; _hs_i < _hs_insize; ++_hs_i) { \
+ _hs_psize++; \
+ _hs_q = ((_hs_q->next != NULL) ? \
+ HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \
+ if (_hs_q == NULL) { \
+ break; \
+ } \
+ } \
+ _hs_qsize = _hs_insize; \
+ while ((_hs_psize != 0U) || ((_hs_qsize != 0U) && (_hs_q != NULL))) { \
+ if (_hs_psize == 0U) { \
+ _hs_e = _hs_q; \
+ _hs_q = ((_hs_q->next != NULL) ? \
+ HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \
+ _hs_qsize--; \
+ } else if ((_hs_qsize == 0U) || (_hs_q == NULL)) { \
+ _hs_e = _hs_p; \
+ if (_hs_p != NULL) { \
+ _hs_p = ((_hs_p->next != NULL) ? \
+ HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL); \
+ } \
+ _hs_psize--; \
+ } else if ((cmpfcn( \
+ DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_p)), \
+ DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_q)) \
+ )) <= 0) { \
+ _hs_e = _hs_p; \
+ if (_hs_p != NULL) { \
+ _hs_p = ((_hs_p->next != NULL) ? \
+ HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL); \
+ } \
+ _hs_psize--; \
+ } else { \
+ _hs_e = _hs_q; \
+ _hs_q = ((_hs_q->next != NULL) ? \
+ HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \
+ _hs_qsize--; \
+ } \
+ if ( _hs_tail != NULL ) { \
+ _hs_tail->next = ((_hs_e != NULL) ? \
+ ELMT_FROM_HH((head)->hh.tbl, _hs_e) : NULL); \
+ } else { \
+ _hs_list = _hs_e; \
+ } \
+ if (_hs_e != NULL) { \
+ _hs_e->prev = ((_hs_tail != NULL) ? \
+ ELMT_FROM_HH((head)->hh.tbl, _hs_tail) : NULL); \
+ } \
+ _hs_tail = _hs_e; \
+ } \
+ _hs_p = _hs_q; \
+ } \
+ if (_hs_tail != NULL) { \
+ _hs_tail->next = NULL; \
+ } \
+ if (_hs_nmerges <= 1U) { \
+ _hs_looping = 0; \
+ (head)->hh.tbl->tail = _hs_tail; \
+ DECLTYPE_ASSIGN(head, ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \
+ } \
+ _hs_insize *= 2U; \
+ } \
+ HASH_FSCK(hh, head, "HASH_SRT"); \
+ } \
+} while (0)
+
+/* This function selects items from one hash into another hash.
+ * The end result is that the selected items have dual presence
+ * in both hashes. There is no copy of the items made; rather
+ * they are added into the new hash through a secondary hash
+ * hash handle that must be present in the structure. */
+#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \
+do { \
+ unsigned _src_bkt, _dst_bkt; \
+ void *_last_elt = NULL, *_elt; \
+ UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \
+ ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \
+ if ((src) != NULL) { \
+ for (_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \
+ for (_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \
+ _src_hh != NULL; \
+ _src_hh = _src_hh->hh_next) { \
+ _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \
+ if (cond(_elt)) { \
+ IF_HASH_NONFATAL_OOM( int _hs_oomed = 0; ) \
+ _dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho); \
+ _dst_hh->key = _src_hh->key; \
+ _dst_hh->keylen = _src_hh->keylen; \
+ _dst_hh->hashv = _src_hh->hashv; \
+ _dst_hh->prev = _last_elt; \
+ _dst_hh->next = NULL; \
+ if (_last_elt_hh != NULL) { \
+ _last_elt_hh->next = _elt; \
+ } \
+ if ((dst) == NULL) { \
+ DECLTYPE_ASSIGN(dst, _elt); \
+ HASH_MAKE_TABLE(hh_dst, dst, _hs_oomed); \
+ IF_HASH_NONFATAL_OOM( \
+ if (_hs_oomed) { \
+ uthash_nonfatal_oom(_elt); \
+ (dst) = NULL; \
+ continue; \
+ } \
+ ) \
+ } else { \
+ _dst_hh->tbl = (dst)->hh_dst.tbl; \
+ } \
+ HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \
+ HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt], hh_dst, _dst_hh, _hs_oomed); \
+ (dst)->hh_dst.tbl->num_items++; \
+ IF_HASH_NONFATAL_OOM( \
+ if (_hs_oomed) { \
+ HASH_ROLLBACK_BKT(hh_dst, dst, _dst_hh); \
+ HASH_DELETE_HH(hh_dst, dst, _dst_hh); \
+ _dst_hh->tbl = NULL; \
+ uthash_nonfatal_oom(_elt); \
+ continue; \
+ } \
+ ) \
+ HASH_BLOOM_ADD(_dst_hh->tbl, _dst_hh->hashv); \
+ _last_elt = _elt; \
+ _last_elt_hh = _dst_hh; \
+ } \
+ } \
+ } \
+ } \
+ HASH_FSCK(hh_dst, dst, "HASH_SELECT"); \
+} while (0)
+
+#define HASH_CLEAR(hh,head) \
+do { \
+ if ((head) != NULL) { \
+ HASH_BLOOM_FREE((head)->hh.tbl); \
+ uthash_free((head)->hh.tbl->buckets, \
+ (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \
+ uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \
+ (head) = NULL; \
+ } \
+} while (0)
+
+#define HASH_OVERHEAD(hh,head) \
+ (((head) != NULL) ? ( \
+ (size_t)(((head)->hh.tbl->num_items * sizeof(UT_hash_handle)) + \
+ ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket)) + \
+ sizeof(UT_hash_table) + \
+ (HASH_BLOOM_BYTELEN))) : 0U)
+
+#ifdef NO_DECLTYPE
+#define HASH_ITER(hh,head,el,tmp) \
+for(((el)=(head)), ((*(char**)(&(tmp)))=(char*)((head!=NULL)?(head)->hh.next:NULL)); \
+ (el) != NULL; ((el)=(tmp)), ((*(char**)(&(tmp)))=(char*)((tmp!=NULL)?(tmp)->hh.next:NULL)))
+#else
+#define HASH_ITER(hh,head,el,tmp) \
+for(((el)=(head)), ((tmp)=DECLTYPE(el)((head!=NULL)?(head)->hh.next:NULL)); \
+ (el) != NULL; ((el)=(tmp)), ((tmp)=DECLTYPE(el)((tmp!=NULL)?(tmp)->hh.next:NULL)))
+#endif
+
+/* obtain a count of items in the hash */
+#define HASH_COUNT(head) HASH_CNT(hh,head)
+#define HASH_CNT(hh,head) ((head != NULL)?((head)->hh.tbl->num_items):0U)
+
+typedef struct UT_hash_bucket {
+ struct UT_hash_handle *hh_head;
+ unsigned count;
+
+ /* expand_mult is normally set to 0. In this situation, the max chain length
+ * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If
+ * the bucket's chain exceeds this length, bucket expansion is triggered).
+ * However, setting expand_mult to a non-zero value delays bucket expansion
+ * (that would be triggered by additions to this particular bucket)
+ * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH.
+ * (The multiplier is simply expand_mult+1). The whole idea of this
+ * multiplier is to reduce bucket expansions, since they are expensive, in
+ * situations where we know that a particular bucket tends to be overused.
+ * It is better to let its chain length grow to a longer yet-still-bounded
+ * value, than to do an O(n) bucket expansion too often.
+ */
+ unsigned expand_mult;
+
+} UT_hash_bucket;
+
+/* random signature used only to find hash tables in external analysis */
+#define HASH_SIGNATURE 0xa0111fe1u
+#define HASH_BLOOM_SIGNATURE 0xb12220f2u
+
+typedef struct UT_hash_table {
+ UT_hash_bucket *buckets;
+ unsigned num_buckets, log2_num_buckets;
+ unsigned num_items;
+ struct UT_hash_handle *tail; /* tail hh in app order, for fast append */
+ ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */
+
+ /* in an ideal situation (all buckets used equally), no bucket would have
+ * more than ceil(#items/#buckets) items. that's the ideal chain length. */
+ unsigned ideal_chain_maxlen;
+
+ /* nonideal_items is the number of items in the hash whose chain position
+ * exceeds the ideal chain maxlen. these items pay the penalty for an uneven
+ * hash distribution; reaching them in a chain traversal takes >ideal steps */
+ unsigned nonideal_items;
+
+ /* ineffective expands occur when a bucket doubling was performed, but
+ * afterward, more than half the items in the hash had nonideal chain
+ * positions. If this happens on two consecutive expansions we inhibit any
+ * further expansion, as it's not helping; this happens when the hash
+ * function isn't a good fit for the key domain. When expansion is inhibited
+ * the hash will still work, albeit no longer in constant time. */
+ unsigned ineff_expands, noexpand;
+
+ uint32_t signature; /* used only to find hash tables in external analysis */
+#ifdef HASH_BLOOM
+ uint32_t bloom_sig; /* used only to test bloom exists in external analysis */
+ uint8_t *bloom_bv;
+ uint8_t bloom_nbits;
+#endif
+
+} UT_hash_table;
+
+typedef struct UT_hash_handle {
+ struct UT_hash_table *tbl;
+ void *prev; /* prev element in app order */
+ void *next; /* next element in app order */
+ struct UT_hash_handle *hh_prev; /* previous hh in bucket order */
+ struct UT_hash_handle *hh_next; /* next hh in bucket order */
+ void *key; /* ptr to enclosing struct's key */
+ unsigned keylen; /* enclosing struct's key len */
+ unsigned hashv; /* result of hash-fcn(key) */
+} UT_hash_handle;
+
+#endif /* UTHASH_H */
diff -Nru mosquitto-1.6.9/deps/utlist.h mosquitto-2.0.15/deps/utlist.h
--- mosquitto-1.6.9/deps/utlist.h 1970-01-01 00:00:00.000000000 +0000
+++ mosquitto-2.0.15/deps/utlist.h 2022-08-16 13:34:02.000000000 +0000
@@ -0,0 +1,1073 @@
+/*
+Copyright (c) 2007-2018, Troy D. Hanson http://troydhanson.github.com/uthash/
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef UTLIST_H
+#define UTLIST_H
+
+#define UTLIST_VERSION 2.1.0
+
+#include