From ec9229d6c7288cea34494f03736d74e888e0380a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20G=C3=A4hwiler?= Date: Fri, 18 Mar 2022 09:35:40 +0100 Subject: [PATCH] updated libmosquitto --- Makefile | 5 +- example-ofxMQTT/src/main.cpp | 4 +- libs/mosquitto/src/actions.c | 246 ++- libs/mosquitto/src/alias_mosq.c | 86 + libs/mosquitto/src/alias_mosq.h | 28 + libs/mosquitto/src/callbacks.c | 54 +- libs/mosquitto/src/config.h | 50 +- libs/mosquitto/src/connect.c | 255 ++- libs/mosquitto/src/dummypthread.h | 5 +- libs/mosquitto/src/handle_auth.c | 55 + libs/mosquitto/src/handle_connack.c | 103 +- libs/mosquitto/src/handle_disconnect.c | 68 + libs/mosquitto/src/handle_ping.c | 34 +- libs/mosquitto/src/handle_pubackcomp.c | 122 +- libs/mosquitto/src/handle_publish.c | 75 +- libs/mosquitto/src/handle_pubrec.c | 89 +- libs/mosquitto/src/handle_pubrel.c | 86 +- libs/mosquitto/src/handle_suback.c | 60 +- libs/mosquitto/src/handle_unsuback.c | 46 +- libs/mosquitto/src/helpers.c | 28 +- libs/mosquitto/src/logging_mosq.c | 19 +- libs/mosquitto/src/logging_mosq.h | 18 +- libs/mosquitto/src/loop.c | 196 ++- libs/mosquitto/src/memory_mosq.c | 39 +- libs/mosquitto/src/memory_mosq.h | 18 +- libs/mosquitto/src/messages_mosq.c | 320 ++-- libs/mosquitto/src/messages_mosq.h | 15 +- libs/mosquitto/src/misc_mosq.c | 210 +++ libs/mosquitto/src/misc_mosq.h | 28 + libs/mosquitto/src/mosquitto.c | 225 +-- libs/mosquitto/src/mosquitto.h | 2029 +++++++++++++++++++---- libs/mosquitto/src/mosquitto_broker.h | 561 +++++++ libs/mosquitto/src/mosquitto_internal.h | 162 +- libs/mosquitto/src/mosquitto_plugin.h | 420 +++++ libs/mosquitto/src/mqtt3_protocol.h | 53 - libs/mosquitto/src/mqtt_protocol.h | 282 ++++ libs/mosquitto/src/net_mosq.c | 560 +++++-- libs/mosquitto/src/net_mosq.h | 47 +- libs/mosquitto/src/net_mosq_ocsp.c | 167 ++ libs/mosquitto/src/options.c | 301 +++- libs/mosquitto/src/packet_datatypes.c | 273 +++ libs/mosquitto/src/packet_mosq.c | 341 ++-- libs/mosquitto/src/packet_mosq.h | 33 +- libs/mosquitto/src/property_mosq.c | 1293 +++++++++++++++ libs/mosquitto/src/property_mosq.h | 54 + libs/mosquitto/src/read_handle.c | 40 +- libs/mosquitto/src/read_handle.h | 18 +- libs/mosquitto/src/send_connect.c | 143 +- libs/mosquitto/src/send_disconnect.c | 60 +- libs/mosquitto/src/send_mosq.c | 93 +- libs/mosquitto/src/send_mosq.h | 35 +- libs/mosquitto/src/send_publish.c | 90 +- libs/mosquitto/src/send_subscribe.c | 48 +- libs/mosquitto/src/send_unsubscribe.c | 49 +- libs/mosquitto/src/socks_mosq.c | 144 +- libs/mosquitto/src/socks_mosq.h | 8 +- libs/mosquitto/src/srv_mosq.c | 40 +- libs/mosquitto/src/strings_mosq.c | 235 +++ libs/mosquitto/src/thread_mosq.c | 52 +- libs/mosquitto/src/time_mosq.c | 14 +- libs/mosquitto/src/time_mosq.h | 12 +- libs/mosquitto/src/tls_mosq.c | 28 +- libs/mosquitto/src/tls_mosq.h | 13 +- libs/mosquitto/src/utf8_mosq.c | 40 +- libs/mosquitto/src/uthash.h | 1227 ++++++++++++++ libs/mosquitto/src/util_mosq.c | 425 ++--- libs/mosquitto/src/util_mosq.h | 31 +- libs/mosquitto/src/util_topic.c | 446 +++++ libs/mosquitto/src/utlist.h | 1073 ++++++++++++ libs/mosquitto/src/will_mosq.c | 76 +- libs/mosquitto/src/will_mosq.h | 14 +- 71 files changed, 11521 insertions(+), 2096 deletions(-) create mode 100644 libs/mosquitto/src/alias_mosq.c create mode 100644 libs/mosquitto/src/alias_mosq.h create mode 100644 libs/mosquitto/src/handle_auth.c create mode 100644 libs/mosquitto/src/handle_disconnect.c create mode 100644 libs/mosquitto/src/misc_mosq.c create mode 100644 libs/mosquitto/src/misc_mosq.h create mode 100644 libs/mosquitto/src/mosquitto_broker.h create mode 100644 libs/mosquitto/src/mosquitto_plugin.h delete mode 100644 libs/mosquitto/src/mqtt3_protocol.h create mode 100644 libs/mosquitto/src/mqtt_protocol.h create mode 100644 libs/mosquitto/src/net_mosq_ocsp.c create mode 100644 libs/mosquitto/src/packet_datatypes.c create mode 100644 libs/mosquitto/src/property_mosq.c create mode 100644 libs/mosquitto/src/property_mosq.h create mode 100644 libs/mosquitto/src/strings_mosq.c create mode 100644 libs/mosquitto/src/uthash.h create mode 100644 libs/mosquitto/src/util_topic.c create mode 100644 libs/mosquitto/src/utlist.h diff --git a/Makefile b/Makefile index fa37428..32d9c7c 100644 --- a/Makefile +++ b/Makefile @@ -6,13 +6,14 @@ update: @echo "clone mosquitto library" git clone https://github.com/eclipse/mosquitto.git ./mosquitto - cd mosquitto; git checkout v1.5.8; cd .. + cd mosquitto; git checkout v2.0.14; cd .. @echo "copy mosquitto files" cp ./mosquitto/config.h ./libs/mosquitto/src/config.h cp ./mosquitto/lib/*.h ./libs/mosquitto/src/ cp ./mosquitto/lib/*.c ./libs/mosquitto/src/ - # cp ./mosquitto/src/deps/utlist.h ./libs/mosquitto/src/utlist.h + cp ./mosquitto/include/*.h ./libs/mosquitto/src/ + cp ./mosquitto/deps/*.h ./libs/mosquitto/src/ @echo "remove temporary files" rm -rf ./mosquitto diff --git a/example-ofxMQTT/src/main.cpp b/example-ofxMQTT/src/main.cpp index 537ed5a..4acb3db 100755 --- a/example-ofxMQTT/src/main.cpp +++ b/example-ofxMQTT/src/main.cpp @@ -1,8 +1,6 @@ #include "ofApp.h" -#include "ofAppGlutWindow.h" int main(){ - ofAppGlutWindow window; - ofSetupOpenGL(&window, 1024, 768, OF_WINDOW); + ofSetupOpenGL(800, 600, OF_WINDOW); ofRunApp(new ofApp()); } diff --git a/libs/mosquitto/src/actions.c b/libs/mosquitto/src/actions.c index 41ffe74..6ab8ac2 100644 --- a/libs/mosquitto/src/actions.c +++ b/libs/mosquitto/src/actions.c @@ -1,15 +1,17 @@ /* -Copyright (c) 2010-2019 Roger Light +Copyright (c) 2010-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. */ @@ -22,25 +24,88 @@ and the Eclipse Distribution License is available at #include "mosquitto_internal.h" #include "memory_mosq.h" #include "messages_mosq.h" -#include "mqtt3_protocol.h" +#include "mqtt_protocol.h" #include "net_mosq.h" +#include "packet_mosq.h" #include "send_mosq.h" #include "util_mosq.h" int mosquitto_publish(struct mosquitto *mosq, int *mid, const char *topic, int payloadlen, const void *payload, int qos, bool retain) +{ + return mosquitto_publish_v5(mosq, mid, topic, payloadlen, payload, qos, retain, NULL); +} + +int mosquitto_publish_v5(struct mosquitto *mosq, int *mid, const char *topic, int payloadlen, const void *payload, int qos, bool retain, const mosquitto_property *properties) { struct mosquitto_message_all *message; uint16_t local_mid; - int queue_status; + const mosquitto_property *p; + const mosquitto_property *outgoing_properties = NULL; + mosquitto_property *properties_copy = NULL; + mosquitto_property local_property; + bool have_topic_alias; + int rc; + size_t tlen = 0; + uint32_t remaining_length; + + if(!mosq || qos<0 || qos>2) return MOSQ_ERR_INVAL; + if(mosq->protocol != mosq_p_mqtt5 && properties) return MOSQ_ERR_NOT_SUPPORTED; + if(qos > mosq->max_qos) return MOSQ_ERR_QOS_NOT_SUPPORTED; + + if(!mosq->retain_available){ + retain = false; + } - if(!mosq || !topic || qos<0 || qos>2) return MOSQ_ERR_INVAL; - if(STREMPTY(topic)) return MOSQ_ERR_INVAL; - if(mosquitto_validate_utf8(topic, strlen(topic))) return MOSQ_ERR_MALFORMED_UTF8; - if(payloadlen < 0 || payloadlen > MQTT_MAX_PAYLOAD) return MOSQ_ERR_PAYLOAD_SIZE; + if(properties){ + if(properties->client_generated){ + outgoing_properties = properties; + }else{ + memcpy(&local_property, properties, sizeof(mosquitto_property)); + local_property.client_generated = true; + local_property.next = NULL; + outgoing_properties = &local_property; + } + rc = mosquitto_property_check_all(CMD_PUBLISH, outgoing_properties); + if(rc) return rc; + } + + if(!topic || STREMPTY(topic)){ + if(topic) topic = NULL; + + if(mosq->protocol == mosq_p_mqtt5){ + p = outgoing_properties; + have_topic_alias = false; + while(p){ + if(p->identifier == MQTT_PROP_TOPIC_ALIAS){ + have_topic_alias = true; + break; + } + p = p->next; + } + if(have_topic_alias == false){ + return MOSQ_ERR_INVAL; + } + }else{ + return MOSQ_ERR_INVAL; + } + }else{ + tlen = strlen(topic); + if(mosquitto_validate_utf8(topic, (int)tlen)) return MOSQ_ERR_MALFORMED_UTF8; + if(payloadlen < 0 || payloadlen > (int)MQTT_MAX_PAYLOAD) return MOSQ_ERR_PAYLOAD_SIZE; + if(mosquitto_pub_topic_check(topic) != MOSQ_ERR_SUCCESS){ + return MOSQ_ERR_INVAL; + } + } - if(mosquitto_pub_topic_check(topic) != MOSQ_ERR_SUCCESS){ - return MOSQ_ERR_INVAL; + if(mosq->maximum_packet_size > 0){ + remaining_length = 1 + 2+(uint32_t)tlen + (uint32_t)payloadlen + property__get_length_all(outgoing_properties); + if(qos > 0){ + remaining_length++; + } + if(packet__check_oversize(mosq, remaining_length)){ + return MOSQ_ERR_OVERSIZE_PACKET; + } } local_mid = mosquitto__mid_generate(mosq); @@ -49,74 +114,167 @@ int mosquitto_publish(struct mosquitto *mosq, int *mid, const char *topic, int p } if(qos == 0){ - return send__publish(mosq, local_mid, topic, payloadlen, payload, qos, retain, false); + return send__publish(mosq, local_mid, topic, (uint32_t)payloadlen, payload, (uint8_t)qos, retain, false, outgoing_properties, NULL, 0); }else{ + if(outgoing_properties){ + rc = mosquitto_property_copy_all(&properties_copy, outgoing_properties); + if(rc) return rc; + } message = mosquitto__calloc(1, sizeof(struct mosquitto_message_all)); - if(!message) return MOSQ_ERR_NOMEM; + if(!message){ + mosquitto_property_free_all(&properties_copy); + return MOSQ_ERR_NOMEM; + } message->next = NULL; message->timestamp = mosquitto_time(); message->msg.mid = local_mid; - message->msg.topic = mosquitto__strdup(topic); - if(!message->msg.topic){ - message__cleanup(&message); - return MOSQ_ERR_NOMEM; + if(topic){ + message->msg.topic = mosquitto__strdup(topic); + if(!message->msg.topic){ + message__cleanup(&message); + mosquitto_property_free_all(&properties_copy); + return MOSQ_ERR_NOMEM; + } } if(payloadlen){ message->msg.payloadlen = payloadlen; - message->msg.payload = mosquitto__malloc(payloadlen*sizeof(uint8_t)); + message->msg.payload = mosquitto__malloc((unsigned int)payloadlen*sizeof(uint8_t)); if(!message->msg.payload){ message__cleanup(&message); + mosquitto_property_free_all(&properties_copy); return MOSQ_ERR_NOMEM; } - memcpy(message->msg.payload, payload, payloadlen*sizeof(uint8_t)); + memcpy(message->msg.payload, payload, (uint32_t)payloadlen*sizeof(uint8_t)); }else{ message->msg.payloadlen = 0; message->msg.payload = NULL; } - message->msg.qos = qos; + message->msg.qos = (uint8_t)qos; message->msg.retain = retain; message->dup = false; + message->properties = properties_copy; - pthread_mutex_lock(&mosq->out_message_mutex); - queue_status = message__queue(mosq, message, mosq_md_out); - if(queue_status == 0){ - if(qos == 1){ - message->state = mosq_ms_wait_for_puback; - }else if(qos == 2){ - message->state = mosq_ms_wait_for_pubrec; - } - pthread_mutex_unlock(&mosq->out_message_mutex); - return send__publish(mosq, message->msg.mid, message->msg.topic, message->msg.payloadlen, message->msg.payload, message->msg.qos, message->msg.retain, message->dup); - }else{ - message->state = mosq_ms_invalid; - pthread_mutex_unlock(&mosq->out_message_mutex); - return MOSQ_ERR_SUCCESS; - } + pthread_mutex_lock(&mosq->msgs_out.mutex); + message->state = mosq_ms_invalid; + rc = message__queue(mosq, message, mosq_md_out); + pthread_mutex_unlock(&mosq->msgs_out.mutex); + return rc; } } int mosquitto_subscribe(struct mosquitto *mosq, int *mid, const char *sub, int qos) { - if(!mosq) return MOSQ_ERR_INVAL; + return mosquitto_subscribe_multiple(mosq, mid, 1, (char *const *const)&sub, qos, 0, NULL); +} + + +int mosquitto_subscribe_v5(struct mosquitto *mosq, int *mid, const char *sub, int qos, int options, const mosquitto_property *properties) +{ + return mosquitto_subscribe_multiple(mosq, mid, 1, (char *const *const)&sub, qos, options, properties); +} + + +int mosquitto_subscribe_multiple(struct mosquitto *mosq, int *mid, int sub_count, char *const *const sub, int qos, int options, const mosquitto_property *properties) +{ + const mosquitto_property *outgoing_properties = NULL; + mosquitto_property local_property; + int i; + int rc; + uint32_t remaining_length = 0; + int slen; + + if(!mosq || !sub_count || !sub) return MOSQ_ERR_INVAL; + if(mosq->protocol != mosq_p_mqtt5 && properties) return MOSQ_ERR_NOT_SUPPORTED; + if(qos < 0 || qos > 2) return MOSQ_ERR_INVAL; + if((options & 0x30) == 0x30 || (options & 0xC0) != 0) return MOSQ_ERR_INVAL; if(mosq->sock == INVALID_SOCKET) return MOSQ_ERR_NO_CONN; - if(mosquitto_sub_topic_check(sub)) return MOSQ_ERR_INVAL; - if(mosquitto_validate_utf8(sub, strlen(sub))) return MOSQ_ERR_MALFORMED_UTF8; + if(properties){ + if(properties->client_generated){ + outgoing_properties = properties; + }else{ + memcpy(&local_property, properties, sizeof(mosquitto_property)); + local_property.client_generated = true; + local_property.next = NULL; + outgoing_properties = &local_property; + } + rc = mosquitto_property_check_all(CMD_SUBSCRIBE, outgoing_properties); + if(rc) return rc; + } + + for(i=0; imaximum_packet_size > 0){ + remaining_length += 2 + property__get_length_all(outgoing_properties); + if(packet__check_oversize(mosq, remaining_length)){ + return MOSQ_ERR_OVERSIZE_PACKET; + } + } + if(mosq->protocol == mosq_p_mqtt311 || mosq->protocol == mosq_p_mqtt31){ + options = 0; + } + + return send__subscribe(mosq, mid, sub_count, sub, qos|options, outgoing_properties); } int mosquitto_unsubscribe(struct mosquitto *mosq, int *mid, const char *sub) { + return mosquitto_unsubscribe_multiple(mosq, mid, 1, (char *const *const)&sub, NULL); +} + +int mosquitto_unsubscribe_v5(struct mosquitto *mosq, int *mid, const char *sub, const mosquitto_property *properties) +{ + return mosquitto_unsubscribe_multiple(mosq, mid, 1, (char *const *const)&sub, properties); +} + +int mosquitto_unsubscribe_multiple(struct mosquitto *mosq, int *mid, int sub_count, char *const *const sub, const mosquitto_property *properties) +{ + const mosquitto_property *outgoing_properties = NULL; + mosquitto_property local_property; + int rc; + int i; + uint32_t remaining_length = 0; + int slen; + if(!mosq) return MOSQ_ERR_INVAL; + if(mosq->protocol != mosq_p_mqtt5 && properties) return MOSQ_ERR_NOT_SUPPORTED; if(mosq->sock == INVALID_SOCKET) return MOSQ_ERR_NO_CONN; - if(mosquitto_sub_topic_check(sub)) return MOSQ_ERR_INVAL; - if(mosquitto_validate_utf8(sub, strlen(sub))) return MOSQ_ERR_MALFORMED_UTF8; + if(properties){ + if(properties->client_generated){ + outgoing_properties = properties; + }else{ + memcpy(&local_property, properties, sizeof(mosquitto_property)); + local_property.client_generated = true; + local_property.next = NULL; + outgoing_properties = &local_property; + } + rc = mosquitto_property_check_all(CMD_UNSUBSCRIBE, outgoing_properties); + if(rc) return rc; + } + + for(i=0; imaximum_packet_size > 0){ + remaining_length += 2U + property__get_length_all(outgoing_properties); + if(packet__check_oversize(mosq, remaining_length)){ + return MOSQ_ERR_OVERSIZE_PACKET; + } + } - return send__unsubscribe(mosq, mid, sub); + return send__unsubscribe(mosq, mid, sub_count, sub, outgoing_properties); } diff --git a/libs/mosquitto/src/alias_mosq.c b/libs/mosquitto/src/alias_mosq.c new file mode 100644 index 0000000..08d4f52 --- /dev/null +++ b/libs/mosquitto/src/alias_mosq.c @@ -0,0 +1,86 @@ +/* +Copyright (c) 2019-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 "mosquitto.h" +#include "alias_mosq.h" +#include "memory_mosq.h" + +int alias__add(struct mosquitto *mosq, const char *topic, uint16_t alias) +{ + int i; + struct mosquitto__alias *aliases; + + for(i=0; ialias_count; i++){ + if(mosq->aliases[i].alias == alias){ + mosquitto__free(mosq->aliases[i].topic); + mosq->aliases[i].topic = mosquitto__strdup(topic); + if(mosq->aliases[i].topic){ + return MOSQ_ERR_SUCCESS; + }else{ + return MOSQ_ERR_NOMEM; + } + } + } + + /* New alias */ + aliases = mosquitto__realloc(mosq->aliases, sizeof(struct mosquitto__alias)*(size_t)(mosq->alias_count+1)); + if(!aliases) return MOSQ_ERR_NOMEM; + + mosq->aliases = aliases; + mosq->aliases[mosq->alias_count].alias = alias; + mosq->aliases[mosq->alias_count].topic = mosquitto__strdup(topic); + if(!mosq->aliases[mosq->alias_count].topic){ + return MOSQ_ERR_NOMEM; + } + mosq->alias_count++; + + return MOSQ_ERR_SUCCESS; +} + + +int alias__find(struct mosquitto *mosq, char **topic, uint16_t alias) +{ + int i; + + for(i=0; ialias_count; i++){ + if(mosq->aliases[i].alias == alias){ + *topic = mosquitto__strdup(mosq->aliases[i].topic); + if(*topic){ + return MOSQ_ERR_SUCCESS; + }else{ + return MOSQ_ERR_NOMEM; + } + } + } + return MOSQ_ERR_INVAL; +} + + +void alias__free_all(struct mosquitto *mosq) +{ + int i; + + for(i=0; ialias_count; i++){ + mosquitto__free(mosq->aliases[i].topic); + } + mosquitto__free(mosq->aliases); + mosq->aliases = NULL; + mosq->alias_count = 0; +} diff --git a/libs/mosquitto/src/alias_mosq.h b/libs/mosquitto/src/alias_mosq.h new file mode 100644 index 0000000..836520f --- /dev/null +++ b/libs/mosquitto/src/alias_mosq.h @@ -0,0 +1,28 @@ +/* +Copyright (c) 2019-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 ALIAS_MOSQ_H +#define ALIAS_MOSQ_H + +#include "mosquitto_internal.h" + +int alias__add(struct mosquitto *mosq, const char *topic, uint16_t alias); +int alias__find(struct mosquitto *mosq, char **topic, uint16_t alias); +void alias__free_all(struct mosquitto *mosq); + +#endif diff --git a/libs/mosquitto/src/callbacks.c b/libs/mosquitto/src/callbacks.c index 4f3cae0..2e4f6bc 100644 --- a/libs/mosquitto/src/callbacks.c +++ b/libs/mosquitto/src/callbacks.c @@ -1,15 +1,17 @@ /* -Copyright (c) 2010-2019 Roger Light +Copyright (c) 2010-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,6 +36,13 @@ void mosquitto_connect_with_flags_callback_set(struct mosquitto *mosq, void (*on pthread_mutex_unlock(&mosq->callback_mutex); } +void mosquitto_connect_v5_callback_set(struct mosquitto *mosq, void (*on_connect)(struct mosquitto *, void *, int, int, const mosquitto_property *)) +{ + pthread_mutex_lock(&mosq->callback_mutex); + mosq->on_connect_v5 = on_connect; + pthread_mutex_unlock(&mosq->callback_mutex); +} + void mosquitto_disconnect_callback_set(struct mosquitto *mosq, void (*on_disconnect)(struct mosquitto *, void *, int)) { pthread_mutex_lock(&mosq->callback_mutex); @@ -41,6 +50,13 @@ void mosquitto_disconnect_callback_set(struct mosquitto *mosq, void (*on_disconn pthread_mutex_unlock(&mosq->callback_mutex); } +void mosquitto_disconnect_v5_callback_set(struct mosquitto *mosq, void (*on_disconnect)(struct mosquitto *, void *, int, const mosquitto_property *)) +{ + pthread_mutex_lock(&mosq->callback_mutex); + mosq->on_disconnect_v5 = on_disconnect; + pthread_mutex_unlock(&mosq->callback_mutex); +} + void mosquitto_publish_callback_set(struct mosquitto *mosq, void (*on_publish)(struct mosquitto *, void *, int)) { pthread_mutex_lock(&mosq->callback_mutex); @@ -48,6 +64,13 @@ void mosquitto_publish_callback_set(struct mosquitto *mosq, void (*on_publish)(s pthread_mutex_unlock(&mosq->callback_mutex); } +void mosquitto_publish_v5_callback_set(struct mosquitto *mosq, void (*on_publish)(struct mosquitto *, void *, int, int, const mosquitto_property *props)) +{ + pthread_mutex_lock(&mosq->callback_mutex); + mosq->on_publish_v5 = on_publish; + pthread_mutex_unlock(&mosq->callback_mutex); +} + void mosquitto_message_callback_set(struct mosquitto *mosq, void (*on_message)(struct mosquitto *, void *, const struct mosquitto_message *)) { pthread_mutex_lock(&mosq->callback_mutex); @@ -55,6 +78,13 @@ void mosquitto_message_callback_set(struct mosquitto *mosq, void (*on_message)(s pthread_mutex_unlock(&mosq->callback_mutex); } +void mosquitto_message_v5_callback_set(struct mosquitto *mosq, void (*on_message)(struct mosquitto *, void *, const struct mosquitto_message *, const mosquitto_property *props)) +{ + pthread_mutex_lock(&mosq->callback_mutex); + mosq->on_message_v5 = on_message; + pthread_mutex_unlock(&mosq->callback_mutex); +} + void mosquitto_subscribe_callback_set(struct mosquitto *mosq, void (*on_subscribe)(struct mosquitto *, void *, int, int, const int *)) { pthread_mutex_lock(&mosq->callback_mutex); @@ -62,6 +92,13 @@ void mosquitto_subscribe_callback_set(struct mosquitto *mosq, void (*on_subscrib pthread_mutex_unlock(&mosq->callback_mutex); } +void mosquitto_subscribe_v5_callback_set(struct mosquitto *mosq, void (*on_subscribe)(struct mosquitto *, void *, int, int, const int *, const mosquitto_property *props)) +{ + pthread_mutex_lock(&mosq->callback_mutex); + mosq->on_subscribe_v5 = on_subscribe; + pthread_mutex_unlock(&mosq->callback_mutex); +} + void mosquitto_unsubscribe_callback_set(struct mosquitto *mosq, void (*on_unsubscribe)(struct mosquitto *, void *, int)) { pthread_mutex_lock(&mosq->callback_mutex); @@ -69,6 +106,13 @@ void mosquitto_unsubscribe_callback_set(struct mosquitto *mosq, void (*on_unsubs pthread_mutex_unlock(&mosq->callback_mutex); } +void mosquitto_unsubscribe_v5_callback_set(struct mosquitto *mosq, void (*on_unsubscribe)(struct mosquitto *, void *, int, const mosquitto_property *props)) +{ + pthread_mutex_lock(&mosq->callback_mutex); + mosq->on_unsubscribe_v5 = on_unsubscribe; + pthread_mutex_unlock(&mosq->callback_mutex); +} + void mosquitto_log_callback_set(struct mosquitto *mosq, void (*on_log)(struct mosquitto *, void *, int, const char *)) { pthread_mutex_lock(&mosq->log_callback_mutex); diff --git a/libs/mosquitto/src/config.h b/libs/mosquitto/src/config.h index 3c77705..9f614f8 100644 --- a/libs/mosquitto/src/config.h +++ b/libs/mosquitto/src/config.h @@ -1,21 +1,31 @@ #ifndef CONFIG_H +#define CONFIG_H /* ============================================================ * Platform options * ============================================================ */ #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 # define _POSIX_C_SOURCE 200809L #endif -#define _GNU_SOURCE + +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +#endif + +#define OPENSSL_LOAD_CONF /* ============================================================ * Compatibility defines @@ -23,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 @@ -34,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 @@ -45,4 +64,27 @@ # endif #endif + +#ifdef __COVERITY__ +# include +/* These are "wrong", but we don't use them so it doesn't matter */ +# define _Float32 uint32_t +# define _Float32x uint32_t +# define _Float64 uint64_t +# define _Float64x uint64_t +# define _Float128 uint64_t +#endif + +#define UNUSED(A) (void)(A) + +/* Android Bionic libpthread implementation doesn't have pthread_cancel */ +#ifndef ANDROID +# 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 --git a/libs/mosquitto/src/connect.c b/libs/mosquitto/src/connect.c index 2737f1d..ab61b66 100644 --- a/libs/mosquitto/src/connect.c +++ b/libs/mosquitto/src/connect.c @@ -1,66 +1,81 @@ /* -Copyright (c) 2010-2019 Roger Light +Copyright (c) 2010-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" +#include + #include "mosquitto.h" #include "mosquitto_internal.h" #include "logging_mosq.h" #include "messages_mosq.h" #include "memory_mosq.h" #include "packet_mosq.h" +#include "mqtt_protocol.h" #include "net_mosq.h" #include "send_mosq.h" #include "socks_mosq.h" +#include "util_mosq.h" + +static char alphanum[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; static int mosquitto__reconnect(struct mosquitto *mosq, bool blocking); -static int mosquitto__connect_init(struct mosquitto *mosq, const char *host, int port, int keepalive, const char *bind_address); +static int mosquitto__connect_init(struct mosquitto *mosq, const char *host, int port, int keepalive); -static int mosquitto__connect_init(struct mosquitto *mosq, const char *host, int port, int keepalive, const char *bind_address) +static int mosquitto__connect_init(struct mosquitto *mosq, const char *host, int port, int keepalive) { + int i; + int rc; + if(!mosq) return MOSQ_ERR_INVAL; - if(!host || port <= 0) return MOSQ_ERR_INVAL; + if(!host || port < 0 || port > UINT16_MAX) return MOSQ_ERR_INVAL; + if(keepalive != 0 && (keepalive < 5 || keepalive > UINT16_MAX)) return MOSQ_ERR_INVAL; + + /* Only MQTT v3.1 requires a client id to be sent */ + if(mosq->id == NULL && (mosq->protocol == mosq_p_mqtt31)){ + mosq->id = (char *)mosquitto__calloc(24, sizeof(char)); + if(!mosq->id){ + return MOSQ_ERR_NOMEM; + } + mosq->id[0] = 'm'; + mosq->id[1] = 'o'; + mosq->id[2] = 's'; + mosq->id[3] = 'q'; + mosq->id[4] = '-'; - mosquitto__free(mosq->host); - mosq->host = mosquitto__strdup(host); - if(!mosq->host) return MOSQ_ERR_NOMEM; - mosq->port = port; + rc = util__random_bytes(&mosq->id[5], 18); + if(rc) return rc; - mosquitto__free(mosq->bind_address); - if(bind_address){ - mosq->bind_address = mosquitto__strdup(bind_address); - if(!mosq->bind_address) return MOSQ_ERR_NOMEM; + for(i=5; i<23; i++){ + mosq->id[i] = alphanum[(mosq->id[i]&0x7F)%(sizeof(alphanum)-1)]; + } } - mosq->keepalive = keepalive; - - if(mosq->sockpairR != INVALID_SOCKET){ - COMPAT_CLOSE(mosq->sockpairR); - mosq->sockpairR = INVALID_SOCKET; - } - if(mosq->sockpairW != INVALID_SOCKET){ - COMPAT_CLOSE(mosq->sockpairW); - mosq->sockpairW = INVALID_SOCKET; - } + mosquitto__free(mosq->host); + mosq->host = mosquitto__strdup(host); + if(!mosq->host) return MOSQ_ERR_NOMEM; + mosq->port = (uint16_t)port; - if(net__socketpair(&mosq->sockpairR, &mosq->sockpairW)){ - log__printf(mosq, MOSQ_LOG_WARNING, - "Warning: Unable to open socket pair, outgoing publish commands may be delayed."); - } + mosq->keepalive = (uint16_t)keepalive; + mosq->msgs_in.inflight_quota = mosq->msgs_in.inflight_maximum; + mosq->msgs_out.inflight_quota = mosq->msgs_out.inflight_maximum; + mosq->retain_available = 1; return MOSQ_ERR_SUCCESS; } @@ -73,14 +88,33 @@ int mosquitto_connect(struct mosquitto *mosq, const char *host, int port, int ke int mosquitto_connect_bind(struct mosquitto *mosq, const char *host, int port, int keepalive, const char *bind_address) +{ + return mosquitto_connect_bind_v5(mosq, host, port, keepalive, bind_address, NULL); +} + +int mosquitto_connect_bind_v5(struct mosquitto *mosq, const char *host, int port, int keepalive, const char *bind_address, const mosquitto_property *properties) { int rc; - rc = mosquitto__connect_init(mosq, host, port, keepalive, bind_address); + + if(bind_address){ + rc = mosquitto_string_option(mosq, MOSQ_OPT_BIND_ADDRESS, bind_address); + if(rc) return rc; + } + + mosquitto_property_free_all(&mosq->connect_properties); + if(properties){ + rc = mosquitto_property_check_all(CMD_CONNECT, properties); + if(rc) return rc; + + rc = mosquitto_property_copy_all(&mosq->connect_properties, properties); + if(rc) return rc; + mosq->connect_properties->client_generated = true; + } + + rc = mosquitto__connect_init(mosq, host, port, keepalive); if(rc) return rc; - pthread_mutex_lock(&mosq->state_mutex); - mosq->state = mosq_cs_new; - pthread_mutex_unlock(&mosq->state_mutex); + mosquitto__set_state(mosq, mosq_cs_new); return mosquitto__reconnect(mosq, true); } @@ -94,12 +128,15 @@ int mosquitto_connect_async(struct mosquitto *mosq, const char *host, int port, int mosquitto_connect_bind_async(struct mosquitto *mosq, const char *host, int port, int keepalive, const char *bind_address) { - int rc = mosquitto__connect_init(mosq, host, port, keepalive, bind_address); - if(rc) return rc; + int rc; - pthread_mutex_lock(&mosq->state_mutex); - mosq->state = mosq_cs_connect_async; - pthread_mutex_unlock(&mosq->state_mutex); + if(bind_address){ + rc = mosquitto_string_option(mosq, MOSQ_OPT_BIND_ADDRESS, bind_address); + if(rc) return rc; + } + + rc = mosquitto__connect_init(mosq, host, port, keepalive); + if(rc) return rc; return mosquitto__reconnect(mosq, false); } @@ -119,21 +156,27 @@ int mosquitto_reconnect(struct mosquitto *mosq) static int mosquitto__reconnect(struct mosquitto *mosq, bool blocking) { + const mosquitto_property *outgoing_properties = NULL; + mosquitto_property local_property; int rc; - struct mosquitto__packet *packet; - if(!mosq) return MOSQ_ERR_INVAL; - if(!mosq->host || mosq->port <= 0) return MOSQ_ERR_INVAL; - pthread_mutex_lock(&mosq->state_mutex); -#ifdef WITH_SOCKS - if(mosq->socks5_host){ - mosq->state = mosq_cs_socks5_new; - }else -#endif - { - mosq->state = mosq_cs_new; + if(!mosq) return MOSQ_ERR_INVAL; + if(!mosq->host) return MOSQ_ERR_INVAL; + + if(mosq->connect_properties){ + if(mosq->protocol != mosq_p_mqtt5) return MOSQ_ERR_NOT_SUPPORTED; + + if(mosq->connect_properties->client_generated){ + outgoing_properties = mosq->connect_properties; + }else{ + memcpy(&local_property, mosq->connect_properties, sizeof(mosquitto_property)); + local_property.client_generated = true; + local_property.next = NULL; + outgoing_properties = &local_property; + } + rc = mosquitto_property_check_all(CMD_CONNECT, outgoing_properties); + if(rc) return rc; } - pthread_mutex_unlock(&mosq->state_mutex); pthread_mutex_lock(&mosq->msgtime_mutex); mosq->last_msg_in = mosquitto_time(); @@ -143,33 +186,13 @@ static int mosquitto__reconnect(struct mosquitto *mosq, bool blocking) mosq->ping_t = 0; packet__cleanup(&mosq->in_packet); - - pthread_mutex_lock(&mosq->current_out_packet_mutex); - pthread_mutex_lock(&mosq->out_packet_mutex); - - if(mosq->out_packet && !mosq->current_out_packet){ - mosq->current_out_packet = mosq->out_packet; - mosq->out_packet = mosq->out_packet->next; - } - - while(mosq->current_out_packet){ - packet = mosq->current_out_packet; - /* Free data and reset values */ - mosq->current_out_packet = mosq->out_packet; - if(mosq->out_packet){ - mosq->out_packet = mosq->out_packet->next; - } - packet__cleanup(packet); - mosquitto__free(packet); - } - pthread_mutex_unlock(&mosq->out_packet_mutex); - pthread_mutex_unlock(&mosq->current_out_packet_mutex); + packet__cleanup_all(mosq); - message__reconnect_reset(mosq); + message__reconnect_reset(mosq, false); if(mosq->sock != INVALID_SOCKET){ - net__socket_close(mosq); //close socket + net__socket_close(mosq); } #ifdef WITH_SOCKS @@ -178,35 +201,101 @@ static int mosquitto__reconnect(struct mosquitto *mosq, bool blocking) }else #endif { - pthread_mutex_lock(&mosq->state_mutex); - mosq->state = mosq_cs_connecting; - pthread_mutex_unlock(&mosq->state_mutex); rc = net__socket_connect(mosq, mosq->host, mosq->port, mosq->bind_address, blocking); } if(rc>0){ + mosquitto__set_state(mosq, mosq_cs_connect_pending); return rc; } #ifdef WITH_SOCKS if(mosq->socks5_host){ + mosquitto__set_state(mosq, mosq_cs_socks5_new); return socks5__send(mosq); }else #endif { - return send__connect(mosq, mosq->keepalive, mosq->clean_session); + mosquitto__set_state(mosq, mosq_cs_connected); + rc = send__connect(mosq, mosq->keepalive, mosq->clean_start, outgoing_properties); + if(rc){ + packet__cleanup_all(mosq); + net__socket_close(mosq); + mosquitto__set_state(mosq, mosq_cs_new); + } + return rc; } } int mosquitto_disconnect(struct mosquitto *mosq) { + return mosquitto_disconnect_v5(mosq, 0, NULL); +} + +int mosquitto_disconnect_v5(struct mosquitto *mosq, int reason_code, const mosquitto_property *properties) +{ + const mosquitto_property *outgoing_properties = NULL; + mosquitto_property local_property; + int rc; if(!mosq) return MOSQ_ERR_INVAL; + if(mosq->protocol != mosq_p_mqtt5 && properties) return MOSQ_ERR_NOT_SUPPORTED; + if(reason_code < 0 || reason_code > UINT8_MAX) return MOSQ_ERR_INVAL; + + if(properties){ + if(properties->client_generated){ + outgoing_properties = properties; + }else{ + memcpy(&local_property, properties, sizeof(mosquitto_property)); + local_property.client_generated = true; + local_property.next = NULL; + outgoing_properties = &local_property; + } + rc = mosquitto_property_check_all(CMD_DISCONNECT, outgoing_properties); + if(rc) return rc; + } + + mosquitto__set_state(mosq, mosq_cs_disconnected); + if(mosq->sock == INVALID_SOCKET){ + return MOSQ_ERR_NO_CONN; + }else{ + return send__disconnect(mosq, (uint8_t)reason_code, outgoing_properties); + } +} - pthread_mutex_lock(&mosq->state_mutex); - mosq->state = mosq_cs_disconnecting; - pthread_mutex_unlock(&mosq->state_mutex); - if(mosq->sock == INVALID_SOCKET) return MOSQ_ERR_NO_CONN; - return send__disconnect(mosq); +void do_client_disconnect(struct mosquitto *mosq, int reason_code, const mosquitto_property *properties) +{ + mosquitto__set_state(mosq, mosq_cs_disconnected); + net__socket_close(mosq); + + /* Free data and reset values */ + pthread_mutex_lock(&mosq->out_packet_mutex); + mosq->current_out_packet = mosq->out_packet; + if(mosq->out_packet){ + mosq->out_packet = mosq->out_packet->next; + if(!mosq->out_packet){ + mosq->out_packet_last = NULL; + } + mosq->out_packet_count--; + } + pthread_mutex_unlock(&mosq->out_packet_mutex); + + pthread_mutex_lock(&mosq->msgtime_mutex); + mosq->next_msg_out = mosquitto_time() + mosq->keepalive; + pthread_mutex_unlock(&mosq->msgtime_mutex); + + pthread_mutex_lock(&mosq->callback_mutex); + if(mosq->on_disconnect){ + mosq->in_callback = true; + mosq->on_disconnect(mosq, mosq->userdata, reason_code); + mosq->in_callback = false; + } + if(mosq->on_disconnect_v5){ + mosq->in_callback = true; + mosq->on_disconnect_v5(mosq, mosq->userdata, reason_code, properties); + mosq->in_callback = false; + } + pthread_mutex_unlock(&mosq->callback_mutex); + pthread_mutex_unlock(&mosq->current_out_packet_mutex); } diff --git a/libs/mosquitto/src/dummypthread.h b/libs/mosquitto/src/dummypthread.h index 4207f3d..c0eb2c1 100644 --- a/libs/mosquitto/src/dummypthread.h +++ b/libs/mosquitto/src/dummypthread.h @@ -4,10 +4,11 @@ #define pthread_create(A, B, C, D) #define pthread_join(A, B) #define pthread_cancel(A) +#define pthread_testcancel() #define pthread_mutex_init(A, B) #define pthread_mutex_destroy(A) -#define pthread_mutex_lock(A) -#define pthread_mutex_unlock(A) +#define pthread_mutex_lock(A) +#define pthread_mutex_unlock(A) #endif diff --git a/libs/mosquitto/src/handle_auth.c b/libs/mosquitto/src/handle_auth.c new file mode 100644 index 0000000..dd8b2f7 --- /dev/null +++ b/libs/mosquitto/src/handle_auth.c @@ -0,0 +1,55 @@ +/* +Copyright (c) 2018-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 "logging_mosq.h" +#include "mosquitto_internal.h" +#include "mqtt_protocol.h" +#include "packet_mosq.h" +#include "property_mosq.h" +#include "read_handle.h" + + +int handle__auth(struct mosquitto *mosq) +{ + int rc = 0; + uint8_t reason_code; + mosquitto_property *properties = NULL; + + if(!mosq) return MOSQ_ERR_INVAL; + log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received AUTH", SAFE_PRINT(mosq->id)); + + if(mosq->protocol != mosq_p_mqtt5){ + return MOSQ_ERR_PROTOCOL; + } + if(mosq->in_packet.command != CMD_AUTH){ + return MOSQ_ERR_MALFORMED_PACKET; + } + + if(packet__read_byte(&mosq->in_packet, &reason_code)) return 1; + + rc = property__read_all(CMD_AUTH, &mosq->in_packet, &properties); + if(rc) return rc; + mosquitto_property_free_all(&properties); /* FIXME - TEMPORARY UNTIL PROPERTIES PROCESSED */ + + return MOSQ_ERR_SUCCESS; +} diff --git a/libs/mosquitto/src/handle_connack.c b/libs/mosquitto/src/handle_connack.c index c5505c1..4c2ec00 100644 --- a/libs/mosquitto/src/handle_connack.c +++ b/libs/mosquitto/src/handle_connack.c @@ -1,15 +1,17 @@ /* -Copyright (c) 2009-2019 Roger Light +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. */ @@ -22,39 +24,104 @@ and the Eclipse Distribution License is available at #include "logging_mosq.h" #include "memory_mosq.h" #include "messages_mosq.h" +#include "mqtt_protocol.h" #include "net_mosq.h" #include "packet_mosq.h" +#include "property_mosq.h" #include "read_handle.h" -int handle__connack(struct mosquitto *mosq) +static void connack_callback(struct mosquitto *mosq, uint8_t reason_code, uint8_t connect_flags, const mosquitto_property *properties) { - uint8_t connect_flags; - uint8_t result; - int rc; - - assert(mosq); - rc = packet__read_byte(&mosq->in_packet, &connect_flags); - if(rc) return rc; - rc = packet__read_byte(&mosq->in_packet, &result); - if(rc) return rc; - log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received CONNACK (%d)", mosq->id, result); + log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received CONNACK (%d)", SAFE_PRINT(mosq->id), reason_code); + if(reason_code == MQTT_RC_SUCCESS){ + mosq->reconnects = 0; + } pthread_mutex_lock(&mosq->callback_mutex); if(mosq->on_connect){ mosq->in_callback = true; - mosq->on_connect(mosq, mosq->userdata, result); + mosq->on_connect(mosq, mosq->userdata, reason_code); mosq->in_callback = false; } if(mosq->on_connect_with_flags){ mosq->in_callback = true; - mosq->on_connect_with_flags(mosq, mosq->userdata, result, connect_flags); + mosq->on_connect_with_flags(mosq, mosq->userdata, reason_code, connect_flags); + mosq->in_callback = false; + } + if(mosq->on_connect_v5){ + mosq->in_callback = true; + mosq->on_connect_v5(mosq, mosq->userdata, reason_code, connect_flags, properties); mosq->in_callback = false; } pthread_mutex_unlock(&mosq->callback_mutex); - switch(result){ +} + + +int handle__connack(struct mosquitto *mosq) +{ + uint8_t connect_flags; + uint8_t reason_code; + int rc; + mosquitto_property *properties = NULL; + char *clientid = NULL; + + assert(mosq); + if(mosq->in_packet.command != CMD_CONNACK){ + return MOSQ_ERR_MALFORMED_PACKET; + } + + rc = packet__read_byte(&mosq->in_packet, &connect_flags); + if(rc) return rc; + rc = packet__read_byte(&mosq->in_packet, &reason_code); + if(rc) return rc; + + if(mosq->protocol == mosq_p_mqtt5){ + rc = property__read_all(CMD_CONNACK, &mosq->in_packet, &properties); + + if(rc == MOSQ_ERR_PROTOCOL && reason_code == CONNACK_REFUSED_PROTOCOL_VERSION){ + /* This could occur because we are connecting to a v3.x broker and + * it has replied with "unacceptable protocol version", but with a + * v3 CONNACK. */ + + connack_callback(mosq, MQTT_RC_UNSUPPORTED_PROTOCOL_VERSION, connect_flags, NULL); + return rc; + }else if(rc){ + return rc; + } + } + + mosquitto_property_read_string(properties, MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER, &clientid, false); + if(clientid){ + if(mosq->id){ + /* We've been sent a client identifier but already have one. This + * shouldn't happen. */ + free(clientid); + mosquitto_property_free_all(&properties); + return MOSQ_ERR_PROTOCOL; + }else{ + mosq->id = clientid; + clientid = NULL; + } + } + + mosquitto_property_read_byte(properties, MQTT_PROP_RETAIN_AVAILABLE, &mosq->retain_available, false); + mosquitto_property_read_byte(properties, MQTT_PROP_MAXIMUM_QOS, &mosq->max_qos, false); + mosquitto_property_read_int16(properties, MQTT_PROP_RECEIVE_MAXIMUM, &mosq->msgs_out.inflight_maximum, false); + mosquitto_property_read_int16(properties, MQTT_PROP_SERVER_KEEP_ALIVE, &mosq->keepalive, false); + mosquitto_property_read_int32(properties, MQTT_PROP_MAXIMUM_PACKET_SIZE, &mosq->maximum_packet_size, false); + + mosq->msgs_out.inflight_quota = mosq->msgs_out.inflight_maximum; + message__reconnect_reset(mosq, true); + + connack_callback(mosq, reason_code, connect_flags, properties); + mosquitto_property_free_all(&properties); + + switch(reason_code){ case 0: + pthread_mutex_lock(&mosq->state_mutex); if(mosq->state != mosq_cs_disconnecting){ - mosq->state = mosq_cs_connected; + mosq->state = mosq_cs_active; } + pthread_mutex_unlock(&mosq->state_mutex); message__retry_check(mosq); return MOSQ_ERR_SUCCESS; case 1: diff --git a/libs/mosquitto/src/handle_disconnect.c b/libs/mosquitto/src/handle_disconnect.c new file mode 100644 index 0000000..e5191b2 --- /dev/null +++ b/libs/mosquitto/src/handle_disconnect.c @@ -0,0 +1,68 @@ +/* +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 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 "logging_mosq.h" +#include "mqtt_protocol.h" +#include "memory_mosq.h" +#include "net_mosq.h" +#include "packet_mosq.h" +#include "property_mosq.h" +#include "read_handle.h" +#include "send_mosq.h" +#include "util_mosq.h" + +int handle__disconnect(struct mosquitto *mosq) +{ + int rc; + uint8_t reason_code; + mosquitto_property *properties = NULL; + + if(!mosq){ + return MOSQ_ERR_INVAL; + } + + if(mosq->protocol != mosq_p_mqtt5){ + return MOSQ_ERR_PROTOCOL; + } + if(mosq->in_packet.command != CMD_DISCONNECT){ + return MOSQ_ERR_MALFORMED_PACKET; + } + + rc = packet__read_byte(&mosq->in_packet, &reason_code); + if(rc) return rc; + + if(mosq->in_packet.remaining_length > 2){ + rc = property__read_all(CMD_DISCONNECT, &mosq->in_packet, &properties); + if(rc) return rc; + mosquitto_property_free_all(&properties); + } + + log__printf(mosq, MOSQ_LOG_DEBUG, "Received DISCONNECT (%d)", reason_code); + + do_client_disconnect(mosq, reason_code, properties); + + mosquitto_property_free_all(&properties); + + return MOSQ_ERR_SUCCESS; +} + diff --git a/libs/mosquitto/src/handle_ping.c b/libs/mosquitto/src/handle_ping.c index 688eee1..545e278 100644 --- a/libs/mosquitto/src/handle_ping.c +++ b/libs/mosquitto/src/handle_ping.c @@ -1,15 +1,17 @@ /* -Copyright (c) 2009-2019 Roger Light +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. */ @@ -28,7 +30,7 @@ and the Eclipse Distribution License is available at #include "logging_mosq.h" #include "memory_mosq.h" #include "messages_mosq.h" -#include "mqtt3_protocol.h" +#include "mqtt_protocol.h" #include "net_mosq.h" #include "packet_mosq.h" #include "read_handle.h" @@ -38,10 +40,18 @@ and the Eclipse Distribution License is available at int handle__pingreq(struct mosquitto *mosq) { assert(mosq); + + if(mosquitto__get_state(mosq) != mosq_cs_active){ + return MOSQ_ERR_PROTOCOL; + } + if(mosq->in_packet.command != CMD_PINGREQ){ + return MOSQ_ERR_MALFORMED_PACKET; + } + #ifdef WITH_BROKER - log__printf(NULL, MOSQ_LOG_DEBUG, "Received PINGREQ from %s", mosq->id); + log__printf(NULL, MOSQ_LOG_DEBUG, "Received PINGREQ from %s", SAFE_PRINT(mosq->id)); #else - log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received PINGREQ", mosq->id); + return MOSQ_ERR_PROTOCOL; #endif return send__pingresp(mosq); } @@ -49,11 +59,19 @@ int handle__pingreq(struct mosquitto *mosq) int handle__pingresp(struct mosquitto *mosq) { assert(mosq); + + if(mosquitto__get_state(mosq) != mosq_cs_active){ + return MOSQ_ERR_PROTOCOL; + } + mosq->ping_t = 0; /* No longer waiting for a PINGRESP. */ #ifdef WITH_BROKER - log__printf(NULL, MOSQ_LOG_DEBUG, "Received PINGRESP from %s", mosq->id); + if(mosq->bridge == NULL){ + return MOSQ_ERR_PROTOCOL; + } + log__printf(NULL, MOSQ_LOG_DEBUG, "Received PINGRESP from %s", SAFE_PRINT(mosq->id)); #else - log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received PINGRESP", mosq->id); + log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received PINGRESP", SAFE_PRINT(mosq->id)); #endif return MOSQ_ERR_SUCCESS; } diff --git a/libs/mosquitto/src/handle_pubackcomp.c b/libs/mosquitto/src/handle_pubackcomp.c index 6612cd3..b2dcf7e 100644 --- a/libs/mosquitto/src/handle_pubackcomp.c +++ b/libs/mosquitto/src/handle_pubackcomp.c @@ -1,15 +1,17 @@ /* -Copyright (c) 2009-2019 Roger Light +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. */ @@ -28,7 +30,7 @@ and the Eclipse Distribution License is available at #include "logging_mosq.h" #include "memory_mosq.h" #include "messages_mosq.h" -#include "mqtt3_protocol.h" +#include "mqtt_protocol.h" #include "net_mosq.h" #include "packet_mosq.h" #include "read_handle.h" @@ -36,39 +38,104 @@ and the Eclipse Distribution License is available at #include "util_mosq.h" -#ifdef WITH_BROKER -int handle__pubackcomp(struct mosquitto_db *db, struct mosquitto *mosq, const char *type) -#else int handle__pubackcomp(struct mosquitto *mosq, const char *type) -#endif { + uint8_t reason_code = 0; uint16_t mid; int rc; + mosquitto_property *properties = NULL; int qos; assert(mosq); + + if(mosquitto__get_state(mosq) != mosq_cs_active){ + return MOSQ_ERR_PROTOCOL; + } + if(mosq->protocol != mosq_p_mqtt31){ + if((mosq->in_packet.command&0x0F) != 0x00){ + return MOSQ_ERR_MALFORMED_PACKET; + } + } + + pthread_mutex_lock(&mosq->msgs_out.mutex); + util__increment_send_quota(mosq); + pthread_mutex_unlock(&mosq->msgs_out.mutex); + rc = packet__read_uint16(&mosq->in_packet, &mid); if(rc) return rc; - qos = type[3] == 'A'?1:2; /* pubAck or pubComp */ -#ifdef WITH_BROKER - log__printf(NULL, MOSQ_LOG_DEBUG, "Received %s from %s (Mid: %d)", type, mosq->id, mid); + if(type[3] == 'A'){ /* pubAck or pubComp */ + if(mosq->in_packet.command != CMD_PUBACK){ + return MOSQ_ERR_MALFORMED_PACKET; + } + qos = 1; + }else{ + if(mosq->in_packet.command != CMD_PUBCOMP){ + return MOSQ_ERR_MALFORMED_PACKET; + } + qos = 2; + } + if(mid == 0){ + return MOSQ_ERR_PROTOCOL; + } - if(mid){ - rc = db__message_delete(db, mosq, mid, mosq_md_out, qos); - if(rc == MOSQ_ERR_NOT_FOUND){ - log__printf(mosq, MOSQ_LOG_WARNING, "Warning: Received %s from %s for an unknown packet identifier %d.", type, mosq->id, mid); - return MOSQ_ERR_SUCCESS; - }else{ + if(mosq->protocol == mosq_p_mqtt5 && mosq->in_packet.remaining_length > 2){ + rc = packet__read_byte(&mosq->in_packet, &reason_code); + if(rc){ return rc; } + + if(mosq->in_packet.remaining_length > 3){ + rc = property__read_all(CMD_PUBACK, &mosq->in_packet, &properties); + if(rc) return rc; + } + if(type[3] == 'A'){ /* pubAck or pubComp */ + if(reason_code != MQTT_RC_SUCCESS + && reason_code != MQTT_RC_NO_MATCHING_SUBSCRIBERS + && reason_code != MQTT_RC_UNSPECIFIED + && reason_code != MQTT_RC_IMPLEMENTATION_SPECIFIC + && reason_code != MQTT_RC_NOT_AUTHORIZED + && reason_code != MQTT_RC_TOPIC_NAME_INVALID + && reason_code != MQTT_RC_PACKET_ID_IN_USE + && reason_code != MQTT_RC_QUOTA_EXCEEDED + && reason_code != MQTT_RC_PAYLOAD_FORMAT_INVALID + ){ + + return MOSQ_ERR_PROTOCOL; + } + }else{ + if(reason_code != MQTT_RC_SUCCESS + && reason_code != MQTT_RC_PACKET_ID_NOT_FOUND + ){ + + return MOSQ_ERR_PROTOCOL; + } + } + } + if(mosq->in_packet.pos < mosq->in_packet.remaining_length){ +#ifdef WITH_BROKER + mosquitto_property_free_all(&properties); +#endif + return MOSQ_ERR_MALFORMED_PACKET; + } + +#ifdef WITH_BROKER + log__printf(NULL, MOSQ_LOG_DEBUG, "Received %s from %s (Mid: %d, RC:%d)", type, SAFE_PRINT(mosq->id), mid, reason_code); + + /* Immediately free, we don't do anything with Reason String or User Property at the moment */ + mosquitto_property_free_all(&properties); + + rc = db__message_delete_outgoing(mosq, mid, mosq_ms_wait_for_pubcomp, qos); + if(rc == MOSQ_ERR_NOT_FOUND){ + log__printf(mosq, MOSQ_LOG_WARNING, "Warning: Received %s from %s for an unknown packet identifier %d.", type, SAFE_PRINT(mosq->id), mid); + return MOSQ_ERR_SUCCESS; + }else{ + return rc; } #else - log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received %s (Mid: %d)", mosq->id, type, mid); + log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received %s (Mid: %d, RC:%d)", SAFE_PRINT(mosq->id), type, mid, reason_code); rc = message__delete(mosq, mid, mosq_md_out, qos); - if(rc){ - return rc; - }else{ + if(rc == MOSQ_ERR_SUCCESS){ /* Only inform the client the message has been sent once. */ pthread_mutex_lock(&mosq->callback_mutex); if(mosq->on_publish){ @@ -76,10 +143,21 @@ int handle__pubackcomp(struct mosquitto *mosq, const char *type) mosq->on_publish(mosq, mosq->userdata, mid); mosq->in_callback = false; } + if(mosq->on_publish_v5){ + mosq->in_callback = true; + mosq->on_publish_v5(mosq, mosq->userdata, mid, reason_code, properties); + mosq->in_callback = false; + } pthread_mutex_unlock(&mosq->callback_mutex); + mosquitto_property_free_all(&properties); + }else if(rc != MOSQ_ERR_NOT_FOUND){ + return rc; } -#endif + pthread_mutex_lock(&mosq->msgs_out.mutex); + message__release_to_inflight(mosq, mosq_md_out); + pthread_mutex_unlock(&mosq->msgs_out.mutex); return MOSQ_ERR_SUCCESS; +#endif } diff --git a/libs/mosquitto/src/handle_publish.c b/libs/mosquitto/src/handle_publish.c index cbf654e..7864b8a 100644 --- a/libs/mosquitto/src/handle_publish.c +++ b/libs/mosquitto/src/handle_publish.c @@ -1,15 +1,17 @@ /* -Copyright (c) 2009-2019 Roger Light +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. */ @@ -23,10 +25,14 @@ and the Eclipse Distribution License is available at #include "mosquitto_internal.h" #include "logging_mosq.h" #include "memory_mosq.h" +#include "mqtt_protocol.h" #include "messages_mosq.h" #include "packet_mosq.h" +#include "property_mosq.h" +#include "read_handle.h" #include "send_mosq.h" #include "time_mosq.h" +#include "util_mosq.h" int handle__publish(struct mosquitto *mosq) @@ -34,11 +40,16 @@ int handle__publish(struct mosquitto *mosq) uint8_t header; struct mosquitto_message_all *message; int rc = 0; - uint16_t mid; - int slen; + uint16_t mid = 0; + uint16_t slen; + mosquitto_property *properties = NULL; assert(mosq); + if(mosquitto__get_state(mosq) != mosq_cs_active){ + return MOSQ_ERR_PROTOCOL; + } + message = mosquitto__calloc(1, sizeof(struct mosquitto_message_all)); if(!message) return MOSQ_ERR_NOMEM; @@ -59,30 +70,52 @@ int handle__publish(struct mosquitto *mosq) } if(message->msg.qos > 0){ + if(mosq->protocol == mosq_p_mqtt5){ + if(mosq->msgs_in.inflight_quota == 0){ + message__cleanup(&message); + /* FIXME - should send a DISCONNECT here */ + return MOSQ_ERR_PROTOCOL; + } + } + rc = packet__read_uint16(&mosq->in_packet, &mid); if(rc){ message__cleanup(&message); return rc; } + if(mid == 0){ + message__cleanup(&message); + return MOSQ_ERR_PROTOCOL; + } message->msg.mid = (int)mid; } - message->msg.payloadlen = mosq->in_packet.remaining_length - mosq->in_packet.pos; + if(mosq->protocol == mosq_p_mqtt5){ + rc = property__read_all(CMD_PUBLISH, &mosq->in_packet, &properties); + if(rc){ + message__cleanup(&message); + return rc; + } + } + + message->msg.payloadlen = (int)(mosq->in_packet.remaining_length - mosq->in_packet.pos); if(message->msg.payloadlen){ - message->msg.payload = mosquitto__calloc(message->msg.payloadlen+1, sizeof(uint8_t)); + message->msg.payload = mosquitto__calloc((size_t)message->msg.payloadlen+1, sizeof(uint8_t)); if(!message->msg.payload){ message__cleanup(&message); + mosquitto_property_free_all(&properties); return MOSQ_ERR_NOMEM; } - rc = packet__read_bytes(&mosq->in_packet, message->msg.payload, message->msg.payloadlen); + rc = packet__read_bytes(&mosq->in_packet, message->msg.payload, (uint32_t)message->msg.payloadlen); if(rc){ message__cleanup(&message); + mosquitto_property_free_all(&properties); return rc; } } log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received PUBLISH (d%d, q%d, r%d, m%d, '%s', ... (%ld bytes))", - mosq->id, message->dup, message->msg.qos, message->msg.retain, + SAFE_PRINT(mosq->id), message->dup, message->msg.qos, message->msg.retain, message->msg.mid, message->msg.topic, (long)message->msg.payloadlen); @@ -95,29 +128,45 @@ int handle__publish(struct mosquitto *mosq) mosq->on_message(mosq, mosq->userdata, &message->msg); mosq->in_callback = false; } + if(mosq->on_message_v5){ + mosq->in_callback = true; + mosq->on_message_v5(mosq, mosq->userdata, &message->msg, properties); + mosq->in_callback = false; + } pthread_mutex_unlock(&mosq->callback_mutex); message__cleanup(&message); + mosquitto_property_free_all(&properties); return MOSQ_ERR_SUCCESS; case 1: - rc = send__puback(mosq, message->msg.mid); + util__decrement_receive_quota(mosq); + rc = send__puback(mosq, mid, 0, NULL); pthread_mutex_lock(&mosq->callback_mutex); if(mosq->on_message){ mosq->in_callback = true; mosq->on_message(mosq, mosq->userdata, &message->msg); mosq->in_callback = false; } + if(mosq->on_message_v5){ + mosq->in_callback = true; + mosq->on_message_v5(mosq, mosq->userdata, &message->msg, properties); + mosq->in_callback = false; + } pthread_mutex_unlock(&mosq->callback_mutex); message__cleanup(&message); + mosquitto_property_free_all(&properties); return rc; case 2: - rc = send__pubrec(mosq, message->msg.mid); - pthread_mutex_lock(&mosq->in_message_mutex); + message->properties = properties; + util__decrement_receive_quota(mosq); + rc = send__pubrec(mosq, mid, 0, NULL); + pthread_mutex_lock(&mosq->msgs_in.mutex); message->state = mosq_ms_wait_for_pubrel; message__queue(mosq, message, mosq_md_in); - pthread_mutex_unlock(&mosq->in_message_mutex); + pthread_mutex_unlock(&mosq->msgs_in.mutex); return rc; default: message__cleanup(&message); + mosquitto_property_free_all(&properties); return MOSQ_ERR_PROTOCOL; } } diff --git a/libs/mosquitto/src/handle_pubrec.c b/libs/mosquitto/src/handle_pubrec.c index bcd3261..d561b0b 100644 --- a/libs/mosquitto/src/handle_pubrec.c +++ b/libs/mosquitto/src/handle_pubrec.c @@ -1,15 +1,17 @@ /* -Copyright (c) 2009-2019 Roger Light +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. */ @@ -28,7 +30,7 @@ and the Eclipse Distribution License is available at #include "logging_mosq.h" #include "memory_mosq.h" #include "messages_mosq.h" -#include "mqtt3_protocol.h" +#include "mqtt_protocol.h" #include "net_mosq.h" #include "packet_mosq.h" #include "read_handle.h" @@ -37,27 +39,94 @@ and the Eclipse Distribution License is available at int handle__pubrec(struct mosquitto *mosq) { + uint8_t reason_code = 0; uint16_t mid; int rc; + mosquitto_property *properties = NULL; assert(mosq); + + if(mosquitto__get_state(mosq) != mosq_cs_active){ + return MOSQ_ERR_PROTOCOL; + } + if(mosq->in_packet.command != CMD_PUBREC){ + return MOSQ_ERR_MALFORMED_PACKET; + } + rc = packet__read_uint16(&mosq->in_packet, &mid); if(rc) return rc; + if(mid == 0) return MOSQ_ERR_PROTOCOL; + + if(mosq->protocol == mosq_p_mqtt5 && mosq->in_packet.remaining_length > 2){ + rc = packet__read_byte(&mosq->in_packet, &reason_code); + if(rc) return rc; + + if(reason_code != MQTT_RC_SUCCESS + && reason_code != MQTT_RC_NO_MATCHING_SUBSCRIBERS + && reason_code != MQTT_RC_UNSPECIFIED + && reason_code != MQTT_RC_IMPLEMENTATION_SPECIFIC + && reason_code != MQTT_RC_NOT_AUTHORIZED + && reason_code != MQTT_RC_TOPIC_NAME_INVALID + && reason_code != MQTT_RC_PACKET_ID_IN_USE + && reason_code != MQTT_RC_QUOTA_EXCEEDED){ + + return MOSQ_ERR_PROTOCOL; + } + + if(mosq->in_packet.remaining_length > 3){ + rc = property__read_all(CMD_PUBREC, &mosq->in_packet, &properties); + if(rc) return rc; + + /* Immediately free, we don't do anything with Reason String or User Property at the moment */ + mosquitto_property_free_all(&properties); + } + } + + if(mosq->in_packet.pos < mosq->in_packet.remaining_length){ #ifdef WITH_BROKER - log__printf(NULL, MOSQ_LOG_DEBUG, "Received PUBREC from %s (Mid: %d)", mosq->id, mid); + mosquitto_property_free_all(&properties); +#endif + return MOSQ_ERR_MALFORMED_PACKET; + } - rc = db__message_update(mosq, mid, mosq_md_out, mosq_ms_wait_for_pubcomp, 2); +#ifdef WITH_BROKER + log__printf(NULL, MOSQ_LOG_DEBUG, "Received PUBREC from %s (Mid: %d)", SAFE_PRINT(mosq->id), mid); + + if(reason_code < 0x80){ + rc = db__message_update_outgoing(mosq, mid, mosq_ms_wait_for_pubcomp, 2); + }else{ + return db__message_delete_outgoing(mosq, mid, mosq_ms_wait_for_pubrec, 2); + } #else - log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received PUBREC (Mid: %d)", mosq->id, mid); - rc = message__out_update(mosq, mid, mosq_ms_wait_for_pubcomp, 2); + log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received PUBREC (Mid: %d)", SAFE_PRINT(mosq->id), mid); + + if(reason_code < 0x80 || mosq->protocol != mosq_p_mqtt5){ + rc = message__out_update(mosq, mid, mosq_ms_wait_for_pubcomp, 2); + }else{ + if(!message__delete(mosq, mid, mosq_md_out, 2)){ + /* Only inform the client the message has been sent once. */ + pthread_mutex_lock(&mosq->callback_mutex); + if(mosq->on_publish_v5){ + mosq->in_callback = true; + mosq->on_publish_v5(mosq, mosq->userdata, mid, reason_code, properties); + mosq->in_callback = false; + } + pthread_mutex_unlock(&mosq->callback_mutex); + } + util__increment_send_quota(mosq); + pthread_mutex_lock(&mosq->msgs_out.mutex); + message__release_to_inflight(mosq, mosq_md_out); + pthread_mutex_unlock(&mosq->msgs_out.mutex); + return MOSQ_ERR_SUCCESS; + } #endif if(rc == MOSQ_ERR_NOT_FOUND){ - log__printf(mosq, MOSQ_LOG_WARNING, "Warning: Received PUBREC from %s for an unknown packet identifier %d.", mosq->id, mid); + log__printf(mosq, MOSQ_LOG_WARNING, "Warning: Received PUBREC from %s for an unknown packet identifier %d.", SAFE_PRINT(mosq->id), mid); }else if(rc != MOSQ_ERR_SUCCESS){ return rc; } - rc = send__pubrel(mosq, mid); + rc = send__pubrel(mosq, mid, NULL); if(rc) return rc; return MOSQ_ERR_SUCCESS; diff --git a/libs/mosquitto/src/handle_pubrel.c b/libs/mosquitto/src/handle_pubrel.c index 9998fff..010f970 100644 --- a/libs/mosquitto/src/handle_pubrel.c +++ b/libs/mosquitto/src/handle_pubrel.c @@ -1,15 +1,17 @@ /* -Copyright (c) 2009-2019 Roger Light +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. */ @@ -28,7 +30,7 @@ and the Eclipse Distribution License is available at #include "logging_mosq.h" #include "memory_mosq.h" #include "messages_mosq.h" -#include "mqtt3_protocol.h" +#include "mqtt_protocol.h" #include "net_mosq.h" #include "packet_mosq.h" #include "read_handle.h" @@ -36,40 +38,82 @@ and the Eclipse Distribution License is available at #include "util_mosq.h" -int handle__pubrel(struct mosquitto_db *db, struct mosquitto *mosq) +int handle__pubrel(struct mosquitto *mosq) { + uint8_t reason_code; uint16_t mid; #ifndef WITH_BROKER struct mosquitto_message_all *message = NULL; #endif int rc; + mosquitto_property *properties = NULL; assert(mosq); - if(mosq->protocol == mosq_p_mqtt311){ + + if(mosquitto__get_state(mosq) != mosq_cs_active){ + return MOSQ_ERR_PROTOCOL; + } + if(mosq->protocol != mosq_p_mqtt31 && mosq->in_packet.command != (CMD_PUBREL|2)){ + return MOSQ_ERR_MALFORMED_PACKET; + } + + if(mosq->protocol != mosq_p_mqtt31){ if((mosq->in_packet.command&0x0F) != 0x02){ return MOSQ_ERR_PROTOCOL; } } rc = packet__read_uint16(&mosq->in_packet, &mid); if(rc) return rc; + if(mid == 0) return MOSQ_ERR_PROTOCOL; + + if(mosq->protocol == mosq_p_mqtt5 && mosq->in_packet.remaining_length > 2){ + rc = packet__read_byte(&mosq->in_packet, &reason_code); + if(rc) return rc; + + if(reason_code != MQTT_RC_SUCCESS && reason_code != MQTT_RC_PACKET_ID_NOT_FOUND){ + return MOSQ_ERR_PROTOCOL; + } + + if(mosq->in_packet.remaining_length > 3){ + rc = property__read_all(CMD_PUBREL, &mosq->in_packet, &properties); + if(rc) return rc; + } + } + + if(mosq->in_packet.pos < mosq->in_packet.remaining_length){ #ifdef WITH_BROKER - log__printf(NULL, MOSQ_LOG_DEBUG, "Received PUBREL from %s (Mid: %d)", mosq->id, mid); + mosquitto_property_free_all(&properties); +#endif + return MOSQ_ERR_MALFORMED_PACKET; + } - rc = db__message_release(db, mosq, mid, mosq_md_in); - if(rc == MOSQ_ERR_PROTOCOL){ - return rc; - }else if(rc != MOSQ_ERR_SUCCESS){ +#ifdef WITH_BROKER + log__printf(NULL, MOSQ_LOG_DEBUG, "Received PUBREL from %s (Mid: %d)", SAFE_PRINT(mosq->id), mid); + + /* Immediately free, we don't do anything with Reason String or User Property at the moment */ + mosquitto_property_free_all(&properties); + + rc = db__message_release_incoming(mosq, mid); + if(rc == MOSQ_ERR_NOT_FOUND){ /* Message not found. Still send a PUBCOMP anyway because this could be * due to a repeated PUBREL after a client has reconnected. */ - log__printf(mosq, MOSQ_LOG_WARNING, "Warning: Received PUBREL from %s for an unknown packet identifier %d.", mosq->id, mid); + }else if(rc != MOSQ_ERR_SUCCESS){ + return rc; } + + rc = send__pubcomp(mosq, mid, NULL); + if(rc) return rc; #else - log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received PUBREL (Mid: %d)", mosq->id, mid); + log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received PUBREL (Mid: %d)", SAFE_PRINT(mosq->id), mid); - rc = message__remove(mosq, mid, mosq_md_in, &message, 2); + rc = send__pubcomp(mosq, mid, NULL); if(rc){ + message__remove(mosq, mid, mosq_md_in, &message, 2); return rc; - }else{ + } + + rc = message__remove(mosq, mid, mosq_md_in, &message, 2); + if(rc == MOSQ_ERR_SUCCESS){ /* Only pass the message on if we have removed it from the queue - this * prevents multiple callbacks for the same message. */ pthread_mutex_lock(&mosq->callback_mutex); @@ -78,12 +122,20 @@ int handle__pubrel(struct mosquitto_db *db, struct mosquitto *mosq) mosq->on_message(mosq, mosq->userdata, &message->msg); mosq->in_callback = false; } + if(mosq->on_message_v5){ + mosq->in_callback = true; + mosq->on_message_v5(mosq, mosq->userdata, &message->msg, message->properties); + mosq->in_callback = false; + } pthread_mutex_unlock(&mosq->callback_mutex); + mosquitto_property_free_all(&properties); message__cleanup(&message); + }else if(rc == MOSQ_ERR_NOT_FOUND){ + return MOSQ_ERR_SUCCESS; + }else{ + return rc; } #endif - rc = send__pubcomp(mosq, mid); - if(rc) return rc; return MOSQ_ERR_SUCCESS; } diff --git a/libs/mosquitto/src/handle_suback.c b/libs/mosquitto/src/handle_suback.c index 388e7c0..64a9013 100644 --- a/libs/mosquitto/src/handle_suback.c +++ b/libs/mosquitto/src/handle_suback.c @@ -1,15 +1,17 @@ /* -Copyright (c) 2009-2019 Roger Light +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. */ @@ -26,7 +28,11 @@ and the Eclipse Distribution License is available at #include "mosquitto_internal.h" #include "logging_mosq.h" #include "memory_mosq.h" +#include "mqtt_protocol.h" #include "packet_mosq.h" +#include "property_mosq.h" +#include "read_handle.h" +#include "util_mosq.h" int handle__suback(struct mosquitto *mosq) @@ -37,36 +43,72 @@ int handle__suback(struct mosquitto *mosq) int qos_count; int i = 0; int rc; + mosquitto_property *properties = NULL; assert(mosq); + + if(mosquitto__get_state(mosq) != mosq_cs_active){ + return MOSQ_ERR_PROTOCOL; + } + if(mosq->in_packet.command != CMD_SUBACK){ + return MOSQ_ERR_MALFORMED_PACKET; + } + #ifdef WITH_BROKER - log__printf(NULL, MOSQ_LOG_DEBUG, "Received SUBACK from %s", mosq->id); + if(mosq->bridge == NULL){ + /* Client is not a bridge, so shouldn't be sending SUBACK */ + return MOSQ_ERR_PROTOCOL; + } + log__printf(NULL, MOSQ_LOG_DEBUG, "Received SUBACK from %s", SAFE_PRINT(mosq->id)); #else - log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received SUBACK", mosq->id); + log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received SUBACK", SAFE_PRINT(mosq->id)); #endif rc = packet__read_uint16(&mosq->in_packet, &mid); if(rc) return rc; + if(mid == 0) return MOSQ_ERR_PROTOCOL; + + if(mosq->protocol == mosq_p_mqtt5){ + rc = property__read_all(CMD_SUBACK, &mosq->in_packet, &properties); + if(rc) return rc; + } - qos_count = mosq->in_packet.remaining_length - mosq->in_packet.pos; - granted_qos = mosquitto__malloc(qos_count*sizeof(int)); - if(!granted_qos) return MOSQ_ERR_NOMEM; + qos_count = (int)(mosq->in_packet.remaining_length - mosq->in_packet.pos); + granted_qos = mosquitto__malloc((size_t)qos_count*sizeof(int)); + if(!granted_qos){ +#ifdef WITH_BROKER + mosquitto_property_free_all(&properties); +#endif + return MOSQ_ERR_NOMEM; + } while(mosq->in_packet.pos < mosq->in_packet.remaining_length){ rc = packet__read_byte(&mosq->in_packet, &qos); if(rc){ mosquitto__free(granted_qos); +#ifdef WITH_BROKER + mosquitto_property_free_all(&properties); +#endif return rc; } granted_qos[i] = (int)qos; i++; } -#ifndef WITH_BROKER +#ifdef WITH_BROKER + /* Immediately free, we don't do anything with Reason String or User Property at the moment */ + mosquitto_property_free_all(&properties); +#else pthread_mutex_lock(&mosq->callback_mutex); if(mosq->on_subscribe){ mosq->in_callback = true; mosq->on_subscribe(mosq, mosq->userdata, mid, qos_count, granted_qos); mosq->in_callback = false; } + if(mosq->on_subscribe_v5){ + mosq->in_callback = true; + mosq->on_subscribe_v5(mosq, mosq->userdata, mid, qos_count, granted_qos, properties); + mosq->in_callback = false; + } pthread_mutex_unlock(&mosq->callback_mutex); + mosquitto_property_free_all(&properties); #endif mosquitto__free(granted_qos); diff --git a/libs/mosquitto/src/handle_unsuback.c b/libs/mosquitto/src/handle_unsuback.c index 4bbf317..bc92cb0 100644 --- a/libs/mosquitto/src/handle_unsuback.c +++ b/libs/mosquitto/src/handle_unsuback.c @@ -1,15 +1,17 @@ /* -Copyright (c) 2009-2019 Roger Light +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. */ @@ -28,9 +30,10 @@ and the Eclipse Distribution License is available at #include "logging_mosq.h" #include "memory_mosq.h" #include "messages_mosq.h" -#include "mqtt3_protocol.h" +#include "mqtt_protocol.h" #include "net_mosq.h" #include "packet_mosq.h" +#include "property_mosq.h" #include "read_handle.h" #include "send_mosq.h" #include "util_mosq.h" @@ -40,23 +43,52 @@ int handle__unsuback(struct mosquitto *mosq) { uint16_t mid; int rc; + mosquitto_property *properties = NULL; assert(mosq); + + if(mosquitto__get_state(mosq) != mosq_cs_active){ + return MOSQ_ERR_PROTOCOL; + } + if(mosq->in_packet.command != CMD_UNSUBACK){ + return MOSQ_ERR_MALFORMED_PACKET; + } + #ifdef WITH_BROKER - log__printf(NULL, MOSQ_LOG_DEBUG, "Received UNSUBACK from %s", mosq->id); + if(mosq->bridge == NULL){ + /* Client is not a bridge, so shouldn't be sending SUBACK */ + return MOSQ_ERR_PROTOCOL; + } + log__printf(NULL, MOSQ_LOG_DEBUG, "Received UNSUBACK from %s", SAFE_PRINT(mosq->id)); #else - log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received UNSUBACK", mosq->id); + log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received UNSUBACK", SAFE_PRINT(mosq->id)); #endif rc = packet__read_uint16(&mosq->in_packet, &mid); if(rc) return rc; -#ifndef WITH_BROKER + if(mid == 0) return MOSQ_ERR_PROTOCOL; + + if(mosq->protocol == mosq_p_mqtt5){ + rc = property__read_all(CMD_UNSUBACK, &mosq->in_packet, &properties); + if(rc) return rc; + } + +#ifdef WITH_BROKER + /* Immediately free, we don't do anything with Reason String or User Property at the moment */ + mosquitto_property_free_all(&properties); +#else pthread_mutex_lock(&mosq->callback_mutex); if(mosq->on_unsubscribe){ mosq->in_callback = true; mosq->on_unsubscribe(mosq, mosq->userdata, mid); mosq->in_callback = false; } + if(mosq->on_unsubscribe_v5){ + mosq->in_callback = true; + mosq->on_unsubscribe_v5(mosq, mosq->userdata, mid, properties); + mosq->in_callback = false; + } pthread_mutex_unlock(&mosq->callback_mutex); + mosquitto_property_free_all(&properties); #endif return MOSQ_ERR_SUCCESS; diff --git a/libs/mosquitto/src/helpers.c b/libs/mosquitto/src/helpers.c index 66fd6ff..b418065 100644 --- a/libs/mosquitto/src/helpers.c +++ b/libs/mosquitto/src/helpers.c @@ -1,15 +1,17 @@ /* -Copyright (c) 2016-2019 Roger Light +Copyright (c) 2016-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. */ @@ -27,7 +29,6 @@ struct userdata__callback { int (*callback)(struct mosquitto *, void *, const struct mosquitto_message *); void *userdata; int qos; - int rc; }; struct userdata__simple { @@ -42,6 +43,8 @@ static void on_connect(struct mosquitto *mosq, void *obj, int rc) { struct userdata__callback *userdata = obj; + UNUSED(rc); + mosquitto_subscribe(mosq, NULL, userdata->topic, userdata->qos); } @@ -111,7 +114,7 @@ libmosq_EXPORT int mosquitto_subscribe_simple( *messages = NULL; - userdata.messages = calloc(sizeof(struct mosquitto_message), msg_count); + userdata.messages = calloc(sizeof(struct mosquitto_message), (size_t)msg_count); if(!userdata.messages){ return MOSQ_ERR_NOMEM; } @@ -166,7 +169,6 @@ libmosq_EXPORT int mosquitto_subscribe_callback( cb_userdata.topic = topic; cb_userdata.qos = qos; - cb_userdata.rc = 0; cb_userdata.userdata = userdata; cb_userdata.callback = callback; @@ -212,14 +214,6 @@ libmosq_EXPORT int mosquitto_subscribe_callback( } rc = mosquitto_loop_forever(mosq, -1, 1); mosquitto_destroy(mosq); - if(cb_userdata.rc){ - rc = cb_userdata.rc; - } - //if(!rc && cb_userdata.max_msg_count == 0){ - //return MOSQ_ERR_SUCCESS; - //}else{ - //return rc; - //} - return MOSQ_ERR_SUCCESS; + return rc; } diff --git a/libs/mosquitto/src/logging_mosq.c b/libs/mosquitto/src/logging_mosq.c index eab6356..348dba8 100644 --- a/libs/mosquitto/src/logging_mosq.c +++ b/libs/mosquitto/src/logging_mosq.c @@ -1,15 +1,17 @@ /* -Copyright (c) 2009-2019 Roger Light +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,15 +23,16 @@ and the Eclipse Distribution License is available at #include #include +#include "logging_mosq.h" #include "mosquitto_internal.h" #include "mosquitto.h" #include "memory_mosq.h" -int log__printf(struct mosquitto *mosq, int priority, const char *fmt, ...) +int log__printf(struct mosquitto *mosq, unsigned int priority, const char *fmt, ...) { va_list va; char *s; - int len; + size_t len; assert(mosq); assert(fmt); @@ -48,7 +51,7 @@ int log__printf(struct mosquitto *mosq, int priority, const char *fmt, ...) va_end(va); s[len-1] = '\0'; /* Ensure string is null terminated. */ - mosq->on_log(mosq, mosq->userdata, priority, s); + mosq->on_log(mosq, mosq->userdata, (int)priority, s); mosquitto__free(s); } diff --git a/libs/mosquitto/src/logging_mosq.h b/libs/mosquitto/src/logging_mosq.h index c3cc29d..ee5a519 100644 --- a/libs/mosquitto/src/logging_mosq.h +++ b/libs/mosquitto/src/logging_mosq.h @@ -1,15 +1,17 @@ /* -Copyright (c) 2009-2019 Roger Light +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. */ @@ -18,6 +20,10 @@ and the Eclipse Distribution License is available at #include "mosquitto.h" -int log__printf(struct mosquitto *mosq, int priority, const char *fmt, ...); +#ifndef __GNUC__ +#define __attribute__(attrib) +#endif + +int log__printf(struct mosquitto *mosq, unsigned int level, const char *fmt, ...) __attribute__((format(printf, 3, 4))); #endif diff --git a/libs/mosquitto/src/loop.c b/libs/mosquitto/src/loop.c index b63dde3..3373c18 100644 --- a/libs/mosquitto/src/loop.c +++ b/libs/mosquitto/src/loop.c @@ -1,15 +1,17 @@ /* -Copyright (c) 2010-2019 Roger Light +Copyright (c) 2010-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. */ @@ -30,7 +32,7 @@ and the Eclipse Distribution License is available at #include "tls_mosq.h" #include "util_mosq.h" -#if !defined(WIN32) && !defined(__SYMBIAN32__) +#if !defined(WIN32) && !defined(__SYMBIAN32__) && !defined(__QNX__) #define HAVE_PSELECT #endif @@ -47,6 +49,7 @@ int mosquitto_loop(struct mosquitto *mosq, int timeout, int max_packets) char pairbuf; int maxfd = 0; time_t now; + time_t timeout_ms; if(!mosq || max_packets < 1) return MOSQ_ERR_INVAL; #ifndef WIN32 @@ -83,17 +86,14 @@ int mosquitto_loop(struct mosquitto *mosq, int timeout, int max_packets) }else{ #ifdef WITH_SRV if(mosq->achan){ - pthread_mutex_lock(&mosq->state_mutex); - if(mosq->state == mosq_cs_connect_srv){ + if(mosquitto__get_state(mosq) == mosq_cs_connect_srv){ rc = ares_fds(mosq->achan, &readfds, &writefds); if(rc > maxfd){ maxfd = rc; } }else{ - pthread_mutex_unlock(&mosq->state_mutex); return MOSQ_ERR_NO_CONN; } - pthread_mutex_unlock(&mosq->state_mutex); } #else return MOSQ_ERR_NO_CONN; @@ -103,31 +103,32 @@ int mosquitto_loop(struct mosquitto *mosq, int timeout, int max_packets) /* sockpairR is used to break out of select() before the timeout, on a * call to publish() etc. */ FD_SET(mosq->sockpairR, &readfds); - if(mosq->sockpairR > maxfd){ + if((int)mosq->sockpairR > maxfd){ maxfd = mosq->sockpairR; } } - if(timeout < 0){ - timeout = 1000; + timeout_ms = timeout; + if(timeout_ms < 0){ + timeout_ms = 1000; } now = mosquitto_time(); - if(mosq->next_msg_out && now + timeout/1000 > mosq->next_msg_out){ - timeout = (mosq->next_msg_out - now)*1000; + if(mosq->next_msg_out && now + timeout_ms/1000 > mosq->next_msg_out){ + timeout_ms = (mosq->next_msg_out - now)*1000; } - if(timeout < 0){ + if(timeout_ms < 0){ /* There has been a delay somewhere which means we should have already * sent a message. */ - timeout = 0; + timeout_ms = 0; } - local_timeout.tv_sec = timeout/1000; + local_timeout.tv_sec = timeout_ms/1000; #ifdef HAVE_PSELECT - local_timeout.tv_nsec = (timeout-local_timeout.tv_sec*1000)*1e6; + local_timeout.tv_nsec = (timeout_ms-local_timeout.tv_sec*1000)*1000000; #else - local_timeout.tv_usec = (timeout-local_timeout.tv_sec*1000)*1000; + local_timeout.tv_usec = (timeout_ms-local_timeout.tv_sec*1000)*1000; #endif #ifdef HAVE_PSELECT @@ -190,28 +191,81 @@ int mosquitto_loop(struct mosquitto *mosq, int timeout, int max_packets) } +static int interruptible_sleep(struct mosquitto *mosq, time_t reconnect_delay) +{ +#ifdef HAVE_PSELECT + struct timespec local_timeout; +#else + struct timeval local_timeout; +#endif + fd_set readfds; + int fdcount; + char pairbuf; + int maxfd = 0; + +#ifndef WIN32 + while(mosq->sockpairR != INVALID_SOCKET && read(mosq->sockpairR, &pairbuf, 1) > 0); +#else + while(mosq->sockpairR != INVALID_SOCKET && recv(mosq->sockpairR, &pairbuf, 1, 0) > 0); +#endif + + local_timeout.tv_sec = reconnect_delay; +#ifdef HAVE_PSELECT + local_timeout.tv_nsec = 0; +#else + local_timeout.tv_usec = 0; +#endif + FD_ZERO(&readfds); + maxfd = 0; + if(mosq->sockpairR != INVALID_SOCKET){ + /* sockpairR is used to break out of select() before the + * timeout, when mosquitto_loop_stop() is called */ + FD_SET(mosq->sockpairR, &readfds); + maxfd = mosq->sockpairR; + } +#ifdef HAVE_PSELECT + fdcount = pselect(maxfd+1, &readfds, NULL, NULL, &local_timeout, NULL); +#else + fdcount = select(maxfd+1, &readfds, NULL, NULL, &local_timeout); +#endif + if(fdcount == -1){ +#ifdef WIN32 + errno = WSAGetLastError(); +#endif + if(errno == EINTR){ + return MOSQ_ERR_SUCCESS; + }else{ + return MOSQ_ERR_ERRNO; + } + }else if(mosq->sockpairR != INVALID_SOCKET && FD_ISSET(mosq->sockpairR, &readfds)){ +#ifndef WIN32 + if(read(mosq->sockpairR, &pairbuf, 1) == 0){ + } +#else + recv(mosq->sockpairR, &pairbuf, 1, 0); +#endif + } + return MOSQ_ERR_SUCCESS; +} + + int mosquitto_loop_forever(struct mosquitto *mosq, int timeout, int max_packets) { int run = 1; - int rc; - unsigned int reconnects = 0; + int rc = MOSQ_ERR_SUCCESS; unsigned long reconnect_delay; -#ifndef WIN32 - struct timespec req, rem; -#endif + enum mosquitto_client_state state; if(!mosq) return MOSQ_ERR_INVAL; - if(mosq->state == mosq_cs_connect_async){ - mosquitto_reconnect(mosq); - } + mosq->reconnects = 0; while(run){ do{ +#ifdef HAVE_PTHREAD_CANCEL + pthread_testcancel(); +#endif rc = mosquitto_loop(mosq, timeout, max_packets); - if (reconnects !=0 && rc == MOSQ_ERR_SUCCESS){ - reconnects = 0; - } }while(run && rc == MOSQ_ERR_SUCCESS); /* Quit after fatal errors. */ switch(rc){ @@ -235,19 +289,19 @@ int mosquitto_loop_forever(struct mosquitto *mosq, int timeout, int max_packets) return rc; } do{ +#ifdef HAVE_PTHREAD_CANCEL + pthread_testcancel(); +#endif rc = MOSQ_ERR_SUCCESS; - pthread_mutex_lock(&mosq->state_mutex); - if(mosq->state == mosq_cs_disconnecting){ + state = mosquitto__get_state(mosq); + if(state == mosq_cs_disconnecting || state == mosq_cs_disconnected){ run = 0; - pthread_mutex_unlock(&mosq->state_mutex); }else{ - pthread_mutex_unlock(&mosq->state_mutex); - if(mosq->reconnect_delay_max > mosq->reconnect_delay){ if(mosq->reconnect_exponential_backoff){ - reconnect_delay = mosq->reconnect_delay*(reconnects+1)*(reconnects+1); + reconnect_delay = mosq->reconnect_delay*(mosq->reconnects+1)*(mosq->reconnects+1); }else{ - reconnect_delay = mosq->reconnect_delay*(reconnects+1); + reconnect_delay = mosq->reconnect_delay*(mosq->reconnects+1); } }else{ reconnect_delay = mosq->reconnect_delay; @@ -256,25 +310,16 @@ int mosquitto_loop_forever(struct mosquitto *mosq, int timeout, int max_packets) if(reconnect_delay > mosq->reconnect_delay_max){ reconnect_delay = mosq->reconnect_delay_max; }else{ - reconnects++; + mosq->reconnects++; } -#ifdef WIN32 - Sleep(reconnect_delay*1000); -#else - req.tv_sec = reconnect_delay; - req.tv_nsec = 0; - while(nanosleep(&req, &rem) == -1 && errno == EINTR){ - req = rem; - } -#endif + rc = interruptible_sleep(mosq, (time_t)reconnect_delay); + if(rc) return rc; - pthread_mutex_lock(&mosq->state_mutex); - if(mosq->state == mosq_cs_disconnecting){ + state = mosquitto__get_state(mosq); + if(state == mosq_cs_disconnecting || state == mosq_cs_disconnected){ run = 0; - pthread_mutex_unlock(&mosq->state_mutex); }else{ - pthread_mutex_unlock(&mosq->state_mutex); rc = mosquitto_reconnect(mosq); } } @@ -295,21 +340,26 @@ int mosquitto_loop_misc(struct mosquitto *mosq) static int mosquitto__loop_rc_handle(struct mosquitto *mosq, int rc) { + enum mosquitto_client_state state; + if(rc){ net__socket_close(mosq); - pthread_mutex_lock(&mosq->state_mutex); - if(mosq->state == mosq_cs_disconnecting){ + state = mosquitto__get_state(mosq); + if(state == mosq_cs_disconnecting || state == mosq_cs_disconnected){ rc = MOSQ_ERR_SUCCESS; } - pthread_mutex_unlock(&mosq->state_mutex); pthread_mutex_lock(&mosq->callback_mutex); if(mosq->on_disconnect){ mosq->in_callback = true; mosq->on_disconnect(mosq, mosq->userdata, rc); mosq->in_callback = false; } + if(mosq->on_disconnect_v5){ + mosq->in_callback = true; + mosq->on_disconnect_v5(mosq, mosq->userdata, rc, NULL); + mosq->in_callback = false; + } pthread_mutex_unlock(&mosq->callback_mutex); - return rc; } return rc; } @@ -317,23 +367,27 @@ static int mosquitto__loop_rc_handle(struct mosquitto *mosq, int rc) int mosquitto_loop_read(struct mosquitto *mosq, int max_packets) { - int rc; + int rc = MOSQ_ERR_SUCCESS; int i; if(max_packets < 1) return MOSQ_ERR_INVAL; #ifdef WITH_TLS if(mosq->want_connect){ - return net__socket_connect_tls(mosq); + rc = net__socket_connect_tls(mosq); + if (MOSQ_ERR_TLS == rc){ + rc = mosquitto__loop_rc_handle(mosq, rc); + } + return rc; } #endif - pthread_mutex_lock(&mosq->out_message_mutex); - max_packets = mosq->out_queue_len; - pthread_mutex_unlock(&mosq->out_message_mutex); + pthread_mutex_lock(&mosq->msgs_out.mutex); + max_packets = mosq->msgs_out.queue_len; + pthread_mutex_unlock(&mosq->msgs_out.mutex); - pthread_mutex_lock(&mosq->in_message_mutex); - max_packets += mosq->in_queue_len; - pthread_mutex_unlock(&mosq->in_message_mutex); + pthread_mutex_lock(&mosq->msgs_in.mutex); + max_packets += mosq->msgs_in.queue_len; + pthread_mutex_unlock(&mosq->msgs_in.mutex); if(max_packets < 1) max_packets = 1; /* Queue len here tells us how many messages are awaiting processing and @@ -358,22 +412,10 @@ int mosquitto_loop_read(struct mosquitto *mosq, int max_packets) int mosquitto_loop_write(struct mosquitto *mosq, int max_packets) { - int rc; + int rc = MOSQ_ERR_SUCCESS; int i; if(max_packets < 1) return MOSQ_ERR_INVAL; - pthread_mutex_lock(&mosq->out_message_mutex); - max_packets = mosq->out_queue_len; - pthread_mutex_unlock(&mosq->out_message_mutex); - - pthread_mutex_lock(&mosq->in_message_mutex); - max_packets += mosq->in_queue_len; - pthread_mutex_unlock(&mosq->in_message_mutex); - - if(max_packets < 1) max_packets = 1; - /* Queue len here tells us how many messages are awaiting processing and - * have QoS > 0. We should try to deal with that many in this loop in order - * to keep up. */ for(i=0; i +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. */ @@ -41,29 +43,19 @@ static unsigned long max_memcount = 0; static size_t mem_limit = 0; void memory__set_limit(size_t lim) { -#ifdef LINUX - struct rlimit r; - - r.rlim_cur = lim; - r.rlim_max = lim; - - setrlimit(RLIMIT_CPU, &r); - - mem_limit = 0; -#else mem_limit = lim; -#endif } #endif void *mosquitto__calloc(size_t nmemb, size_t size) { + void *mem; #ifdef REAL_WITH_MEMORY_TRACKING if(mem_limit && memcount + size > mem_limit){ return NULL; } #endif - void *mem = calloc(nmemb, size); + mem = calloc(nmemb, size); #ifdef REAL_WITH_MEMORY_TRACKING if(mem){ @@ -90,12 +82,15 @@ void mosquitto__free(void *mem) void *mosquitto__malloc(size_t size) { + void *mem; + #ifdef REAL_WITH_MEMORY_TRACKING if(mem_limit && memcount + size > mem_limit){ return NULL; } #endif - void *mem = malloc(size); + + mem = malloc(size); #ifdef REAL_WITH_MEMORY_TRACKING if(mem){ @@ -123,13 +118,11 @@ unsigned long mosquitto__max_memory_used(void) void *mosquitto__realloc(void *ptr, size_t size) { + void *mem; #ifdef REAL_WITH_MEMORY_TRACKING if(mem_limit && memcount + size > mem_limit){ return NULL; } -#endif - void *mem; -#ifdef REAL_WITH_MEMORY_TRACKING if(ptr){ memcount -= malloc_usable_size(ptr); } @@ -150,12 +143,13 @@ void *mosquitto__realloc(void *ptr, size_t size) char *mosquitto__strdup(const char *s) { + char *str; #ifdef REAL_WITH_MEMORY_TRACKING if(mem_limit && memcount + strlen(s) > mem_limit){ return NULL; } #endif - char *str = strdup(s); + str = strdup(s); #ifdef REAL_WITH_MEMORY_TRACKING if(str){ @@ -168,4 +162,3 @@ char *mosquitto__strdup(const char *s) return str; } - diff --git a/libs/mosquitto/src/memory_mosq.h b/libs/mosquitto/src/memory_mosq.h index 63386a2..d76ed5a 100644 --- a/libs/mosquitto/src/memory_mosq.h +++ b/libs/mosquitto/src/memory_mosq.h @@ -1,15 +1,17 @@ /* -Copyright (c) 2010-2019 Roger Light +Copyright (c) 2010-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. */ @@ -20,8 +22,10 @@ and the Eclipse Distribution License is available at #include #include -#if defined(WITH_MEMORY_TRACKING) && defined(WITH_BROKER) && defined(__GLIBC__) -#define REAL_WITH_MEMORY_TRACKING +#if defined(WITH_MEMORY_TRACKING) && defined(WITH_BROKER) +# if defined(__APPLE__) || defined(__FreeBSD__) || defined(__GLIBC__) +# define REAL_WITH_MEMORY_TRACKING +# endif #endif void *mosquitto__calloc(size_t nmemb, size_t size); diff --git a/libs/mosquitto/src/messages_mosq.c b/libs/mosquitto/src/messages_mosq.c index 8bb7a01..92c92ea 100644 --- a/libs/mosquitto/src/messages_mosq.c +++ b/libs/mosquitto/src/messages_mosq.c @@ -1,15 +1,17 @@ /* -Copyright (c) 2010-2019 Roger Light +Copyright (c) 2010-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. */ @@ -19,6 +21,7 @@ and the Eclipse Distribution License is available at #include #include #include +#include #include "mosquitto_internal.h" #include "mosquitto.h" @@ -26,6 +29,7 @@ and the Eclipse Distribution License is available at #include "messages_mosq.h" #include "send_mosq.h" #include "time_mosq.h" +#include "util_mosq.h" void message__cleanup(struct mosquitto_message_all **message) { @@ -37,24 +41,23 @@ void message__cleanup(struct mosquitto_message_all **message) mosquitto__free(msg->msg.topic); mosquitto__free(msg->msg.payload); + mosquitto_property_free_all(&msg->properties); mosquitto__free(msg); } void message__cleanup_all(struct mosquitto *mosq) { - struct mosquitto_message_all *tmp; + struct mosquitto_message_all *tail, *tmp; assert(mosq); - while(mosq->in_messages){ - tmp = mosq->in_messages->next; - message__cleanup(&mosq->in_messages); - mosq->in_messages = tmp; + DL_FOREACH_SAFE(mosq->msgs_in.inflight, tail, tmp){ + DL_DELETE(mosq->msgs_in.inflight, tail); + message__cleanup(&tail); } - while(mosq->out_messages){ - tmp = mosq->out_messages->next; - message__cleanup(&mosq->out_messages); - mosq->out_messages = tmp; + DL_FOREACH_SAFE(mosq->msgs_out.inflight, tail, tmp){ + DL_DELETE(mosq->msgs_out.inflight, tail); + message__cleanup(&tail); } } @@ -68,12 +71,12 @@ int mosquitto_message_copy(struct mosquitto_message *dst, const struct mosquitto dst->qos = src->qos; dst->retain = src->retain; if(src->payloadlen){ - dst->payload = mosquitto__calloc(src->payloadlen+1, sizeof(uint8_t)); + dst->payload = mosquitto__calloc((unsigned int)src->payloadlen+1, sizeof(uint8_t)); if(!dst->payload){ mosquitto__free(dst->topic); return MOSQ_ERR_NOMEM; } - memcpy(dst->payload, src->payload, src->payloadlen); + memcpy(dst->payload, src->payload, (unsigned int)src->payloadlen); dst->payloadlen = src->payloadlen; }else{ dst->payloadlen = 0; @@ -118,203 +121,152 @@ void mosquitto_message_free_contents(struct mosquitto_message *message) int message__queue(struct mosquitto *mosq, struct mosquitto_message_all *message, enum mosquitto_msg_direction dir) { - int rc = 0; - /* mosq->*_message_mutex should be locked before entering this function */ assert(mosq); assert(message); + assert(message->msg.qos != 0); if(dir == mosq_md_out){ - mosq->out_queue_len++; - message->next = NULL; - if(mosq->out_messages_last){ - mosq->out_messages_last->next = message; - }else{ - mosq->out_messages = message; - } - mosq->out_messages_last = message; - if(message->msg.qos > 0){ - if(mosq->max_inflight_messages == 0 || mosq->inflight_messages < mosq->max_inflight_messages){ - mosq->inflight_messages++; - }else{ - rc = 1; - } - } + DL_APPEND(mosq->msgs_out.inflight, message); + mosq->msgs_out.queue_len++; }else{ - mosq->in_queue_len++; - message->next = NULL; - if(mosq->in_messages_last){ - mosq->in_messages_last->next = message; - }else{ - mosq->in_messages = message; - } - mosq->in_messages_last = message; + DL_APPEND(mosq->msgs_in.inflight, message); + mosq->msgs_in.queue_len++; } - return rc; + + return message__release_to_inflight(mosq, dir); } -void message__reconnect_reset(struct mosquitto *mosq) +void message__reconnect_reset(struct mosquitto *mosq, bool update_quota_only) { - struct mosquitto_message_all *message; - struct mosquitto_message_all *prev = NULL; + struct mosquitto_message_all *message, *tmp; assert(mosq); - pthread_mutex_lock(&mosq->in_message_mutex); - message = mosq->in_messages; - mosq->in_queue_len = 0; - while(message){ - mosq->in_queue_len++; + pthread_mutex_lock(&mosq->msgs_in.mutex); + mosq->msgs_in.inflight_quota = mosq->msgs_in.inflight_maximum; + mosq->msgs_in.queue_len = 0; + DL_FOREACH_SAFE(mosq->msgs_in.inflight, message, tmp){ + mosq->msgs_in.queue_len++; message->timestamp = 0; if(message->msg.qos != 2){ - if(prev){ - prev->next = message->next; - message__cleanup(&message); - message = prev; - }else{ - mosq->in_messages = message->next; - message__cleanup(&message); - message = mosq->in_messages; - } + DL_DELETE(mosq->msgs_in.inflight, message); + message__cleanup(&message); }else{ /* Message state can be preserved here because it should match * whatever the client has got. */ + util__decrement_receive_quota(mosq); } - prev = message; - if(message) message = message->next; } - mosq->in_messages_last = prev; - pthread_mutex_unlock(&mosq->in_message_mutex); + pthread_mutex_unlock(&mosq->msgs_in.mutex); - pthread_mutex_lock(&mosq->out_message_mutex); - mosq->inflight_messages = 0; - message = mosq->out_messages; - mosq->out_queue_len = 0; - while(message){ - mosq->out_queue_len++; - message->timestamp = 0; + pthread_mutex_lock(&mosq->msgs_out.mutex); + mosq->msgs_out.inflight_quota = mosq->msgs_out.inflight_maximum; + mosq->msgs_out.queue_len = 0; + DL_FOREACH_SAFE(mosq->msgs_out.inflight, message, tmp){ + mosq->msgs_out.queue_len++; - if(mosq->max_inflight_messages == 0 || mosq->inflight_messages < mosq->max_inflight_messages){ - if(message->msg.qos > 0){ - mosq->inflight_messages++; - } - if(message->msg.qos == 1){ - message->state = mosq_ms_publish_qos1; - }else if(message->msg.qos == 2){ - if(message->state == mosq_ms_wait_for_pubrec){ - message->state = mosq_ms_publish_qos2; - }else if(message->state == mosq_ms_wait_for_pubcomp){ - message->state = mosq_ms_resend_pubrel; + message->timestamp = 0; + if(mosq->msgs_out.inflight_quota != 0){ + util__decrement_send_quota(mosq); + if (update_quota_only == false){ + if(message->msg.qos == 1){ + message->state = mosq_ms_publish_qos1; + }else if(message->msg.qos == 2){ + if(message->state == mosq_ms_wait_for_pubrec){ + message->state = mosq_ms_publish_qos2; + }else if(message->state == mosq_ms_wait_for_pubcomp){ + message->state = mosq_ms_resend_pubrel; + } + /* Should be able to preserve state. */ } - /* Should be able to preserve state. */ } }else{ message->state = mosq_ms_invalid; } - prev = message; - message = message->next; } - mosq->out_messages_last = prev; - pthread_mutex_unlock(&mosq->out_message_mutex); + pthread_mutex_unlock(&mosq->msgs_out.mutex); } + +int message__release_to_inflight(struct mosquitto *mosq, enum mosquitto_msg_direction dir) +{ + /* mosq->*_message_mutex should be locked before entering this function */ + struct mosquitto_message_all *cur, *tmp; + int rc = MOSQ_ERR_SUCCESS; + + if(dir == mosq_md_out){ + DL_FOREACH_SAFE(mosq->msgs_out.inflight, cur, tmp){ + if(mosq->msgs_out.inflight_quota > 0){ + if(cur->msg.qos > 0 && cur->state == mosq_ms_invalid){ + if(cur->msg.qos == 1){ + cur->state = mosq_ms_wait_for_puback; + }else if(cur->msg.qos == 2){ + cur->state = mosq_ms_wait_for_pubrec; + } + rc = send__publish(mosq, (uint16_t)cur->msg.mid, cur->msg.topic, (uint32_t)cur->msg.payloadlen, cur->msg.payload, (uint8_t)cur->msg.qos, cur->msg.retain, cur->dup, cur->properties, NULL, 0); + if(rc){ + return rc; + } + util__decrement_send_quota(mosq); + } + }else{ + return MOSQ_ERR_SUCCESS; + } + } + } + + return rc; +} + + int message__remove(struct mosquitto *mosq, uint16_t mid, enum mosquitto_msg_direction dir, struct mosquitto_message_all **message, int qos) { - struct mosquitto_message_all *cur, *prev = NULL; + struct mosquitto_message_all *cur, *tmp; bool found = false; - int rc; assert(mosq); assert(message); if(dir == mosq_md_out){ - pthread_mutex_lock(&mosq->out_message_mutex); - cur = mosq->out_messages; - while(cur){ - if(cur->msg.mid == mid){ + pthread_mutex_lock(&mosq->msgs_out.mutex); + + DL_FOREACH_SAFE(mosq->msgs_out.inflight, cur, tmp){ + if(found == false && cur->msg.mid == mid){ if(cur->msg.qos != qos){ - pthread_mutex_unlock(&mosq->out_message_mutex); + pthread_mutex_unlock(&mosq->msgs_out.mutex); return MOSQ_ERR_PROTOCOL; } - if(prev){ - prev->next = cur->next; - }else{ - mosq->out_messages = cur->next; - } + DL_DELETE(mosq->msgs_out.inflight, cur); + *message = cur; - mosq->out_queue_len--; - if(cur->next == NULL){ - mosq->out_messages_last = prev; - }else if(!mosq->out_messages){ - mosq->out_messages_last = NULL; - } - if(cur->msg.qos > 0){ - mosq->inflight_messages--; - } + mosq->msgs_out.queue_len--; found = true; break; } - prev = cur; - cur = cur->next; } - + pthread_mutex_unlock(&mosq->msgs_out.mutex); if(found){ - cur = mosq->out_messages; - while(cur){ - if(mosq->max_inflight_messages == 0 || mosq->inflight_messages < mosq->max_inflight_messages){ - if(cur->msg.qos > 0 && cur->state == mosq_ms_invalid){ - mosq->inflight_messages++; - if(cur->msg.qos == 1){ - cur->state = mosq_ms_wait_for_puback; - }else if(cur->msg.qos == 2){ - cur->state = mosq_ms_wait_for_pubrec; - } - rc = send__publish(mosq, cur->msg.mid, cur->msg.topic, cur->msg.payloadlen, cur->msg.payload, cur->msg.qos, cur->msg.retain, cur->dup); - if(rc){ - pthread_mutex_unlock(&mosq->out_message_mutex); - return rc; - } - } - }else{ - pthread_mutex_unlock(&mosq->out_message_mutex); - return MOSQ_ERR_SUCCESS; - } - cur = cur->next; - } - pthread_mutex_unlock(&mosq->out_message_mutex); return MOSQ_ERR_SUCCESS; }else{ - pthread_mutex_unlock(&mosq->out_message_mutex); return MOSQ_ERR_NOT_FOUND; } }else{ - pthread_mutex_lock(&mosq->in_message_mutex); - cur = mosq->in_messages; - while(cur){ + pthread_mutex_lock(&mosq->msgs_in.mutex); + DL_FOREACH_SAFE(mosq->msgs_in.inflight, cur, tmp){ if(cur->msg.mid == mid){ if(cur->msg.qos != qos){ - pthread_mutex_unlock(&mosq->in_message_mutex); + pthread_mutex_unlock(&mosq->msgs_in.mutex); return MOSQ_ERR_PROTOCOL; } - if(prev){ - prev->next = cur->next; - }else{ - mosq->in_messages = cur->next; - } + DL_DELETE(mosq->msgs_in.inflight, cur); *message = cur; - mosq->in_queue_len--; - if(cur->next == NULL){ - mosq->in_messages_last = prev; - }else if(!mosq->in_messages){ - mosq->in_messages_last = NULL; - } + mosq->msgs_in.queue_len--; found = true; break; } - prev = cur; - cur = cur->next; } - pthread_mutex_unlock(&mosq->in_message_mutex); + pthread_mutex_unlock(&mosq->msgs_in.mutex); if(found){ return MOSQ_ERR_SUCCESS; }else{ @@ -323,91 +275,75 @@ int message__remove(struct mosquitto *mosq, uint16_t mid, enum mosquitto_msg_dir } } -#ifdef WITH_THREADING -void message__retry_check_actual(struct mosquitto *mosq, struct mosquitto_message_all *messages, pthread_mutex_t *mutex) -#else -void message__retry_check_actual(struct mosquitto *mosq, struct mosquitto_message_all *messages) -#endif +void message__retry_check(struct mosquitto *mosq) { + struct mosquitto_message_all *msg; time_t now = mosquitto_time(); assert(mosq); #ifdef WITH_THREADING - pthread_mutex_lock(mutex); + pthread_mutex_lock(&mosq->msgs_out.mutex); #endif - while(messages){ - switch(messages->state){ + DL_FOREACH(mosq->msgs_out.inflight, msg){ + switch(msg->state){ case mosq_ms_publish_qos1: case mosq_ms_publish_qos2: - messages->timestamp = now; - messages->dup = true; - send__publish(mosq, messages->msg.mid, messages->msg.topic, messages->msg.payloadlen, messages->msg.payload, messages->msg.qos, messages->msg.retain, messages->dup); + msg->timestamp = now; + msg->dup = true; + send__publish(mosq, (uint16_t)msg->msg.mid, msg->msg.topic, (uint32_t)msg->msg.payloadlen, msg->msg.payload, (uint8_t)msg->msg.qos, msg->msg.retain, msg->dup, msg->properties, NULL, 0); break; case mosq_ms_wait_for_pubrel: - messages->timestamp = now; - messages->dup = true; - send__pubrec(mosq, messages->msg.mid); + msg->timestamp = now; + msg->dup = true; + send__pubrec(mosq, (uint16_t)msg->msg.mid, 0, NULL); break; case mosq_ms_resend_pubrel: case mosq_ms_wait_for_pubcomp: - messages->timestamp = now; - messages->dup = true; - send__pubrel(mosq, messages->msg.mid); + msg->timestamp = now; + msg->dup = true; + send__pubrel(mosq, (uint16_t)msg->msg.mid, NULL); break; default: break; } - messages = messages->next; } #ifdef WITH_THREADING - pthread_mutex_unlock(mutex); + pthread_mutex_unlock(&mosq->msgs_out.mutex); #endif } -void message__retry_check(struct mosquitto *mosq) -{ -#ifdef WITH_THREADING - message__retry_check_actual(mosq, mosq->out_messages, &mosq->out_message_mutex); -#else - message__retry_check_actual(mosq, mosq->out_messages); -#endif -} void mosquitto_message_retry_set(struct mosquitto *mosq, unsigned int message_retry) { + UNUSED(mosq); + UNUSED(message_retry); } int message__out_update(struct mosquitto *mosq, uint16_t mid, enum mosquitto_msg_state state, int qos) { - struct mosquitto_message_all *message; + struct mosquitto_message_all *message, *tmp; assert(mosq); - pthread_mutex_lock(&mosq->out_message_mutex); - message = mosq->out_messages; - while(message){ + pthread_mutex_lock(&mosq->msgs_out.mutex); + DL_FOREACH_SAFE(mosq->msgs_out.inflight, message, tmp){ if(message->msg.mid == mid){ if(message->msg.qos != qos){ - pthread_mutex_unlock(&mosq->out_message_mutex); + pthread_mutex_unlock(&mosq->msgs_out.mutex); return MOSQ_ERR_PROTOCOL; } message->state = state; message->timestamp = mosquitto_time(); - pthread_mutex_unlock(&mosq->out_message_mutex); + pthread_mutex_unlock(&mosq->msgs_out.mutex); return MOSQ_ERR_SUCCESS; } - message = message->next; } - pthread_mutex_unlock(&mosq->out_message_mutex); + pthread_mutex_unlock(&mosq->msgs_out.mutex); return MOSQ_ERR_NOT_FOUND; } int mosquitto_max_inflight_messages_set(struct mosquitto *mosq, unsigned int max_inflight_messages) { - if(!mosq) return MOSQ_ERR_INVAL; - - mosq->max_inflight_messages = max_inflight_messages; - - return MOSQ_ERR_SUCCESS; + return mosquitto_int_option(mosq, MOSQ_OPT_SEND_MAXIMUM, (int)max_inflight_messages); } diff --git a/libs/mosquitto/src/messages_mosq.h b/libs/mosquitto/src/messages_mosq.h index 0bc5012..b8f5e19 100644 --- a/libs/mosquitto/src/messages_mosq.h +++ b/libs/mosquitto/src/messages_mosq.h @@ -1,15 +1,17 @@ /* -Copyright (c) 2010-2019 Roger Light +Copyright (c) 2010-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. */ @@ -23,7 +25,8 @@ void message__cleanup_all(struct mosquitto *mosq); void message__cleanup(struct mosquitto_message_all **message); int message__delete(struct mosquitto *mosq, uint16_t mid, enum mosquitto_msg_direction dir, int qos); int message__queue(struct mosquitto *mosq, struct mosquitto_message_all *message, enum mosquitto_msg_direction dir); -void message__reconnect_reset(struct mosquitto *mosq); +void message__reconnect_reset(struct mosquitto *mosq, bool update_quota_only); +int message__release_to_inflight(struct mosquitto *mosq, enum mosquitto_msg_direction dir); int message__remove(struct mosquitto *mosq, uint16_t mid, enum mosquitto_msg_direction dir, struct mosquitto_message_all **message, int qos); void message__retry_check(struct mosquitto *mosq); int message__out_update(struct mosquitto *mosq, uint16_t mid, enum mosquitto_msg_state state, int qos); diff --git a/libs/mosquitto/src/misc_mosq.c b/libs/mosquitto/src/misc_mosq.c new file mode 100644 index 0000000..5004125 --- /dev/null +++ b/libs/mosquitto/src/misc_mosq.c @@ -0,0 +1,210 @@ +/* +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 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. +*/ + +/* This contains general purpose utility functions that are not specific to + * Mosquitto/MQTT features. */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#ifdef WIN32 +# include +# include +# include +# include +# include +#else +# include +#endif + +#include "misc_mosq.h" +#include "logging_mosq.h" + + +FILE *mosquitto__fopen(const char *path, const char *mode, bool restrict_read) +{ +#ifdef WIN32 + char buf[4096]; + int rc; + int flags = 0; + + rc = ExpandEnvironmentStringsA(path, buf, 4096); + if(rc == 0 || rc > 4096){ + return NULL; + }else{ + if (restrict_read) { + HANDLE hfile; + SECURITY_ATTRIBUTES sec; + EXPLICIT_ACCESS_A ea; + PACL pacl = NULL; + char username[UNLEN + 1]; + DWORD ulen = UNLEN; + SECURITY_DESCRIPTOR sd; + DWORD dwCreationDisposition; + int fd; + FILE *fptr; + + switch(mode[0]){ + case 'a': + dwCreationDisposition = OPEN_ALWAYS; + flags = _O_APPEND; + break; + case 'r': + dwCreationDisposition = OPEN_EXISTING; + flags = _O_RDONLY; + break; + case 'w': + dwCreationDisposition = CREATE_ALWAYS; + break; + default: + return NULL; + } + + GetUserNameA(username, &ulen); + if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) { + return NULL; + } + BuildExplicitAccessWithNameA(&ea, username, GENERIC_ALL, SET_ACCESS, NO_INHERITANCE); + if (SetEntriesInAclA(1, &ea, NULL, &pacl) != ERROR_SUCCESS) { + return NULL; + } + if (!SetSecurityDescriptorDacl(&sd, TRUE, pacl, FALSE)) { + LocalFree(pacl); + return NULL; + } + + memset(&sec, 0, sizeof(sec)); + sec.nLength = sizeof(SECURITY_ATTRIBUTES); + sec.bInheritHandle = FALSE; + sec.lpSecurityDescriptor = &sd; + + hfile = CreateFileA(buf, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, + &sec, + dwCreationDisposition, + FILE_ATTRIBUTE_NORMAL, + NULL); + + LocalFree(pacl); + + fd = _open_osfhandle((intptr_t)hfile, flags); + if (fd < 0) { + return NULL; + } + + fptr = _fdopen(fd, mode); + if (!fptr) { + _close(fd); + return NULL; + } + if(mode[0] == 'a'){ + fseek(fptr, 0, SEEK_END); + } + return fptr; + + }else { + return fopen(buf, mode); + } + } +#else + if(mode[0] == 'r'){ + struct stat statbuf; + if(stat(path, &statbuf) < 0){ + return NULL; + } + + if(!S_ISREG(statbuf.st_mode) && !S_ISLNK(statbuf.st_mode)){ + log__printf(NULL, MOSQ_LOG_ERR, "Error: %s is not a file.", path); + return NULL; + } + } + + if (restrict_read) { + FILE *fptr; + mode_t old_mask; + + old_mask = umask(0077); + fptr = fopen(path, mode); + umask(old_mask); + + return fptr; + }else{ + return fopen(path, mode); + } +#endif +} + + +char *misc__trimblanks(char *str) +{ + char *endptr; + + if(str == NULL) return NULL; + + while(isspace(str[0])){ + str++; + } + endptr = &str[strlen(str)-1]; + while(endptr > str && isspace(endptr[0])){ + endptr[0] = '\0'; + endptr--; + } + return str; +} + + +char *fgets_extending(char **buf, int *buflen, FILE *stream) +{ + char *rc; + char endchar; + int offset = 0; + char *newbuf; + size_t len; + + if(stream == NULL || buf == NULL || buflen == NULL || *buflen < 1){ + return NULL; + } + + do{ + rc = fgets(&((*buf)[offset]), (*buflen)-offset, stream); + if(feof(stream) || rc == NULL){ + return rc; + } + + len = strlen(*buf); + if(len == 0){ + return rc; + } + endchar = (*buf)[len-1]; + if(endchar == '\n'){ + return rc; + } + /* No EOL char found, so extend buffer */ + offset = (*buflen)-1; + *buflen += 1000; + newbuf = realloc(*buf, (size_t)*buflen); + if(!newbuf){ + return NULL; + } + *buf = newbuf; + }while(1); +} diff --git a/libs/mosquitto/src/misc_mosq.h b/libs/mosquitto/src/misc_mosq.h new file mode 100644 index 0000000..b758db3 --- /dev/null +++ b/libs/mosquitto/src/misc_mosq.h @@ -0,0 +1,28 @@ +/* +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 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 MISC_MOSQ_H +#define MISC_MOSQ_H + +#include +#include + +FILE *mosquitto__fopen(const char *path, const char *mode, bool restrict_read); +char *misc__trimblanks(char *str); +char *fgets_extending(char **buf, int *buflen, FILE *stream); + +#endif diff --git a/libs/mosquitto/src/mosquitto.c b/libs/mosquitto/src/mosquitto.c index f27b954..0d68d31 100644 --- a/libs/mosquitto/src/mosquitto.c +++ b/libs/mosquitto/src/mosquitto.c @@ -1,15 +1,17 @@ /* -Copyright (c) 2010-2019 Roger Light +Copyright (c) 2010-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,16 +23,24 @@ and the Eclipse Distribution License is available at #include #ifndef WIN32 #include +#include +#endif + +#if defined(__APPLE__) +# include #endif +#include "logging_mosq.h" #include "mosquitto.h" #include "mosquitto_internal.h" #include "memory_mosq.h" #include "messages_mosq.h" +#include "mqtt_protocol.h" #include "net_mosq.h" #include "packet_mosq.h" #include "will_mosq.h" +static unsigned int init_refcount = 0; void mosquitto__destroy(struct mosquitto *mosq); @@ -44,41 +54,57 @@ int mosquitto_lib_version(int *major, int *minor, int *revision) int mosquitto_lib_init(void) { + int rc; + + if (init_refcount == 0) { #ifdef WIN32 - srand(GetTickCount64()); + srand((unsigned int)GetTickCount64()); #elif _POSIX_TIMERS>0 && defined(_POSIX_MONOTONIC_CLOCK) - struct timespec tp; + struct timespec tp; - clock_gettime(CLOCK_MONOTONIC, &tp); - srand(tp.tv_nsec); + clock_gettime(CLOCK_MONOTONIC, &tp); + srand((unsigned int)tp.tv_nsec); #elif defined(__APPLE__) - uint64_t ticks; + uint64_t ticks; - ticks = mach_absolute_time(); - srand((unsigned int)ticks); + ticks = mach_absolute_time(); + srand((unsigned int)ticks); #else - struct timeval tv; + struct timeval tv; - gettimeofday(&tv, NULL); - srand(tv.tv_sec*1000 + tv.tv_usec/1000); + gettimeofday(&tv, NULL); + srand(tv.tv_sec*1000 + tv.tv_usec/1000); #endif - return net__init(); + rc = net__init(); + if (rc != MOSQ_ERR_SUCCESS) { + return rc; + } + } + + init_refcount++; + return MOSQ_ERR_SUCCESS; } int mosquitto_lib_cleanup(void) { - net__cleanup(); + if (init_refcount == 1) { + net__cleanup(); + } + + if (init_refcount > 0) { + --init_refcount; + } return MOSQ_ERR_SUCCESS; } -struct mosquitto *mosquitto_new(const char *id, bool clean_session, void *userdata) +struct mosquitto *mosquitto_new(const char *id, bool clean_start, void *userdata) { struct mosquitto *mosq = NULL; int rc; - if(clean_session == false && id == NULL){ + if(clean_start == false && id == NULL){ errno = EINVAL; return NULL; } @@ -90,12 +116,12 @@ struct mosquitto *mosquitto_new(const char *id, bool clean_session, void *userda mosq = (struct mosquitto *)mosquitto__calloc(1, sizeof(struct mosquitto)); if(mosq){ mosq->sock = INVALID_SOCKET; - mosq->sockpairR = INVALID_SOCKET; - mosq->sockpairW = INVALID_SOCKET; #ifdef WITH_THREADING mosq->thread_id = pthread_self(); #endif - rc = mosquitto_reinitialise(mosq, id, clean_session, userdata); + mosq->sockpairR = INVALID_SOCKET; + mosq->sockpairW = INVALID_SOCKET; + rc = mosquitto_reinitialise(mosq, id, clean_start, userdata); if(rc){ mosquitto_destroy(mosq); if(rc == MOSQ_ERR_INVAL){ @@ -111,13 +137,11 @@ struct mosquitto *mosquitto_new(const char *id, bool clean_session, void *userda return mosq; } -int mosquitto_reinitialise(struct mosquitto *mosq, const char *id, bool clean_session, void *userdata) +int mosquitto_reinitialise(struct mosquitto *mosq, const char *id, bool clean_start, void *userdata) { - int i; - if(!mosq) return MOSQ_ERR_INVAL; - if(clean_session == false && id == NULL){ + if(clean_start == false && id == NULL){ return MOSQ_ERR_INVAL; } @@ -134,44 +158,31 @@ int mosquitto_reinitialise(struct mosquitto *mosq, const char *id, bool clean_se mosq->sockpairR = INVALID_SOCKET; mosq->sockpairW = INVALID_SOCKET; mosq->keepalive = 60; - mosq->clean_session = clean_session; + mosq->clean_start = clean_start; if(id){ if(STREMPTY(id)){ return MOSQ_ERR_INVAL; } - if(mosquitto_validate_utf8(id, strlen(id))){ + if(mosquitto_validate_utf8(id, (int)strlen(id))){ return MOSQ_ERR_MALFORMED_UTF8; } mosq->id = mosquitto__strdup(id); - }else{ - mosq->id = (char *)mosquitto__calloc(24, sizeof(char)); - if(!mosq->id){ - return MOSQ_ERR_NOMEM; - } - mosq->id[0] = 'm'; - mosq->id[1] = 'o'; - mosq->id[2] = 's'; - mosq->id[3] = 'q'; - mosq->id[4] = '/'; - - for(i=5; i<23; i++){ - mosq->id[i] = (rand()%73)+48; - } } mosq->in_packet.payload = NULL; packet__cleanup(&mosq->in_packet); mosq->out_packet = NULL; + mosq->out_packet_count = 0; mosq->current_out_packet = NULL; mosq->last_msg_in = mosquitto_time(); mosq->next_msg_out = mosquitto_time() + mosq->keepalive; mosq->ping_t = 0; mosq->last_mid = 0; mosq->state = mosq_cs_new; - mosq->in_messages = NULL; - mosq->in_messages_last = NULL; - mosq->out_messages = NULL; - mosq->out_messages_last = NULL; - mosq->max_inflight_messages = 20; + mosq->max_qos = 2; + mosq->msgs_in.inflight_maximum = 20; + mosq->msgs_out.inflight_maximum = 20; + mosq->msgs_in.inflight_quota = 20; + mosq->msgs_out.inflight_quota = 20; mosq->will = NULL; mosq->on_connect = NULL; mosq->on_publish = NULL; @@ -181,8 +192,6 @@ int mosquitto_reinitialise(struct mosquitto *mosq, const char *id, bool clean_se mosq->host = NULL; mosq->port = 1883; mosq->in_callback = false; - mosq->in_queue_len = 0; - mosq->out_queue_len = 0; mosq->reconnect_delay = 1; mosq->reconnect_delay_max = 1; mosq->reconnect_exponential_backoff = false; @@ -190,9 +199,11 @@ int mosquitto_reinitialise(struct mosquitto *mosq, const char *id, bool clean_se #ifdef WITH_TLS mosq->ssl = NULL; mosq->ssl_ctx = NULL; + mosq->ssl_ctx_defaults = true; mosq->tls_cert_reqs = SSL_VERIFY_PEER; mosq->tls_insecure = false; mosq->want_write = false; + mosq->tls_ocsp_required = false; #endif #ifdef WITH_THREADING pthread_mutex_init(&mosq->callback_mutex, NULL); @@ -201,11 +212,17 @@ int mosquitto_reinitialise(struct mosquitto *mosq, const char *id, bool clean_se pthread_mutex_init(&mosq->out_packet_mutex, NULL); pthread_mutex_init(&mosq->current_out_packet_mutex, NULL); pthread_mutex_init(&mosq->msgtime_mutex, NULL); - pthread_mutex_init(&mosq->in_message_mutex, NULL); - pthread_mutex_init(&mosq->out_message_mutex, NULL); + pthread_mutex_init(&mosq->msgs_in.mutex, NULL); + pthread_mutex_init(&mosq->msgs_out.mutex, NULL); pthread_mutex_init(&mosq->mid_mutex, NULL); mosq->thread_id = pthread_self(); #endif + /* This must be after pthread_mutex_init(), otherwise the log mutex may be + * used before being initialised. */ + if(net__socketpair(&mosq->sockpairR, &mosq->sockpairW)){ + log__printf(mosq, MOSQ_LOG_WARNING, + "Warning: Unable to open socket pair, outgoing publish commands may be delayed."); + } return MOSQ_ERR_SUCCESS; } @@ -213,15 +230,16 @@ int mosquitto_reinitialise(struct mosquitto *mosq, const char *id, bool clean_se void mosquitto__destroy(struct mosquitto *mosq) { - struct mosquitto__packet *packet; if(!mosq) return; #ifdef WITH_THREADING +# ifdef HAVE_PTHREAD_CANCEL if(mosq->threaded == mosq_ts_self && !pthread_equal(mosq->thread_id, pthread_self())){ pthread_cancel(mosq->thread_id); pthread_join(mosq->thread_id, NULL); mosq->threaded = mosq_ts_none; } +# endif if(mosq->id){ /* If mosq->id is not NULL then the client has already been initialised @@ -233,8 +251,8 @@ void mosquitto__destroy(struct mosquitto *mosq) pthread_mutex_destroy(&mosq->out_packet_mutex); pthread_mutex_destroy(&mosq->current_out_packet_mutex); pthread_mutex_destroy(&mosq->msgtime_mutex); - pthread_mutex_destroy(&mosq->in_message_mutex); - pthread_mutex_destroy(&mosq->out_message_mutex); + pthread_mutex_destroy(&mosq->msgs_in.mutex); + pthread_mutex_destroy(&mosq->msgs_out.mutex); pthread_mutex_destroy(&mosq->mid_mutex); } #endif @@ -259,6 +277,7 @@ void mosquitto__destroy(struct mosquitto *mosq) mosquitto__free(mosq->tls_ciphers); mosquitto__free(mosq->tls_psk); mosquitto__free(mosq->tls_psk_identity); + mosquitto__free(mosq->tls_alpn); #endif mosquitto__free(mosq->address); @@ -279,22 +298,9 @@ void mosquitto__destroy(struct mosquitto *mosq) mosquitto__free(mosq->bind_address); mosq->bind_address = NULL; - /* Out packet cleanup */ - if(mosq->out_packet && !mosq->current_out_packet){ - mosq->current_out_packet = mosq->out_packet; - mosq->out_packet = mosq->out_packet->next; - } - while(mosq->current_out_packet){ - packet = mosq->current_out_packet; - /* Free data and reset values */ - mosq->current_out_packet = mosq->out_packet; - if(mosq->out_packet){ - mosq->out_packet = mosq->out_packet->next; - } + mosquitto_property_free_all(&mosq->connect_properties); - packet__cleanup(packet); - mosquitto__free(packet); - } + packet__cleanup_all_no_locks(mosq); packet__cleanup(&mosq->in_packet); if(mosq->sockpairR != INVALID_SOCKET){ @@ -341,80 +347,14 @@ bool mosquitto_want_write(struct mosquitto *mosq) } -const char *mosquitto_strerror(int mosq_errno) -{ - switch(mosq_errno){ - case MOSQ_ERR_CONN_PENDING: - return "Connection pending."; - case MOSQ_ERR_SUCCESS: - return "No error."; - case MOSQ_ERR_NOMEM: - return "Out of memory."; - case MOSQ_ERR_PROTOCOL: - return "A network protocol error occurred when communicating with the broker."; - case MOSQ_ERR_INVAL: - return "Invalid function arguments provided."; - case MOSQ_ERR_NO_CONN: - return "The client is not currently connected."; - case MOSQ_ERR_CONN_REFUSED: - return "The connection was refused."; - case MOSQ_ERR_NOT_FOUND: - return "Message not found (internal error)."; - case MOSQ_ERR_CONN_LOST: - return "The connection was lost."; - case MOSQ_ERR_TLS: - return "A TLS error occurred."; - case MOSQ_ERR_PAYLOAD_SIZE: - return "Payload too large."; - case MOSQ_ERR_NOT_SUPPORTED: - return "This feature is not supported."; - case MOSQ_ERR_AUTH: - return "Authorisation failed."; - case MOSQ_ERR_ACL_DENIED: - return "Access denied by ACL."; - case MOSQ_ERR_UNKNOWN: - return "Unknown error."; - case MOSQ_ERR_ERRNO: - return strerror(errno); - case MOSQ_ERR_EAI: - return "Lookup error."; - case MOSQ_ERR_PROXY: - return "Proxy error."; - case MOSQ_ERR_MALFORMED_UTF8: - return "Malformed UTF-8"; - default: - return "Unknown error."; - } -} - -const char *mosquitto_connack_string(int connack_code) -{ - switch(connack_code){ - case 0: - return "Connection Accepted."; - case 1: - return "Connection Refused: unacceptable protocol version."; - case 2: - return "Connection Refused: identifier rejected."; - case 3: - return "Connection Refused: broker unavailable."; - case 4: - return "Connection Refused: bad user name or password."; - case 5: - return "Connection Refused: not authorised."; - default: - return "Connection Refused: unknown reason."; - } -} - int mosquitto_sub_topic_tokenise(const char *subtopic, char ***topics, int *count) { - int len; - int hier_count = 1; - int start, stop; - int hier; - int tlen; - int i, j; + size_t len; + size_t hier_count = 1; + size_t start, stop; + size_t hier; + size_t tlen; + size_t i, j; if(!subtopic || !topics || !count) return MOSQ_ERR_INVAL; @@ -434,7 +374,6 @@ int mosquitto_sub_topic_tokenise(const char *subtopic, char ***topics, int *coun if(!(*topics)) return MOSQ_ERR_NOMEM; start = 0; - stop = 0; hier = 0; for(i=0; i +Copyright (c) 2010-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. */ @@ -17,21 +19,37 @@ and the Eclipse Distribution License is available at #ifndef MOSQUITTO_H #define MOSQUITTO_H +/* + * File: mosquitto.h + * + * This header contains functions and definitions for use with libmosquitto, the Mosquitto client library. + * + * The definitions are also used in Mosquitto broker plugins, and some functions are available to plugins. + */ #ifdef __cplusplus extern "C" { #endif -#if defined(WIN32) && !defined(WITH_BROKER) && !defined(LIBMOSQUITTO_STATIC) -# ifdef libmosquitto_EXPORTS -# define libmosq_EXPORT __declspec(dllexport) -# else -# define libmosq_EXPORT __declspec(dllimport) -# endif + +#ifdef WIN32 +# ifdef mosquitto_EXPORTS +# define libmosq_EXPORT __declspec(dllexport) +# else +# ifndef LIBMOSQUITTO_STATIC +# ifdef libmosquitto_EXPORTS +# define libmosq_EXPORT __declspec(dllexport) +# else +# define libmosq_EXPORT __declspec(dllimport) +# endif +# else +# define libmosq_EXPORT +# endif +# endif #else -# define libmosq_EXPORT +# define libmosq_EXPORT #endif -#if defined(_MSC_VER) && _MSC_VER < 1900 +#if defined(_MSC_VER) && _MSC_VER < 1900 && !defined(bool) # ifndef __cplusplus # define bool char # define true 1 @@ -44,27 +62,32 @@ extern "C" { #endif #include +#include -#define LIBMOSQUITTO_MAJOR 1 -#define LIBMOSQUITTO_MINOR 5 -#define LIBMOSQUITTO_REVISION 8 +#define LIBMOSQUITTO_MAJOR 2 +#define LIBMOSQUITTO_MINOR 0 +#define LIBMOSQUITTO_REVISION 14 /* LIBMOSQUITTO_VERSION_NUMBER looks like 1002001 for e.g. version 1.2.1. */ #define LIBMOSQUITTO_VERSION_NUMBER (LIBMOSQUITTO_MAJOR*1000000+LIBMOSQUITTO_MINOR*1000+LIBMOSQUITTO_REVISION) /* Log types */ -#define MOSQ_LOG_NONE 0x00 -#define MOSQ_LOG_INFO 0x01 -#define MOSQ_LOG_NOTICE 0x02 -#define MOSQ_LOG_WARNING 0x04 -#define MOSQ_LOG_ERR 0x08 -#define MOSQ_LOG_DEBUG 0x10 -#define MOSQ_LOG_SUBSCRIBE 0x20 -#define MOSQ_LOG_UNSUBSCRIBE 0x40 -#define MOSQ_LOG_WEBSOCKETS 0x80 -#define MOSQ_LOG_ALL 0xFFFF +#define MOSQ_LOG_NONE 0 +#define MOSQ_LOG_INFO (1<<0) +#define MOSQ_LOG_NOTICE (1<<1) +#define MOSQ_LOG_WARNING (1<<2) +#define MOSQ_LOG_ERR (1<<3) +#define MOSQ_LOG_DEBUG (1<<4) +#define MOSQ_LOG_SUBSCRIBE (1<<5) +#define MOSQ_LOG_UNSUBSCRIBE (1<<6) +#define MOSQ_LOG_WEBSOCKETS (1<<7) +#define MOSQ_LOG_INTERNAL 0x80000000U +#define MOSQ_LOG_ALL 0xFFFFFFFFU /* Error values */ enum mosq_err_t { + MOSQ_ERR_AUTH_CONTINUE = -4, + MOSQ_ERR_NO_SUBSCRIBERS = -3, + MOSQ_ERR_SUB_EXISTS = -2, MOSQ_ERR_CONN_PENDING = -1, MOSQ_ERR_SUCCESS = 0, MOSQ_ERR_NOMEM = 1, @@ -87,20 +110,43 @@ enum mosq_err_t { MOSQ_ERR_MALFORMED_UTF8 = 18, MOSQ_ERR_KEEPALIVE = 19, MOSQ_ERR_LOOKUP = 20, + MOSQ_ERR_MALFORMED_PACKET = 21, + MOSQ_ERR_DUPLICATE_PROPERTY = 22, + MOSQ_ERR_TLS_HANDSHAKE = 23, + MOSQ_ERR_QOS_NOT_SUPPORTED = 24, + MOSQ_ERR_OVERSIZE_PACKET = 25, + MOSQ_ERR_OCSP = 26, + MOSQ_ERR_TIMEOUT = 27, + MOSQ_ERR_RETAIN_NOT_SUPPORTED = 28, + MOSQ_ERR_TOPIC_ALIAS_INVALID = 29, + MOSQ_ERR_ADMINISTRATIVE_ACTION = 30, + MOSQ_ERR_ALREADY_EXISTS = 31, }; -/* Error values */ +/* Option values */ enum mosq_opt_t { MOSQ_OPT_PROTOCOL_VERSION = 1, MOSQ_OPT_SSL_CTX = 2, MOSQ_OPT_SSL_CTX_WITH_DEFAULTS = 3, + MOSQ_OPT_RECEIVE_MAXIMUM = 4, + MOSQ_OPT_SEND_MAXIMUM = 5, + MOSQ_OPT_TLS_KEYFORM = 6, + MOSQ_OPT_TLS_ENGINE = 7, + MOSQ_OPT_TLS_ENGINE_KPASS_SHA1 = 8, + MOSQ_OPT_TLS_OCSP_REQUIRED = 9, + MOSQ_OPT_TLS_ALPN = 10, + MOSQ_OPT_TCP_NODELAY = 11, + MOSQ_OPT_BIND_ADDRESS = 12, + MOSQ_OPT_TLS_USE_OS_CERTS = 13, }; + /* MQTT specification restricts client ids to a maximum of 23 characters */ #define MOSQ_MQTT_ID_MAX_LENGTH 23 #define MQTT_PROTOCOL_V31 3 #define MQTT_PROTOCOL_V311 4 +#define MQTT_PROTOCOL_V5 5 struct mosquitto_message{ int mid; @@ -112,12 +158,16 @@ struct mosquitto_message{ }; struct mosquitto; +typedef struct mqtt5__property mosquitto_property; /* * Topic: Threads * libmosquitto provides thread safe operation, with the exception of * which is not thread safe. * + * If the library has been compiled without thread support it is *not* + * guaranteed to be thread safe. + * * If your application uses threads you must use to * tell the library this is the case, otherwise it makes some optimisations * for the single threaded case that may result in unexpected behaviour for @@ -143,6 +193,12 @@ struct mosquitto; * mosquitto_publish() ***************************************************/ + +/* ====================================================================== + * + * Section: Library version, init, and cleanup + * + * ====================================================================== */ /* * Function: mosquitto_lib_version * @@ -160,7 +216,7 @@ struct mosquitto; * be returned in this variable. * * Returns: - * LIBMOSQUITTO_VERSION_NUMBER, which is a unique number based on the major, + * LIBMOSQUITTO_VERSION_NUMBER - which is a unique number based on the major, * minor and revision values. * See Also: * , @@ -175,7 +231,8 @@ libmosq_EXPORT int mosquitto_lib_version(int *major, int *minor, int *revision); * This function is *not* thread safe. * * Returns: - * MOSQ_ERR_SUCCESS - always + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_UNKNOWN - on Windows, if sockets couldn't be initialized. * * See Also: * , @@ -195,6 +252,12 @@ libmosq_EXPORT int mosquitto_lib_init(void); */ libmosq_EXPORT int mosquitto_lib_cleanup(void); + +/* ====================================================================== + * + * Section: Client creation, destruction, and reinitialisation + * + * ====================================================================== */ /* * Function: mosquitto_new * @@ -268,12 +331,21 @@ libmosq_EXPORT void mosquitto_destroy(struct mosquitto *mosq); */ libmosq_EXPORT int mosquitto_reinitialise(struct mosquitto *mosq, const char *id, bool clean_session, void *obj); + +/* ====================================================================== + * + * Section: Will + * + * ====================================================================== */ /* * Function: mosquitto_will_set * * Configure will information for a mosquitto instance. By default, clients do * not have a will. This must be called before calling . * + * It is valid to use this function for clients using all MQTT protocol versions. + * If you need to set MQTT v5 Will properties, use instead. + * * Parameters: * mosq - a valid mosquitto instance. * topic - the topic on which to publish the will. @@ -294,6 +366,49 @@ libmosq_EXPORT int mosquitto_reinitialise(struct mosquitto *mosq, const char *id */ libmosq_EXPORT int mosquitto_will_set(struct mosquitto *mosq, const char *topic, int payloadlen, const void *payload, int qos, bool retain); +/* + * Function: mosquitto_will_set_v5 + * + * Configure will information for a mosquitto instance, with attached + * properties. By default, clients do not have a will. This must be called + * before calling . + * + * If the mosquitto instance `mosq` is using MQTT v5, the `properties` argument + * will be applied to the Will. For MQTT v3.1.1 and below, the `properties` + * argument will be ignored. + * + * Set your client to use MQTT v5 immediately after it is created: + * + * mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5); + * + * Parameters: + * mosq - a valid mosquitto instance. + * topic - the topic on which to publish the will. + * payloadlen - the size of the payload (bytes). Valid values are between 0 and + * 268,435,455. + * payload - pointer to the data to send. If payloadlen > 0 this must be a + * valid memory location. + * qos - integer value 0, 1 or 2 indicating the Quality of Service to be + * used for the will. + * retain - set to true to make the will a retained message. + * properties - list of MQTT 5 properties. Can be NULL. On success only, the + * property list becomes the property of libmosquitto once this + * function is called and will be freed by the library. The + * property list must be freed by the application on error. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * MOSQ_ERR_PAYLOAD_SIZE - if payloadlen is too large. + * MOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8. + * MOSQ_ERR_NOT_SUPPORTED - if properties is not NULL and the client is not + * using MQTT v5 + * MOSQ_ERR_PROTOCOL - if a property is invalid for use with wills. + * MOSQ_ERR_DUPLICATE_PROPERTY - if a property is duplicated where it is forbidden. + */ +libmosq_EXPORT int mosquitto_will_set_v5(struct mosquitto *mosq, const char *topic, int payloadlen, const void *payload, int qos, bool retain, mosquitto_property *properties); + /* * Function: mosquitto_will_clear * @@ -309,14 +424,18 @@ libmosq_EXPORT int mosquitto_will_set(struct mosquitto *mosq, const char *topic, */ libmosq_EXPORT int mosquitto_will_clear(struct mosquitto *mosq); + +/* ====================================================================== + * + * Section: Username and password + * + * ====================================================================== */ /* * Function: mosquitto_username_pw_set * - * Configure username and password for a mosquitton instance. This is only - * supported by brokers that implement the MQTT spec v3.1. By default, no - * username or password will be sent. - * If username is NULL, the password argument is ignored. - * This must be called before calling mosquitto_connect(). + * Configure username and password for a mosquitto instance. By default, no + * username or password will be sent. For v3.1 and v3.1.1 clients, if username + * is NULL, the password argument is ignored. * * This is must be called before calling . * @@ -334,11 +453,21 @@ libmosq_EXPORT int mosquitto_will_clear(struct mosquitto *mosq); */ libmosq_EXPORT int mosquitto_username_pw_set(struct mosquitto *mosq, const char *username, const char *password); + +/* ====================================================================== + * + * Section: Connecting, reconnecting, disconnecting + * + * ====================================================================== */ /* * Function: mosquitto_connect * * Connect to an MQTT broker. * + * It is valid to use this function for clients using all MQTT protocol versions. + * If you need to set MQTT v5 CONNECT properties, use + * instead. + * * Parameters: * mosq - a valid mosquitto instance. * host - the hostname or ip address of the broker to connect to. @@ -349,7 +478,11 @@ libmosq_EXPORT int mosquitto_username_pw_set(struct mosquitto *mosq, const char * * Returns: * MOSQ_ERR_SUCCESS - on success. - * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_INVAL - if the input parameters were invalid, which could be any of: + * * mosq == NULL + * * host == NULL + * * port < 0 + * * keepalive < 5 * MOSQ_ERR_ERRNO - if a system call returned an error. The variable errno * contains the error code, even on Windows. * Use strerror_r() where available or FormatMessage() on @@ -375,7 +508,8 @@ libmosq_EXPORT int mosquitto_connect(struct mosquitto *mosq, const char *host, i * message to the client if no other messages have been exchanged * in that time. * bind_address - the hostname or ip address of the local network interface to - * bind to. + * bind to. If you do not want to bind to a specific interface, + * set this to NULL. * * Returns: * MOSQ_ERR_SUCCESS - on success. @@ -390,6 +524,57 @@ libmosq_EXPORT int mosquitto_connect(struct mosquitto *mosq, const char *host, i */ libmosq_EXPORT int mosquitto_connect_bind(struct mosquitto *mosq, const char *host, int port, int keepalive, const char *bind_address); +/* + * Function: mosquitto_connect_bind_v5 + * + * Connect to an MQTT broker. This extends the functionality of + * by adding the bind_address parameter and MQTT v5 + * properties. Use this function if you need to restrict network communication + * over a particular interface. + * + * Use e.g. and similar to create a list of + * properties, then attach them to this publish. Properties need freeing with + * . + * + * If the mosquitto instance `mosq` is using MQTT v5, the `properties` argument + * will be applied to the CONNECT message. For MQTT v3.1.1 and below, the + * `properties` argument will be ignored. + * + * Set your client to use MQTT v5 immediately after it is created: + * + * mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5); + * + * Parameters: + * mosq - a valid mosquitto instance. + * host - the hostname or ip address of the broker to connect to. + * port - the network port to connect to. Usually 1883. + * keepalive - the number of seconds after which the broker should send a PING + * message to the client if no other messages have been exchanged + * in that time. + * bind_address - the hostname or ip address of the local network interface to + * bind to. If you do not want to bind to a specific interface, + * set this to NULL. + * properties - the MQTT 5 properties for the connect (not for the Will). + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid, which could be any of: + * * mosq == NULL + * * host == NULL + * * port < 0 + * * keepalive < 5 + * MOSQ_ERR_ERRNO - if a system call returned an error. The variable errno + * contains the error code, even on Windows. + * Use strerror_r() where available or FormatMessage() on + * Windows. + * MOSQ_ERR_DUPLICATE_PROPERTY - if a property is duplicated where it is forbidden. + * MOSQ_ERR_PROTOCOL - if any property is invalid for use with CONNECT. + * + * See Also: + * , , + */ +libmosq_EXPORT int mosquitto_connect_bind_v5(struct mosquitto *mosq, const char *host, int port, int keepalive, const char *bind_address, const mosquitto_property *properties); + /* * Function: mosquitto_connect_async * @@ -443,11 +628,16 @@ libmosq_EXPORT int mosquitto_connect_async(struct mosquitto *mosq, const char *h * message to the client if no other messages have been exchanged * in that time. * bind_address - the hostname or ip address of the local network interface to - * bind to. + * bind to. If you do not want to bind to a specific interface, + * set this to NULL. * * Returns: * MOSQ_ERR_SUCCESS - on success. - * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_INVAL - if the input parameters were invalid, which could be any of: + * * mosq == NULL + * * host == NULL + * * port < 0 + * * keepalive < 5 * MOSQ_ERR_ERRNO - if a system call returned an error. The variable errno * contains the error code, even on Windows. * Use strerror_r() where available or FormatMessage() on @@ -461,29 +651,32 @@ libmosq_EXPORT int mosquitto_connect_bind_async(struct mosquitto *mosq, const ch /* * Function: mosquitto_connect_srv * - * Connect to an MQTT broker. This is a non-blocking call. If you use - * your client must use the threaded interface - * . If you need to use , you must use - * to connect the client. + * Connect to an MQTT broker. * - * This extends the functionality of by adding the - * bind_address parameter. Use this function if you need to restrict network - * communication over a particular interface. + * If you set `host` to `example.com`, then this call will attempt to retrieve + * the DNS SRV record for `_secure-mqtt._tcp.example.com` or + * `_mqtt._tcp.example.com` to discover which actual host to connect to. * - * May be called before or after . + * DNS SRV support is not usually compiled in to libmosquitto, use of this call + * is not recommended. * * Parameters: * mosq - a valid mosquitto instance. - * host - the hostname or ip address of the broker to connect to. + * host - the hostname to search for an SRV record. * keepalive - the number of seconds after which the broker should send a PING * message to the client if no other messages have been exchanged * in that time. * bind_address - the hostname or ip address of the local network interface to - * bind to. + * bind to. If you do not want to bind to a specific interface, + * set this to NULL. * * Returns: * MOSQ_ERR_SUCCESS - on success. - * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_INVAL - if the input parameters were invalid, which could be any of: + * * mosq == NULL + * * host == NULL + * * port < 0 + * * keepalive < 5 * MOSQ_ERR_ERRNO - if a system call returned an error. The variable errno * contains the error code, even on Windows. * Use strerror_r() where available or FormatMessage() on @@ -511,10 +704,6 @@ libmosq_EXPORT int mosquitto_connect_srv(struct mosquitto *mosq, const char *hos * MOSQ_ERR_SUCCESS - on success. * MOSQ_ERR_INVAL - if the input parameters were invalid. * MOSQ_ERR_NOMEM - if an out of memory condition occurred. - * - * Returns: - * MOSQ_ERR_SUCCESS - on success. - * MOSQ_ERR_INVAL - if the input parameters were invalid. * MOSQ_ERR_ERRNO - if a system call returned an error. The variable errno * contains the error code, even on Windows. * Use strerror_r() where available or FormatMessage() on @@ -542,10 +731,6 @@ libmosq_EXPORT int mosquitto_reconnect(struct mosquitto *mosq); * MOSQ_ERR_SUCCESS - on success. * MOSQ_ERR_INVAL - if the input parameters were invalid. * MOSQ_ERR_NOMEM - if an out of memory condition occurred. - * - * Returns: - * MOSQ_ERR_SUCCESS - on success. - * MOSQ_ERR_INVAL - if the input parameters were invalid. * MOSQ_ERR_ERRNO - if a system call returned an error. The variable errno * contains the error code, even on Windows. * Use strerror_r() where available or FormatMessage() on @@ -561,6 +746,10 @@ libmosq_EXPORT int mosquitto_reconnect_async(struct mosquitto *mosq); * * Disconnect from the broker. * + * It is valid to use this function for clients using all MQTT protocol versions. + * If you need to set MQTT v5 DISCONNECT properties, use + * instead. + * * Parameters: * mosq - a valid mosquitto instance. * @@ -571,11 +760,52 @@ libmosq_EXPORT int mosquitto_reconnect_async(struct mosquitto *mosq); */ libmosq_EXPORT int mosquitto_disconnect(struct mosquitto *mosq); +/* + * Function: mosquitto_disconnect_v5 + * + * Disconnect from the broker, with attached MQTT properties. + * + * Use e.g. and similar to create a list of + * properties, then attach them to this publish. Properties need freeing with + * . + * + * If the mosquitto instance `mosq` is using MQTT v5, the `properties` argument + * will be applied to the DISCONNECT message. For MQTT v3.1.1 and below, the + * `properties` argument will be ignored. + * + * Set your client to use MQTT v5 immediately after it is created: + * + * mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5); + * + * Parameters: + * mosq - a valid mosquitto instance. + * reason_code - the disconnect reason code. + * properties - a valid mosquitto_property list, or NULL. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. + * MOSQ_ERR_DUPLICATE_PROPERTY - if a property is duplicated where it is forbidden. + * MOSQ_ERR_PROTOCOL - if any property is invalid for use with DISCONNECT. + */ +libmosq_EXPORT int mosquitto_disconnect_v5(struct mosquitto *mosq, int reason_code, const mosquitto_property *properties); + + +/* ====================================================================== + * + * Section: Publishing, subscribing, unsubscribing + * + * ====================================================================== */ /* * Function: mosquitto_publish * * Publish a message on a given topic. * + * It is valid to use this function for clients using all MQTT protocol versions. + * If you need to set MQTT v5 PUBLISH properties, use + * instead. + * * Parameters: * mosq - a valid mosquitto instance. * mid - pointer to an int. If not NULL, the function will set this @@ -603,17 +833,89 @@ libmosq_EXPORT int mosquitto_disconnect(struct mosquitto *mosq); * broker. * MOSQ_ERR_PAYLOAD_SIZE - if payloadlen is too large. * MOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8 + * MOSQ_ERR_QOS_NOT_SUPPORTED - if the QoS is greater than that supported by + * the broker. + * MOSQ_ERR_OVERSIZE_PACKET - if the resulting packet would be larger than + * supported by the broker. * * See Also: * */ libmosq_EXPORT int mosquitto_publish(struct mosquitto *mosq, int *mid, const char *topic, int payloadlen, const void *payload, int qos, bool retain); + +/* + * Function: mosquitto_publish_v5 + * + * Publish a message on a given topic, with attached MQTT properties. + * + * Use e.g. and similar to create a list of + * properties, then attach them to this publish. Properties need freeing with + * . + * + * If the mosquitto instance `mosq` is using MQTT v5, the `properties` argument + * will be applied to the PUBLISH message. For MQTT v3.1.1 and below, the + * `properties` argument will be ignored. + * + * Set your client to use MQTT v5 immediately after it is created: + * + * mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5); + * + * Parameters: + * mosq - a valid mosquitto instance. + * mid - pointer to an int. If not NULL, the function will set this + * to the message id of this particular message. This can be then + * used with the publish callback to determine when the message + * has been sent. + * Note that although the MQTT protocol doesn't use message ids + * for messages with QoS=0, libmosquitto assigns them message ids + * so they can be tracked with this parameter. + * topic - null terminated string of the topic to publish to. + * payloadlen - the size of the payload (bytes). Valid values are between 0 and + * 268,435,455. + * payload - pointer to the data to send. If payloadlen > 0 this must be a + * valid memory location. + * qos - integer value 0, 1 or 2 indicating the Quality of Service to be + * used for the message. + * retain - set to true to make the message retained. + * properties - a valid mosquitto_property list, or NULL. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. + * MOSQ_ERR_PROTOCOL - if there is a protocol error communicating with the + * broker. + * MOSQ_ERR_PAYLOAD_SIZE - if payloadlen is too large. + * MOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8 + * MOSQ_ERR_DUPLICATE_PROPERTY - if a property is duplicated where it is forbidden. + * MOSQ_ERR_PROTOCOL - if any property is invalid for use with PUBLISH. + * MOSQ_ERR_QOS_NOT_SUPPORTED - if the QoS is greater than that supported by + * the broker. + * MOSQ_ERR_OVERSIZE_PACKET - if the resulting packet would be larger than + * supported by the broker. + */ +libmosq_EXPORT int mosquitto_publish_v5( + struct mosquitto *mosq, + int *mid, + const char *topic, + int payloadlen, + const void *payload, + int qos, + bool retain, + const mosquitto_property *properties); + + /* * Function: mosquitto_subscribe * * Subscribe to a topic. * + * It is valid to use this function for clients using all MQTT protocol versions. + * If you need to set MQTT v5 SUBSCRIBE properties, use + * instead. + * * Parameters: * mosq - a valid mosquitto instance. * mid - a pointer to an int. If not NULL, the function will set this to @@ -629,9 +931,88 @@ libmosq_EXPORT int mosquitto_publish(struct mosquitto *mosq, int *mid, const cha * MOSQ_ERR_NOMEM - if an out of memory condition occurred. * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. * MOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8 + * MOSQ_ERR_OVERSIZE_PACKET - if the resulting packet would be larger than + * supported by the broker. */ libmosq_EXPORT int mosquitto_subscribe(struct mosquitto *mosq, int *mid, const char *sub, int qos); +/* + * Function: mosquitto_subscribe_v5 + * + * Subscribe to a topic, with attached MQTT properties. + * + * Use e.g. and similar to create a list of + * properties, then attach them to this publish. Properties need freeing with + * . + * + * If the mosquitto instance `mosq` is using MQTT v5, the `properties` argument + * will be applied to the PUBLISH message. For MQTT v3.1.1 and below, the + * `properties` argument will be ignored. + * + * Set your client to use MQTT v5 immediately after it is created: + * + * mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5); + * + * Parameters: + * mosq - a valid mosquitto instance. + * mid - a pointer to an int. If not NULL, the function will set this to + * the message id of this particular message. This can be then used + * with the subscribe callback to determine when the message has been + * sent. + * sub - the subscription pattern. + * qos - the requested Quality of Service for this subscription. + * options - options to apply to this subscription, OR'd together. Set to 0 to + * use the default options, otherwise choose from list of + * properties - a valid mosquitto_property list, or NULL. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. + * MOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8 + * MOSQ_ERR_DUPLICATE_PROPERTY - if a property is duplicated where it is forbidden. + * MOSQ_ERR_PROTOCOL - if any property is invalid for use with SUBSCRIBE. + * MOSQ_ERR_OVERSIZE_PACKET - if the resulting packet would be larger than + * supported by the broker. + */ +libmosq_EXPORT int mosquitto_subscribe_v5(struct mosquitto *mosq, int *mid, const char *sub, int qos, int options, const mosquitto_property *properties); + +/* + * Function: mosquitto_subscribe_multiple + * + * Subscribe to multiple topics. + * + * Parameters: + * mosq - a valid mosquitto instance. + * mid - a pointer to an int. If not NULL, the function will set this to + * the message id of this particular message. This can be then used + * with the subscribe callback to determine when the message has been + * sent. + * sub_count - the count of subscriptions to be made + * sub - array of sub_count pointers, each pointing to a subscription string. + * The "char *const *const" datatype ensures that neither the array of + * pointers nor the strings that they point to are mutable. If you aren't + * familiar with this, just think of it as a safer "char **", + * equivalent to "const char *" for a simple string pointer. + * qos - the requested Quality of Service for each subscription. + * options - options to apply to this subscription, OR'd together. This + * argument is not used for MQTT v3 susbcriptions. Set to 0 to use + * the default options, otherwise choose from list of + * properties - a valid mosquitto_property list, or NULL. Only used with MQTT + * v5 clients. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. + * MOSQ_ERR_MALFORMED_UTF8 - if a topic is not valid UTF-8 + * MOSQ_ERR_OVERSIZE_PACKET - if the resulting packet would be larger than + * supported by the broker. + */ +libmosq_EXPORT int mosquitto_subscribe_multiple(struct mosquitto *mosq, int *mid, int sub_count, char *const *const sub, int qos, int options, const mosquitto_property *properties); + /* * Function: mosquitto_unsubscribe * @@ -651,9 +1032,92 @@ libmosq_EXPORT int mosquitto_subscribe(struct mosquitto *mosq, int *mid, const c * MOSQ_ERR_NOMEM - if an out of memory condition occurred. * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. * MOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8 + * MOSQ_ERR_OVERSIZE_PACKET - if the resulting packet would be larger than + * supported by the broker. */ libmosq_EXPORT int mosquitto_unsubscribe(struct mosquitto *mosq, int *mid, const char *sub); +/* + * Function: mosquitto_unsubscribe_v5 + * + * Unsubscribe from a topic, with attached MQTT properties. + * + * It is valid to use this function for clients using all MQTT protocol versions. + * If you need to set MQTT v5 UNSUBSCRIBE properties, use + * instead. + * + * Use e.g. and similar to create a list of + * properties, then attach them to this publish. Properties need freeing with + * . + * + * If the mosquitto instance `mosq` is using MQTT v5, the `properties` argument + * will be applied to the PUBLISH message. For MQTT v3.1.1 and below, the + * `properties` argument will be ignored. + * + * Set your client to use MQTT v5 immediately after it is created: + * + * mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5); + * + * Parameters: + * mosq - a valid mosquitto instance. + * mid - a pointer to an int. If not NULL, the function will set this to + * the message id of this particular message. This can be then used + * with the unsubscribe callback to determine when the message has been + * sent. + * sub - the unsubscription pattern. + * properties - a valid mosquitto_property list, or NULL. Only used with MQTT + * v5 clients. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. + * MOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8 + * MOSQ_ERR_DUPLICATE_PROPERTY - if a property is duplicated where it is forbidden. + * MOSQ_ERR_PROTOCOL - if any property is invalid for use with UNSUBSCRIBE. + * MOSQ_ERR_OVERSIZE_PACKET - if the resulting packet would be larger than + * supported by the broker. + */ +libmosq_EXPORT int mosquitto_unsubscribe_v5(struct mosquitto *mosq, int *mid, const char *sub, const mosquitto_property *properties); + +/* + * Function: mosquitto_unsubscribe_multiple + * + * Unsubscribe from multiple topics. + * + * Parameters: + * mosq - a valid mosquitto instance. + * mid - a pointer to an int. If not NULL, the function will set this to + * the message id of this particular message. This can be then used + * with the subscribe callback to determine when the message has been + * sent. + * sub_count - the count of unsubscriptions to be made + * sub - array of sub_count pointers, each pointing to an unsubscription string. + * The "char *const *const" datatype ensures that neither the array of + * pointers nor the strings that they point to are mutable. If you aren't + * familiar with this, just think of it as a safer "char **", + * equivalent to "const char *" for a simple string pointer. + * properties - a valid mosquitto_property list, or NULL. Only used with MQTT + * v5 clients. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. + * MOSQ_ERR_MALFORMED_UTF8 - if a topic is not valid UTF-8 + * MOSQ_ERR_OVERSIZE_PACKET - if the resulting packet would be larger than + * supported by the broker. + */ +libmosq_EXPORT int mosquitto_unsubscribe_multiple(struct mosquitto *mosq, int *mid, int sub_count, char *const *const sub, const mosquitto_property *properties); + + +/* ====================================================================== + * + * Section: Struct mosquitto_message helper functions + * + * ====================================================================== */ /* * Function: mosquitto_message_copy * @@ -700,51 +1164,21 @@ libmosq_EXPORT void mosquitto_message_free(struct mosquitto_message **message); */ libmosq_EXPORT void mosquitto_message_free_contents(struct mosquitto_message *message); -/* - * Function: mosquitto_loop + +/* ====================================================================== * - * The main network loop for the client. You must call this frequently in order - * to keep communications between the client and broker working. If incoming - * data is present it will then be processed. Outgoing commands, from e.g. - * , are normally sent immediately that their function is - * called, but this is not always possible. will also attempt - * to send any remaining outgoing messages, which also includes commands that - * are part of the flow for messages with QoS>0. + * Section: Network loop (managed by libmosquitto) * - * An alternative approach is to use to run the client - * loop in its own thread. + * The internal network loop must be called at a regular interval. The two + * recommended approaches are to use either or + * . is a blocking call and is + * suitable for the situation where you only want to handle incoming messages + * in callbacks. is a non-blocking call, it creates a + * separate thread to run the loop for you. Use this function when you have + * other tasks you need to run at the same time as the MQTT client, e.g. + * reading data from a sensor. * - * This calls select() to monitor the client network socket. If you want to - * integrate mosquitto client operation with your own select() call, use - * , , and - * . - * - * Threads: - * - * Parameters: - * mosq - a valid mosquitto instance. - * timeout - Maximum number of milliseconds to wait for network activity - * in the select() call before timing out. Set to 0 for instant - * return. Set negative to use the default of 1000ms. - * max_packets - this parameter is currently unused and should be set to 1 for - * future compatibility. - * - * Returns: - * MOSQ_ERR_SUCCESS - on success. - * MOSQ_ERR_INVAL - if the input parameters were invalid. - * MOSQ_ERR_NOMEM - if an out of memory condition occurred. - * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. - * MOSQ_ERR_CONN_LOST - if the connection to the broker was lost. - * MOSQ_ERR_PROTOCOL - if there is a protocol error communicating with the - * broker. - * MOSQ_ERR_ERRNO - if a system call returned an error. The variable errno - * contains the error code, even on Windows. - * Use strerror_r() where available or FormatMessage() on - * Windows. - * See Also: - * , , - */ -libmosq_EXPORT int mosquitto_loop(struct mosquitto *mosq, int timeout, int max_packets); + * ====================================================================== */ /* * Function: mosquitto_loop_forever @@ -827,19 +1261,57 @@ libmosq_EXPORT int mosquitto_loop_start(struct mosquitto *mosq); libmosq_EXPORT int mosquitto_loop_stop(struct mosquitto *mosq, bool force); /* - * Function: mosquitto_socket + * Function: mosquitto_loop * - * Return the socket handle for a mosquitto instance. Useful if you want to - * include a mosquitto client in your own select() calls. + * The main network loop for the client. This must be called frequently + * to keep communications between the client and broker working. This is + * carried out by and , which + * are the recommended ways of handling the network loop. You may also use this + * function if you wish. It must not be called inside a callback. + * + * If incoming data is present it will then be processed. Outgoing commands, + * from e.g. , are normally sent immediately that their + * function is called, but this is not always possible. will + * also attempt to send any remaining outgoing messages, which also includes + * commands that are part of the flow for messages with QoS>0. + * + * This calls select() to monitor the client network socket. If you want to + * integrate mosquitto client operation with your own select() call, use + * , , and + * . + * + * Threads: * * Parameters: - * mosq - a valid mosquitto instance. + * mosq - a valid mosquitto instance. + * timeout - Maximum number of milliseconds to wait for network activity + * in the select() call before timing out. Set to 0 for instant + * return. Set negative to use the default of 1000ms. + * max_packets - this parameter is currently unused and should be set to 1 for + * future compatibility. * * Returns: - * The socket for the mosquitto client or -1 on failure. + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. + * MOSQ_ERR_CONN_LOST - if the connection to the broker was lost. + * MOSQ_ERR_PROTOCOL - if there is a protocol error communicating with the + * broker. + * MOSQ_ERR_ERRNO - if a system call returned an error. The variable errno + * contains the error code, even on Windows. + * Use strerror_r() where available or FormatMessage() on + * Windows. + * See Also: + * , , */ -libmosq_EXPORT int mosquitto_socket(struct mosquitto *mosq); +libmosq_EXPORT int mosquitto_loop(struct mosquitto *mosq, int timeout, int max_packets); +/* ====================================================================== + * + * Section: Network loop (for use in other event loops) + * + * ====================================================================== */ /* * Function: mosquitto_loop_read * @@ -908,7 +1380,8 @@ libmosq_EXPORT int mosquitto_loop_write(struct mosquitto *mosq, int max_packets) * monitoring the client network socket for activity yourself. * * This function deals with handling PINGs and checking whether messages need - * to be retried, so should be called fairly frequently. + * to be retried, so should be called fairly frequently, around once per second + * is sufficient. * * Parameters: * mosq - a valid mosquitto instance. @@ -923,6 +1396,26 @@ libmosq_EXPORT int mosquitto_loop_write(struct mosquitto *mosq, int max_packets) */ libmosq_EXPORT int mosquitto_loop_misc(struct mosquitto *mosq); + +/* ====================================================================== + * + * Section: Network loop (helper functions) + * + * ====================================================================== */ +/* + * Function: mosquitto_socket + * + * Return the socket handle for a mosquitto instance. Useful if you want to + * include a mosquitto client in your own select() calls. + * + * Parameters: + * mosq - a valid mosquitto instance. + * + * Returns: + * The socket for the mosquitto client or -1 on failure. + */ +libmosq_EXPORT int mosquitto_socket(struct mosquitto *mosq); + /* * Function: mosquitto_want_write * @@ -953,44 +1446,277 @@ libmosq_EXPORT bool mosquitto_want_write(struct mosquitto *mosq); */ libmosq_EXPORT int mosquitto_threaded_set(struct mosquitto *mosq, bool threaded); + +/* ====================================================================== + * + * Section: Client options + * + * ====================================================================== */ /* * Function: mosquitto_opts_set * * Used to set options for the client. * + * This function is deprecated, the replacement , + * and functions should + * be used instead. + * * Parameters: * mosq - a valid mosquitto instance. * option - the option to set. * value - the option specific value. * * Options: - * MOSQ_OPT_PROTOCOL_VERSION - * Value must be an int, set to either MQTT_PROTOCOL_V31 or - * MQTT_PROTOCOL_V311. Must be set before the client connects. + * MOSQ_OPT_PROTOCOL_VERSION - Value must be an int, set to either + * MQTT_PROTOCOL_V31 or MQTT_PROTOCOL_V311. Must be set + * before the client connects. * Defaults to MQTT_PROTOCOL_V31. * - * MOSQ_OPT_SSL_CTX - * Pass an openssl SSL_CTX to be used when creating TLS connections - * rather than libmosquitto creating its own. This must be called - * before connecting to have any effect. If you use this option, the - * onus is on you to ensure that you are using secure settings. + * MOSQ_OPT_SSL_CTX - Pass an openssl SSL_CTX to be used when creating + * TLS connections rather than libmosquitto creating its own. + * This must be called before connecting to have any effect. + * If you use this option, the onus is on you to ensure that + * you are using secure settings. * Setting to NULL means that libmosquitto will use its own SSL_CTX * if TLS is to be used. * This option is only available for openssl 1.1.0 and higher. * - * MOSQ_OPT_SSL_CTX_WITH_DEFAULTS - * Value must be an int set to 1 or 0. If set to 1, then the user - * specified SSL_CTX passed in using MOSQ_OPT_SSL_CTX will have the - * default options applied to it. This means that you only need to - * change the values that are relevant to you. If you use this - * option then you must configure the TLS options as normal, i.e. - * you should use to configure the cafile/capath - * as a minimum. + * MOSQ_OPT_SSL_CTX_WITH_DEFAULTS - Value must be an int set to 1 or 0. + * If set to 1, then the user specified SSL_CTX passed in using + * MOSQ_OPT_SSL_CTX will have the default options applied to it. + * This means that you only need to change the values that are + * relevant to you. If you use this option then you must configure + * the TLS options as normal, i.e. you should use + * to configure the cafile/capath as a minimum. * This option is only available for openssl 1.1.0 and higher. */ libmosq_EXPORT int mosquitto_opts_set(struct mosquitto *mosq, enum mosq_opt_t option, void *value); +/* + * Function: mosquitto_int_option + * + * Used to set integer options for the client. + * + * Parameters: + * mosq - a valid mosquitto instance. + * option - the option to set. + * value - the option specific value. + * + * Options: + * MOSQ_OPT_TCP_NODELAY - Set to 1 to disable Nagle's algorithm on client + * sockets. This has the effect of reducing latency of individual + * messages at the potential cost of increasing the number of + * packets being sent. + * Defaults to 0, which means Nagle remains enabled. + * + * MOSQ_OPT_PROTOCOL_VERSION - Value must be set to either MQTT_PROTOCOL_V31, + * MQTT_PROTOCOL_V311, or MQTT_PROTOCOL_V5. Must be set before the + * client connects. Defaults to MQTT_PROTOCOL_V311. + * + * MOSQ_OPT_RECEIVE_MAXIMUM - Value can be set between 1 and 65535 inclusive, + * and represents the maximum number of incoming QoS 1 and QoS 2 + * messages that this client wants to process at once. Defaults to + * 20. This option is not valid for MQTT v3.1 or v3.1.1 clients. + * Note that if the MQTT_PROP_RECEIVE_MAXIMUM property is in the + * proplist passed to mosquitto_connect_v5(), then that property + * will override this option. Using this option is the recommended + * method however. + * + * MOSQ_OPT_SEND_MAXIMUM - Value can be set between 1 and 65535 inclusive, + * and represents the maximum number of outgoing QoS 1 and QoS 2 + * messages that this client will attempt to have "in flight" at + * once. Defaults to 20. + * This option is not valid for MQTT v3.1 or v3.1.1 clients. + * Note that if the broker being connected to sends a + * MQTT_PROP_RECEIVE_MAXIMUM property that has a lower value than + * this option, then the broker provided value will be used. + * + * MOSQ_OPT_SSL_CTX_WITH_DEFAULTS - If value is set to a non zero value, + * then the user specified SSL_CTX passed in using MOSQ_OPT_SSL_CTX + * will have the default options applied to it. This means that + * you only need to change the values that are relevant to you. + * If you use this option then you must configure the TLS options + * as normal, i.e. you should use to + * configure the cafile/capath as a minimum. + * This option is only available for openssl 1.1.0 and higher. + * + * MOSQ_OPT_TLS_OCSP_REQUIRED - Set whether OCSP checking on TLS + * connections is required. Set to 1 to enable checking, + * or 0 (the default) for no checking. + * + * MOSQ_OPT_TLS_USE_OS_CERTS - Set to 1 to instruct the client to load and + * trust OS provided CA certificates for use with TLS connections. + * Set to 0 (the default) to only use manually specified CA certs. + */ +libmosq_EXPORT int mosquitto_int_option(struct mosquitto *mosq, enum mosq_opt_t option, int value); + + +/* + * Function: mosquitto_string_option + * + * Used to set const char* options for the client. + * + * Parameters: + * mosq - a valid mosquitto instance. + * option - the option to set. + * value - the option specific value. + * + * Options: + * MOSQ_OPT_TLS_ENGINE - Configure the client for TLS Engine support. + * Pass a TLS Engine ID to be used when creating TLS + * connections. Must be set before . + * + * MOSQ_OPT_TLS_KEYFORM - Configure the client to treat the keyfile + * differently depending on its type. Must be set + * before . + * Set as either "pem" or "engine", to determine from where the + * private key for a TLS connection will be obtained. Defaults to + * "pem", a normal private key file. + * + * MOSQ_OPT_TLS_KPASS_SHA1 - Where the TLS Engine requires the use of + * a password to be accessed, this option allows a hex encoded + * SHA1 hash of the private key password to be passed to the + * engine directly. Must be set before . + * + * MOSQ_OPT_TLS_ALPN - If the broker being connected to has multiple + * services available on a single TLS port, such as both MQTT + * and WebSockets, use this option to configure the ALPN + * option for the connection. + * + * MOSQ_OPT_BIND_ADDRESS - Set the hostname or ip address of the local network + * interface to bind to when connecting. + */ +libmosq_EXPORT int mosquitto_string_option(struct mosquitto *mosq, enum mosq_opt_t option, const char *value); + + +/* + * Function: mosquitto_void_option + * + * Used to set void* options for the client. + * + * Parameters: + * mosq - a valid mosquitto instance. + * option - the option to set. + * value - the option specific value. + * + * Options: + * MOSQ_OPT_SSL_CTX - Pass an openssl SSL_CTX to be used when creating TLS + * connections rather than libmosquitto creating its own. This must + * be called before connecting to have any effect. If you use this + * option, the onus is on you to ensure that you are using secure + * settings. + * Setting to NULL means that libmosquitto will use its own SSL_CTX + * if TLS is to be used. + * This option is only available for openssl 1.1.0 and higher. + */ +libmosq_EXPORT int mosquitto_void_option(struct mosquitto *mosq, enum mosq_opt_t option, void *value); + +/* + * Function: mosquitto_reconnect_delay_set + * + * Control the behaviour of the client when it has unexpectedly disconnected in + * or after . The default + * behaviour if this function is not used is to repeatedly attempt to reconnect + * with a delay of 1 second until the connection succeeds. + * + * Use reconnect_delay parameter to change the delay between successive + * reconnection attempts. You may also enable exponential backoff of the time + * between reconnections by setting reconnect_exponential_backoff to true and + * set an upper bound on the delay with reconnect_delay_max. + * + * Example 1: + * delay=2, delay_max=10, exponential_backoff=False + * Delays would be: 2, 4, 6, 8, 10, 10, ... + * + * Example 2: + * delay=3, delay_max=30, exponential_backoff=True + * Delays would be: 3, 6, 12, 24, 30, 30, ... + * + * Parameters: + * mosq - a valid mosquitto instance. + * reconnect_delay - the number of seconds to wait between + * reconnects. + * reconnect_delay_max - the maximum number of seconds to wait + * between reconnects. + * reconnect_exponential_backoff - use exponential backoff between + * reconnect attempts. Set to true to enable + * exponential backoff. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + */ +libmosq_EXPORT int mosquitto_reconnect_delay_set(struct mosquitto *mosq, unsigned int reconnect_delay, unsigned int reconnect_delay_max, bool reconnect_exponential_backoff); + +/* + * Function: mosquitto_max_inflight_messages_set + * + * This function is deprected. Use the function with the + * MOSQ_OPT_SEND_MAXIMUM option instead. + * + * Set the number of QoS 1 and 2 messages that can be "in flight" at one time. + * An in flight message is part way through its delivery flow. Attempts to send + * further messages with will result in the messages being + * queued until the number of in flight messages reduces. + * + * A higher number here results in greater message throughput, but if set + * higher than the maximum in flight messages on the broker may lead to + * delays in the messages being acknowledged. + * + * Set to 0 for no maximum. + * + * Parameters: + * mosq - a valid mosquitto instance. + * max_inflight_messages - the maximum number of inflight messages. Defaults + * to 20. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + */ +libmosq_EXPORT int mosquitto_max_inflight_messages_set(struct mosquitto *mosq, unsigned int max_inflight_messages); + +/* + * Function: mosquitto_message_retry_set + * + * This function now has no effect. + */ +libmosq_EXPORT void mosquitto_message_retry_set(struct mosquitto *mosq, unsigned int message_retry); + +/* + * Function: mosquitto_user_data_set + * + * When is called, the pointer given as the "obj" parameter + * will be passed to the callbacks as user data. The + * function allows this obj parameter to be updated at any time. This function + * will not modify the memory pointed to by the current user data pointer. If + * it is dynamically allocated memory you must free it yourself. + * + * Parameters: + * mosq - a valid mosquitto instance. + * obj - A user pointer that will be passed as an argument to any callbacks + * that are specified. + */ +libmosq_EXPORT void mosquitto_user_data_set(struct mosquitto *mosq, void *obj); + +/* Function: mosquitto_userdata + * + * Retrieve the "userdata" variable for a mosquitto client. + * + * Parameters: + * mosq - a valid mosquitto instance. + * + * Returns: + * A pointer to the userdata member variable. + */ +libmosq_EXPORT void *mosquitto_userdata(struct mosquitto *mosq); + +/* ====================================================================== + * + * Section: TLS support + * + * ====================================================================== */ /* * Function: mosquitto_tls_set * @@ -1132,10 +1858,33 @@ libmosq_EXPORT int mosquitto_tls_opts_set(struct mosquitto *mosq, int cert_reqs, */ libmosq_EXPORT int mosquitto_tls_psk_set(struct mosquitto *mosq, const char *psk, const char *identity, const char *ciphers); + +/* + * Function: mosquitto_ssl_get + * + * Retrieve a pointer to the SSL structure used for TLS connections in this + * client. This can be used in e.g. the connect callback to carry out + * additional verification steps. + * + * Parameters: + * mosq - a valid mosquitto instance + * + * Returns: + * A valid pointer to an openssl SSL structure - if the client is using TLS. + * NULL - if the client is not using TLS, or TLS support is not compiled in. + */ +libmosq_EXPORT void *mosquitto_ssl_get(struct mosquitto *mosq); + + +/* ====================================================================== + * + * Section: Callbacks + * + * ====================================================================== */ /* * Function: mosquitto_connect_callback_set * - * Set the connect callback. This is called when the broker sends a CONNACK + * Set the connect callback. This is called when the library receives a CONNACK * message in response to a connection. * * Parameters: @@ -1146,20 +1895,17 @@ libmosq_EXPORT int mosquitto_tls_psk_set(struct mosquitto *mosq, const char *psk * Callback Parameters: * mosq - the mosquitto instance making the callback. * obj - the user data provided in - * rc - the return code of the connection response, one of: - * - * * 0 - success - * * 1 - connection refused (unacceptable protocol version) - * * 2 - connection refused (identifier rejected) - * * 3 - connection refused (broker unavailable) - * * 4-255 - reserved for future use + * rc - the return code of the connection response. The values are defined by + * the MQTT protocol version in use. + * For MQTT v5.0, look at section 3.2.2.2 Connect Reason code: https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html + * For MQTT v3.1.1, look at section 3.2.2.3 Connect Return code: http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/mqtt-v3.1.1.html */ libmosq_EXPORT void mosquitto_connect_callback_set(struct mosquitto *mosq, void (*on_connect)(struct mosquitto *, void *, int)); /* * Function: mosquitto_connect_with_flags_callback_set * - * Set the connect callback. This is called when the broker sends a CONNACK + * Set the connect callback. This is called when the library receives a CONNACK * message in response to a connection. * * Parameters: @@ -1170,42 +1916,100 @@ libmosq_EXPORT void mosquitto_connect_callback_set(struct mosquitto *mosq, void * Callback Parameters: * mosq - the mosquitto instance making the callback. * obj - the user data provided in - * rc - the return code of the connection response, one of: + * rc - the return code of the connection response. The values are defined by + * the MQTT protocol version in use. + * For MQTT v5.0, look at section 3.2.2.2 Connect Reason code: https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html + * For MQTT v3.1.1, look at section 3.2.2.3 Connect Return code: http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/mqtt-v3.1.1.html * flags - the connect flags. - * - * * 0 - success - * * 1 - connection refused (unacceptable protocol version) - * * 2 - connection refused (identifier rejected) - * * 3 - connection refused (broker unavailable) - * * 4-255 - reserved for future use */ libmosq_EXPORT void mosquitto_connect_with_flags_callback_set(struct mosquitto *mosq, void (*on_connect)(struct mosquitto *, void *, int, int)); /* - * Function: mosquitto_disconnect_callback_set + * Function: mosquitto_connect_v5_callback_set * - * Set the disconnect callback. This is called when the broker has received the - * DISCONNECT command and has disconnected the client. + * Set the connect callback. This is called when the library receives a CONNACK + * message in response to a connection. + * + * It is valid to set this callback for all MQTT protocol versions. If it is + * used with MQTT clients that use MQTT v3.1.1 or earlier, then the `props` + * argument will always be NULL. * * Parameters: - * mosq - a valid mosquitto instance. - * on_disconnect - a callback function in the following form: - * void callback(struct mosquitto *mosq, void *obj) + * mosq - a valid mosquitto instance. + * on_connect - a callback function in the following form: + * void callback(struct mosquitto *mosq, void *obj, int rc) * * Callback Parameters: * mosq - the mosquitto instance making the callback. - * obj - the user data provided in + * obj - the user data provided in + * rc - the return code of the connection response. The values are defined by + * the MQTT protocol version in use. + * For MQTT v5.0, look at section 3.2.2.2 Connect Reason code: https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html + * For MQTT v3.1.1, look at section 3.2.2.3 Connect Return code: http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/mqtt-v3.1.1.html + * flags - the connect flags. + * props - list of MQTT 5 properties, or NULL + * + */ +libmosq_EXPORT void mosquitto_connect_v5_callback_set(struct mosquitto *mosq, void (*on_connect)(struct mosquitto *, void *, int, int, const mosquitto_property *props)); + +/* + * Function: mosquitto_disconnect_callback_set + * + * Set the disconnect callback. This is called when the broker has received the + * DISCONNECT command and has disconnected the client. + * + * Parameters: + * mosq - a valid mosquitto instance. + * on_disconnect - a callback function in the following form: + * void callback(struct mosquitto *mosq, void *obj) + * + * Callback Parameters: + * mosq - the mosquitto instance making the callback. + * obj - the user data provided in * rc - integer value indicating the reason for the disconnect. A value of 0 * means the client has called . Any other value * indicates that the disconnect is unexpected. */ libmosq_EXPORT void mosquitto_disconnect_callback_set(struct mosquitto *mosq, void (*on_disconnect)(struct mosquitto *, void *, int)); +/* + * Function: mosquitto_disconnect_v5_callback_set + * + * Set the disconnect callback. This is called when the broker has received the + * DISCONNECT command and has disconnected the client. + * + * It is valid to set this callback for all MQTT protocol versions. If it is + * used with MQTT clients that use MQTT v3.1.1 or earlier, then the `props` + * argument will always be NULL. + * + * Parameters: + * mosq - a valid mosquitto instance. + * on_disconnect - a callback function in the following form: + * void callback(struct mosquitto *mosq, void *obj) + * + * Callback Parameters: + * mosq - the mosquitto instance making the callback. + * obj - the user data provided in + * rc - integer value indicating the reason for the disconnect. A value of 0 + * means the client has called . Any other value + * indicates that the disconnect is unexpected. + * props - list of MQTT 5 properties, or NULL + */ +libmosq_EXPORT void mosquitto_disconnect_v5_callback_set(struct mosquitto *mosq, void (*on_disconnect)(struct mosquitto *, void *, int, const mosquitto_property *props)); + /* * Function: mosquitto_publish_callback_set * * Set the publish callback. This is called when a message initiated with - * has been sent to the broker successfully. + * has been sent to the broker. "Sent" means different + * things depending on the QoS of the message: + * + * QoS 0: The PUBLISH was passed to the local operating system for delivery, + * there is no guarantee that it was delivered to the remote broker. + * QoS 1: The PUBLISH was sent to the remote broker and the corresponding + * PUBACK was received by the library. + * QoS 2: The PUBLISH was sent to the remote broker and the corresponding + * PUBCOMP was received by the library. * * Parameters: * mosq - a valid mosquitto instance. @@ -1219,11 +2023,46 @@ libmosq_EXPORT void mosquitto_disconnect_callback_set(struct mosquitto *mosq, vo */ libmosq_EXPORT void mosquitto_publish_callback_set(struct mosquitto *mosq, void (*on_publish)(struct mosquitto *, void *, int)); +/* + * Function: mosquitto_publish_v5_callback_set + * + * Set the publish callback. This is called when a message initiated with + * has been sent to the broker. This callback will be + * called both if the message is sent successfully, or if the broker responded + * with an error, which will be reflected in the reason_code parameter. + * "Sent" means different things depending on the QoS of the message: + * + * QoS 0: The PUBLISH was passed to the local operating system for delivery, + * there is no guarantee that it was delivered to the remote broker. + * QoS 1: The PUBLISH was sent to the remote broker and the corresponding + * PUBACK was received by the library. + * QoS 2: The PUBLISH was sent to the remote broker and the corresponding + * PUBCOMP was received by the library. + * + * + * It is valid to set this callback for all MQTT protocol versions. If it is + * used with MQTT clients that use MQTT v3.1.1 or earlier, then the `props` + * argument will always be NULL. + * + * Parameters: + * mosq - a valid mosquitto instance. + * on_publish - a callback function in the following form: + * void callback(struct mosquitto *mosq, void *obj, int mid) + * + * Callback Parameters: + * mosq - the mosquitto instance making the callback. + * obj - the user data provided in + * mid - the message id of the sent message. + * reason_code - the MQTT 5 reason code + * props - list of MQTT 5 properties, or NULL + */ +libmosq_EXPORT void mosquitto_publish_v5_callback_set(struct mosquitto *mosq, void (*on_publish)(struct mosquitto *, void *, int, int, const mosquitto_property *props)); + /* * Function: mosquitto_message_callback_set * * Set the message callback. This is called when a message is received from the - * broker. + * broker and the required QoS flow has completed. * * Parameters: * mosq - a valid mosquitto instance. @@ -1242,11 +2081,39 @@ libmosq_EXPORT void mosquitto_publish_callback_set(struct mosquitto *mosq, void */ libmosq_EXPORT void mosquitto_message_callback_set(struct mosquitto *mosq, void (*on_message)(struct mosquitto *, void *, const struct mosquitto_message *)); +/* + * Function: mosquitto_message_v5_callback_set + * + * Set the message callback. This is called when a message is received from the + * broker and the required QoS flow has completed. + * + * It is valid to set this callback for all MQTT protocol versions. If it is + * used with MQTT clients that use MQTT v3.1.1 or earlier, then the `props` + * argument will always be NULL. + * + * Parameters: + * mosq - a valid mosquitto instance. + * on_message - a callback function in the following form: + * void callback(struct mosquitto *mosq, void *obj, const struct mosquitto_message *message) + * + * Callback Parameters: + * mosq - the mosquitto instance making the callback. + * obj - the user data provided in + * message - the message data. This variable and associated memory will be + * freed by the library after the callback completes. The client + * should make copies of any of the data it requires. + * props - list of MQTT 5 properties, or NULL + * + * See Also: + * + */ +libmosq_EXPORT void mosquitto_message_v5_callback_set(struct mosquitto *mosq, void (*on_message)(struct mosquitto *, void *, const struct mosquitto_message *, const mosquitto_property *props)); + /* * Function: mosquitto_subscribe_callback_set * - * Set the subscribe callback. This is called when the broker responds to a - * subscription request. + * Set the subscribe callback. This is called when the library receives a + * SUBACK message in response to a SUBSCRIBE. * * Parameters: * mosq - a valid mosquitto instance. @@ -1263,11 +2130,37 @@ libmosq_EXPORT void mosquitto_message_callback_set(struct mosquitto *mosq, void */ libmosq_EXPORT void mosquitto_subscribe_callback_set(struct mosquitto *mosq, void (*on_subscribe)(struct mosquitto *, void *, int, int, const int *)); +/* + * Function: mosquitto_subscribe_v5_callback_set + * + * Set the subscribe callback. This is called when the library receives a + * SUBACK message in response to a SUBSCRIBE. + * + * It is valid to set this callback for all MQTT protocol versions. If it is + * used with MQTT clients that use MQTT v3.1.1 or earlier, then the `props` + * argument will always be NULL. + * + * Parameters: + * mosq - a valid mosquitto instance. + * on_subscribe - a callback function in the following form: + * void callback(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos) + * + * Callback Parameters: + * mosq - the mosquitto instance making the callback. + * obj - the user data provided in + * mid - the message id of the subscribe message. + * qos_count - the number of granted subscriptions (size of granted_qos). + * granted_qos - an array of integers indicating the granted QoS for each of + * the subscriptions. + * props - list of MQTT 5 properties, or NULL + */ +libmosq_EXPORT void mosquitto_subscribe_v5_callback_set(struct mosquitto *mosq, void (*on_subscribe)(struct mosquitto *, void *, int, int, const int *, const mosquitto_property *props)); + /* * Function: mosquitto_unsubscribe_callback_set * - * Set the unsubscribe callback. This is called when the broker responds to a - * unsubscription request. + * Set the unsubscribe callback. This is called when the library receives a + * UNSUBACK message in response to an UNSUBSCRIBE. * * Parameters: * mosq - a valid mosquitto instance. @@ -1281,6 +2174,29 @@ libmosq_EXPORT void mosquitto_subscribe_callback_set(struct mosquitto *mosq, voi */ libmosq_EXPORT void mosquitto_unsubscribe_callback_set(struct mosquitto *mosq, void (*on_unsubscribe)(struct mosquitto *, void *, int)); +/* + * Function: mosquitto_unsubscribe_v5_callback_set + * + * Set the unsubscribe callback. This is called when the library receives a + * UNSUBACK message in response to an UNSUBSCRIBE. + * + * It is valid to set this callback for all MQTT protocol versions. If it is + * used with MQTT clients that use MQTT v3.1.1 or earlier, then the `props` + * argument will always be NULL. + * + * Parameters: + * mosq - a valid mosquitto instance. + * on_unsubscribe - a callback function in the following form: + * void callback(struct mosquitto *mosq, void *obj, int mid) + * + * Callback Parameters: + * mosq - the mosquitto instance making the callback. + * obj - the user data provided in + * mid - the message id of the unsubscribe message. + * props - list of MQTT 5 properties, or NULL + */ +libmosq_EXPORT void mosquitto_unsubscribe_v5_callback_set(struct mosquitto *mosq, void (*on_unsubscribe)(struct mosquitto *, void *, int, const mosquitto_property *props)); + /* * Function: mosquitto_log_callback_set * @@ -1304,90 +2220,6 @@ libmosq_EXPORT void mosquitto_unsubscribe_callback_set(struct mosquitto *mosq, v */ libmosq_EXPORT void mosquitto_log_callback_set(struct mosquitto *mosq, void (*on_log)(struct mosquitto *, void *, int, const char *)); -/* - * Function: mosquitto_reconnect_delay_set - * - * Control the behaviour of the client when it has unexpectedly disconnected in - * or after . The default - * behaviour if this function is not used is to repeatedly attempt to reconnect - * with a delay of 1 second until the connection succeeds. - * - * Use reconnect_delay parameter to change the delay between successive - * reconnection attempts. You may also enable exponential backoff of the time - * between reconnections by setting reconnect_exponential_backoff to true and - * set an upper bound on the delay with reconnect_delay_max. - * - * Example 1: - * delay=2, delay_max=10, exponential_backoff=False - * Delays would be: 2, 4, 6, 8, 10, 10, ... - * - * Example 2: - * delay=3, delay_max=30, exponential_backoff=True - * Delays would be: 3, 6, 12, 24, 30, 30, ... - * - * Parameters: - * mosq - a valid mosquitto instance. - * reconnect_delay - the number of seconds to wait between - * reconnects. - * reconnect_delay_max - the maximum number of seconds to wait - * between reconnects. - * reconnect_exponential_backoff - use exponential backoff between - * reconnect attempts. Set to true to enable - * exponential backoff. - * - * Returns: - * MOSQ_ERR_SUCCESS - on success. - * MOSQ_ERR_INVAL - if the input parameters were invalid. - */ -libmosq_EXPORT int mosquitto_reconnect_delay_set(struct mosquitto *mosq, unsigned int reconnect_delay, unsigned int reconnect_delay_max, bool reconnect_exponential_backoff); - -/* - * Function: mosquitto_max_inflight_messages_set - * - * Set the number of QoS 1 and 2 messages that can be "in flight" at one time. - * An in flight message is part way through its delivery flow. Attempts to send - * further messages with will result in the messages being - * queued until the number of in flight messages reduces. - * - * A higher number here results in greater message throughput, but if set - * higher than the maximum in flight messages on the broker may lead to - * delays in the messages being acknowledged. - * - * Set to 0 for no maximum. - * - * Parameters: - * mosq - a valid mosquitto instance. - * max_inflight_messages - the maximum number of inflight messages. Defaults - * to 20. - * - * Returns: - * MOSQ_ERR_SUCCESS - on success. - * MOSQ_ERR_INVAL - if the input parameters were invalid. - */ -libmosq_EXPORT int mosquitto_max_inflight_messages_set(struct mosquitto *mosq, unsigned int max_inflight_messages); - -/* - * Function: mosquitto_message_retry_set - * - * This function now has no effect. - */ -libmosq_EXPORT void mosquitto_message_retry_set(struct mosquitto *mosq, unsigned int message_retry); - -/* - * Function: mosquitto_user_data_set - * - * When is called, the pointer given as the "obj" parameter - * will be passed to the callbacks as user data. The - * function allows this obj parameter to be updated at any time. This function - * will not modify the memory pointed to by the current user data pointer. If - * it is dynamically allocated memory you must free it yourself. - * - * Parameters: - * mosq - a valid mosquitto instance. - * obj - A user pointer that will be passed as an argument to any callbacks - * that are specified. - */ -libmosq_EXPORT void mosquitto_user_data_set(struct mosquitto *mosq, void *obj); /* ============================================================================= * @@ -1413,6 +2245,7 @@ libmosq_EXPORT void mosquitto_user_data_set(struct mosquitto *mosq, void *obj); */ libmosq_EXPORT int mosquitto_socks5_set(struct mosquitto *mosq, const char *host, int port, const char *username, const char *password); + /* ============================================================================= * * Section: Utility functions @@ -1446,6 +2279,40 @@ libmosq_EXPORT const char *mosquitto_strerror(int mosq_errno); */ libmosq_EXPORT const char *mosquitto_connack_string(int connack_code); +/* + * Function: mosquitto_reason_string + * + * Call to obtain a const string description of an MQTT reason code. + * + * Parameters: + * reason_code - an MQTT reason code. + * + * Returns: + * A constant string describing the reason. + */ +libmosq_EXPORT const char *mosquitto_reason_string(int reason_code); + +/* Function: mosquitto_string_to_command + * + * Take a string input representing an MQTT command and convert it to the + * libmosquitto integer representation. + * + * Parameters: + * str - the string to parse. + * cmd - pointer to an int, for the result. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - on an invalid input. + * + * Example: + * (start code) + * mosquitto_string_to_command("CONNECT", &cmd); + * // cmd == CMD_CONNECT + * (end) + */ +libmosq_EXPORT int mosquitto_string_to_command(const char *str, int *cmd); + /* * Function: mosquitto_sub_topic_tokenise * @@ -1454,26 +2321,26 @@ libmosq_EXPORT const char *mosquitto_connack_string(int connack_code); * * For example: * - * subtopic: "a/deep/topic/hierarchy" + * subtopic: "a/deep/topic/hierarchy" * - * Would result in: + * Would result in: * - * topics[0] = "a" - * topics[1] = "deep" - * topics[2] = "topic" - * topics[3] = "hierarchy" + * topics[0] = "a" + * topics[1] = "deep" + * topics[2] = "topic" + * topics[3] = "hierarchy" * - * and: + * and: * - * subtopic: "/a/deep/topic/hierarchy/" + * subtopic: "/a/deep/topic/hierarchy/" * - * Would result in: + * Would result in: * - * topics[0] = NULL - * topics[1] = "a" - * topics[2] = "deep" - * topics[3] = "topic" - * topics[4] = "hierarchy" + * topics[0] = NULL + * topics[1] = "a" + * topics[2] = "deep" + * topics[3] = "topic" + * topics[4] = "hierarchy" * * Parameters: * subtopic - the subscription/topic to tokenise @@ -1522,7 +2389,6 @@ libmosq_EXPORT int mosquitto_sub_topic_tokens_free(char ***topics, int count); /* * Function: mosquitto_topic_matches_sub - * Function: mosquitto_topic_matches_sub2 * * Check whether a topic matches a subscription. * @@ -1533,9 +2399,7 @@ libmosq_EXPORT int mosquitto_sub_topic_tokens_free(char ***topics, int count); * * Parameters: * sub - subscription string to check topic against. - * sublen - length in bytes of sub string * topic - topic to check. - * topiclen - length in bytes of topic string * result - bool pointer to hold result. Will be set to true if the topic * matches the subscription. * @@ -1545,6 +2409,31 @@ libmosq_EXPORT int mosquitto_sub_topic_tokens_free(char ***topics, int count); * MOSQ_ERR_NOMEM - if an out of memory condition occurred. */ libmosq_EXPORT int mosquitto_topic_matches_sub(const char *sub, const char *topic, bool *result); + + +/* + * Function: mosquitto_topic_matches_sub2 + * + * Check whether a topic matches a subscription. + * + * For example: + * + * foo/bar would match the subscription foo/# or +/bar + * non/matching would not match the subscription non/+/+ + * + * Parameters: + * sub - subscription string to check topic against. + * sublen - length in bytes of sub string + * topic - topic to check. + * topiclen - length in bytes of topic string + * result - bool pointer to hold result. Will be set to true if the topic + * matches the subscription. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + */ libmosq_EXPORT int mosquitto_topic_matches_sub2(const char *sub, size_t sublen, const char *topic, size_t topiclen, bool *result); /* @@ -1561,50 +2450,126 @@ libmosq_EXPORT int mosquitto_topic_matches_sub2(const char *sub, size_t sublen, * * Parameters: * topic - the topic to check - * topiclen - length of the topic in bytes * * Returns: * MOSQ_ERR_SUCCESS - for a valid topic * MOSQ_ERR_INVAL - if the topic contains a + or a #, or if it is too long. - * MOSQ_ERR_MALFORMED_UTF8 - if sub or topic is not valid UTF-8 + * MOSQ_ERR_MALFORMED_UTF8 - if topic is not valid UTF-8 * * See Also: * */ libmosq_EXPORT int mosquitto_pub_topic_check(const char *topic); -libmosq_EXPORT int mosquitto_pub_topic_check2(const char *topic, size_t topiclen); /* - * Function: mosquitto_sub_topic_check + * Function: mosquitto_pub_topic_check2 * - * Check whether a topic to be used for subscribing is valid. + * Check whether a topic to be used for publishing is valid. * - * This searches for + or # in a topic and checks that they aren't in invalid - * positions, such as with foo/#/bar, foo/+bar or foo/bar#, and checks its - * length. + * This searches for + or # in a topic and checks its length. * - * This check is already carried out in and - * , there is no need to call it directly before them. - * It may be useful if you wish to check the validity of a topic in advance of + * This check is already carried out in and + * , there is no need to call it directly before them. It + * may be useful if you wish to check the validity of a topic in advance of + * making a connection for example. + * + * Parameters: + * topic - the topic to check + * topiclen - length of the topic in bytes + * + * Returns: + * MOSQ_ERR_SUCCESS - for a valid topic + * MOSQ_ERR_INVAL - if the topic contains a + or a #, or if it is too long. + * MOSQ_ERR_MALFORMED_UTF8 - if topic is not valid UTF-8 + * + * See Also: + * + */ +libmosq_EXPORT int mosquitto_pub_topic_check2(const char *topic, size_t topiclen); + +/* + * Function: mosquitto_sub_topic_check + * + * Check whether a topic to be used for subscribing is valid. + * + * This searches for + or # in a topic and checks that they aren't in invalid + * positions, such as with foo/#/bar, foo/+bar or foo/bar#, and checks its + * length. + * + * This check is already carried out in and + * , there is no need to call it directly before them. + * It may be useful if you wish to check the validity of a topic in advance of * making a connection for example. * * Parameters: * topic - the topic to check - * topiclen - the length in bytes of the topic * * Returns: * MOSQ_ERR_SUCCESS - for a valid topic * MOSQ_ERR_INVAL - if the topic contains a + or a # that is in an * invalid position, or if it is too long. - * MOSQ_ERR_MALFORMED_UTF8 - if topic is not valid UTF-8 + * MOSQ_ERR_MALFORMED_UTF8 - if topic is not valid UTF-8 * * See Also: * */ libmosq_EXPORT int mosquitto_sub_topic_check(const char *topic); + +/* + * Function: mosquitto_sub_topic_check2 + * + * Check whether a topic to be used for subscribing is valid. + * + * This searches for + or # in a topic and checks that they aren't in invalid + * positions, such as with foo/#/bar, foo/+bar or foo/bar#, and checks its + * length. + * + * This check is already carried out in and + * , there is no need to call it directly before them. + * It may be useful if you wish to check the validity of a topic in advance of + * making a connection for example. + * + * Parameters: + * topic - the topic to check + * topiclen - the length in bytes of the topic + * + * Returns: + * MOSQ_ERR_SUCCESS - for a valid topic + * MOSQ_ERR_INVAL - if the topic contains a + or a # that is in an + * invalid position, or if it is too long. + * MOSQ_ERR_MALFORMED_UTF8 - if topic is not valid UTF-8 + * + * See Also: + * + */ libmosq_EXPORT int mosquitto_sub_topic_check2(const char *topic, size_t topiclen); +/* + * Function: mosquitto_validate_utf8 + * + * Helper function to validate whether a UTF-8 string is valid, according to + * the UTF-8 spec and the MQTT additions. + * + * Parameters: + * str - a string to check + * len - the length of the string in bytes + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if str is NULL or len<0 or len>65536 + * MOSQ_ERR_MALFORMED_UTF8 - if str is not valid UTF-8 + */ +libmosq_EXPORT int mosquitto_validate_utf8(const char *str, int len); + + +/* ============================================================================= + * + * Section: One line client helper functions + * + * ============================================================================= + */ + struct libmosquitto_will { char *topic; void *payload; @@ -1664,7 +2629,7 @@ struct libmosquitto_tls { * * Returns: * MOSQ_ERR_SUCCESS - on success - * >0 - on error. + * Greater than 0 - on error. */ libmosq_EXPORT int mosquitto_subscribe_simple( struct mosquitto_message **messages, @@ -1717,7 +2682,7 @@ libmosq_EXPORT int mosquitto_subscribe_simple( * * Returns: * MOSQ_ERR_SUCCESS - on success - * >0 - on error. + * Greater than 0 - on error. */ libmosq_EXPORT int mosquitto_subscribe_callback( int (*callback)(struct mosquitto *, void *, const struct mosquitto_message *), @@ -1735,35 +2700,541 @@ libmosq_EXPORT int mosquitto_subscribe_callback( const struct libmosquitto_tls *tls); +/* ============================================================================= + * + * Section: Properties + * + * ============================================================================= + */ + + /* - * Function: mosquitto_validate_utf8 + * Function: mosquitto_property_add_byte * - * Helper function to validate whether a UTF-8 string is valid, according to - * the UTF-8 spec and the MQTT additions. + * Add a new byte property to a property list. + * + * If *proplist == NULL, a new list will be created, otherwise the new property + * will be appended to the list. * * Parameters: - * str - a string to check - * len - the length of the string in bytes + * proplist - pointer to mosquitto_property pointer, the list of properties + * identifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR) + * value - integer value for the new property * * Returns: - * MOSQ_ERR_SUCCESS - on success - * MOSQ_ERR_INVAL - if str is NULL or len<0 or len>65536 - * MOSQ_ERR_MALFORMED_UTF8 - if str is not valid UTF-8 + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if identifier is invalid, or if proplist is NULL + * MOSQ_ERR_NOMEM - on out of memory + * + * Example: + * > mosquitto_property *proplist = NULL; + * > mosquitto_property_add_byte(&proplist, MQTT_PROP_PAYLOAD_FORMAT_IDENTIFIER, 1); */ -libmosq_EXPORT int mosquitto_validate_utf8(const char *str, int len); +libmosq_EXPORT int mosquitto_property_add_byte(mosquitto_property **proplist, int identifier, uint8_t value); +/* + * Function: mosquitto_property_add_int16 + * + * Add a new int16 property to a property list. + * + * If *proplist == NULL, a new list will be created, otherwise the new property + * will be appended to the list. + * + * Parameters: + * proplist - pointer to mosquitto_property pointer, the list of properties + * identifier - property identifier (e.g. MQTT_PROP_RECEIVE_MAXIMUM) + * value - integer value for the new property + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if identifier is invalid, or if proplist is NULL + * MOSQ_ERR_NOMEM - on out of memory + * + * Example: + * > mosquitto_property *proplist = NULL; + * > mosquitto_property_add_int16(&proplist, MQTT_PROP_RECEIVE_MAXIMUM, 1000); + */ +libmosq_EXPORT int mosquitto_property_add_int16(mosquitto_property **proplist, int identifier, uint16_t value); -/* Function: mosquitto_userdata +/* + * Function: mosquitto_property_add_int32 * - * Retrieve the "userdata" variable for a mosquitto client. + * Add a new int32 property to a property list. + * + * If *proplist == NULL, a new list will be created, otherwise the new property + * will be appended to the list. * * Parameters: - * mosq - a valid mosquitto instance. + * proplist - pointer to mosquitto_property pointer, the list of properties + * identifier - property identifier (e.g. MQTT_PROP_MESSAGE_EXPIRY_INTERVAL) + * value - integer value for the new property * * Returns: - * A pointer to the userdata member variable. + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if identifier is invalid, or if proplist is NULL + * MOSQ_ERR_NOMEM - on out of memory + * + * Example: + * > mosquitto_property *proplist = NULL; + * > mosquitto_property_add_int32(&proplist, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, 86400); */ -libmosq_EXPORT void *mosquitto_userdata(struct mosquitto *mosq); +libmosq_EXPORT int mosquitto_property_add_int32(mosquitto_property **proplist, int identifier, uint32_t value); + +/* + * Function: mosquitto_property_add_varint + * + * Add a new varint property to a property list. + * + * If *proplist == NULL, a new list will be created, otherwise the new property + * will be appended to the list. + * + * Parameters: + * proplist - pointer to mosquitto_property pointer, the list of properties + * identifier - property identifier (e.g. MQTT_PROP_SUBSCRIPTION_IDENTIFIER) + * value - integer value for the new property + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if identifier is invalid, or if proplist is NULL + * MOSQ_ERR_NOMEM - on out of memory + * + * Example: + * > mosquitto_property *proplist = NULL; + * > mosquitto_property_add_varint(&proplist, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 1); + */ +libmosq_EXPORT int mosquitto_property_add_varint(mosquitto_property **proplist, int identifier, uint32_t value); + +/* + * Function: mosquitto_property_add_binary + * + * Add a new binary property to a property list. + * + * If *proplist == NULL, a new list will be created, otherwise the new property + * will be appended to the list. + * + * Parameters: + * proplist - pointer to mosquitto_property pointer, the list of properties + * identifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR) + * value - pointer to the property data + * len - length of property data in bytes + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if identifier is invalid, or if proplist is NULL + * MOSQ_ERR_NOMEM - on out of memory + * + * Example: + * > mosquitto_property *proplist = NULL; + * > mosquitto_property_add_binary(&proplist, MQTT_PROP_AUTHENTICATION_DATA, auth_data, auth_data_len); + */ +libmosq_EXPORT int mosquitto_property_add_binary(mosquitto_property **proplist, int identifier, const void *value, uint16_t len); + +/* + * Function: mosquitto_property_add_string + * + * Add a new string property to a property list. + * + * If *proplist == NULL, a new list will be created, otherwise the new property + * will be appended to the list. + * + * Parameters: + * proplist - pointer to mosquitto_property pointer, the list of properties + * identifier - property identifier (e.g. MQTT_PROP_CONTENT_TYPE) + * value - string value for the new property, must be UTF-8 and zero terminated + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if identifier is invalid, if value is NULL, or if proplist is NULL + * MOSQ_ERR_NOMEM - on out of memory + * MOSQ_ERR_MALFORMED_UTF8 - value is not valid UTF-8. + * + * Example: + * > mosquitto_property *proplist = NULL; + * > mosquitto_property_add_string(&proplist, MQTT_PROP_CONTENT_TYPE, "application/json"); + */ +libmosq_EXPORT int mosquitto_property_add_string(mosquitto_property **proplist, int identifier, const char *value); + +/* + * Function: mosquitto_property_add_string_pair + * + * Add a new string pair property to a property list. + * + * If *proplist == NULL, a new list will be created, otherwise the new property + * will be appended to the list. + * + * Parameters: + * proplist - pointer to mosquitto_property pointer, the list of properties + * identifier - property identifier (e.g. MQTT_PROP_USER_PROPERTY) + * name - string name for the new property, must be UTF-8 and zero terminated + * value - string value for the new property, must be UTF-8 and zero terminated + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if identifier is invalid, if name or value is NULL, or if proplist is NULL + * MOSQ_ERR_NOMEM - on out of memory + * MOSQ_ERR_MALFORMED_UTF8 - if name or value are not valid UTF-8. + * + * Example: + * > mosquitto_property *proplist = NULL; + * > mosquitto_property_add_string_pair(&proplist, MQTT_PROP_USER_PROPERTY, "client", "mosquitto_pub"); + */ +libmosq_EXPORT int mosquitto_property_add_string_pair(mosquitto_property **proplist, int identifier, const char *name, const char *value); + + +/* + * Function: mosquitto_property_identifier + * + * Return the property identifier for a single property. + * + * Parameters: + * property - pointer to a valid mosquitto_property pointer. + * + * Returns: + * A valid property identifier on success + * 0 - on error + */ +libmosq_EXPORT int mosquitto_property_identifier(const mosquitto_property *property); + + +/* + * Function: mosquitto_property_next + * + * Return the next property in a property list. Use to iterate over a property + * list, e.g.: + * + * (start code) + * for(prop = proplist; prop != NULL; prop = mosquitto_property_next(prop)){ + * if(mosquitto_property_identifier(prop) == MQTT_PROP_CONTENT_TYPE){ + * ... + * } + * } + * (end) + * + * Parameters: + * proplist - pointer to mosquitto_property pointer, the list of properties + * + * Returns: + * Pointer to the next item in the list + * NULL, if proplist is NULL, or if there are no more items in the list. + */ +libmosq_EXPORT const mosquitto_property *mosquitto_property_next(const mosquitto_property *proplist); + + +/* + * Function: mosquitto_property_read_byte + * + * Attempt to read a byte property matching an identifier, from a property list + * or single property. This function can search for multiple entries of the + * same identifier by using the returned value and skip_first. Note however + * that it is forbidden for most properties to be duplicated. + * + * If the property is not found, *value will not be modified, so it is safe to + * pass a variable with a default value to be potentially overwritten: + * + * (start code) + * uint16_t keepalive = 60; // default value + * // Get value from property list, or keep default if not found. + * mosquitto_property_read_int16(proplist, MQTT_PROP_SERVER_KEEP_ALIVE, &keepalive, false); + * (end) + * + * Parameters: + * proplist - mosquitto_property pointer, the list of properties or single property + * identifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR) + * value - pointer to store the value, or NULL if the value is not required. + * skip_first - boolean that indicates whether the first item in the list + * should be ignored or not. Should usually be set to false. + * + * Returns: + * A valid property pointer if the property is found + * NULL, if the property is not found, or proplist is NULL. + * + * Example: + * (start code) + * // proplist is obtained from a callback + * mosquitto_property *prop; + * prop = mosquitto_property_read_byte(proplist, identifier, &value, false); + * while(prop){ + * printf("value: %s\n", value); + * prop = mosquitto_property_read_byte(prop, identifier, &value); + * } + * (end) + */ +libmosq_EXPORT const mosquitto_property *mosquitto_property_read_byte( + const mosquitto_property *proplist, + int identifier, + uint8_t *value, + bool skip_first); + +/* + * Function: mosquitto_property_read_int16 + * + * Read an int16 property value from a property. + * + * Parameters: + * property - property to read + * identifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR) + * value - pointer to store the value, or NULL if the value is not required. + * skip_first - boolean that indicates whether the first item in the list + * should be ignored or not. Should usually be set to false. + * + * Returns: + * A valid property pointer if the property is found + * NULL, if the property is not found, or proplist is NULL. + * + * Example: + * See + */ +libmosq_EXPORT const mosquitto_property *mosquitto_property_read_int16( + const mosquitto_property *proplist, + int identifier, + uint16_t *value, + bool skip_first); + +/* + * Function: mosquitto_property_read_int32 + * + * Read an int32 property value from a property. + * + * Parameters: + * property - pointer to mosquitto_property pointer, the list of properties + * identifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR) + * value - pointer to store the value, or NULL if the value is not required. + * skip_first - boolean that indicates whether the first item in the list + * should be ignored or not. Should usually be set to false. + * + * Returns: + * A valid property pointer if the property is found + * NULL, if the property is not found, or proplist is NULL. + * + * Example: + * See + */ +libmosq_EXPORT const mosquitto_property *mosquitto_property_read_int32( + const mosquitto_property *proplist, + int identifier, + uint32_t *value, + bool skip_first); + +/* + * Function: mosquitto_property_read_varint + * + * Read a varint property value from a property. + * + * Parameters: + * property - property to read + * identifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR) + * value - pointer to store the value, or NULL if the value is not required. + * skip_first - boolean that indicates whether the first item in the list + * should be ignored or not. Should usually be set to false. + * + * Returns: + * A valid property pointer if the property is found + * NULL, if the property is not found, or proplist is NULL. + * + * Example: + * See + */ +libmosq_EXPORT const mosquitto_property *mosquitto_property_read_varint( + const mosquitto_property *proplist, + int identifier, + uint32_t *value, + bool skip_first); + +/* + * Function: mosquitto_property_read_binary + * + * Read a binary property value from a property. + * + * On success, value must be free()'d by the application. + * + * Parameters: + * property - property to read + * identifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR) + * value - pointer to store the value, or NULL if the value is not required. + * skip_first - boolean that indicates whether the first item in the list + * should be ignored or not. Should usually be set to false. + * + * Returns: + * A valid property pointer if the property is found + * NULL, if the property is not found, or proplist is NULL, or if an out of memory condition occurred. + * + * Example: + * See + */ +libmosq_EXPORT const mosquitto_property *mosquitto_property_read_binary( + const mosquitto_property *proplist, + int identifier, + void **value, + uint16_t *len, + bool skip_first); + +/* + * Function: mosquitto_property_read_string + * + * Read a string property value from a property. + * + * On success, value must be free()'d by the application. + * + * Parameters: + * property - property to read + * identifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR) + * value - pointer to char*, for the property data to be stored in, or NULL if + * the value is not required. + * skip_first - boolean that indicates whether the first item in the list + * should be ignored or not. Should usually be set to false. + * + * Returns: + * A valid property pointer if the property is found + * NULL, if the property is not found, or proplist is NULL, or if an out of memory condition occurred. + * + * Example: + * See + */ +libmosq_EXPORT const mosquitto_property *mosquitto_property_read_string( + const mosquitto_property *proplist, + int identifier, + char **value, + bool skip_first); + +/* + * Function: mosquitto_property_read_string_pair + * + * Read a string pair property value pair from a property. + * + * On success, name and value must be free()'d by the application. + * + * Parameters: + * property - property to read + * identifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR) + * name - pointer to char* for the name property data to be stored in, or NULL + * if the name is not required. + * value - pointer to char*, for the property data to be stored in, or NULL if + * the value is not required. + * skip_first - boolean that indicates whether the first item in the list + * should be ignored or not. Should usually be set to false. + * + * Returns: + * A valid property pointer if the property is found + * NULL, if the property is not found, or proplist is NULL, or if an out of memory condition occurred. + * + * Example: + * See + */ +libmosq_EXPORT const mosquitto_property *mosquitto_property_read_string_pair( + const mosquitto_property *proplist, + int identifier, + char **name, + char **value, + bool skip_first); + +/* + * Function: mosquitto_property_free_all + * + * Free all properties from a list of properties. Frees the list and sets *properties to NULL. + * + * Parameters: + * properties - list of properties to free + * + * Example: + * > mosquitto_properties *properties = NULL; + * > // Add properties + * > mosquitto_property_free_all(&properties); + */ +libmosq_EXPORT void mosquitto_property_free_all(mosquitto_property **properties); + +/* + * Function: mosquitto_property_copy_all + * + * Parameters: + * dest - pointer for new property list + * src - property list + * + * Returns: + * MOSQ_ERR_SUCCESS - on successful copy + * MOSQ_ERR_INVAL - if dest is NULL + * MOSQ_ERR_NOMEM - on out of memory (dest will be set to NULL) + */ +libmosq_EXPORT int mosquitto_property_copy_all(mosquitto_property **dest, const mosquitto_property *src); + +/* + * Function: mosquitto_property_check_command + * + * Check whether a property identifier is valid for the given command. + * + * Parameters: + * command - MQTT command (e.g. CMD_CONNECT) + * identifier - MQTT property (e.g. MQTT_PROP_USER_PROPERTY) + * + * Returns: + * MOSQ_ERR_SUCCESS - if the identifier is valid for command + * MOSQ_ERR_PROTOCOL - if the identifier is not valid for use with command. + */ +libmosq_EXPORT int mosquitto_property_check_command(int command, int identifier); + + +/* + * Function: mosquitto_property_check_all + * + * Check whether a list of properties are valid for a particular command, + * whether there are duplicates, and whether the values are valid where + * possible. + * + * Note that this function is used internally in the library whenever + * properties are passed to it, so in basic use this is not needed, but should + * be helpful to check property lists *before* the point of using them. + * + * Parameters: + * command - MQTT command (e.g. CMD_CONNECT) + * properties - list of MQTT properties to check. + * + * Returns: + * MOSQ_ERR_SUCCESS - if all properties are valid + * MOSQ_ERR_DUPLICATE_PROPERTY - if a property is duplicated where it is forbidden. + * MOSQ_ERR_PROTOCOL - if any property is invalid + */ +libmosq_EXPORT int mosquitto_property_check_all(int command, const mosquitto_property *properties); + +/* + * Function: mosquitto_property_identifier_to_string + * + * Return the property name as a string for a property identifier. + * The property name is as defined in the MQTT specification, with - as a + * separator, for example: payload-format-indicator. + * + * Parameters: + * identifier - valid MQTT property identifier integer + * + * Returns: + * A const string to the property name on success + * NULL on failure + */ +libmosq_EXPORT const char *mosquitto_property_identifier_to_string(int identifier); + + +/* Function: mosquitto_string_to_property_info + * + * Parse a property name string and convert to a property identifier and data type. + * The property name is as defined in the MQTT specification, with - as a + * separator, for example: payload-format-indicator. + * + * Parameters: + * propname - the string to parse + * identifier - pointer to an int to receive the property identifier + * type - pointer to an int to receive the property type + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if the string does not match a property + * + * Example: + * (start code) + * mosquitto_string_to_property_info("response-topic", &id, &type); + * // id == MQTT_PROP_RESPONSE_TOPIC + * // type == MQTT_PROP_TYPE_STRING + * (end) + */ +libmosq_EXPORT int mosquitto_string_to_property_info(const char *propname, int *identifier, int *type); + #ifdef __cplusplus } diff --git a/libs/mosquitto/src/mosquitto_broker.h b/libs/mosquitto/src/mosquitto_broker.h new file mode 100644 index 0000000..9a6ba1e --- /dev/null +++ b/libs/mosquitto/src/mosquitto_broker.h @@ -0,0 +1,561 @@ +/* +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 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. +*/ + +/* + * File: mosquitto_broker.h + * + * This header contains functions for use by plugins. + */ +#ifndef MOSQUITTO_BROKER_H +#define MOSQUITTO_BROKER_H + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(WIN32) && defined(mosquitto_EXPORTS) +# define mosq_EXPORT __declspec(dllexport) +#else +# define mosq_EXPORT +#endif + +#include +#include +#include +#include + +struct mosquitto; +typedef struct mqtt5__property mosquitto_property; + +enum mosquitto_protocol { + mp_mqtt, + mp_mqttsn, + mp_websockets +}; + +/* ========================================================================= + * + * Section: Register callbacks. + * + * ========================================================================= */ + +/* Callback events */ +enum mosquitto_plugin_event { + MOSQ_EVT_RELOAD = 1, + MOSQ_EVT_ACL_CHECK = 2, + MOSQ_EVT_BASIC_AUTH = 3, + MOSQ_EVT_EXT_AUTH_START = 4, + MOSQ_EVT_EXT_AUTH_CONTINUE = 5, + MOSQ_EVT_CONTROL = 6, + MOSQ_EVT_MESSAGE = 7, + MOSQ_EVT_PSK_KEY = 8, + MOSQ_EVT_TICK = 9, + MOSQ_EVT_DISCONNECT = 10, +}; + +/* Data for the MOSQ_EVT_RELOAD event */ +struct mosquitto_evt_reload { + void *future; + struct mosquitto_opt *options; + int option_count; + void *future2[4]; +}; + +/* Data for the MOSQ_EVT_ACL_CHECK event */ +struct mosquitto_evt_acl_check { + void *future; + struct mosquitto *client; + const char *topic; + const void *payload; + mosquitto_property *properties; + int access; + uint32_t payloadlen; + uint8_t qos; + bool retain; + void *future2[4]; +}; + +/* Data for the MOSQ_EVT_BASIC_AUTH event */ +struct mosquitto_evt_basic_auth { + void *future; + struct mosquitto *client; + char *username; + char *password; + void *future2[4]; +}; + +/* Data for the MOSQ_EVT_PSK_KEY event */ +struct mosquitto_evt_psk_key { + void *future; + struct mosquitto *client; + const char *hint; + const char *identity; + char *key; + int max_key_len; + void *future2[4]; +}; + +/* Data for the MOSQ_EVT_EXTENDED_AUTH event */ +struct mosquitto_evt_extended_auth { + void *future; + struct mosquitto *client; + const void *data_in; + void *data_out; + uint16_t data_in_len; + uint16_t data_out_len; + const char *auth_method; + void *future2[3]; +}; + +/* Data for the MOSQ_EVT_CONTROL event */ +struct mosquitto_evt_control { + void *future; + struct mosquitto *client; + const char *topic; + const void *payload; + const mosquitto_property *properties; + char *reason_string; + uint32_t payloadlen; + uint8_t qos; + uint8_t reason_code; + bool retain; + void *future2[4]; +}; + +/* Data for the MOSQ_EVT_MESSAGE event */ +struct mosquitto_evt_message { + void *future; + struct mosquitto *client; + char *topic; + void *payload; + mosquitto_property *properties; + char *reason_string; + uint32_t payloadlen; + uint8_t qos; + uint8_t reason_code; + bool retain; + void *future2[4]; +}; + + +/* Data for the MOSQ_EVT_TICK event */ +struct mosquitto_evt_tick { + void *future; + long now_ns; + long next_ns; + time_t now_s; + time_t next_s; + void *future2[4]; +}; + +/* Data for the MOSQ_EVT_DISCONNECT event */ +struct mosquitto_evt_disconnect { + void *future; + struct mosquitto *client; + int reason; + void *future2[4]; +}; + + +/* Callback definition */ +typedef int (*MOSQ_FUNC_generic_callback)(int, void *, void *); + +typedef struct mosquitto_plugin_id_t mosquitto_plugin_id_t; + +/* + * Function: mosquitto_callback_register + * + * Register a callback for an event. + * + * Parameters: + * identifier - the plugin identifier, as provided by . + * event - the event to register a callback for. Can be one of: + * * MOSQ_EVT_RELOAD + * * MOSQ_EVT_ACL_CHECK + * * MOSQ_EVT_BASIC_AUTH + * * MOSQ_EVT_EXT_AUTH_START + * * MOSQ_EVT_EXT_AUTH_CONTINUE + * * MOSQ_EVT_CONTROL + * * MOSQ_EVT_MESSAGE + * * MOSQ_EVT_PSK_KEY + * * MOSQ_EVT_TICK + * * MOSQ_EVT_DISCONNECT + * cb_func - the callback function + * event_data - event specific data + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if cb_func is NULL + * MOSQ_ERR_NOMEM - on out of memory + * MOSQ_ERR_ALREADY_EXISTS - if cb_func has already been registered for this event + * MOSQ_ERR_NOT_SUPPORTED - if the event is not supported + */ +mosq_EXPORT int mosquitto_callback_register( + mosquitto_plugin_id_t *identifier, + int event, + MOSQ_FUNC_generic_callback cb_func, + const void *event_data, + void *userdata); + +/* + * Function: mosquitto_callback_unregister + * + * Unregister a previously registered callback function. + * + * Parameters: + * identifier - the plugin identifier, as provided by . + * event - the event to register a callback for. Can be one of: + * * MOSQ_EVT_RELOAD + * * MOSQ_EVT_ACL_CHECK + * * MOSQ_EVT_BASIC_AUTH + * * MOSQ_EVT_EXT_AUTH_START + * * MOSQ_EVT_EXT_AUTH_CONTINUE + * * MOSQ_EVT_CONTROL + * * MOSQ_EVT_MESSAGE + * * MOSQ_EVT_PSK_KEY + * * MOSQ_EVT_TICK + * * MOSQ_EVT_DISCONNECT + * cb_func - the callback function + * event_data - event specific data + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if cb_func is NULL + * MOSQ_ERR_NOT_FOUND - if cb_func was not registered for this event + * MOSQ_ERR_NOT_SUPPORTED - if the event is not supported + */ +mosq_EXPORT int mosquitto_callback_unregister( + mosquitto_plugin_id_t *identifier, + int event, + MOSQ_FUNC_generic_callback cb_func, + const void *event_data); + + +/* ========================================================================= + * + * Section: Memory allocation. + * + * Use these functions when allocating or freeing memory to have your memory + * included in the memory tracking on the broker. + * + * ========================================================================= */ + +/* + * Function: mosquitto_calloc + */ +mosq_EXPORT void *mosquitto_calloc(size_t nmemb, size_t size); + +/* + * Function: mosquitto_free + */ +mosq_EXPORT void mosquitto_free(void *mem); + +/* + * Function: mosquitto_malloc + */ +mosq_EXPORT void *mosquitto_malloc(size_t size); + +/* + * Function: mosquitto_realloc + */ +mosq_EXPORT void *mosquitto_realloc(void *ptr, size_t size); + +/* + * Function: mosquitto_strdup + */ +mosq_EXPORT char *mosquitto_strdup(const char *s); + +/* ========================================================================= + * + * Section: Utility Functions + * + * Use these functions from within your plugin. + * + * ========================================================================= */ + + +/* + * Function: mosquitto_log_printf + * + * Write a log message using the broker configured logging. + * + * Parameters: + * level - Log message priority. Can currently be one of: + * + * * MOSQ_LOG_INFO + * * MOSQ_LOG_NOTICE + * * MOSQ_LOG_WARNING + * * MOSQ_LOG_ERR + * * MOSQ_LOG_DEBUG + * * MOSQ_LOG_SUBSCRIBE (not recommended for use by plugins) + * * MOSQ_LOG_UNSUBSCRIBE (not recommended for use by plugins) + * + * These values are defined in mosquitto.h. + * + * fmt, ... - printf style format and arguments. + */ +mosq_EXPORT void mosquitto_log_printf(int level, const char *fmt, ...); + + +/* ========================================================================= + * + * Client Functions + * + * Use these functions to access client information. + * + * ========================================================================= */ + +/* + * Function: mosquitto_client_address + * + * Retrieve the IP address of the client as a string. + */ +mosq_EXPORT const char *mosquitto_client_address(const struct mosquitto *client); + + +/* + * Function: mosquitto_client_clean_session + * + * Retrieve the clean session flag value for a client. + */ +mosq_EXPORT bool mosquitto_client_clean_session(const struct mosquitto *client); + + +/* + * Function: mosquitto_client_id + * + * Retrieve the client id associated with a client. + */ +mosq_EXPORT const char *mosquitto_client_id(const struct mosquitto *client); + + +/* + * Function: mosquitto_client_keepalive + * + * Retrieve the keepalive value for a client. + */ +mosq_EXPORT int mosquitto_client_keepalive(const struct mosquitto *client); + + +/* + * Function: mosquitto_client_certificate + * + * If TLS support is enabled, return the certificate provided by a client as an + * X509 pointer from openssl. If the client did not provide a certificate, then + * NULL will be returned. This function will only ever return a non-NULL value + * if the `require_certificate` option is set to true. + * + * When you have finished with the x509 pointer, it must be freed using + * X509_free(). + * + * If TLS is not supported, this function will always return NULL. + */ +mosq_EXPORT void *mosquitto_client_certificate(const struct mosquitto *client); + + +/* + * Function: mosquitto_client_protocol + * + * Retrieve the protocol with which the client has connected. Can be one of: + * + * mp_mqtt (MQTT over TCP) + * mp_mqttsn (MQTT-SN) + * mp_websockets (MQTT over Websockets) + */ +mosq_EXPORT int mosquitto_client_protocol(const struct mosquitto *client); + + +/* + * Function: mosquitto_client_protocol_version + * + * Retrieve the MQTT protocol version with which the client has connected. Can be one of: + * + * Returns: + * 3 - for MQTT v3 / v3.1 + * 4 - for MQTT v3.1.1 + * 5 - for MQTT v5 + */ +mosq_EXPORT int mosquitto_client_protocol_version(const struct mosquitto *client); + + +/* + * Function: mosquitto_client_sub_count + * + * Retrieve the number of subscriptions that have been made by a client. + */ +mosq_EXPORT int mosquitto_client_sub_count(const struct mosquitto *client); + + +/* + * Function: mosquitto_client_username + * + * Retrieve the username associated with a client. + */ +mosq_EXPORT const char *mosquitto_client_username(const struct mosquitto *client); + + +/* Function: mosquitto_set_username + * + * Set the username for a client. + * + * This removes and replaces the current username for a client and hence + * updates its access. + * + * username can be NULL, in which case the client will become anonymous, but + * must not be zero length. + * + * In the case of error, the client will be left with its original username. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if client is NULL, or if username is zero length + * MOSQ_ERR_NOMEM - on out of memory + */ +mosq_EXPORT int mosquitto_set_username(struct mosquitto *client, const char *username); + + +/* ========================================================================= + * + * Section: Client control + * + * ========================================================================= */ + +/* Function: mosquitto_kick_client_by_clientid + * + * Forcefully disconnect a client from the broker. + * + * If clientid != NULL, then the client with the matching client id is + * disconnected from the broker. + * If clientid == NULL, then all clients are disconnected from the broker. + * + * If with_will == true, then if the client has a Last Will and Testament + * defined then this will be sent. If false, the LWT will not be sent. + */ +mosq_EXPORT int mosquitto_kick_client_by_clientid(const char *clientid, bool with_will); + +/* Function: mosquitto_kick_client_by_username + * + * Forcefully disconnect a client from the broker. + * + * If username != NULL, then all clients with a matching username are kicked + * from the broker. + * If username == NULL, then all clients that do not have a username are + * kicked. + * + * If with_will == true, then if the client has a Last Will and Testament + * defined then this will be sent. If false, the LWT will not be sent. + */ +mosq_EXPORT int mosquitto_kick_client_by_username(const char *username, bool with_will); + + +/* ========================================================================= + * + * Section: Publishing functions + * + * ========================================================================= */ + +/* Function: mosquitto_broker_publish + * + * Publish a message from within a plugin. + * + * This function allows a plugin to publish a message. Messages published in + * this way are treated as coming from the broker and so will not be passed to + * `mosquitto_auth_acl_check(, MOSQ_ACL_WRITE, , )` for checking. Read access + * will be enforced as normal for individual clients when they are due to + * receive the message. + * + * It can be used to send messages to all clients that have a matching + * subscription, or to a single client whether or not it has a matching + * subscription. + * + * Parameters: + * clientid - optional string. If set to NULL, the message is delivered to all + * clients. If non-NULL, the message is delivered only to the + * client with the corresponding client id. If the client id + * specified is not connected, the message will be dropped. + * topic - message topic + * payloadlen - payload length in bytes. Can be 0 for an empty payload. + * payload - payload bytes. If payloadlen > 0 this must not be NULL. Must + * be allocated on the heap. Will be freed by mosquitto after use if the + * function returns success. + * qos - message QoS to use. + * retain - should retain be set on the message. This does not apply if + * clientid is non-NULL. + * properties - MQTT v5 properties to attach to the message. If the function + * returns success, then properties is owned by the broker and + * will be freed at a later point. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if topic is NULL, if payloadlen < 0, if payloadlen > 0 + * and payload is NULL, if qos is not 0, 1, or 2. + * MOSQ_ERR_NOMEM - on out of memory + */ +mosq_EXPORT int mosquitto_broker_publish( + const char *clientid, + const char *topic, + int payloadlen, + void *payload, + int qos, + bool retain, + mosquitto_property *properties); + + +/* Function: mosquitto_broker_publish_copy + * + * Publish a message from within a plugin. + * + * This function is identical to mosquitto_broker_publish, except that a copy + * of `payload` is taken. + * + * Parameters: + * clientid - optional string. If set to NULL, the message is delivered to all + * clients. If non-NULL, the message is delivered only to the + * client with the corresponding client id. If the client id + * specified is not connected, the message will be dropped. + * topic - message topic + * payloadlen - payload length in bytes. Can be 0 for an empty payload. + * payload - payload bytes. If payloadlen > 0 this must not be NULL. + * Memory remains the property of the calling function. + * qos - message QoS to use. + * retain - should retain be set on the message. This does not apply if + * clientid is non-NULL. + * properties - MQTT v5 properties to attach to the message. If the function + * returns success, then properties is owned by the broker and + * will be freed at a later point. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if topic is NULL, if payloadlen < 0, if payloadlen > 0 + * and payload is NULL, if qos is not 0, 1, or 2. + * MOSQ_ERR_NOMEM - on out of memory + */ +mosq_EXPORT int mosquitto_broker_publish_copy( + const char *clientid, + const char *topic, + int payloadlen, + const void *payload, + int qos, + bool retain, + mosquitto_property *properties); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libs/mosquitto/src/mosquitto_internal.h b/libs/mosquitto/src/mosquitto_internal.h index aceda1f..8d06638 100644 --- a/libs/mosquitto/src/mosquitto_internal.h +++ b/libs/mosquitto/src/mosquitto_internal.h @@ -1,15 +1,17 @@ /* -Copyright (c) 2010-2019 Roger Light +Copyright (c) 2010-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. Tatsuzo Osawa - Add epoll. @@ -70,6 +72,8 @@ typedef SOCKET mosq_sock_t; typedef int mosq_sock_t; #endif +#define SAFE_PRINT(A) (A)?(A):"null" + enum mosquitto_msg_direction { mosq_md_in = 0, mosq_md_out = 1 @@ -94,7 +98,7 @@ enum mosquitto_client_state { mosq_cs_new = 0, mosq_cs_connected = 1, mosq_cs_disconnecting = 2, - mosq_cs_connect_async = 3, + mosq_cs_active = 3, mosq_cs_connect_pending = 4, mosq_cs_connect_srv = 5, mosq_cs_disconnect_ws = 6, @@ -107,15 +111,19 @@ enum mosquitto_client_state { mosq_cs_socks5_userpass_reply = 13, mosq_cs_socks5_send_userpass = 14, mosq_cs_expiring = 15, - mosq_cs_connecting = 16, mosq_cs_duplicate = 17, /* client that has been taken over by another with the same id */ + mosq_cs_disconnect_with_will = 18, + mosq_cs_disused = 19, /* client that has been added to the disused list to be freed */ + mosq_cs_authenticating = 20, /* Client has sent CONNECT but is still undergoing extended authentication */ + mosq_cs_reauthenticating = 21, /* Client is undergoing reauthentication and shouldn't do anything else until complete */ }; enum mosquitto__protocol { mosq_p_invalid = 0, mosq_p_mqtt31 = 1, mosq_p_mqtt311 = 2, - mosq_p_mqtts = 3 + mosq_p_mqtts = 3, + mosq_p_mqtt5 = 5, }; enum mosquitto__threaded_state { @@ -131,6 +139,18 @@ enum mosquitto__transport { mosq_t_sctp = 3 }; + +struct mosquitto__alias{ + char *topic; + uint16_t alias; +}; + +struct session_expiry_list { + struct mosquitto *context; + struct session_expiry_list *prev; + struct session_expiry_list *next; +}; + struct mosquitto__packet{ uint8_t *payload; struct mosquitto__packet *next; @@ -146,18 +166,62 @@ struct mosquitto__packet{ struct mosquitto_message_all{ struct mosquitto_message_all *next; + struct mosquitto_message_all *prev; + mosquitto_property *properties; time_t timestamp; - //enum mosquitto_msg_direction direction; enum mosquitto_msg_state state; bool dup; struct mosquitto_message msg; + uint32_t expiry_interval; +}; + +#ifdef WITH_TLS +enum mosquitto__keyform { + mosq_k_pem = 0, + mosq_k_engine = 1, +}; +#endif + +struct will_delay_list { + struct mosquitto *context; + struct will_delay_list *prev; + struct will_delay_list *next; +}; + +struct mosquitto_msg_data{ +#ifdef WITH_BROKER + struct mosquitto_client_msg *inflight; + struct mosquitto_client_msg *queued; + long inflight_bytes; + long inflight_bytes12; + int inflight_count; + int inflight_count12; + long queued_bytes; + long queued_bytes12; + int queued_count; + int queued_count12; +#else + struct mosquitto_message_all *inflight; + int queue_len; +# ifdef WITH_THREADING + pthread_mutex_t mutex; +# endif +#endif + int inflight_quota; + uint16_t inflight_maximum; }; + struct mosquitto { +#if defined(WITH_BROKER) && defined(WITH_EPOLL) + /* This *must* be the first element in the struct. */ + int ident; +#endif mosq_sock_t sock; #ifndef WITH_BROKER mosq_sock_t sockpairR, sockpairW; #endif + uint32_t maximum_packet_size; #if defined(__GLIBC__) && defined(WITH_ADNS) struct gaicb *adns; /* For getaddrinfo_a */ #endif @@ -175,10 +239,19 @@ struct mosquitto { struct mosquitto__packet in_packet; struct mosquitto__packet *current_out_packet; struct mosquitto__packet *out_packet; - struct mosquitto_message *will; + struct mosquitto_message_all *will; + struct mosquitto__alias *aliases; + struct will_delay_list *will_delay_entry; + int alias_count; + int out_packet_count; + uint32_t will_delay_interval; + time_t will_delay_time; #ifdef WITH_TLS SSL *ssl; SSL_CTX *ssl_ctx; +#ifndef WITH_BROKER + SSL_CTX *user_ssl_ctx; +#endif char *tls_cafile; char *tls_capath; char *tls_certfile; @@ -188,9 +261,15 @@ struct mosquitto { char *tls_ciphers; char *tls_psk; char *tls_psk_identity; + char *tls_engine; + char *tls_engine_kpass_sha1; + char *tls_alpn; int tls_cert_reqs; bool tls_insecure; bool ssl_ctx_defaults; + bool tls_ocsp_required; + bool tls_use_os_certs; + enum mosquitto__keyform tls_keyform; #endif bool want_write; bool want_connect; @@ -201,91 +280,90 @@ struct mosquitto { pthread_mutex_t out_packet_mutex; pthread_mutex_t current_out_packet_mutex; pthread_mutex_t state_mutex; - pthread_mutex_t in_message_mutex; - pthread_mutex_t out_message_mutex; pthread_mutex_t mid_mutex; pthread_t thread_id; #endif - bool clean_session; + bool clean_start; + time_t session_expiry_time; + uint32_t session_expiry_interval; #ifdef WITH_BROKER - bool removed_from_by_id; /* True if removed from by_id hash */ + bool in_by_id; bool is_dropping; bool is_bridge; struct mosquitto__bridge *bridge; - struct mosquitto_client_msg *inflight_msgs; - struct mosquitto_client_msg *last_inflight_msg; - struct mosquitto_client_msg *queued_msgs; - struct mosquitto_client_msg *last_queued_msg; - unsigned long msg_bytes; - unsigned long msg_bytes12; - int msg_count; - int msg_count12; + struct mosquitto_msg_data msgs_in; + struct mosquitto_msg_data msgs_out; struct mosquitto__acl_user *acl_list; struct mosquitto__listener *listener; - time_t disconnect_t; struct mosquitto__packet *out_packet_last; - struct mosquitto__subhier **subs; + struct mosquitto__client_sub **subs; + char *auth_method; int sub_count; +# ifndef WITH_EPOLL int pollfd_index; +# endif # ifdef WITH_WEBSOCKETS -# if defined(LWS_LIBRARY_VERSION_NUMBER) struct lws *wsi; -# else - struct libwebsocket_context *ws_context; - struct libwebsocket *wsi; -# endif # endif bool ws_want_write; + bool assigned_id; #else # ifdef WITH_SOCKS char *socks5_host; - int socks5_port; + uint16_t socks5_port; char *socks5_username; char *socks5_password; # endif void *userdata; bool in_callback; - struct mosquitto_message_all *in_messages; - struct mosquitto_message_all *in_messages_last; - struct mosquitto_message_all *out_messages; - struct mosquitto_message_all *out_messages_last; + struct mosquitto_msg_data msgs_in; + struct mosquitto_msg_data msgs_out; void (*on_connect)(struct mosquitto *, void *userdata, int rc); void (*on_connect_with_flags)(struct mosquitto *, void *userdata, int rc, int flags); + void (*on_connect_v5)(struct mosquitto *, void *userdata, int rc, int flags, const mosquitto_property *props); void (*on_disconnect)(struct mosquitto *, void *userdata, int rc); + void (*on_disconnect_v5)(struct mosquitto *, void *userdata, int rc, const mosquitto_property *props); void (*on_publish)(struct mosquitto *, void *userdata, int mid); + void (*on_publish_v5)(struct mosquitto *, void *userdata, int mid, int reason_code, const mosquitto_property *props); void (*on_message)(struct mosquitto *, void *userdata, const struct mosquitto_message *message); + void (*on_message_v5)(struct mosquitto *, void *userdata, const struct mosquitto_message *message, const mosquitto_property *props); void (*on_subscribe)(struct mosquitto *, void *userdata, int mid, int qos_count, const int *granted_qos); + void (*on_subscribe_v5)(struct mosquitto *, void *userdata, int mid, int qos_count, const int *granted_qos, const mosquitto_property *props); void (*on_unsubscribe)(struct mosquitto *, void *userdata, int mid); + void (*on_unsubscribe_v5)(struct mosquitto *, void *userdata, int mid, const mosquitto_property *props); void (*on_log)(struct mosquitto *, void *userdata, int level, const char *str); - //void (*on_error)(); + /*void (*on_error)();*/ char *host; - int port; - int in_queue_len; - int out_queue_len; + uint16_t port; char *bind_address; + unsigned int reconnects; unsigned int reconnect_delay; unsigned int reconnect_delay_max; bool reconnect_exponential_backoff; char threaded; struct mosquitto__packet *out_packet_last; - int inflight_messages; - int max_inflight_messages; + mosquitto_property *connect_properties; # ifdef WITH_SRV ares_channel achan; # endif #endif + uint8_t max_qos; + uint8_t retain_available; + bool tcp_nodelay; #ifdef WITH_BROKER UT_hash_handle hh_id; UT_hash_handle hh_sock; struct mosquitto *for_free_next; + struct session_expiry_list *expiry_list_item; + uint16_t remote_port; #endif -#ifdef WITH_EPOLL uint32_t events; -#endif }; #define STREMPTY(str) (str[0] == '\0') +void do_client_disconnect(struct mosquitto *mosq, int reason_code, const mosquitto_property *properties); + #endif diff --git a/libs/mosquitto/src/mosquitto_plugin.h b/libs/mosquitto/src/mosquitto_plugin.h new file mode 100644 index 0000000..5b5974d --- /dev/null +++ b/libs/mosquitto/src/mosquitto_plugin.h @@ -0,0 +1,420 @@ +/* +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. +*/ + +#ifndef MOSQUITTO_PLUGIN_H +#define MOSQUITTO_PLUGIN_H + +/* + * File: mosquitto_plugin.h + * + * This header contains function declarations for use when writing a Mosquitto plugin. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* The generic plugin interface starts at version 5 */ +#define MOSQ_PLUGIN_VERSION 5 + +/* The old auth only interface stopped at version 4 */ +#define MOSQ_AUTH_PLUGIN_VERSION 4 + +#define MOSQ_ACL_NONE 0x00 +#define MOSQ_ACL_READ 0x01 +#define MOSQ_ACL_WRITE 0x02 +#define MOSQ_ACL_SUBSCRIBE 0x04 +#define MOSQ_ACL_UNSUBSCRIBE 0x08 + +#include +#include + +#include + +struct mosquitto; + +struct mosquitto_opt { + char *key; + char *value; +}; + +struct mosquitto_auth_opt { + char *key; + char *value; +}; + +struct mosquitto_acl_msg { + const char *topic; + const void *payload; + long payloadlen; + int qos; + bool retain; +}; + +#ifdef WIN32 +# define mosq_plugin_EXPORT __declspec(dllexport) +#else +# define mosq_plugin_EXPORT +#endif + +/* + * To create an authentication plugin you must include this file then implement + * the functions listed in the "Plugin Functions" section below. The resulting + * code should then be compiled as a shared library. Using gcc this can be + * achieved as follows: + * + * gcc -I -fPIC -shared plugin.c -o plugin.so + * + * On Mac OS X: + * + * gcc -I -fPIC -shared plugin.c -undefined dynamic_lookup -o plugin.so + * + * Authentication plugins can implement one or both of authentication and + * access control. If your plugin does not wish to handle either of + * authentication or access control it should return MOSQ_ERR_PLUGIN_DEFER. In + * this case, the next plugin will handle it. If all plugins return + * MOSQ_ERR_PLUGIN_DEFER, the request will be denied. + * + * For each check, the following flow happens: + * + * * The default password file and/or acl file checks are made. If either one + * of these is not defined, then they are considered to be deferred. If either + * one accepts the check, no further checks are made. If an error occurs, the + * check is denied + * * The first plugin does the check, if it returns anything other than + * MOSQ_ERR_PLUGIN_DEFER, then the check returns immediately. If the plugin + * returns MOSQ_ERR_PLUGIN_DEFER then the next plugin runs its check. + * * If the final plugin returns MOSQ_ERR_PLUGIN_DEFER, then access will be + * denied. + */ + +/* ========================================================================= + * + * Helper Functions + * + * ========================================================================= */ + +/* There are functions that are available for plugin developers to use in + * mosquitto_broker.h, including logging and accessor functions. + */ + + +/* ========================================================================= + * + * Section: Plugin Functions v5 + * + * This is the plugin version 5 interface, which covers authentication, access + * control, the $CONTROL topic space handling, and message inspection and + * modification. + * + * This interface is available from v2.0 onwards. + * + * There are just three functions to implement in your plugin. You should + * register callbacks to handle different events in your + * mosquitto_plugin_init() function. See mosquitto_broker.h for the events and + * callback registering functions. + * + * ========================================================================= */ + +/* + * Function: mosquitto_plugin_version + * + * The broker will attempt to call this function immediately after loading the + * plugin to check it is a supported plugin version. Your code must simply + * return the plugin interface version you support, i.e. 5. + * + * The supported_versions array tells you which plugin versions the broker supports. + * + * If the broker does not support the version that you require, return -1 to + * indicate failure. + */ +mosq_plugin_EXPORT int mosquitto_plugin_version(int supported_version_count, const int *supported_versions); + +/* + * Function: mosquitto_plugin_init + * + * Called after the plugin has been loaded and + * has been called. This will only ever be called once and can be used to + * initialise the plugin. + * + * Parameters: + * + * identifier - This is a pointer to an opaque structure which you must + * save and use when registering/unregistering callbacks. + * user_data - The pointer set here will be passed to the other plugin + * functions. Use to hold connection information for example. + * opts - Pointer to an array of struct mosquitto_opt, which + * provides the plugin options defined in the configuration file. + * opt_count - The number of elements in the opts array. + * + * Return value: + * Return 0 on success + * Return >0 on failure. + */ +mosq_plugin_EXPORT int mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **userdata, struct mosquitto_opt *options, int option_count); + + +/* + * Function: mosquitto_plugin_cleanup + * + * Called when the broker is shutting down. This will only ever be called once + * per plugin. + * + * Parameters: + * + * user_data - The pointer provided in . + * opts - Pointer to an array of struct mosquitto_opt, which + * provides the plugin options defined in the configuration file. + * opt_count - The number of elements in the opts array. + * + * Return value: + * Return 0 on success + * Return >0 on failure. + */ +mosq_plugin_EXPORT int mosquitto_plugin_cleanup(void *userdata, struct mosquitto_opt *options, int option_count); + + + +/* ========================================================================= + * + * Section: Plugin Functions v4 + * + * This is the plugin version 4 interface, which is exclusively for + * authentication and access control, and which is still supported for existing + * plugins. If you are developing a new plugin, please use the v5 interface. + * + * You must implement these functions in your plugin. + * + * ========================================================================= */ + +/* + * Function: mosquitto_auth_plugin_version + * + * The broker will call this function immediately after loading the plugin to + * check it is a supported plugin version. Your code must simply return + * the version of the plugin interface you support, i.e. 4. + */ +mosq_plugin_EXPORT int mosquitto_auth_plugin_version(void); + + +/* + * Function: mosquitto_auth_plugin_init + * + * Called after the plugin has been loaded and + * has been called. This will only ever be called once and can be used to + * initialise the plugin. + * + * Parameters: + * + * user_data - The pointer set here will be passed to the other plugin + * functions. Use to hold connection information for example. + * opts - Pointer to an array of struct mosquitto_opt, which + * provides the plugin options defined in the configuration file. + * opt_count - The number of elements in the opts array. + * + * Return value: + * Return 0 on success + * Return >0 on failure. + */ +mosq_plugin_EXPORT int mosquitto_auth_plugin_init(void **user_data, struct mosquitto_opt *opts, int opt_count); + + +/* + * Function: mosquitto_auth_plugin_cleanup + * + * Called when the broker is shutting down. This will only ever be called once + * per plugin. + * Note that will be called directly before + * this function. + * + * Parameters: + * + * user_data - The pointer provided in . + * opts - Pointer to an array of struct mosquitto_opt, which + * provides the plugin options defined in the configuration file. + * opt_count - The number of elements in the opts array. + * + * Return value: + * Return 0 on success + * Return >0 on failure. + */ +mosq_plugin_EXPORT int mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_opt *opts, int opt_count); + + +/* + * Function: mosquitto_auth_security_init + * + * This function is called in two scenarios: + * + * 1. When the broker starts up. + * 2. If the broker is requested to reload its configuration whilst running. In + * this case, will be called first, then + * this function will be called. In this situation, the reload parameter + * will be true. + * + * Parameters: + * + * user_data - The pointer provided in . + * opts - Pointer to an array of struct mosquitto_opt, which + * provides the plugin options defined in the configuration file. + * opt_count - The number of elements in the opts array. + * reload - If set to false, this is the first time the function has + * been called. If true, the broker has received a signal + * asking to reload its configuration. + * + * Return value: + * Return 0 on success + * Return >0 on failure. + */ +mosq_plugin_EXPORT int mosquitto_auth_security_init(void *user_data, struct mosquitto_opt *opts, int opt_count, bool reload); + + +/* + * Function: mosquitto_auth_security_cleanup + * + * This function is called in two scenarios: + * + * 1. When the broker is shutting down. + * 2. If the broker is requested to reload its configuration whilst running. In + * this case, this function will be called, followed by + * . In this situation, the reload parameter + * will be true. + * + * Parameters: + * + * user_data - The pointer provided in . + * opts - Pointer to an array of struct mosquitto_opt, which + * provides the plugin options defined in the configuration file. + * opt_count - The number of elements in the opts array. + * reload - If set to false, this is the first time the function has + * been called. If true, the broker has received a signal + * asking to reload its configuration. + * + * Return value: + * Return 0 on success + * Return >0 on failure. + */ +mosq_plugin_EXPORT int mosquitto_auth_security_cleanup(void *user_data, struct mosquitto_opt *opts, int opt_count, bool reload); + + +/* + * Function: mosquitto_auth_acl_check + * + * Called by the broker when topic access must be checked. access will be one + * of: + * MOSQ_ACL_SUBSCRIBE when a client is asking to subscribe to a topic string. + * This differs from MOSQ_ACL_READ in that it allows you to + * deny access to topic strings rather than by pattern. For + * example, you may use MOSQ_ACL_SUBSCRIBE to deny + * subscriptions to '#', but allow all topics in + * MOSQ_ACL_READ. This allows clients to subscribe to any + * topic they want, but not discover what topics are in use + * on the server. + * MOSQ_ACL_READ when a message is about to be sent to a client (i.e. whether + * it can read that topic or not). + * MOSQ_ACL_WRITE when a message has been received from a client (i.e. whether + * it can write to that topic or not). + * + * Return: + * MOSQ_ERR_SUCCESS if access was granted. + * MOSQ_ERR_ACL_DENIED if access was not granted. + * MOSQ_ERR_UNKNOWN for an application specific error. + * MOSQ_ERR_PLUGIN_DEFER if your plugin does not wish to handle this check. + */ +mosq_plugin_EXPORT int mosquitto_auth_acl_check(void *user_data, int access, struct mosquitto *client, const struct mosquitto_acl_msg *msg); + + +/* + * Function: mosquitto_auth_unpwd_check + * + * This function is OPTIONAL. Only include this function in your plugin if you + * are making basic username/password checks. + * + * Called by the broker when a username/password must be checked. + * + * Return: + * MOSQ_ERR_SUCCESS if the user is authenticated. + * MOSQ_ERR_AUTH if authentication failed. + * MOSQ_ERR_UNKNOWN for an application specific error. + * MOSQ_ERR_PLUGIN_DEFER if your plugin does not wish to handle this check. + */ +mosq_plugin_EXPORT int mosquitto_auth_unpwd_check(void *user_data, struct mosquitto *client, const char *username, const char *password); + + +/* + * Function: mosquitto_psk_key_get + * + * This function is OPTIONAL. Only include this function in your plugin if you + * are making TLS-PSK checks. + * + * Called by the broker when a client connects to a listener using TLS/PSK. + * This is used to retrieve the pre-shared-key associated with a client + * identity. + * + * Examine hint and identity to determine the required PSK (which must be a + * hexadecimal string with no leading "0x") and copy this string into key. + * + * Parameters: + * user_data - the pointer provided in . + * hint - the psk_hint for the listener the client is connecting to. + * identity - the identity string provided by the client + * key - a string where the hex PSK should be copied + * max_key_len - the size of key + * + * Return value: + * Return 0 on success. + * Return >0 on failure. + * Return MOSQ_ERR_PLUGIN_DEFER if your plugin does not wish to handle this check. + */ +mosq_plugin_EXPORT int mosquitto_auth_psk_key_get(void *user_data, struct mosquitto *client, const char *hint, const char *identity, char *key, int max_key_len); + +/* + * Function: mosquitto_auth_start + * + * This function is OPTIONAL. Only include this function in your plugin if you + * are making extended authentication checks. + * + * Parameters: + * user_data - the pointer provided in . + * method - the authentication method + * reauth - this is set to false if this is the first authentication attempt + * on a connection, set to true if the client is attempting to + * reauthenticate. + * data_in - pointer to authentication data, or NULL + * data_in_len - length of data_in, in bytes + * data_out - if your plugin wishes to send authentication data back to the + * client, allocate some memory using malloc or friends and set + * data_out. The broker will free the memory after use. + * data_out_len - Set the length of data_out in bytes. + * + * Return value: + * Return MOSQ_ERR_SUCCESS if authentication was successful. + * Return MOSQ_ERR_AUTH_CONTINUE if the authentication is a multi step process and can continue. + * Return MOSQ_ERR_AUTH if authentication was valid but did not succeed. + * Return any other relevant positive integer MOSQ_ERR_* to produce an error. + */ +mosq_plugin_EXPORT int mosquitto_auth_start(void *user_data, struct mosquitto *client, const char *method, bool reauth, const void *data_in, uint16_t data_in_len, void **data_out, uint16_t *data_out_len); + +mosq_plugin_EXPORT int mosquitto_auth_continue(void *user_data, struct mosquitto *client, const char *method, const void *data_in, uint16_t data_in_len, void **data_out, uint16_t *data_out_len); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libs/mosquitto/src/mqtt3_protocol.h b/libs/mosquitto/src/mqtt3_protocol.h deleted file mode 100644 index 8515f08..0000000 --- a/libs/mosquitto/src/mqtt3_protocol.h +++ /dev/null @@ -1,53 +0,0 @@ -/* -Copyright (c) 2009-2019 Roger Light - -All rights reserved. This program and the accompanying materials -are made available under the terms of the Eclipse Public License v1.0 -and Eclipse Distribution License v1.0 which accompany this distribution. - -The Eclipse Public License is available at - http://www.eclipse.org/legal/epl-v10.html -and the Eclipse Distribution License is available at - http://www.eclipse.org/org/documents/edl-v10.php. - -Contributors: - Roger Light - initial implementation and documentation. -*/ - -#ifndef MQTT3_PROTOCOL_H -#define MQTT3_PROTOCOL_H - -/* For version 3 of the MQTT protocol */ - -#define PROTOCOL_NAME_v31 "MQIsdp" -#define PROTOCOL_VERSION_v31 3 - -#define PROTOCOL_NAME_v311 "MQTT" -#define PROTOCOL_VERSION_v311 4 - -/* Message types */ -#define CONNECT 0x10 -#define CONNACK 0x20 -#define PUBLISH 0x30 -#define PUBACK 0x40 -#define PUBREC 0x50 -#define PUBREL 0x60 -#define PUBCOMP 0x70 -#define SUBSCRIBE 0x80 -#define SUBACK 0x90 -#define UNSUBSCRIBE 0xA0 -#define UNSUBACK 0xB0 -#define PINGREQ 0xC0 -#define PINGRESP 0xD0 -#define DISCONNECT 0xE0 - -#define CONNACK_ACCEPTED 0 -#define CONNACK_REFUSED_PROTOCOL_VERSION 1 -#define CONNACK_REFUSED_IDENTIFIER_REJECTED 2 -#define CONNACK_REFUSED_SERVER_UNAVAILABLE 3 -#define CONNACK_REFUSED_BAD_USERNAME_PASSWORD 4 -#define CONNACK_REFUSED_NOT_AUTHORIZED 5 - -#define MQTT_MAX_PAYLOAD 268435455 - -#endif diff --git a/libs/mosquitto/src/mqtt_protocol.h b/libs/mosquitto/src/mqtt_protocol.h new file mode 100644 index 0000000..15c4c3e --- /dev/null +++ b/libs/mosquitto/src/mqtt_protocol.h @@ -0,0 +1,282 @@ +/* +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 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 MQTT_PROTOCOL_H +#define MQTT_PROTOCOL_H + +/* + * File: mqtt_protocol.h + * + * This header contains definitions of MQTT values as defined in the specifications. + */ +#define PROTOCOL_NAME_v31 "MQIsdp" +#define PROTOCOL_VERSION_v31 3 + +#define PROTOCOL_NAME "MQTT" + +#define PROTOCOL_VERSION_v311 4 +#define PROTOCOL_VERSION_v5 5 + + +/* Message types */ +#define CMD_CONNECT 0x10U +#define CMD_CONNACK 0x20U +#define CMD_PUBLISH 0x30U +#define CMD_PUBACK 0x40U +#define CMD_PUBREC 0x50U +#define CMD_PUBREL 0x60U +#define CMD_PUBCOMP 0x70U +#define CMD_SUBSCRIBE 0x80U +#define CMD_SUBACK 0x90U +#define CMD_UNSUBSCRIBE 0xA0U +#define CMD_UNSUBACK 0xB0U +#define CMD_PINGREQ 0xC0U +#define CMD_PINGRESP 0xD0U +#define CMD_DISCONNECT 0xE0U +#define CMD_AUTH 0xF0U + +/* Mosquitto only: for distinguishing CONNECT and WILL properties */ +#define CMD_WILL 0x100 + +/* Enum: mqtt311_connack_codes + * + * The CONNACK results for MQTT v3.1.1, and v3.1. + * + * Values: + * CONNACK_ACCEPTED - 0 + * CONNACK_REFUSED_PROTOCOL_VERSION - 1 + * CONNACK_REFUSED_IDENTIFIER_REJECTED - 2 + * CONNACK_REFUSED_SERVER_UNAVAILABLE - 3 + * CONNACK_REFUSED_BAD_USERNAME_PASSWORD - 4 + * CONNACK_REFUSED_NOT_AUTHORIZED - 5 + */ +enum mqtt311_connack_codes { + CONNACK_ACCEPTED = 0, + CONNACK_REFUSED_PROTOCOL_VERSION = 1, + CONNACK_REFUSED_IDENTIFIER_REJECTED = 2, + CONNACK_REFUSED_SERVER_UNAVAILABLE = 3, + CONNACK_REFUSED_BAD_USERNAME_PASSWORD = 4, + CONNACK_REFUSED_NOT_AUTHORIZED = 5, +}; + +/* Enum: mqtt5_return_codes + * The reason codes returned in various MQTT commands. + * + * Values: + * MQTT_RC_SUCCESS - 0 + * MQTT_RC_NORMAL_DISCONNECTION - 0 + * MQTT_RC_GRANTED_QOS0 - 0 + * MQTT_RC_GRANTED_QOS1 - 1 + * MQTT_RC_GRANTED_QOS2 - 2 + * MQTT_RC_DISCONNECT_WITH_WILL_MSG - 4 + * MQTT_RC_NO_MATCHING_SUBSCRIBERS - 16 + * MQTT_RC_NO_SUBSCRIPTION_EXISTED - 17 + * MQTT_RC_CONTINUE_AUTHENTICATION - 24 + * MQTT_RC_REAUTHENTICATE - 25 + * MQTT_RC_UNSPECIFIED - 128 + * MQTT_RC_MALFORMED_PACKET - 129 + * MQTT_RC_PROTOCOL_ERROR - 130 + * MQTT_RC_IMPLEMENTATION_SPECIFIC - 131 + * MQTT_RC_UNSUPPORTED_PROTOCOL_VERSION - 132 + * MQTT_RC_CLIENTID_NOT_VALID - 133 + * MQTT_RC_BAD_USERNAME_OR_PASSWORD - 134 + * MQTT_RC_NOT_AUTHORIZED - 135 + * MQTT_RC_SERVER_UNAVAILABLE - 136 + * MQTT_RC_SERVER_BUSY - 137 + * MQTT_RC_BANNED - 138 + * MQTT_RC_SERVER_SHUTTING_DOWN - 139 + * MQTT_RC_BAD_AUTHENTICATION_METHOD - 140 + * MQTT_RC_KEEP_ALIVE_TIMEOUT - 141 + * MQTT_RC_SESSION_TAKEN_OVER - 142 + * MQTT_RC_TOPIC_FILTER_INVALID - 143 + * MQTT_RC_TOPIC_NAME_INVALID - 144 + * MQTT_RC_PACKET_ID_IN_USE - 145 + * MQTT_RC_PACKET_ID_NOT_FOUND - 146 + * MQTT_RC_RECEIVE_MAXIMUM_EXCEEDED - 147 + * MQTT_RC_TOPIC_ALIAS_INVALID - 148 + * MQTT_RC_PACKET_TOO_LARGE - 149 + * MQTT_RC_MESSAGE_RATE_TOO_HIGH - 150 + * MQTT_RC_QUOTA_EXCEEDED - 151 + * MQTT_RC_ADMINISTRATIVE_ACTION - 152 + * MQTT_RC_PAYLOAD_FORMAT_INVALID - 153 + * MQTT_RC_RETAIN_NOT_SUPPORTED - 154 + * MQTT_RC_QOS_NOT_SUPPORTED - 155 + * MQTT_RC_USE_ANOTHER_SERVER - 156 + * MQTT_RC_SERVER_MOVED - 157 + * MQTT_RC_SHARED_SUBS_NOT_SUPPORTED - 158 + * MQTT_RC_CONNECTION_RATE_EXCEEDED - 159 + * MQTT_RC_MAXIMUM_CONNECT_TIME - 160 + * MQTT_RC_SUBSCRIPTION_IDS_NOT_SUPPORTED - 161 + * MQTT_RC_WILDCARD_SUBS_NOT_SUPPORTED - 162 + */ +enum mqtt5_return_codes { + MQTT_RC_SUCCESS = 0, /* CONNACK, PUBACK, PUBREC, PUBREL, PUBCOMP, UNSUBACK, AUTH */ + MQTT_RC_NORMAL_DISCONNECTION = 0, /* DISCONNECT */ + MQTT_RC_GRANTED_QOS0 = 0, /* SUBACK */ + MQTT_RC_GRANTED_QOS1 = 1, /* SUBACK */ + MQTT_RC_GRANTED_QOS2 = 2, /* SUBACK */ + MQTT_RC_DISCONNECT_WITH_WILL_MSG = 4, /* DISCONNECT */ + MQTT_RC_NO_MATCHING_SUBSCRIBERS = 16, /* PUBACK, PUBREC */ + MQTT_RC_NO_SUBSCRIPTION_EXISTED = 17, /* UNSUBACK */ + MQTT_RC_CONTINUE_AUTHENTICATION = 24, /* AUTH */ + MQTT_RC_REAUTHENTICATE = 25, /* AUTH */ + + MQTT_RC_UNSPECIFIED = 128, /* CONNACK, PUBACK, PUBREC, SUBACK, UNSUBACK, DISCONNECT */ + MQTT_RC_MALFORMED_PACKET = 129, /* CONNACK, DISCONNECT */ + MQTT_RC_PROTOCOL_ERROR = 130, /* DISCONNECT */ + MQTT_RC_IMPLEMENTATION_SPECIFIC = 131, /* CONNACK, PUBACK, PUBREC, SUBACK, UNSUBACK, DISCONNECT */ + MQTT_RC_UNSUPPORTED_PROTOCOL_VERSION = 132, /* CONNACK */ + MQTT_RC_CLIENTID_NOT_VALID = 133, /* CONNACK */ + MQTT_RC_BAD_USERNAME_OR_PASSWORD = 134, /* CONNACK */ + MQTT_RC_NOT_AUTHORIZED = 135, /* CONNACK, PUBACK, PUBREC, SUBACK, UNSUBACK, DISCONNECT */ + MQTT_RC_SERVER_UNAVAILABLE = 136, /* CONNACK */ + MQTT_RC_SERVER_BUSY = 137, /* CONNACK, DISCONNECT */ + MQTT_RC_BANNED = 138, /* CONNACK */ + MQTT_RC_SERVER_SHUTTING_DOWN = 139, /* DISCONNECT */ + MQTT_RC_BAD_AUTHENTICATION_METHOD = 140, /* CONNACK */ + MQTT_RC_KEEP_ALIVE_TIMEOUT = 141, /* DISCONNECT */ + MQTT_RC_SESSION_TAKEN_OVER = 142, /* DISCONNECT */ + MQTT_RC_TOPIC_FILTER_INVALID = 143, /* SUBACK, UNSUBACK, DISCONNECT */ + MQTT_RC_TOPIC_NAME_INVALID = 144, /* CONNACK, PUBACK, PUBREC, DISCONNECT */ + MQTT_RC_PACKET_ID_IN_USE = 145, /* PUBACK, SUBACK, UNSUBACK */ + MQTT_RC_PACKET_ID_NOT_FOUND = 146, /* PUBREL, PUBCOMP */ + MQTT_RC_RECEIVE_MAXIMUM_EXCEEDED = 147, /* DISCONNECT */ + MQTT_RC_TOPIC_ALIAS_INVALID = 148, /* DISCONNECT */ + MQTT_RC_PACKET_TOO_LARGE = 149, /* CONNACK, PUBACK, PUBREC, DISCONNECT */ + MQTT_RC_MESSAGE_RATE_TOO_HIGH = 150, /* DISCONNECT */ + MQTT_RC_QUOTA_EXCEEDED = 151, /* PUBACK, PUBREC, SUBACK, DISCONNECT */ + MQTT_RC_ADMINISTRATIVE_ACTION = 152, /* DISCONNECT */ + MQTT_RC_PAYLOAD_FORMAT_INVALID = 153, /* CONNACK, DISCONNECT */ + MQTT_RC_RETAIN_NOT_SUPPORTED = 154, /* CONNACK, DISCONNECT */ + MQTT_RC_QOS_NOT_SUPPORTED = 155, /* CONNACK, DISCONNECT */ + MQTT_RC_USE_ANOTHER_SERVER = 156, /* CONNACK, DISCONNECT */ + MQTT_RC_SERVER_MOVED = 157, /* CONNACK, DISCONNECT */ + MQTT_RC_SHARED_SUBS_NOT_SUPPORTED = 158, /* SUBACK, DISCONNECT */ + MQTT_RC_CONNECTION_RATE_EXCEEDED = 159, /* CONNACK, DISCONNECT */ + MQTT_RC_MAXIMUM_CONNECT_TIME = 160, /* DISCONNECT */ + MQTT_RC_SUBSCRIPTION_IDS_NOT_SUPPORTED = 161, /* SUBACK, DISCONNECT */ + MQTT_RC_WILDCARD_SUBS_NOT_SUPPORTED = 162, /* SUBACK, DISCONNECT */ +}; + +/* Enum: mqtt5_property + * Options for use with MQTTv5 properties. + * Options: + * + * MQTT_PROP_PAYLOAD_FORMAT_INDICATOR - property option. + * MQTT_PROP_MESSAGE_EXPIRY_INTERVAL - property option. + * MQTT_PROP_CONTENT_TYPE - property option. + * MQTT_PROP_RESPONSE_TOPIC - property option. + * MQTT_PROP_CORRELATION_DATA - property option. + * MQTT_PROP_SUBSCRIPTION_IDENTIFIER - property option. + * MQTT_PROP_SESSION_EXPIRY_INTERVAL - property option. + * MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER - property option. + * MQTT_PROP_SERVER_KEEP_ALIVE - property option. + * MQTT_PROP_AUTHENTICATION_METHOD - property option. + * MQTT_PROP_AUTHENTICATION_DATA - property option. + * MQTT_PROP_REQUEST_PROBLEM_INFORMATION - property option. + * MQTT_PROP_WILL_DELAY_INTERVAL - property option. + * MQTT_PROP_REQUEST_RESPONSE_INFORMATION - property option. + * MQTT_PROP_RESPONSE_INFORMATION - property option. + * MQTT_PROP_SERVER_REFERENCE - property option. + * MQTT_PROP_REASON_STRING - property option. + * MQTT_PROP_RECEIVE_MAXIMUM - property option. + * MQTT_PROP_TOPIC_ALIAS_MAXIMUM - property option. + * MQTT_PROP_TOPIC_ALIAS - property option. + * MQTT_PROP_MAXIMUM_QOS - property option. + * MQTT_PROP_RETAIN_AVAILABLE - property option. + * MQTT_PROP_USER_PROPERTY - property option. + * MQTT_PROP_MAXIMUM_PACKET_SIZE - property option. + * MQTT_PROP_WILDCARD_SUB_AVAILABLE - property option. + * MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE - property option. + * MQTT_PROP_SHARED_SUB_AVAILABLE - property option. + */ +enum mqtt5_property { + MQTT_PROP_PAYLOAD_FORMAT_INDICATOR = 1, /* Byte : PUBLISH, Will Properties */ + MQTT_PROP_MESSAGE_EXPIRY_INTERVAL = 2, /* 4 byte int : PUBLISH, Will Properties */ + MQTT_PROP_CONTENT_TYPE = 3, /* UTF-8 string : PUBLISH, Will Properties */ + MQTT_PROP_RESPONSE_TOPIC = 8, /* UTF-8 string : PUBLISH, Will Properties */ + MQTT_PROP_CORRELATION_DATA = 9, /* Binary Data : PUBLISH, Will Properties */ + MQTT_PROP_SUBSCRIPTION_IDENTIFIER = 11, /* Variable byte int : PUBLISH, SUBSCRIBE */ + MQTT_PROP_SESSION_EXPIRY_INTERVAL = 17, /* 4 byte int : CONNECT, CONNACK, DISCONNECT */ + MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER = 18, /* UTF-8 string : CONNACK */ + MQTT_PROP_SERVER_KEEP_ALIVE = 19, /* 2 byte int : CONNACK */ + MQTT_PROP_AUTHENTICATION_METHOD = 21, /* UTF-8 string : CONNECT, CONNACK, AUTH */ + MQTT_PROP_AUTHENTICATION_DATA = 22, /* Binary Data : CONNECT, CONNACK, AUTH */ + MQTT_PROP_REQUEST_PROBLEM_INFORMATION = 23, /* Byte : CONNECT */ + MQTT_PROP_WILL_DELAY_INTERVAL = 24, /* 4 byte int : Will properties */ + MQTT_PROP_REQUEST_RESPONSE_INFORMATION = 25,/* Byte : CONNECT */ + MQTT_PROP_RESPONSE_INFORMATION = 26, /* UTF-8 string : CONNACK */ + MQTT_PROP_SERVER_REFERENCE = 28, /* UTF-8 string : CONNACK, DISCONNECT */ + MQTT_PROP_REASON_STRING = 31, /* UTF-8 string : All except Will properties */ + MQTT_PROP_RECEIVE_MAXIMUM = 33, /* 2 byte int : CONNECT, CONNACK */ + MQTT_PROP_TOPIC_ALIAS_MAXIMUM = 34, /* 2 byte int : CONNECT, CONNACK */ + MQTT_PROP_TOPIC_ALIAS = 35, /* 2 byte int : PUBLISH */ + MQTT_PROP_MAXIMUM_QOS = 36, /* Byte : CONNACK */ + MQTT_PROP_RETAIN_AVAILABLE = 37, /* Byte : CONNACK */ + MQTT_PROP_USER_PROPERTY = 38, /* UTF-8 string pair : All */ + MQTT_PROP_MAXIMUM_PACKET_SIZE = 39, /* 4 byte int : CONNECT, CONNACK */ + MQTT_PROP_WILDCARD_SUB_AVAILABLE = 40, /* Byte : CONNACK */ + MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE = 41, /* Byte : CONNACK */ + MQTT_PROP_SHARED_SUB_AVAILABLE = 42, /* Byte : CONNACK */ +}; + +enum mqtt5_property_type { + MQTT_PROP_TYPE_BYTE = 1, + MQTT_PROP_TYPE_INT16 = 2, + MQTT_PROP_TYPE_INT32 = 3, + MQTT_PROP_TYPE_VARINT = 4, + MQTT_PROP_TYPE_BINARY = 5, + MQTT_PROP_TYPE_STRING = 6, + MQTT_PROP_TYPE_STRING_PAIR = 7 +}; + +/* Enum: mqtt5_sub_options + * Options for use with MQTTv5 subscriptions. + * + * MQTT_SUB_OPT_NO_LOCAL - with this option set, if this client publishes to + * a topic to which it is subscribed, the broker will not publish the + * message back to the client. + * + * MQTT_SUB_OPT_RETAIN_AS_PUBLISHED - with this option set, messages + * published for this subscription will keep the retain flag as was set by + * the publishing client. The default behaviour without this option set has + * the retain flag indicating whether a message is fresh/stale. + * + * MQTT_SUB_OPT_SEND_RETAIN_ALWAYS - with this option set, pre-existing + * retained messages are sent as soon as the subscription is made, even + * if the subscription already exists. This is the default behaviour, so + * it is not necessary to set this option. + * + * MQTT_SUB_OPT_SEND_RETAIN_NEW - with this option set, pre-existing retained + * messages for this subscription will be sent when the subscription is made, + * but only if the subscription does not already exist. + * + * MQTT_SUB_OPT_SEND_RETAIN_NEVER - with this option set, pre-existing + * retained messages will never be sent for this subscription. + */ +enum mqtt5_sub_options { + MQTT_SUB_OPT_NO_LOCAL = 0x04, + MQTT_SUB_OPT_RETAIN_AS_PUBLISHED = 0x08, + MQTT_SUB_OPT_SEND_RETAIN_ALWAYS = 0x00, + MQTT_SUB_OPT_SEND_RETAIN_NEW = 0x10, + MQTT_SUB_OPT_SEND_RETAIN_NEVER = 0x20, +}; + +#define MQTT_MAX_PAYLOAD 268435455U + +#endif diff --git a/libs/mosquitto/src/net_mosq.c b/libs/mosquitto/src/net_mosq.c index 62afa75..22f5a31 100644 --- a/libs/mosquitto/src/net_mosq.c +++ b/libs/mosquitto/src/net_mosq.c @@ -1,15 +1,17 @@ /* -Copyright (c) 2009-2019 Roger Light +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. */ @@ -25,6 +27,7 @@ and the Eclipse Distribution License is available at #ifndef WIN32 #define _GNU_SOURCE #include +#include #include #include #else @@ -42,6 +45,10 @@ and the Eclipse Distribution License is available at # include #endif +#ifdef WITH_UNIX_SOCKETS +# include +#endif + #ifdef __QNX__ #include #endif @@ -50,6 +57,7 @@ and the Eclipse Distribution License is available at #include #include #include +#include #include #endif @@ -64,13 +72,60 @@ and the Eclipse Distribution License is available at #include "logging_mosq.h" #include "memory_mosq.h" -#include "mqtt3_protocol.h" +#include "mqtt_protocol.h" #include "net_mosq.h" #include "time_mosq.h" #include "util_mosq.h" #ifdef WITH_TLS int tls_ex_index_mosq = -1; +UI_METHOD *_ui_method = NULL; + +static bool is_tls_initialized = false; + +/* Functions taken from OpenSSL s_server/s_client */ +static int ui_open(UI *ui) +{ + return UI_method_get_opener(UI_OpenSSL())(ui); +} + +static int ui_read(UI *ui, UI_STRING *uis) +{ + return UI_method_get_reader(UI_OpenSSL())(ui, uis); +} + +static int ui_write(UI *ui, UI_STRING *uis) +{ + return UI_method_get_writer(UI_OpenSSL())(ui, uis); +} + +static int ui_close(UI *ui) +{ + return UI_method_get_closer(UI_OpenSSL())(ui); +} + +static void setup_ui_method(void) +{ + _ui_method = UI_create_method("OpenSSL application user interface"); + UI_method_set_opener(_ui_method, ui_open); + UI_method_set_reader(_ui_method, ui_read); + UI_method_set_writer(_ui_method, ui_write); + UI_method_set_closer(_ui_method, ui_close); +} + +static void cleanup_ui_method(void) +{ + if(_ui_method){ + UI_destroy_method(_ui_method); + _ui_method = NULL; + } +} + +UI_METHOD *net__get_ui_method(void) +{ + return _ui_method; +} + #endif int net__init(void) @@ -86,16 +141,6 @@ int net__init(void) ares_library_init(ARES_LIB_INIT_ALL); #endif -#ifdef WITH_TLS -# if OPENSSL_VERSION_NUMBER < 0x10100000L - SSL_load_error_strings(); - SSL_library_init(); - OpenSSL_add_all_algorithms(); -# endif - if(tls_ex_index_mosq == -1){ - tls_ex_index_mosq = SSL_get_ex_new_index(0, "client context", NULL, NULL, NULL); - } -#endif return MOSQ_ERR_SUCCESS; } @@ -111,9 +156,11 @@ void net__cleanup(void) # if !defined(OPENSSL_NO_ENGINE) ENGINE_cleanup(); # endif + is_tls_initialized = false; # endif CONF_modules_unload(1); + cleanup_ui_method(); #endif #ifdef WITH_SRV @@ -125,18 +172,42 @@ void net__cleanup(void) #endif } +#ifdef WITH_TLS +void net__init_tls(void) +{ + if(is_tls_initialized) return; + +# if OPENSSL_VERSION_NUMBER < 0x10100000L + SSL_load_error_strings(); + SSL_library_init(); + OpenSSL_add_all_algorithms(); +# else + OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS \ + | OPENSSL_INIT_ADD_ALL_DIGESTS \ + | OPENSSL_INIT_LOAD_CONFIG, NULL); +# endif +#if !defined(OPENSSL_NO_ENGINE) + ENGINE_load_builtin_engines(); +#endif + setup_ui_method(); + if(tls_ex_index_mosq == -1){ + tls_ex_index_mosq = SSL_get_ex_new_index(0, "client context", NULL, NULL, NULL); + } + + is_tls_initialized = true; +} +#endif /* Close a socket associated with a context and set it to -1. * Returns 1 on failure (context is NULL) * Returns 0 on success. */ -#ifdef WITH_BROKER -int net__socket_close(struct mosquitto_db *db, struct mosquitto *mosq) -#else int net__socket_close(struct mosquitto *mosq) -#endif { int rc = 0; +#ifdef WITH_BROKER + struct mosquitto *mosq_found; +#endif assert(mosq); #ifdef WITH_TLS @@ -145,7 +216,9 @@ int net__socket_close(struct mosquitto *mosq) #endif { if(mosq->ssl){ - SSL_shutdown(mosq->ssl); + if(!SSL_in_init(mosq->ssl)){ + SSL_shutdown(mosq->ssl); + } SSL_free(mosq->ssl); mosq->ssl = NULL; } @@ -156,15 +229,18 @@ int net__socket_close(struct mosquitto *mosq) if(mosq->wsi) { if(mosq->state != mosq_cs_disconnecting){ - mosq->state = mosq_cs_disconnect_ws; + mosquitto__set_state(mosq, mosq_cs_disconnect_ws); } - libwebsocket_callback_on_writable(mosq->ws_context, mosq->wsi); + lws_callback_on_writable(mosq->wsi); }else #endif { - if((int)mosq->sock >= 0){ + if(mosq->sock != INVALID_SOCKET){ #ifdef WITH_BROKER - HASH_DELETE(hh_sock, db->contexts_by_sock, mosq); + HASH_FIND(hh_sock, db.contexts_by_sock, &mosq->sock, sizeof(mosq->sock), mosq_found); + if(mosq_found){ + HASH_DELETE(hh_sock, db.contexts_by_sock, mosq_found); + } #endif rc = COMPAT_CLOSE(mosq->sock); mosq->sock = INVALID_SOCKET; @@ -174,6 +250,7 @@ int net__socket_close(struct mosquitto *mosq) #ifdef WITH_BROKER if(mosq->listener){ mosq->listener->client_count--; + mosq->listener = NULL; } #endif @@ -189,14 +266,16 @@ static unsigned int psk_client_callback(SSL *ssl, const char *hint, struct mosquitto *mosq; int len; + UNUSED(hint); + mosq = SSL_get_ex_data(ssl, tls_ex_index_mosq); if(!mosq) return 0; snprintf(identity, max_identity_len, "%s", mosq->tls_psk_identity); - len = mosquitto__hex2bin(mosq->tls_psk, psk, max_psk_len); + len = mosquitto__hex2bin(mosq->tls_psk, psk, (int)max_psk_len); if (len < 0) return 0; - return len; + return (unsigned int)len; } #endif @@ -308,16 +387,15 @@ int net__try_connect_step2(struct mosquitto *mosq, uint16_t port, mosq_sock_t *s #endif -int net__try_connect(struct mosquitto *mosq, const char *host, uint16_t port, mosq_sock_t *sock, const char *bind_address, bool blocking) +static int net__try_connect_tcp(const char *host, uint16_t port, mosq_sock_t *sock, const char *bind_address, bool blocking) { struct addrinfo hints; struct addrinfo *ainfo, *rp; struct addrinfo *ainfo_bind, *rp_bind; int s; int rc = MOSQ_ERR_SUCCESS; -#ifdef WIN32 - uint32_t val = 1; -#endif + + ainfo_bind = NULL; *sock = INVALID_SOCKET; memset(&hints, 0, sizeof(struct addrinfo)); @@ -405,16 +483,67 @@ int net__try_connect(struct mosquitto *mosq, const char *host, uint16_t port, mo } +#ifdef WITH_UNIX_SOCKETS +static int net__try_connect_unix(const char *host, mosq_sock_t *sock) +{ + struct sockaddr_un addr; + int s; + int rc; + + if(host == NULL || strlen(host) == 0 || strlen(host) > sizeof(addr.sun_path)-1){ + return MOSQ_ERR_INVAL; + } + + memset(&addr, 0, sizeof(struct sockaddr_un)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, host, sizeof(addr.sun_path)-1); + + s = socket(AF_UNIX, SOCK_STREAM, 0); + if(s < 0){ + return MOSQ_ERR_ERRNO; + } + rc = net__socket_nonblock(&s); + if(rc) return rc; + + rc = connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)); + if(rc < 0){ + close(s); + return MOSQ_ERR_ERRNO; + } + + *sock = s; + + return 0; +} +#endif + + +int net__try_connect(const char *host, uint16_t port, mosq_sock_t *sock, const char *bind_address, bool blocking) +{ + if(port == 0){ +#ifdef WITH_UNIX_SOCKETS + return net__try_connect_unix(host, sock); +#else + return MOSQ_ERR_NOT_SUPPORTED; +#endif + }else{ + return net__try_connect_tcp(host, port, sock, bind_address, blocking); + } +} + + #ifdef WITH_TLS void net__print_ssl_error(struct mosquitto *mosq) { char ebuf[256]; unsigned long e; + int num = 0; e = ERR_get_error(); while(e){ - log__printf(mosq, MOSQ_LOG_ERR, "OpenSSL Error: %s", ERR_error_string(e, ebuf)); + log__printf(mosq, MOSQ_LOG_ERR, "OpenSSL Error[%d]: %s", num, ERR_error_string(e, ebuf)); e = ERR_get_error(); + num++; } } @@ -422,8 +551,25 @@ void net__print_ssl_error(struct mosquitto *mosq) int net__socket_connect_tls(struct mosquitto *mosq) { int ret, err; + long res; ERR_clear_error(); + if (mosq->tls_ocsp_required) { + /* Note: OCSP is available in all currently supported OpenSSL versions. */ + if ((res=SSL_set_tlsext_status_type(mosq->ssl, TLSEXT_STATUSTYPE_ocsp)) != 1) { + log__printf(mosq, MOSQ_LOG_ERR, "Could not activate OCSP (error: %ld)", res); + return MOSQ_ERR_OCSP; + } + if ((res=SSL_CTX_set_tlsext_status_cb(mosq->ssl_ctx, mosquitto__verify_ocsp_status_cb)) != 1) { + log__printf(mosq, MOSQ_LOG_ERR, "Could not activate OCSP (error: %ld)", res); + return MOSQ_ERR_OCSP; + } + if ((res=SSL_CTX_set_tlsext_status_arg(mosq->ssl_ctx, mosq)) != 1) { + log__printf(mosq, MOSQ_LOG_ERR, "Could not activate OCSP (error: %ld)", res); + return MOSQ_ERR_OCSP; + } + } + ret = SSL_connect(mosq->ssl); if(ret != 1) { err = SSL_get_error(mosq->ssl, ret); @@ -454,24 +600,94 @@ int net__socket_connect_tls(struct mosquitto *mosq) #ifdef WITH_TLS +static int net__tls_load_ca(struct mosquitto *mosq) +{ + int ret; + + if(mosq->tls_use_os_certs){ + SSL_CTX_set_default_verify_paths(mosq->ssl_ctx); + } +#if OPENSSL_VERSION_NUMBER < 0x30000000L + if(mosq->tls_cafile || mosq->tls_capath){ + ret = SSL_CTX_load_verify_locations(mosq->ssl_ctx, mosq->tls_cafile, mosq->tls_capath); + if(ret == 0){ +# ifdef WITH_BROKER + if(mosq->tls_cafile && mosq->tls_capath){ + log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load CA certificates, check bridge_cafile \"%s\" and bridge_capath \"%s\".", mosq->tls_cafile, mosq->tls_capath); + }else if(mosq->tls_cafile){ + log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load CA certificates, check bridge_cafile \"%s\".", mosq->tls_cafile); + }else{ + log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load CA certificates, check bridge_capath \"%s\".", mosq->tls_capath); + } +# else + if(mosq->tls_cafile && mosq->tls_capath){ + log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load CA certificates, check cafile \"%s\" and capath \"%s\".", mosq->tls_cafile, mosq->tls_capath); + }else if(mosq->tls_cafile){ + log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load CA certificates, check cafile \"%s\".", mosq->tls_cafile); + }else{ + log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load CA certificates, check capath \"%s\".", mosq->tls_capath); + } +# endif + return MOSQ_ERR_TLS; + } + } +#else + if(mosq->tls_cafile){ + ret = SSL_CTX_load_verify_file(mosq->ssl_ctx, mosq->tls_cafile); + if(ret == 0){ +# ifdef WITH_BROKER + log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load CA certificates, check bridge_cafile \"%s\".", mosq->tls_cafile); +# else + log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load CA certificates, check cafile \"%s\".", mosq->tls_cafile); +# endif + return MOSQ_ERR_TLS; + } + } + if(mosq->tls_capath){ + ret = SSL_CTX_load_verify_dir(mosq->ssl_ctx, mosq->tls_capath); + if(ret == 0){ +# ifdef WITH_BROKER + log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load CA certificates, check bridge_capath \"%s\".", mosq->tls_capath); +# else + log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load CA certificates, check capath \"%s\".", mosq->tls_capath); +# endif + return MOSQ_ERR_TLS; + } + } +#endif + return MOSQ_ERR_SUCCESS; +} + + static int net__init_ssl_ctx(struct mosquitto *mosq) { int ret; + ENGINE *engine = NULL; + uint8_t tls_alpn_wire[256]; + uint8_t tls_alpn_len; +#if !defined(OPENSSL_NO_ENGINE) + EVP_PKEY *pkey; +#endif - if(mosq->ssl_ctx){ +#ifndef WITH_BROKER + if(mosq->user_ssl_ctx){ + mosq->ssl_ctx = mosq->user_ssl_ctx; if(!mosq->ssl_ctx_defaults){ return MOSQ_ERR_SUCCESS; }else if(!mosq->tls_cafile && !mosq->tls_capath && !mosq->tls_psk){ - log__printf(mosq, MOSQ_LOG_ERR, "Error: MOSQ_OPT_SSL_CTX_WITH_DEFAULTS used without specifying cafile, capath or psk."); + log__printf(mosq, MOSQ_LOG_ERR, "Error: If you use MOSQ_OPT_SSL_CTX then MOSQ_OPT_SSL_CTX_WITH_DEFAULTS must be true, or at least one of cafile, capath or psk must be specified."); return MOSQ_ERR_INVAL; } } +#endif /* Apply default SSL_CTX settings. This is only used if MOSQ_OPT_SSL_CTX * has not been set, or if both of MOSQ_OPT_SSL_CTX and * MOSQ_OPT_SSL_CTX_WITH_DEFAULTS are set. */ - if(mosq->tls_cafile || mosq->tls_capath || mosq->tls_psk){ + if(mosq->tls_cafile || mosq->tls_capath || mosq->tls_psk || mosq->tls_use_os_certs){ if(!mosq->ssl_ctx){ + net__init_tls(); + #if OPENSSL_VERSION_NUMBER < 0x10100000L mosq->ssl_ctx = SSL_CTX_new(SSLv23_client_method()); #else @@ -480,68 +696,86 @@ static int net__init_ssl_ctx(struct mosquitto *mosq) if(!mosq->ssl_ctx){ log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to create TLS context."); - COMPAT_CLOSE(mosq->sock); - mosq->sock = INVALID_SOCKET; net__print_ssl_error(mosq); return MOSQ_ERR_TLS; } } +#ifdef SSL_OP_NO_TLSv1_3 + if(mosq->tls_psk){ + SSL_CTX_set_options(mosq->ssl_ctx, SSL_OP_NO_TLSv1_3); + } +#endif + if(!mosq->tls_version){ - SSL_CTX_set_options(mosq->ssl_ctx, SSL_OP_NO_SSLv3); + SSL_CTX_set_options(mosq->ssl_ctx, SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1); +#ifdef SSL_OP_NO_TLSv1_3 + }else if(!strcmp(mosq->tls_version, "tlsv1.3")){ + SSL_CTX_set_options(mosq->ssl_ctx, SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2); +#endif }else if(!strcmp(mosq->tls_version, "tlsv1.2")){ - SSL_CTX_set_options(mosq->ssl_ctx, SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1); + SSL_CTX_set_options(mosq->ssl_ctx, SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1); }else if(!strcmp(mosq->tls_version, "tlsv1.1")){ - SSL_CTX_set_options(mosq->ssl_ctx, SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1_2 | SSL_OP_NO_TLSv1); - }else if(!strcmp(mosq->tls_version, "tlsv1")){ - SSL_CTX_set_options(mosq->ssl_ctx, SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1_2 | SSL_OP_NO_TLSv1_1); + SSL_CTX_set_options(mosq->ssl_ctx, SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1); }else{ log__printf(mosq, MOSQ_LOG_ERR, "Error: Protocol %s not supported.", mosq->tls_version); - COMPAT_CLOSE(mosq->sock); - mosq->sock = INVALID_SOCKET; return MOSQ_ERR_INVAL; } +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + /* Allow use of DHE ciphers */ + SSL_CTX_set_dh_auto(mosq->ssl_ctx, 1); +#endif /* Disable compression */ SSL_CTX_set_options(mosq->ssl_ctx, SSL_OP_NO_COMPRESSION); + /* Set ALPN */ + if(mosq->tls_alpn) { + tls_alpn_len = (uint8_t) strnlen(mosq->tls_alpn, 254); + tls_alpn_wire[0] = tls_alpn_len; /* first byte is length of string */ + memcpy(tls_alpn_wire + 1, mosq->tls_alpn, tls_alpn_len); + SSL_CTX_set_alpn_protos(mosq->ssl_ctx, tls_alpn_wire, tls_alpn_len + 1U); + } + #ifdef SSL_MODE_RELEASE_BUFFERS /* Use even less memory per SSL connection. */ SSL_CTX_set_mode(mosq->ssl_ctx, SSL_MODE_RELEASE_BUFFERS); #endif +#if !defined(OPENSSL_NO_ENGINE) + if(mosq->tls_engine){ + engine = ENGINE_by_id(mosq->tls_engine); + if(!engine){ + log__printf(mosq, MOSQ_LOG_ERR, "Error loading %s engine\n", mosq->tls_engine); + return MOSQ_ERR_TLS; + } + if(!ENGINE_init(engine)){ + log__printf(mosq, MOSQ_LOG_ERR, "Failed engine initialisation\n"); + ENGINE_free(engine); + return MOSQ_ERR_TLS; + } + ENGINE_set_default(engine, ENGINE_METHOD_ALL); + ENGINE_free(engine); /* release the structural reference from ENGINE_by_id() */ + } +#endif + if(mosq->tls_ciphers){ ret = SSL_CTX_set_cipher_list(mosq->ssl_ctx, mosq->tls_ciphers); if(ret == 0){ log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to set TLS ciphers. Check cipher list \"%s\".", mosq->tls_ciphers); - COMPAT_CLOSE(mosq->sock); - mosq->sock = INVALID_SOCKET; +#if !defined(OPENSSL_NO_ENGINE) + ENGINE_FINISH(engine); +#endif net__print_ssl_error(mosq); return MOSQ_ERR_TLS; } } - if(mosq->tls_cafile || mosq->tls_capath){ - ret = SSL_CTX_load_verify_locations(mosq->ssl_ctx, mosq->tls_cafile, mosq->tls_capath); - if(ret == 0){ -#ifdef WITH_BROKER - if(mosq->tls_cafile && mosq->tls_capath){ - log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load CA certificates, check bridge_cafile \"%s\" and bridge_capath \"%s\".", mosq->tls_cafile, mosq->tls_capath); - }else if(mosq->tls_cafile){ - log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load CA certificates, check bridge_cafile \"%s\".", mosq->tls_cafile); - }else{ - log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load CA certificates, check bridge_capath \"%s\".", mosq->tls_capath); - } -#else - if(mosq->tls_cafile && mosq->tls_capath){ - log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load CA certificates, check cafile \"%s\" and capath \"%s\".", mosq->tls_cafile, mosq->tls_capath); - }else if(mosq->tls_cafile){ - log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load CA certificates, check cafile \"%s\".", mosq->tls_cafile); - }else{ - log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load CA certificates, check capath \"%s\".", mosq->tls_capath); - } -#endif - COMPAT_CLOSE(mosq->sock); - mosq->sock = INVALID_SOCKET; + if(mosq->tls_cafile || mosq->tls_capath || mosq->tls_use_os_certs){ + ret = net__tls_load_ca(mosq); + if(ret != MOSQ_ERR_SUCCESS){ +# if !defined(OPENSSL_NO_ENGINE) + ENGINE_FINISH(engine); +# endif net__print_ssl_error(mosq); return MOSQ_ERR_TLS; } @@ -564,30 +798,67 @@ static int net__init_ssl_ctx(struct mosquitto *mosq) #else log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load client certificate \"%s\".", mosq->tls_certfile); #endif - COMPAT_CLOSE(mosq->sock); - mosq->sock = INVALID_SOCKET; +#if !defined(OPENSSL_NO_ENGINE) + ENGINE_FINISH(engine); +#endif net__print_ssl_error(mosq); return MOSQ_ERR_TLS; } } if(mosq->tls_keyfile){ - ret = SSL_CTX_use_PrivateKey_file(mosq->ssl_ctx, mosq->tls_keyfile, SSL_FILETYPE_PEM); - if(ret != 1){ + if(mosq->tls_keyform == mosq_k_engine){ +#if !defined(OPENSSL_NO_ENGINE) + UI_METHOD *ui_method = net__get_ui_method(); + if(mosq->tls_engine_kpass_sha1){ + if(!ENGINE_ctrl_cmd(engine, ENGINE_SECRET_MODE, ENGINE_SECRET_MODE_SHA, NULL, NULL, 0)){ + log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to set engine secret mode sha1"); + ENGINE_FINISH(engine); + net__print_ssl_error(mosq); + return MOSQ_ERR_TLS; + } + if(!ENGINE_ctrl_cmd(engine, ENGINE_PIN, 0, mosq->tls_engine_kpass_sha1, NULL, 0)){ + log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to set engine pin"); + ENGINE_FINISH(engine); + net__print_ssl_error(mosq); + return MOSQ_ERR_TLS; + } + ui_method = NULL; + } + pkey = ENGINE_load_private_key(engine, mosq->tls_keyfile, ui_method, NULL); + if(!pkey){ + log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load engine private key file \"%s\".", mosq->tls_keyfile); + ENGINE_FINISH(engine); + net__print_ssl_error(mosq); + return MOSQ_ERR_TLS; + } + if(SSL_CTX_use_PrivateKey(mosq->ssl_ctx, pkey) <= 0){ + log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to use engine private key file \"%s\".", mosq->tls_keyfile); + ENGINE_FINISH(engine); + net__print_ssl_error(mosq); + return MOSQ_ERR_TLS; + } +#endif + }else{ + ret = SSL_CTX_use_PrivateKey_file(mosq->ssl_ctx, mosq->tls_keyfile, SSL_FILETYPE_PEM); + if(ret != 1){ #ifdef WITH_BROKER - log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load client key file, check bridge_keyfile \"%s\".", mosq->tls_keyfile); + log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load client key file, check bridge_keyfile \"%s\".", mosq->tls_keyfile); #else - log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load client key file \"%s\".", mosq->tls_keyfile); + log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load client key file \"%s\".", mosq->tls_keyfile); #endif - COMPAT_CLOSE(mosq->sock); - mosq->sock = INVALID_SOCKET; - net__print_ssl_error(mosq); - return MOSQ_ERR_TLS; +#if !defined(OPENSSL_NO_ENGINE) + ENGINE_FINISH(engine); +#endif + net__print_ssl_error(mosq); + return MOSQ_ERR_TLS; + } } ret = SSL_CTX_check_private_key(mosq->ssl_ctx); if(ret != 1){ log__printf(mosq, MOSQ_LOG_ERR, "Error: Client certificate/key are inconsistent."); - COMPAT_CLOSE(mosq->sock); - mosq->sock = INVALID_SOCKET; +#if !defined(OPENSSL_NO_ENGINE) + ENGINE_FINISH(engine); +#endif net__print_ssl_error(mosq); return MOSQ_ERR_TLS; } @@ -595,6 +866,9 @@ static int net__init_ssl_ctx(struct mosquitto *mosq) #ifdef FINAL_WITH_TLS_PSK }else if(mosq->tls_psk){ SSL_CTX_set_psk_client_callback(mosq->ssl_ctx, psk_client_callback); + if(mosq->tls_ciphers == NULL){ + SSL_CTX_set_cipher_list(mosq->ssl_ctx, "PSK"); + } #endif } } @@ -610,7 +884,10 @@ int net__socket_connect_step3(struct mosquitto *mosq, const char *host) BIO *bio; int rc = net__init_ssl_ctx(mosq); - if(rc) return rc; + if(rc){ + net__socket_close(mosq); + return rc; + } if(mosq->ssl_ctx){ if(mosq->ssl){ @@ -618,8 +895,7 @@ int net__socket_connect_step3(struct mosquitto *mosq, const char *host) } mosq->ssl = SSL_new(mosq->ssl_ctx); if(!mosq->ssl){ - COMPAT_CLOSE(mosq->sock); - mosq->sock = INVALID_SOCKET; + net__socket_close(mosq); net__print_ssl_error(mosq); return MOSQ_ERR_TLS; } @@ -627,8 +903,7 @@ int net__socket_connect_step3(struct mosquitto *mosq, const char *host) SSL_set_ex_data(mosq->ssl, tls_ex_index_mosq, mosq); bio = BIO_new_socket(mosq->sock, BIO_NOCLOSE); if(!bio){ - COMPAT_CLOSE(mosq->sock); - mosq->sock = INVALID_SOCKET; + net__socket_close(mosq); net__print_ssl_error(mosq); return MOSQ_ERR_TLS; } @@ -638,16 +913,19 @@ int net__socket_connect_step3(struct mosquitto *mosq, const char *host) * required for the SNI resolving */ if(SSL_set_tlsext_host_name(mosq->ssl, host) != 1) { - COMPAT_CLOSE(mosq->sock); - mosq->sock = INVALID_SOCKET; + net__socket_close(mosq); return MOSQ_ERR_TLS; } if(net__socket_connect_tls(mosq)){ + net__socket_close(mosq); return MOSQ_ERR_TLS; } } +#else + UNUSED(mosq); + UNUSED(host); #endif return MOSQ_ERR_SUCCESS; } @@ -655,56 +933,76 @@ int net__socket_connect_step3(struct mosquitto *mosq, const char *host) /* Create a socket and connect it to 'ip' on port 'port'. */ int net__socket_connect(struct mosquitto *mosq, const char *host, uint16_t port, const char *bind_address, bool blocking) { - mosq_sock_t sock = INVALID_SOCKET; - int rc; + int rc, rc2; - if(!mosq || !host || !port) return MOSQ_ERR_INVAL; + if(!mosq || !host) return MOSQ_ERR_INVAL; - rc = net__try_connect(mosq, host, port, &sock, bind_address, blocking); + rc = net__try_connect(host, port, &mosq->sock, bind_address, blocking); if(rc > 0) return rc; - mosq->sock = sock; + if(mosq->tcp_nodelay){ + int flag = 1; + if(setsockopt(mosq->sock, IPPROTO_TCP, TCP_NODELAY, (const void*)&flag, sizeof(int)) != 0){ + log__printf(mosq, MOSQ_LOG_WARNING, "Warning: Unable to set TCP_NODELAY."); + } + } #if defined(WITH_SOCKS) && !defined(WITH_BROKER) if(!mosq->socks5_host) #endif { - rc = net__socket_connect_step3(mosq, host); - if(rc) return rc; + rc2 = net__socket_connect_step3(mosq, host); + if(rc2) return rc2; } - return MOSQ_ERR_SUCCESS; + return rc; } +#ifdef WITH_TLS +static int net__handle_ssl(struct mosquitto* mosq, int ret) +{ + int err; + + err = SSL_get_error(mosq->ssl, ret); + if (err == SSL_ERROR_WANT_READ) { + ret = -1; + errno = EAGAIN; + } + else if (err == SSL_ERROR_WANT_WRITE) { + ret = -1; +#ifdef WITH_BROKER + mux__add_out(mosq); +#else + mosq->want_write = true; +#endif + errno = EAGAIN; + } + else { + net__print_ssl_error(mosq); + errno = EPROTO; + } + ERR_clear_error(); +#ifdef WIN32 + WSASetLastError(errno); +#endif + + return ret; +} +#endif + ssize_t net__read(struct mosquitto *mosq, void *buf, size_t count) { #ifdef WITH_TLS int ret; - int err; #endif assert(mosq); errno = 0; #ifdef WITH_TLS if(mosq->ssl){ - ERR_clear_error(); - ret = SSL_read(mosq->ssl, buf, count); + ret = SSL_read(mosq->ssl, buf, (int)count); if(ret <= 0){ - err = SSL_get_error(mosq->ssl, ret); - if(err == SSL_ERROR_WANT_READ){ - ret = -1; - errno = EAGAIN; - }else if(err == SSL_ERROR_WANT_WRITE){ - ret = -1; - mosq->want_write = true; - errno = EAGAIN; - }else{ - net__print_ssl_error(mosq); - errno = EPROTO; - } -#ifdef WIN32 - WSASetLastError(errno); -#endif + ret = net__handle_ssl(mosq, ret); } return (ssize_t )ret; }else{ @@ -723,11 +1021,10 @@ ssize_t net__read(struct mosquitto *mosq, void *buf, size_t count) #endif } -ssize_t net__write(struct mosquitto *mosq, void *buf, size_t count) +ssize_t net__write(struct mosquitto *mosq, const void *buf, size_t count) { #ifdef WITH_TLS int ret; - int err; #endif assert(mosq); @@ -735,24 +1032,9 @@ ssize_t net__write(struct mosquitto *mosq, void *buf, size_t count) #ifdef WITH_TLS if(mosq->ssl){ mosq->want_write = false; - ERR_clear_error(); - ret = SSL_write(mosq->ssl, buf, count); + ret = SSL_write(mosq->ssl, buf, (int)count); if(ret < 0){ - err = SSL_get_error(mosq->ssl, ret); - if(err == SSL_ERROR_WANT_READ){ - ret = -1; - errno = EAGAIN; - }else if(err == SSL_ERROR_WANT_WRITE){ - ret = -1; - mosq->want_write = true; - errno = EAGAIN; - }else{ - net__print_ssl_error(mosq); - errno = EPROTO; - } -#ifdef WIN32 - WSASetLastError(errno); -#endif + ret = net__handle_ssl(mosq, ret); } return (ssize_t )ret; }else{ @@ -785,12 +1067,14 @@ int net__socket_nonblock(mosq_sock_t *sock) if(fcntl(*sock, F_SETFL, opt | O_NONBLOCK) == -1){ /* If either fcntl fails, don't want to allow this client to connect. */ COMPAT_CLOSE(*sock); + *sock = INVALID_SOCKET; return MOSQ_ERR_ERRNO; } #else unsigned long opt = 1; if(ioctlsocket(*sock, FIONBIO, &opt)){ COMPAT_CLOSE(*sock); + *sock = INVALID_SOCKET; return MOSQ_ERR_ERRNO; } #endif @@ -908,6 +1192,9 @@ int net__socketpair(mosq_sock_t *pairR, mosq_sock_t *pairW) #else int sv[2]; + *pairR = INVALID_SOCKET; + *pairW = INVALID_SOCKET; + if(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1){ return MOSQ_ERR_ERRNO; } @@ -925,3 +1212,16 @@ int net__socketpair(mosq_sock_t *pairR, mosq_sock_t *pairW) #endif } #endif + +#ifndef WITH_BROKER +void *mosquitto_ssl_get(struct mosquitto *mosq) +{ +#ifdef WITH_TLS + return mosq->ssl; +#else + UNUSED(mosq); + + return NULL; +#endif +} +#endif diff --git a/libs/mosquitto/src/net_mosq.h b/libs/mosquitto/src/net_mosq.h index 6033fd6..37a2146 100644 --- a/libs/mosquitto/src/net_mosq.h +++ b/libs/mosquitto/src/net_mosq.h @@ -1,15 +1,17 @@ /* -Copyright (c) 2010-2019 Roger Light +Copyright (c) 2010-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. */ @@ -17,26 +19,30 @@ and the Eclipse Distribution License is available at #define NET_MOSQ_H #ifndef WIN32 -#include +# include #else -#include +# include +# ifndef _SSIZE_T_DEFINED typedef SSIZE_T ssize_t; +# define _SSIZE_T_DEFINED +# endif #endif #include "mosquitto_internal.h" #include "mosquitto.h" -#ifdef WITH_BROKER -struct mosquitto_db; -#endif - #ifdef WIN32 # define COMPAT_CLOSE(a) closesocket(a) # define COMPAT_ECONNRESET WSAECONNRESET +# define COMPAT_EINTR WSAEINTR # define COMPAT_EWOULDBLOCK WSAEWOULDBLOCK +# ifndef EINPROGRESS +# define EINPROGRESS WSAEINPROGRESS +# endif #else # define COMPAT_CLOSE(a) close(a) # define COMPAT_ECONNRESET ECONNRESET +# define COMPAT_EINTR EINTR # define COMPAT_EWOULDBLOCK EWOULDBLOCK #endif @@ -52,13 +58,13 @@ struct mosquitto_db; int net__init(void); void net__cleanup(void); +#ifdef WITH_TLS +void net__init_tls(void); +#endif + int net__socket_connect(struct mosquitto *mosq, const char *host, uint16_t port, const char *bind_address, bool blocking); -#ifdef WITH_BROKER -int net__socket_close(struct mosquitto_db *db, struct mosquitto *mosq); -#else int net__socket_close(struct mosquitto *mosq); -#endif -int net__try_connect(struct mosquitto *mosq, const char *host, uint16_t port, mosq_sock_t *sock, const char *bind_address, bool blocking); +int net__try_connect(const char *host, uint16_t port, mosq_sock_t *sock, const char *bind_address, bool blocking); int net__try_connect_step1(struct mosquitto *mosq, const char *host); int net__try_connect_step2(struct mosquitto *mosq, uint16_t port, mosq_sock_t *sock); int net__socket_connect_step3(struct mosquitto *mosq, const char *host); @@ -66,11 +72,18 @@ int net__socket_nonblock(mosq_sock_t *sock); int net__socketpair(mosq_sock_t *sp1, mosq_sock_t *sp2); ssize_t net__read(struct mosquitto *mosq, void *buf, size_t count); -ssize_t net__write(struct mosquitto *mosq, void *buf, size_t count); +ssize_t net__write(struct mosquitto *mosq, const void *buf, size_t count); #ifdef WITH_TLS +void net__print_ssl_error(struct mosquitto *mosq); int net__socket_apply_tls(struct mosquitto *mosq); int net__socket_connect_tls(struct mosquitto *mosq); +int mosquitto__verify_ocsp_status_cb(SSL * ssl, void *arg); +UI_METHOD *net__get_ui_method(void); +#define ENGINE_FINISH(e) if(e) ENGINE_finish(e) +#define ENGINE_SECRET_MODE "SECRET_MODE" +#define ENGINE_SECRET_MODE_SHA 0x1000 +#define ENGINE_PIN "PIN" #endif #endif diff --git a/libs/mosquitto/src/net_mosq_ocsp.c b/libs/mosquitto/src/net_mosq_ocsp.c new file mode 100644 index 0000000..8c76237 --- /dev/null +++ b/libs/mosquitto/src/net_mosq_ocsp.c @@ -0,0 +1,167 @@ +/* +Copyright (c) 2009-2020 Roger Light +Copyright (c) 2017 Bayerische Motoren Werke Aktiengesellschaft (BMW AG), Dr. Lars Voelker + +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: + Dr. Lars Voelker, BMW AG +*/ + +/* +COPYRIGHT AND PERMISSION NOTICE of curl on which the ocsp code is based: + +Copyright (c) 1996 - 2016, Daniel Stenberg, , and many +contributors, see the THANKS file. + +All rights reserved. + +Permission to use, copy, modify, and distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright +notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN +NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall not +be used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization of the copyright holder. +*/ + +#include "config.h" + +#ifdef WITH_TLS +#include +#include +#include + +#include +#include +#include +#include + +int mosquitto__verify_ocsp_status_cb(SSL * ssl, void *arg) +{ + struct mosquitto *mosq = (struct mosquitto *)arg; + int ocsp_status, result2, i; + unsigned char *p; + const unsigned char *cp; + OCSP_RESPONSE *rsp = NULL; + OCSP_BASICRESP *br = NULL; + X509_STORE *st = NULL; + STACK_OF(X509) *ch = NULL; + long len; + + UNUSED(ssl); + + len = SSL_get_tlsext_status_ocsp_resp(mosq->ssl, &p); + log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: SSL_get_tlsext_status_ocsp_resp returned %ld bytes", len); + + /* the following functions expect a const pointer */ + cp = (const unsigned char *)p; + + if (!cp || len <= 0) { + log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: no response"); + goto end; + } + + + rsp = d2i_OCSP_RESPONSE(NULL, &cp, len); + if (rsp==NULL) { + log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: invalid response"); + goto end; + } + + ocsp_status = OCSP_response_status(rsp); + if(ocsp_status != OCSP_RESPONSE_STATUS_SUCCESSFUL) { + log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: invalid status: %s (%d)", + OCSP_response_status_str(ocsp_status), ocsp_status); + goto end; + } + + br = OCSP_response_get1_basic(rsp); + if (!br) { + log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: invalid response"); + goto end; + } + + ch = SSL_get_peer_cert_chain(mosq->ssl); + if (sk_X509_num(ch) <= 0) { + log__printf(mosq, MOSQ_LOG_ERR, "OCSP: we did not receive certificates of the server (num: %d)", sk_X509_num(ch)); + goto end; + } + + st = SSL_CTX_get_cert_store(mosq->ssl_ctx); + + /* Note: + * Other checkers often fix problems in OpenSSL before 1.0.2a (e.g. libcurl). + * For all currently supported versions of the OpenSSL project, this is not needed anymore. + */ + + if ((result2=OCSP_basic_verify(br, ch, st, 0)) <= 0) { + log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: response verification failed (error: %d)", result2); + goto end; + } + + for(i = 0; i < OCSP_resp_count(br); i++) { + int cert_status, crl_reason; + OCSP_SINGLERESP *single = NULL; + + ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd; + + single = OCSP_resp_get0(br, i); + if(!single) + continue; + + cert_status = OCSP_single_get0_status(single, &crl_reason, &rev, &thisupd, &nextupd); + + log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: SSL certificate status: %s (%d)", + OCSP_cert_status_str(cert_status), cert_status); + + switch(cert_status) { + case V_OCSP_CERTSTATUS_GOOD: + /* Note: A OCSP stapling result will be accepted up to 5 minutes after it expired! */ + if(!OCSP_check_validity(thisupd, nextupd, 300L, -1L)) { + log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: OCSP response has expired"); + goto end; + } + break; + + case V_OCSP_CERTSTATUS_REVOKED: + log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: SSL certificate revocation reason: %s (%d)", + OCSP_crl_reason_str(crl_reason), crl_reason); + goto end; + + case V_OCSP_CERTSTATUS_UNKNOWN: + goto end; + + default: + log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: SSL certificate revocation status unknown"); + goto end; + } + } + + if (br!=NULL) OCSP_BASICRESP_free(br); + if (rsp!=NULL) OCSP_RESPONSE_free(rsp); + return 1; /* OK */ + +end: + if (br!=NULL) OCSP_BASICRESP_free(br); + if (rsp!=NULL) OCSP_RESPONSE_free(rsp); + return 0; /* Not OK */ +} +#endif diff --git a/libs/mosquitto/src/options.c b/libs/mosquitto/src/options.c index c6b8ef4..29f8225 100644 --- a/libs/mosquitto/src/options.c +++ b/libs/mosquitto/src/options.c @@ -1,15 +1,17 @@ /* -Copyright (c) 2010-2019 Roger Light +Copyright (c) 2010-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. */ @@ -22,17 +24,40 @@ and the Eclipse Distribution License is available at #include +#ifdef WITH_TLS +# ifdef WIN32 +# include +# endif +# include +#endif + #include "mosquitto.h" #include "mosquitto_internal.h" #include "memory_mosq.h" +#include "misc_mosq.h" +#include "mqtt_protocol.h" #include "util_mosq.h" #include "will_mosq.h" int mosquitto_will_set(struct mosquitto *mosq, const char *topic, int payloadlen, const void *payload, int qos, bool retain) { + return mosquitto_will_set_v5(mosq, topic, payloadlen, payload, qos, retain, NULL); +} + + +int mosquitto_will_set_v5(struct mosquitto *mosq, const char *topic, int payloadlen, const void *payload, int qos, bool retain, mosquitto_property *properties) +{ + int rc; + if(!mosq) return MOSQ_ERR_INVAL; - return will__set(mosq, topic, payloadlen, payload, qos, retain); + + if(properties){ + rc = mosquitto_property_check_all(CMD_WILL, properties); + if(rc) return rc; + } + + return will__set(mosq, topic, payloadlen, payload, qos, retain, properties); } @@ -45,8 +70,16 @@ int mosquitto_will_clear(struct mosquitto *mosq) int mosquitto_username_pw_set(struct mosquitto *mosq, const char *username, const char *password) { + size_t slen; + if(!mosq) return MOSQ_ERR_INVAL; + if(mosq->protocol == mosq_p_mqtt311 || mosq->protocol == mosq_p_mqtt31){ + if(password != NULL && username == NULL){ + return MOSQ_ERR_INVAL; + } + } + mosquitto__free(mosq->username); mosq->username = NULL; @@ -54,18 +87,23 @@ int mosquitto_username_pw_set(struct mosquitto *mosq, const char *username, cons mosq->password = NULL; if(username){ - if(mosquitto_validate_utf8(username, strlen(username))){ + slen = strlen(username); + if(slen > UINT16_MAX){ + return MOSQ_ERR_INVAL; + } + if(mosquitto_validate_utf8(username, (int)slen)){ return MOSQ_ERR_MALFORMED_UTF8; } mosq->username = mosquitto__strdup(username); if(!mosq->username) return MOSQ_ERR_NOMEM; - if(password){ - mosq->password = mosquitto__strdup(password); - if(!mosq->password){ - mosquitto__free(mosq->username); - mosq->username = NULL; - return MOSQ_ERR_NOMEM; - } + } + + if(password){ + mosq->password = mosquitto__strdup(password); + if(!mosq->password){ + mosquitto__free(mosq->username); + mosq->username = NULL; + return MOSQ_ERR_NOMEM; } } return MOSQ_ERR_SUCCESS; @@ -75,15 +113,14 @@ int mosquitto_username_pw_set(struct mosquitto *mosq, const char *username, cons int mosquitto_reconnect_delay_set(struct mosquitto *mosq, unsigned int reconnect_delay, unsigned int reconnect_delay_max, bool reconnect_exponential_backoff) { if(!mosq) return MOSQ_ERR_INVAL; - + if(reconnect_delay == 0) reconnect_delay = 1; mosq->reconnect_delay = reconnect_delay; mosq->reconnect_delay_max = reconnect_delay_max; mosq->reconnect_exponential_backoff = reconnect_exponential_backoff; - + return MOSQ_ERR_SUCCESS; - } @@ -167,6 +204,13 @@ int mosquitto_tls_set(struct mosquitto *mosq, const char *cafile, const char *ca return MOSQ_ERR_SUCCESS; #else + UNUSED(mosq); + UNUSED(cafile); + UNUSED(capath); + UNUSED(certfile); + UNUSED(keyfile); + UNUSED(pw_callback); + return MOSQ_ERR_NOT_SUPPORTED; #endif @@ -180,9 +224,9 @@ int mosquitto_tls_opts_set(struct mosquitto *mosq, int cert_reqs, const char *tl mosq->tls_cert_reqs = cert_reqs; if(tls_version){ - if(!strcasecmp(tls_version, "tlsv1.2") - || !strcasecmp(tls_version, "tlsv1.1") - || !strcasecmp(tls_version, "tlsv1")){ + if(!strcasecmp(tls_version, "tlsv1.3") + || !strcasecmp(tls_version, "tlsv1.2") + || !strcasecmp(tls_version, "tlsv1.1")){ mosq->tls_version = mosquitto__strdup(tls_version); if(!mosq->tls_version) return MOSQ_ERR_NOMEM; @@ -203,8 +247,12 @@ int mosquitto_tls_opts_set(struct mosquitto *mosq, int cert_reqs, const char *tl return MOSQ_ERR_SUCCESS; #else - return MOSQ_ERR_NOT_SUPPORTED; + UNUSED(mosq); + UNUSED(cert_reqs); + UNUSED(tls_version); + UNUSED(ciphers); + return MOSQ_ERR_NOT_SUPPORTED; #endif } @@ -216,11 +264,102 @@ int mosquitto_tls_insecure_set(struct mosquitto *mosq, bool value) mosq->tls_insecure = value; return MOSQ_ERR_SUCCESS; #else + UNUSED(mosq); + UNUSED(value); + return MOSQ_ERR_NOT_SUPPORTED; #endif } +int mosquitto_string_option(struct mosquitto *mosq, enum mosq_opt_t option, const char *value) +{ +#ifdef WITH_TLS + ENGINE *eng; + char *str; +#endif + + if(!mosq) return MOSQ_ERR_INVAL; + + switch(option){ + case MOSQ_OPT_TLS_ENGINE: +#if defined(WITH_TLS) && !defined(OPENSSL_NO_ENGINE) + eng = ENGINE_by_id(value); + if(!eng){ + return MOSQ_ERR_INVAL; + } + ENGINE_free(eng); /* release the structural reference from ENGINE_by_id() */ + mosq->tls_engine = mosquitto__strdup(value); + if(!mosq->tls_engine){ + return MOSQ_ERR_NOMEM; + } + return MOSQ_ERR_SUCCESS; +#else + return MOSQ_ERR_NOT_SUPPORTED; +#endif + break; + + case MOSQ_OPT_TLS_KEYFORM: +#ifdef WITH_TLS + if(!value) return MOSQ_ERR_INVAL; + if(!strcasecmp(value, "pem")){ + mosq->tls_keyform = mosq_k_pem; + }else if (!strcasecmp(value, "engine")){ + mosq->tls_keyform = mosq_k_engine; + }else{ + return MOSQ_ERR_INVAL; + } + return MOSQ_ERR_SUCCESS; +#else + return MOSQ_ERR_NOT_SUPPORTED; +#endif + break; + + + case MOSQ_OPT_TLS_ENGINE_KPASS_SHA1: +#ifdef WITH_TLS + if(mosquitto__hex2bin_sha1(value, (unsigned char**)&str) != MOSQ_ERR_SUCCESS){ + return MOSQ_ERR_INVAL; + } + mosq->tls_engine_kpass_sha1 = str; + return MOSQ_ERR_SUCCESS; +#else + return MOSQ_ERR_NOT_SUPPORTED; +#endif + break; + + case MOSQ_OPT_TLS_ALPN: +#ifdef WITH_TLS + mosq->tls_alpn = mosquitto__strdup(value); + if(!mosq->tls_alpn){ + return MOSQ_ERR_NOMEM; + } + return MOSQ_ERR_SUCCESS; +#else + return MOSQ_ERR_NOT_SUPPORTED; +#endif + break; + + case MOSQ_OPT_BIND_ADDRESS: + mosquitto__free(mosq->bind_address); + if(value){ + mosq->bind_address = mosquitto__strdup(value); + if(mosq->bind_address){ + return MOSQ_ERR_SUCCESS; + }else{ + return MOSQ_ERR_NOMEM; + } + }else{ + return MOSQ_ERR_SUCCESS; + } + + + default: + return MOSQ_ERR_INVAL; + } +} + + int mosquitto_tls_psk_set(struct mosquitto *mosq, const char *psk, const char *identity, const char *ciphers) { #ifdef FINAL_WITH_TLS_PSK @@ -247,6 +386,11 @@ int mosquitto_tls_psk_set(struct mosquitto *mosq, const char *psk, const char *i return MOSQ_ERR_SUCCESS; #else + UNUSED(mosq); + UNUSED(psk); + UNUSED(identity); + UNUSED(ciphers); + return MOSQ_ERR_NOT_SUPPORTED; #endif } @@ -256,36 +400,121 @@ int mosquitto_opts_set(struct mosquitto *mosq, enum mosq_opt_t option, void *val { int ival; - if(!mosq || !value) return MOSQ_ERR_INVAL; + if(!mosq) return MOSQ_ERR_INVAL; switch(option){ case MOSQ_OPT_PROTOCOL_VERSION: + if(value == NULL){ + return MOSQ_ERR_INVAL; + } ival = *((int *)value); - if(ival == MQTT_PROTOCOL_V31){ + return mosquitto_int_option(mosq, option, ival); + case MOSQ_OPT_SSL_CTX: + return mosquitto_void_option(mosq, option, value); + default: + return MOSQ_ERR_INVAL; + } + return MOSQ_ERR_SUCCESS; +} + + +int mosquitto_int_option(struct mosquitto *mosq, enum mosq_opt_t option, int value) +{ + if(!mosq) return MOSQ_ERR_INVAL; + + switch(option){ + case MOSQ_OPT_PROTOCOL_VERSION: + if(value == MQTT_PROTOCOL_V31){ mosq->protocol = mosq_p_mqtt31; - }else if(ival == MQTT_PROTOCOL_V311){ + }else if(value == MQTT_PROTOCOL_V311){ mosq->protocol = mosq_p_mqtt311; + }else if(value == MQTT_PROTOCOL_V5){ + mosq->protocol = mosq_p_mqtt5; }else{ return MOSQ_ERR_INVAL; } break; - case MOSQ_OPT_SSL_CTX: -#ifdef WITH_TLS - mosq->ssl_ctx = (SSL_CTX *)value; - if(mosq->ssl_ctx){ -#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(LIBRESSL_VERSION_NUMBER) - SSL_CTX_up_ref(mosq->ssl_ctx); + + case MOSQ_OPT_RECEIVE_MAXIMUM: + if(value < 0 || value > UINT16_MAX){ + return MOSQ_ERR_INVAL; + } + if(value == 0){ + mosq->msgs_in.inflight_maximum = UINT16_MAX; + }else{ + mosq->msgs_in.inflight_maximum = (uint16_t)value; + } + break; + + case MOSQ_OPT_SEND_MAXIMUM: + if(value < 0 || value > UINT16_MAX){ + return MOSQ_ERR_INVAL; + } + if(value == 0){ + mosq->msgs_out.inflight_maximum = UINT16_MAX; + }else{ + mosq->msgs_out.inflight_maximum = (uint16_t)value; + } + break; + + case MOSQ_OPT_SSL_CTX_WITH_DEFAULTS: +#if defined(WITH_TLS) && OPENSSL_VERSION_NUMBER >= 0x10100000L + if(value){ + mosq->ssl_ctx_defaults = true; + }else{ + mosq->ssl_ctx_defaults = false; + } + break; #else - CRYPTO_add(&(mosq->ssl_ctx)->references, 1, CRYPTO_LOCK_SSL_CTX); + return MOSQ_ERR_NOT_SUPPORTED; #endif + + case MOSQ_OPT_TLS_USE_OS_CERTS: +#ifdef WITH_TLS + if(value){ + mosq->tls_use_os_certs = true; + }else{ + mosq->tls_use_os_certs = false; } break; #else return MOSQ_ERR_NOT_SUPPORTED; #endif - case MOSQ_OPT_SSL_CTX_WITH_DEFAULTS: -#if defined(WITH_TLS) && OPENSSL_VERSION_NUMBER >= 0x10100000L - mosq->ssl_ctx_defaults = true; + + case MOSQ_OPT_TLS_OCSP_REQUIRED: +#ifdef WITH_TLS + mosq->tls_ocsp_required = (bool)value; +#else + return MOSQ_ERR_NOT_SUPPORTED; +#endif + break; + + case MOSQ_OPT_TCP_NODELAY: + mosq->tcp_nodelay = (bool)value; + break; + + default: + return MOSQ_ERR_INVAL; + } + return MOSQ_ERR_SUCCESS; +} + + +int mosquitto_void_option(struct mosquitto *mosq, enum mosq_opt_t option, void *value) +{ + if(!mosq) return MOSQ_ERR_INVAL; + + switch(option){ + case MOSQ_OPT_SSL_CTX: +#ifdef WITH_TLS + mosq->user_ssl_ctx = (SSL_CTX *)value; + if(mosq->user_ssl_ctx){ +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) + SSL_CTX_up_ref(mosq->user_ssl_ctx); +#else + CRYPTO_add(&(mosq->user_ssl_ctx)->references, 1, CRYPTO_LOCK_SSL_CTX); +#endif + } break; #else return MOSQ_ERR_NOT_SUPPORTED; @@ -304,3 +533,7 @@ void mosquitto_user_data_set(struct mosquitto *mosq, void *userdata) } } +void *mosquitto_userdata(struct mosquitto *mosq) +{ + return mosq->userdata; +} diff --git a/libs/mosquitto/src/packet_datatypes.c b/libs/mosquitto/src/packet_datatypes.c new file mode 100644 index 0000000..57a1bc9 --- /dev/null +++ b/libs/mosquitto/src/packet_datatypes.c @@ -0,0 +1,273 @@ +/* +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 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 + +#ifdef WITH_BROKER +# include "mosquitto_broker_internal.h" +# ifdef WITH_WEBSOCKETS +# include +# endif +#else +# include "read_handle.h" +#endif + +#include "memory_mosq.h" +#include "mqtt_protocol.h" +#include "net_mosq.h" +#include "packet_mosq.h" +#include "read_handle.h" +#ifdef WITH_BROKER +# include "sys_tree.h" +#else +# define G_BYTES_RECEIVED_INC(A) +# define G_BYTES_SENT_INC(A) +# define G_MSGS_SENT_INC(A) +# define G_PUB_MSGS_SENT_INC(A) +#endif + + +int packet__read_byte(struct mosquitto__packet *packet, uint8_t *byte) +{ + assert(packet); + if(packet->pos+1 > packet->remaining_length) return MOSQ_ERR_MALFORMED_PACKET; + + *byte = packet->payload[packet->pos]; + packet->pos++; + + return MOSQ_ERR_SUCCESS; +} + + +void packet__write_byte(struct mosquitto__packet *packet, uint8_t byte) +{ + assert(packet); + assert(packet->pos+1 <= packet->packet_length); + + packet->payload[packet->pos] = byte; + packet->pos++; +} + + +int packet__read_bytes(struct mosquitto__packet *packet, void *bytes, uint32_t count) +{ + assert(packet); + if(packet->pos+count > packet->remaining_length) return MOSQ_ERR_MALFORMED_PACKET; + + memcpy(bytes, &(packet->payload[packet->pos]), count); + packet->pos += count; + + return MOSQ_ERR_SUCCESS; +} + + +void packet__write_bytes(struct mosquitto__packet *packet, const void *bytes, uint32_t count) +{ + assert(packet); + assert(packet->pos+count <= packet->packet_length); + + memcpy(&(packet->payload[packet->pos]), bytes, count); + packet->pos += count; +} + + +int packet__read_binary(struct mosquitto__packet *packet, uint8_t **data, uint16_t *length) +{ + uint16_t slen; + int rc; + + assert(packet); + rc = packet__read_uint16(packet, &slen); + if(rc) return rc; + + if(slen == 0){ + *data = NULL; + *length = 0; + return MOSQ_ERR_SUCCESS; + } + + if(packet->pos+slen > packet->remaining_length) return MOSQ_ERR_MALFORMED_PACKET; + + *data = mosquitto__malloc(slen+1U); + if(*data){ + memcpy(*data, &(packet->payload[packet->pos]), slen); + ((uint8_t *)(*data))[slen] = '\0'; + packet->pos += slen; + }else{ + return MOSQ_ERR_NOMEM; + } + + *length = slen; + return MOSQ_ERR_SUCCESS; +} + + +int packet__read_string(struct mosquitto__packet *packet, char **str, uint16_t *length) +{ + int rc; + + rc = packet__read_binary(packet, (uint8_t **)str, length); + if(rc) return rc; + if(*length == 0) return MOSQ_ERR_SUCCESS; + + if(mosquitto_validate_utf8(*str, *length)){ + mosquitto__free(*str); + *str = NULL; + *length = 0; + return MOSQ_ERR_MALFORMED_UTF8; + } + + return MOSQ_ERR_SUCCESS; +} + + +void packet__write_string(struct mosquitto__packet *packet, const char *str, uint16_t length) +{ + assert(packet); + packet__write_uint16(packet, length); + packet__write_bytes(packet, str, length); +} + + +int packet__read_uint16(struct mosquitto__packet *packet, uint16_t *word) +{ + uint8_t msb, lsb; + + assert(packet); + if(packet->pos+2 > packet->remaining_length) return MOSQ_ERR_MALFORMED_PACKET; + + msb = packet->payload[packet->pos]; + packet->pos++; + lsb = packet->payload[packet->pos]; + packet->pos++; + + *word = (uint16_t)((msb<<8) + lsb); + + return MOSQ_ERR_SUCCESS; +} + + +void packet__write_uint16(struct mosquitto__packet *packet, uint16_t word) +{ + packet__write_byte(packet, MOSQ_MSB(word)); + packet__write_byte(packet, MOSQ_LSB(word)); +} + + +int packet__read_uint32(struct mosquitto__packet *packet, uint32_t *word) +{ + uint32_t val = 0; + int i; + + assert(packet); + if(packet->pos+4 > packet->remaining_length) return MOSQ_ERR_MALFORMED_PACKET; + + for(i=0; i<4; i++){ + val = (val << 8) + packet->payload[packet->pos]; + packet->pos++; + } + + *word = val; + + return MOSQ_ERR_SUCCESS; +} + + +void packet__write_uint32(struct mosquitto__packet *packet, uint32_t word) +{ + packet__write_byte(packet, (uint8_t)((word & 0xFF000000) >> 24)); + packet__write_byte(packet, (uint8_t)((word & 0x00FF0000) >> 16)); + packet__write_byte(packet, (uint8_t)((word & 0x0000FF00) >> 8)); + packet__write_byte(packet, (uint8_t)((word & 0x000000FF))); +} + + +int packet__read_varint(struct mosquitto__packet *packet, uint32_t *word, uint8_t *bytes) +{ + int i; + uint8_t byte; + unsigned int remaining_mult = 1; + uint32_t lword = 0; + uint8_t lbytes = 0; + + for(i=0; i<4; i++){ + if(packet->pos < packet->remaining_length){ + lbytes++; + byte = packet->payload[packet->pos]; + lword += (byte & 127) * remaining_mult; + remaining_mult *= 128; + packet->pos++; + if((byte & 128) == 0){ + if(lbytes > 1 && byte == 0){ + /* Catch overlong encodings */ + return MOSQ_ERR_MALFORMED_PACKET; + }else{ + *word = lword; + if(bytes) (*bytes) = lbytes; + return MOSQ_ERR_SUCCESS; + } + } + }else{ + return MOSQ_ERR_MALFORMED_PACKET; + } + } + return MOSQ_ERR_MALFORMED_PACKET; +} + + +int packet__write_varint(struct mosquitto__packet *packet, uint32_t word) +{ + uint8_t byte; + int count = 0; + + do{ + byte = (uint8_t)(word % 128); + word = word / 128; + /* If there are more digits to encode, set the top bit of this digit */ + if(word > 0){ + byte = byte | 0x80; + } + packet__write_byte(packet, byte); + count++; + }while(word > 0 && count < 5); + + if(count == 5){ + return MOSQ_ERR_MALFORMED_PACKET; + } + return MOSQ_ERR_SUCCESS; +} + + +unsigned int packet__varint_bytes(uint32_t word) +{ + if(word < 128){ + return 1; + }else if(word < 16384){ + return 2; + }else if(word < 2097152){ + return 3; + }else if(word < 268435456){ + return 4; + }else{ + return 5; + } +} diff --git a/libs/mosquitto/src/packet_mosq.c b/libs/mosquitto/src/packet_mosq.c index 7152ea0..f65769f 100644 --- a/libs/mosquitto/src/packet_mosq.c +++ b/libs/mosquitto/src/packet_mosq.c @@ -1,15 +1,17 @@ /* -Copyright (c) 2009-2019 Roger Light +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. */ @@ -30,12 +32,14 @@ and the Eclipse Distribution License is available at #endif #include "memory_mosq.h" -#include "mqtt3_protocol.h" +#include "mqtt_protocol.h" #include "net_mosq.h" #include "packet_mosq.h" #include "read_handle.h" +#include "util_mosq.h" #ifdef WITH_BROKER # include "sys_tree.h" +# include "send_mosq.h" #else # define G_BYTES_RECEIVED_INC(A) # define G_BYTES_SENT_INC(A) @@ -65,9 +69,9 @@ int packet__alloc(struct mosquitto__packet *packet) packet->remaining_count++; }while(remaining_length > 0 && packet->remaining_count < 5); if(packet->remaining_count == 5) return MOSQ_ERR_PAYLOAD_SIZE; - packet->packet_length = packet->remaining_length + 1 + packet->remaining_count; + packet->packet_length = packet->remaining_length + 1 + (uint8_t)packet->remaining_count; #ifdef WITH_WEBSOCKETS - packet->payload = mosquitto__malloc(sizeof(uint8_t)*packet->packet_length + LWS_SEND_BUFFER_PRE_PADDING + LWS_SEND_BUFFER_POST_PADDING); + packet->payload = mosquitto__malloc(sizeof(uint8_t)*packet->packet_length + LWS_PRE); #else packet->payload = mosquitto__malloc(sizeof(uint8_t)*packet->packet_length); #endif @@ -77,7 +81,7 @@ int packet__alloc(struct mosquitto__packet *packet) for(i=0; iremaining_count; i++){ packet->payload[i+1] = remaining_bytes[i]; } - packet->pos = 1 + packet->remaining_count; + packet->pos = 1U + (uint8_t)packet->remaining_count; return MOSQ_ERR_SUCCESS; } @@ -97,6 +101,44 @@ void packet__cleanup(struct mosquitto__packet *packet) packet->pos = 0; } + +void packet__cleanup_all_no_locks(struct mosquitto *mosq) +{ + struct mosquitto__packet *packet; + + /* Out packet cleanup */ + if(mosq->out_packet && !mosq->current_out_packet){ + mosq->current_out_packet = mosq->out_packet; + mosq->out_packet = mosq->out_packet->next; + } + while(mosq->current_out_packet){ + packet = mosq->current_out_packet; + /* Free data and reset values */ + mosq->current_out_packet = mosq->out_packet; + if(mosq->out_packet){ + mosq->out_packet = mosq->out_packet->next; + } + + packet__cleanup(packet); + mosquitto__free(packet); + } + mosq->out_packet_count = 0; + + packet__cleanup(&mosq->in_packet); +} + +void packet__cleanup_all(struct mosquitto *mosq) +{ + pthread_mutex_lock(&mosq->current_out_packet_mutex); + pthread_mutex_lock(&mosq->out_packet_mutex); + + packet__cleanup_all_no_locks(mosq); + + pthread_mutex_unlock(&mosq->out_packet_mutex); + pthread_mutex_unlock(&mosq->current_out_packet_mutex); +} + + int packet__queue(struct mosquitto *mosq, struct mosquitto__packet *packet) { #ifndef WITH_BROKER @@ -116,11 +158,12 @@ int packet__queue(struct mosquitto *mosq, struct mosquitto__packet *packet) mosq->out_packet = packet; } mosq->out_packet_last = packet; + mosq->out_packet_count++; pthread_mutex_unlock(&mosq->out_packet_mutex); #ifdef WITH_BROKER # ifdef WITH_WEBSOCKETS if(mosq->wsi){ - libwebsocket_callback_on_writable(mosq->ws_context, mosq->wsi); + lws_callback_on_writable(mosq->wsi); return MOSQ_ERR_SUCCESS; }else{ return packet__write(mosq); @@ -150,105 +193,18 @@ int packet__queue(struct mosquitto *mosq, struct mosquitto__packet *packet) } -int packet__read_byte(struct mosquitto__packet *packet, uint8_t *byte) -{ - assert(packet); - if(packet->pos+1 > packet->remaining_length) return MOSQ_ERR_PROTOCOL; - - *byte = packet->payload[packet->pos]; - packet->pos++; - - return MOSQ_ERR_SUCCESS; -} - - -void packet__write_byte(struct mosquitto__packet *packet, uint8_t byte) +int packet__check_oversize(struct mosquitto *mosq, uint32_t remaining_length) { - assert(packet); - assert(packet->pos+1 <= packet->packet_length); - - packet->payload[packet->pos] = byte; - packet->pos++; -} + uint32_t len; + if(mosq->maximum_packet_size == 0) return MOSQ_ERR_SUCCESS; -int packet__read_bytes(struct mosquitto__packet *packet, void *bytes, uint32_t count) -{ - assert(packet); - if(packet->pos+count > packet->remaining_length) return MOSQ_ERR_PROTOCOL; - - memcpy(bytes, &(packet->payload[packet->pos]), count); - packet->pos += count; - - return MOSQ_ERR_SUCCESS; -} - - -void packet__write_bytes(struct mosquitto__packet *packet, const void *bytes, uint32_t count) -{ - assert(packet); - assert(packet->pos+count <= packet->packet_length); - - memcpy(&(packet->payload[packet->pos]), bytes, count); - packet->pos += count; -} - - -int packet__read_string(struct mosquitto__packet *packet, char **str, int *length) -{ - uint16_t slen; - int rc; - - assert(packet); - rc = packet__read_uint16(packet, &slen); - if(rc) return rc; - - if(packet->pos+slen > packet->remaining_length) return MOSQ_ERR_PROTOCOL; - - *str = mosquitto__malloc(slen+1); - if(*str){ - memcpy(*str, &(packet->payload[packet->pos]), slen); - (*str)[slen] = '\0'; - packet->pos += slen; + len = remaining_length + packet__varint_bytes(remaining_length); + if(len > mosq->maximum_packet_size){ + return MOSQ_ERR_OVERSIZE_PACKET; }else{ - return MOSQ_ERR_NOMEM; + return MOSQ_ERR_SUCCESS; } - - *length = slen; - return MOSQ_ERR_SUCCESS; -} - - -void packet__write_string(struct mosquitto__packet *packet, const char *str, uint16_t length) -{ - assert(packet); - packet__write_uint16(packet, length); - packet__write_bytes(packet, str, length); -} - - -int packet__read_uint16(struct mosquitto__packet *packet, uint16_t *word) -{ - uint8_t msb, lsb; - - assert(packet); - if(packet->pos+2 > packet->remaining_length) return MOSQ_ERR_PROTOCOL; - - msb = packet->payload[packet->pos]; - packet->pos++; - lsb = packet->payload[packet->pos]; - packet->pos++; - - *word = (msb<<8) + lsb; - - return MOSQ_ERR_SUCCESS; -} - - -void packet__write_uint16(struct mosquitto__packet *packet, uint16_t word) -{ - packet__write_byte(packet, MOSQ_MSB(word)); - packet__write_byte(packet, MOSQ_LSB(word)); } @@ -256,6 +212,7 @@ int packet__write(struct mosquitto *mosq) { ssize_t write_length; struct mosquitto__packet *packet; + enum mosquitto_client_state state; if(!mosq) return MOSQ_ERR_INVAL; if(mosq->sock == INVALID_SOCKET) return MOSQ_ERR_NO_CONN; @@ -268,13 +225,21 @@ int packet__write(struct mosquitto *mosq) if(!mosq->out_packet){ mosq->out_packet_last = NULL; } + mosq->out_packet_count--; } pthread_mutex_unlock(&mosq->out_packet_mutex); +#ifdef WITH_BROKER + if(mosq->current_out_packet){ + mux__add_out(mosq); + } +#endif + + state = mosquitto__get_state(mosq); #if defined(WITH_TLS) && !defined(WITH_BROKER) - if((mosq->state == mosq_cs_connect_pending) || mosq->want_connect){ + if(state == mosq_cs_connect_pending || mosq->want_connect){ #else - if(mosq->state == mosq_cs_connect_pending){ + if(state == mosq_cs_connect_pending){ #endif pthread_mutex_unlock(&mosq->current_out_packet_mutex); return MOSQ_ERR_SUCCESS; @@ -287,13 +252,17 @@ int packet__write(struct mosquitto *mosq) write_length = net__write(mosq, &(packet->payload[packet->pos]), packet->to_process); if(write_length > 0){ G_BYTES_SENT_INC(write_length); - packet->to_process -= write_length; - packet->pos += write_length; + packet->to_process -= (uint32_t)write_length; + packet->pos += (uint32_t)write_length; }else{ #ifdef WIN32 errno = WSAGetLastError(); #endif - if(errno == EAGAIN || errno == COMPAT_EWOULDBLOCK){ + if(errno == EAGAIN || errno == COMPAT_EWOULDBLOCK +#ifdef WIN32 + || errno == WSAENOTCONN +#endif + ){ pthread_mutex_unlock(&mosq->current_out_packet_mutex); return MOSQ_ERR_SUCCESS; }else{ @@ -301,6 +270,8 @@ int packet__write(struct mosquitto *mosq) switch(errno){ case COMPAT_ECONNRESET: return MOSQ_ERR_CONN_LOST; + case COMPAT_EINTR: + return MOSQ_ERR_SUCCESS; default: return MOSQ_ERR_ERRNO; } @@ -309,7 +280,7 @@ int packet__write(struct mosquitto *mosq) } G_MSGS_SENT_INC(1); - if(((packet->command)&0xF6) == PUBLISH){ + if(((packet->command)&0xF6) == CMD_PUBLISH){ G_PUB_MSGS_SENT_INC(1); #ifndef WITH_BROKER pthread_mutex_lock(&mosq->callback_mutex); @@ -319,43 +290,21 @@ int packet__write(struct mosquitto *mosq) mosq->on_publish(mosq, mosq->userdata, packet->mid); mosq->in_callback = false; } - pthread_mutex_unlock(&mosq->callback_mutex); - }else if(((packet->command)&0xF0) == DISCONNECT){ - /* FIXME what cleanup needs doing here? - * incoming/outgoing messages? */ - net__socket_close(mosq); - - /* Start of duplicate, possibly unnecessary code. - * This does leave things in a consistent state at least. */ - /* Free data and reset values */ - pthread_mutex_lock(&mosq->out_packet_mutex); - mosq->current_out_packet = mosq->out_packet; - if(mosq->out_packet){ - mosq->out_packet = mosq->out_packet->next; - if(!mosq->out_packet){ - mosq->out_packet_last = NULL; - } - } - pthread_mutex_unlock(&mosq->out_packet_mutex); - - packet__cleanup(packet); - mosquitto__free(packet); - - pthread_mutex_lock(&mosq->msgtime_mutex); - mosq->next_msg_out = mosquitto_time() + mosq->keepalive; - pthread_mutex_unlock(&mosq->msgtime_mutex); - /* End of duplicate, possibly unnecessary code */ - - pthread_mutex_lock(&mosq->callback_mutex); - if(mosq->on_disconnect){ + if(mosq->on_publish_v5){ + /* This is a QoS=0 message */ mosq->in_callback = true; - mosq->on_disconnect(mosq, mosq->userdata, MOSQ_ERR_SUCCESS); + mosq->on_publish_v5(mosq, mosq->userdata, packet->mid, 0, NULL); mosq->in_callback = false; } pthread_mutex_unlock(&mosq->callback_mutex); - pthread_mutex_unlock(&mosq->current_out_packet_mutex); + }else if(((packet->command)&0xF0) == CMD_DISCONNECT){ + do_client_disconnect(mosq, MOSQ_ERR_SUCCESS, NULL); + packet__cleanup(packet); + mosquitto__free(packet); return MOSQ_ERR_SUCCESS; #endif + }else if(((packet->command)&0xF0) == CMD_PUBLISH){ + G_PUB_MSGS_SENT_INC(1); } /* Free data and reset values */ @@ -366,34 +315,47 @@ int packet__write(struct mosquitto *mosq) if(!mosq->out_packet){ mosq->out_packet_last = NULL; } + mosq->out_packet_count--; } pthread_mutex_unlock(&mosq->out_packet_mutex); packet__cleanup(packet); mosquitto__free(packet); +#ifdef WITH_BROKER + mosq->next_msg_out = db.now_s + mosq->keepalive; +#else pthread_mutex_lock(&mosq->msgtime_mutex); mosq->next_msg_out = mosquitto_time() + mosq->keepalive; pthread_mutex_unlock(&mosq->msgtime_mutex); +#endif + } +#ifdef WITH_BROKER + if (mosq->current_out_packet == NULL) { + mux__remove_out(mosq); } +#endif pthread_mutex_unlock(&mosq->current_out_packet_mutex); return MOSQ_ERR_SUCCESS; } -#ifdef WITH_BROKER -int packet__read(struct mosquitto_db *db, struct mosquitto *mosq) -#else int packet__read(struct mosquitto *mosq) -#endif { uint8_t byte; ssize_t read_length; int rc = 0; + enum mosquitto_client_state state; - if(!mosq) return MOSQ_ERR_INVAL; - if(mosq->sock == INVALID_SOCKET) return MOSQ_ERR_NO_CONN; - if(mosq->state == mosq_cs_connect_pending){ + if(!mosq){ + return MOSQ_ERR_INVAL; + } + if(mosq->sock == INVALID_SOCKET){ + return MOSQ_ERR_NO_CONN; + } + + state = mosquitto__get_state(mosq); + if(state == mosq_cs_connect_pending){ return MOSQ_ERR_SUCCESS; } @@ -418,10 +380,14 @@ int packet__read(struct mosquitto *mosq) #ifdef WITH_BROKER G_BYTES_RECEIVED_INC(1); /* Clients must send CONNECT as their first command. */ - if(!(mosq->bridge) && mosq->state == mosq_cs_new && (byte&0xF0) != CONNECT) return MOSQ_ERR_PROTOCOL; + if(!(mosq->bridge) && state == mosq_cs_connected && (byte&0xF0) != CMD_CONNECT){ + return MOSQ_ERR_PROTOCOL; + } #endif }else{ - if(read_length == 0) return MOSQ_ERR_CONN_LOST; /* EOF */ + if(read_length == 0){ + return MOSQ_ERR_CONN_LOST; /* EOF */ + } #ifdef WIN32 errno = WSAGetLastError(); #endif @@ -431,6 +397,8 @@ int packet__read(struct mosquitto *mosq) switch(errno){ case COMPAT_ECONNRESET: return MOSQ_ERR_CONN_LOST; + case COMPAT_EINTR: + return MOSQ_ERR_SUCCESS; default: return MOSQ_ERR_ERRNO; } @@ -454,13 +422,17 @@ int packet__read(struct mosquitto *mosq) /* Max 4 bytes length for remaining length as defined by protocol. * Anything more likely means a broken/malicious client. */ - if(mosq->in_packet.remaining_count < -4) return MOSQ_ERR_PROTOCOL; + if(mosq->in_packet.remaining_count < -4){ + return MOSQ_ERR_MALFORMED_PACKET; + } G_BYTES_RECEIVED_INC(1); mosq->in_packet.remaining_length += (byte & 127) * mosq->in_packet.remaining_mult; mosq->in_packet.remaining_mult *= 128; }else{ - if(read_length == 0) return MOSQ_ERR_CONN_LOST; /* EOF */ + if(read_length == 0){ + return MOSQ_ERR_CONN_LOST; /* EOF */ + } #ifdef WIN32 errno = WSAGetLastError(); #endif @@ -470,6 +442,8 @@ int packet__read(struct mosquitto *mosq) switch(errno){ case COMPAT_ECONNRESET: return MOSQ_ERR_CONN_LOST; + case COMPAT_EINTR: + return MOSQ_ERR_SUCCESS; default: return MOSQ_ERR_ERRNO; } @@ -478,11 +452,54 @@ int packet__read(struct mosquitto *mosq) }while((byte & 128) != 0); /* We have finished reading remaining_length, so make remaining_count * positive. */ - mosq->in_packet.remaining_count *= -1; + mosq->in_packet.remaining_count = (int8_t)(mosq->in_packet.remaining_count * -1); + +#ifdef WITH_BROKER + switch(mosq->in_packet.command & 0xF0){ + case CMD_CONNECT: + if(mosq->in_packet.remaining_length > 100000){ /* Arbitrary limit, make configurable */ + return MOSQ_ERR_MALFORMED_PACKET; + } + break; + + case CMD_PUBACK: + case CMD_PUBREC: + case CMD_PUBREL: + case CMD_PUBCOMP: + case CMD_UNSUBACK: + if(mosq->protocol != mosq_p_mqtt5 && mosq->in_packet.remaining_length != 2){ + return MOSQ_ERR_MALFORMED_PACKET; + } + break; + + case CMD_PINGREQ: + case CMD_PINGRESP: + if(mosq->in_packet.remaining_length != 0){ + return MOSQ_ERR_MALFORMED_PACKET; + } + break; + case CMD_DISCONNECT: + if(mosq->protocol != mosq_p_mqtt5 && mosq->in_packet.remaining_length != 0){ + return MOSQ_ERR_MALFORMED_PACKET; + } + break; + } + + if(db.config->max_packet_size > 0 && mosq->in_packet.remaining_length+1 > db.config->max_packet_size){ + if(mosq->protocol == mosq_p_mqtt5){ + send__disconnect(mosq, MQTT_RC_PACKET_TOO_LARGE, NULL); + } + return MOSQ_ERR_OVERSIZE_PACKET; + } +#else + /* FIXME - client case for incoming message received from broker too large */ +#endif if(mosq->in_packet.remaining_length > 0){ mosq->in_packet.payload = mosquitto__malloc(mosq->in_packet.remaining_length*sizeof(uint8_t)); - if(!mosq->in_packet.payload) return MOSQ_ERR_NOMEM; + if(!mosq->in_packet.payload){ + return MOSQ_ERR_NOMEM; + } mosq->in_packet.to_process = mosq->in_packet.remaining_length; } } @@ -490,8 +507,8 @@ int packet__read(struct mosquitto *mosq) read_length = net__read(mosq, &(mosq->in_packet.payload[mosq->in_packet.pos]), mosq->in_packet.to_process); if(read_length > 0){ G_BYTES_RECEIVED_INC(read_length); - mosq->in_packet.to_process -= read_length; - mosq->in_packet.pos += read_length; + mosq->in_packet.to_process -= (uint32_t)read_length; + mosq->in_packet.pos += (uint32_t)read_length; }else{ #ifdef WIN32 errno = WSAGetLastError(); @@ -503,15 +520,21 @@ int packet__read(struct mosquitto *mosq) * This is an arbitrary limit, but with some consideration. * If a client can't send 1000 bytes in a second it * probably shouldn't be using a 1 second keep alive. */ +#ifdef WITH_BROKER + keepalive__update(mosq); +#else pthread_mutex_lock(&mosq->msgtime_mutex); mosq->last_msg_in = mosquitto_time(); pthread_mutex_unlock(&mosq->msgtime_mutex); +#endif } return MOSQ_ERR_SUCCESS; }else{ switch(errno){ case COMPAT_ECONNRESET: return MOSQ_ERR_CONN_LOST; + case COMPAT_EINTR: + return MOSQ_ERR_SUCCESS; default: return MOSQ_ERR_ERRNO; } @@ -523,19 +546,21 @@ int packet__read(struct mosquitto *mosq) mosq->in_packet.pos = 0; #ifdef WITH_BROKER G_MSGS_RECEIVED_INC(1); - if(((mosq->in_packet.command)&0xF5) == PUBLISH){ + if(((mosq->in_packet.command)&0xF5) == CMD_PUBLISH){ G_PUB_MSGS_RECEIVED_INC(1); } - rc = handle__packet(db, mosq); -#else - rc = handle__packet(mosq); #endif + rc = handle__packet(mosq); /* Free data and reset values */ packet__cleanup(&mosq->in_packet); +#ifdef WITH_BROKER + keepalive__update(mosq); +#else pthread_mutex_lock(&mosq->msgtime_mutex); mosq->last_msg_in = mosquitto_time(); pthread_mutex_unlock(&mosq->msgtime_mutex); +#endif return rc; } diff --git a/libs/mosquitto/src/packet_mosq.h b/libs/mosquitto/src/packet_mosq.h index b570818..827aeb1 100644 --- a/libs/mosquitto/src/packet_mosq.h +++ b/libs/mosquitto/src/packet_mosq.h @@ -1,15 +1,17 @@ /* -Copyright (c) 2010-2019 Roger Light +Copyright (c) 2010-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. */ @@ -19,29 +21,32 @@ and the Eclipse Distribution License is available at #include "mosquitto_internal.h" #include "mosquitto.h" -#ifdef WITH_BROKER -struct mosquitto_db; -#endif - int packet__alloc(struct mosquitto__packet *packet); void packet__cleanup(struct mosquitto__packet *packet); +void packet__cleanup_all(struct mosquitto *mosq); +void packet__cleanup_all_no_locks(struct mosquitto *mosq); int packet__queue(struct mosquitto *mosq, struct mosquitto__packet *packet); +int packet__check_oversize(struct mosquitto *mosq, uint32_t remaining_length); + int packet__read_byte(struct mosquitto__packet *packet, uint8_t *byte); int packet__read_bytes(struct mosquitto__packet *packet, void *bytes, uint32_t count); -int packet__read_string(struct mosquitto__packet *packet, char **str, int *length); +int packet__read_binary(struct mosquitto__packet *packet, uint8_t **data, uint16_t *length); +int packet__read_string(struct mosquitto__packet *packet, char **str, uint16_t *length); int packet__read_uint16(struct mosquitto__packet *packet, uint16_t *word); +int packet__read_uint32(struct mosquitto__packet *packet, uint32_t *word); +int packet__read_varint(struct mosquitto__packet *packet, uint32_t *word, uint8_t *bytes); void packet__write_byte(struct mosquitto__packet *packet, uint8_t byte); void packet__write_bytes(struct mosquitto__packet *packet, const void *bytes, uint32_t count); void packet__write_string(struct mosquitto__packet *packet, const char *str, uint16_t length); void packet__write_uint16(struct mosquitto__packet *packet, uint16_t word); +void packet__write_uint32(struct mosquitto__packet *packet, uint32_t word); +int packet__write_varint(struct mosquitto__packet *packet, uint32_t word); + +unsigned int packet__varint_bytes(uint32_t word); int packet__write(struct mosquitto *mosq); -#ifdef WITH_BROKER -int packet__read(struct mosquitto_db *db, struct mosquitto *mosq); -#else int packet__read(struct mosquitto *mosq); -#endif #endif diff --git a/libs/mosquitto/src/property_mosq.c b/libs/mosquitto/src/property_mosq.c new file mode 100644 index 0000000..6249869 --- /dev/null +++ b/libs/mosquitto/src/property_mosq.c @@ -0,0 +1,1293 @@ +/* +Copyright (c) 2018-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 + +#ifndef WIN32 +# include +#endif + +#include "logging_mosq.h" +#include "memory_mosq.h" +#include "mqtt_protocol.h" +#include "packet_mosq.h" +#include "property_mosq.h" + + +static int property__read(struct mosquitto__packet *packet, uint32_t *len, mosquitto_property *property) +{ + int rc; + uint32_t property_identifier; + uint8_t byte; + uint8_t byte_count; + uint16_t uint16; + uint32_t uint32; + uint32_t varint; + char *str1, *str2; + uint16_t slen1, slen2; + + if(!property) return MOSQ_ERR_INVAL; + + rc = packet__read_varint(packet, &property_identifier, NULL); + if(rc){ + return rc; + } + *len -= 1; + + memset(property, 0, sizeof(mosquitto_property)); + + property->identifier = (int32_t)property_identifier; + + switch(property_identifier){ + case MQTT_PROP_PAYLOAD_FORMAT_INDICATOR: + case MQTT_PROP_REQUEST_PROBLEM_INFORMATION: + case MQTT_PROP_REQUEST_RESPONSE_INFORMATION: + case MQTT_PROP_MAXIMUM_QOS: + case MQTT_PROP_RETAIN_AVAILABLE: + case MQTT_PROP_WILDCARD_SUB_AVAILABLE: + case MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE: + case MQTT_PROP_SHARED_SUB_AVAILABLE: + rc = packet__read_byte(packet, &byte); + if(rc) return rc; + *len -= 1; /* byte */ + property->value.i8 = byte; + break; + + case MQTT_PROP_SERVER_KEEP_ALIVE: + case MQTT_PROP_RECEIVE_MAXIMUM: + case MQTT_PROP_TOPIC_ALIAS_MAXIMUM: + case MQTT_PROP_TOPIC_ALIAS: + rc = packet__read_uint16(packet, &uint16); + if(rc) return rc; + *len -= 2; /* uint16 */ + property->value.i16 = uint16; + break; + + case MQTT_PROP_MESSAGE_EXPIRY_INTERVAL: + case MQTT_PROP_SESSION_EXPIRY_INTERVAL: + case MQTT_PROP_WILL_DELAY_INTERVAL: + case MQTT_PROP_MAXIMUM_PACKET_SIZE: + rc = packet__read_uint32(packet, &uint32); + if(rc) return rc; + *len -= 4; /* uint32 */ + property->value.i32 = uint32; + break; + + case MQTT_PROP_SUBSCRIPTION_IDENTIFIER: + rc = packet__read_varint(packet, &varint, &byte_count); + if(rc) return rc; + *len -= byte_count; + property->value.varint = varint; + break; + + case MQTT_PROP_CONTENT_TYPE: + case MQTT_PROP_RESPONSE_TOPIC: + case MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER: + case MQTT_PROP_AUTHENTICATION_METHOD: + case MQTT_PROP_RESPONSE_INFORMATION: + case MQTT_PROP_SERVER_REFERENCE: + case MQTT_PROP_REASON_STRING: + rc = packet__read_string(packet, &str1, &slen1); + if(rc) return rc; + *len = (*len) - 2 - slen1; /* uint16, string len */ + property->value.s.v = str1; + property->value.s.len = slen1; + break; + + case MQTT_PROP_AUTHENTICATION_DATA: + case MQTT_PROP_CORRELATION_DATA: + rc = packet__read_binary(packet, (uint8_t **)&str1, &slen1); + if(rc) return rc; + *len = (*len) - 2 - slen1; /* uint16, binary len */ + property->value.bin.v = str1; + property->value.bin.len = slen1; + break; + + case MQTT_PROP_USER_PROPERTY: + rc = packet__read_string(packet, &str1, &slen1); + if(rc) return rc; + *len = (*len) - 2 - slen1; /* uint16, string len */ + + rc = packet__read_string(packet, &str2, &slen2); + if(rc){ + mosquitto__free(str1); + return rc; + } + *len = (*len) - 2 - slen2; /* uint16, string len */ + + property->name.v = str1; + property->name.len = slen1; + property->value.s.v = str2; + property->value.s.len = slen2; + break; + + default: + log__printf(NULL, MOSQ_LOG_DEBUG, "Unsupported property type: %d", property_identifier); + return MOSQ_ERR_MALFORMED_PACKET; + } + + return MOSQ_ERR_SUCCESS; +} + + +int property__read_all(int command, struct mosquitto__packet *packet, mosquitto_property **properties) +{ + int rc; + uint32_t proplen; + mosquitto_property *p, *tail = NULL; + + rc = packet__read_varint(packet, &proplen, NULL); + if(rc) return rc; + + *properties = NULL; + + /* The order of properties must be preserved for some types, so keep the + * same order for all */ + while(proplen > 0){ + p = mosquitto__calloc(1, sizeof(mosquitto_property)); + if(!p){ + mosquitto_property_free_all(properties); + return MOSQ_ERR_NOMEM; + } + + rc = property__read(packet, &proplen, p); + if(rc){ + mosquitto__free(p); + mosquitto_property_free_all(properties); + return rc; + } + + if(!(*properties)){ + *properties = p; + }else{ + tail->next = p; + } + tail = p; + + } + + rc = mosquitto_property_check_all(command, *properties); + if(rc){ + mosquitto_property_free_all(properties); + return rc; + } + return MOSQ_ERR_SUCCESS; +} + + +void property__free(mosquitto_property **property) +{ + if(!property || !(*property)) return; + + switch((*property)->identifier){ + case MQTT_PROP_CONTENT_TYPE: + case MQTT_PROP_RESPONSE_TOPIC: + case MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER: + case MQTT_PROP_AUTHENTICATION_METHOD: + case MQTT_PROP_RESPONSE_INFORMATION: + case MQTT_PROP_SERVER_REFERENCE: + case MQTT_PROP_REASON_STRING: + mosquitto__free((*property)->value.s.v); + break; + + case MQTT_PROP_AUTHENTICATION_DATA: + case MQTT_PROP_CORRELATION_DATA: + mosquitto__free((*property)->value.bin.v); + break; + + case MQTT_PROP_USER_PROPERTY: + mosquitto__free((*property)->name.v); + mosquitto__free((*property)->value.s.v); + break; + + case MQTT_PROP_PAYLOAD_FORMAT_INDICATOR: + case MQTT_PROP_MESSAGE_EXPIRY_INTERVAL: + case MQTT_PROP_SUBSCRIPTION_IDENTIFIER: + case MQTT_PROP_SESSION_EXPIRY_INTERVAL: + case MQTT_PROP_SERVER_KEEP_ALIVE: + case MQTT_PROP_REQUEST_PROBLEM_INFORMATION: + case MQTT_PROP_WILL_DELAY_INTERVAL: + case MQTT_PROP_REQUEST_RESPONSE_INFORMATION: + case MQTT_PROP_RECEIVE_MAXIMUM: + case MQTT_PROP_TOPIC_ALIAS_MAXIMUM: + case MQTT_PROP_TOPIC_ALIAS: + case MQTT_PROP_MAXIMUM_QOS: + case MQTT_PROP_RETAIN_AVAILABLE: + case MQTT_PROP_MAXIMUM_PACKET_SIZE: + case MQTT_PROP_WILDCARD_SUB_AVAILABLE: + case MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE: + case MQTT_PROP_SHARED_SUB_AVAILABLE: + /* Nothing to free */ + break; + } + + free(*property); + *property = NULL; +} + + +void mosquitto_property_free_all(mosquitto_property **property) +{ + mosquitto_property *p, *next; + + if(!property) return; + + p = *property; + while(p){ + next = p->next; + property__free(&p); + p = next; + } + *property = NULL; +} + + +unsigned int property__get_length(const mosquitto_property *property) +{ + if(!property) return 0; + + switch(property->identifier){ + /* Byte */ + case MQTT_PROP_PAYLOAD_FORMAT_INDICATOR: + case MQTT_PROP_REQUEST_PROBLEM_INFORMATION: + case MQTT_PROP_REQUEST_RESPONSE_INFORMATION: + case MQTT_PROP_MAXIMUM_QOS: + case MQTT_PROP_RETAIN_AVAILABLE: + case MQTT_PROP_WILDCARD_SUB_AVAILABLE: + case MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE: + case MQTT_PROP_SHARED_SUB_AVAILABLE: + return 2; /* 1 (identifier) + 1 byte */ + + /* uint16 */ + case MQTT_PROP_SERVER_KEEP_ALIVE: + case MQTT_PROP_RECEIVE_MAXIMUM: + case MQTT_PROP_TOPIC_ALIAS_MAXIMUM: + case MQTT_PROP_TOPIC_ALIAS: + return 3; /* 1 (identifier) + 2 bytes */ + + /* uint32 */ + case MQTT_PROP_MESSAGE_EXPIRY_INTERVAL: + case MQTT_PROP_WILL_DELAY_INTERVAL: + case MQTT_PROP_MAXIMUM_PACKET_SIZE: + case MQTT_PROP_SESSION_EXPIRY_INTERVAL: + return 5; /* 1 (identifier) + 4 bytes */ + + /* varint */ + case MQTT_PROP_SUBSCRIPTION_IDENTIFIER: + if(property->value.varint < 128){ + return 2; + }else if(property->value.varint < 16384){ + return 3; + }else if(property->value.varint < 2097152){ + return 4; + }else if(property->value.varint < 268435456){ + return 5; + }else{ + return 0; + } + + /* binary */ + case MQTT_PROP_CORRELATION_DATA: + case MQTT_PROP_AUTHENTICATION_DATA: + return 3U + property->value.bin.len; /* 1 + 2 bytes (len) + X bytes (payload) */ + + /* string */ + case MQTT_PROP_CONTENT_TYPE: + case MQTT_PROP_RESPONSE_TOPIC: + case MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER: + case MQTT_PROP_AUTHENTICATION_METHOD: + case MQTT_PROP_RESPONSE_INFORMATION: + case MQTT_PROP_SERVER_REFERENCE: + case MQTT_PROP_REASON_STRING: + return 3U + property->value.s.len; /* 1 + 2 bytes (len) + X bytes (string) */ + + /* string pair */ + case MQTT_PROP_USER_PROPERTY: + return 5U + property->value.s.len + property->name.len; /* 1 + 2*(2 bytes (len) + X bytes (string))*/ + + default: + return 0; + } + return 0; +} + + +unsigned int property__get_length_all(const mosquitto_property *property) +{ + const mosquitto_property *p; + unsigned int len = 0; + + p = property; + while(p){ + len += property__get_length(p); + p = p->next; + } + return len; +} + + +/* Return the number of bytes we need to add on to the remaining length when + * encoding these properties. */ +unsigned int property__get_remaining_length(const mosquitto_property *props) +{ + unsigned int proplen, varbytes; + + proplen = property__get_length_all(props); + varbytes = packet__varint_bytes(proplen); + return proplen + varbytes; +} + + +static int property__write(struct mosquitto__packet *packet, const mosquitto_property *property) +{ + int rc; + + rc = packet__write_varint(packet, (uint32_t)property->identifier); + if(rc) return rc; + + switch(property->identifier){ + case MQTT_PROP_PAYLOAD_FORMAT_INDICATOR: + case MQTT_PROP_REQUEST_PROBLEM_INFORMATION: + case MQTT_PROP_REQUEST_RESPONSE_INFORMATION: + case MQTT_PROP_MAXIMUM_QOS: + case MQTT_PROP_RETAIN_AVAILABLE: + case MQTT_PROP_WILDCARD_SUB_AVAILABLE: + case MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE: + case MQTT_PROP_SHARED_SUB_AVAILABLE: + packet__write_byte(packet, property->value.i8); + break; + + case MQTT_PROP_SERVER_KEEP_ALIVE: + case MQTT_PROP_RECEIVE_MAXIMUM: + case MQTT_PROP_TOPIC_ALIAS_MAXIMUM: + case MQTT_PROP_TOPIC_ALIAS: + packet__write_uint16(packet, property->value.i16); + break; + + case MQTT_PROP_MESSAGE_EXPIRY_INTERVAL: + case MQTT_PROP_SESSION_EXPIRY_INTERVAL: + case MQTT_PROP_WILL_DELAY_INTERVAL: + case MQTT_PROP_MAXIMUM_PACKET_SIZE: + packet__write_uint32(packet, property->value.i32); + break; + + case MQTT_PROP_SUBSCRIPTION_IDENTIFIER: + return packet__write_varint(packet, property->value.varint); + + case MQTT_PROP_CONTENT_TYPE: + case MQTT_PROP_RESPONSE_TOPIC: + case MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER: + case MQTT_PROP_AUTHENTICATION_METHOD: + case MQTT_PROP_RESPONSE_INFORMATION: + case MQTT_PROP_SERVER_REFERENCE: + case MQTT_PROP_REASON_STRING: + packet__write_string(packet, property->value.s.v, property->value.s.len); + break; + + case MQTT_PROP_AUTHENTICATION_DATA: + case MQTT_PROP_CORRELATION_DATA: + packet__write_uint16(packet, property->value.bin.len); + packet__write_bytes(packet, property->value.bin.v, property->value.bin.len); + break; + + case MQTT_PROP_USER_PROPERTY: + packet__write_string(packet, property->name.v, property->name.len); + packet__write_string(packet, property->value.s.v, property->value.s.len); + break; + + default: + log__printf(NULL, MOSQ_LOG_DEBUG, "Unsupported property type: %d", property->identifier); + return MOSQ_ERR_INVAL; + } + + return MOSQ_ERR_SUCCESS; +} + + +int property__write_all(struct mosquitto__packet *packet, const mosquitto_property *properties, bool write_len) +{ + int rc; + const mosquitto_property *p; + + if(write_len){ + rc = packet__write_varint(packet, property__get_length_all(properties)); + if(rc) return rc; + } + + p = properties; + while(p){ + rc = property__write(packet, p); + if(rc) return rc; + p = p->next; + } + + return MOSQ_ERR_SUCCESS; +} + + +int mosquitto_property_check_command(int command, int identifier) +{ + switch(identifier){ + case MQTT_PROP_PAYLOAD_FORMAT_INDICATOR: + case MQTT_PROP_MESSAGE_EXPIRY_INTERVAL: + case MQTT_PROP_CONTENT_TYPE: + case MQTT_PROP_RESPONSE_TOPIC: + case MQTT_PROP_CORRELATION_DATA: + if(command != CMD_PUBLISH && command != CMD_WILL){ + return MOSQ_ERR_PROTOCOL; + } + break; + + case MQTT_PROP_SUBSCRIPTION_IDENTIFIER: + if(command != CMD_PUBLISH && command != CMD_SUBSCRIBE){ + return MOSQ_ERR_PROTOCOL; + } + break; + + case MQTT_PROP_SESSION_EXPIRY_INTERVAL: + if(command != CMD_CONNECT && command != CMD_CONNACK && command != CMD_DISCONNECT){ + return MOSQ_ERR_PROTOCOL; + } + break; + + case MQTT_PROP_AUTHENTICATION_METHOD: + case MQTT_PROP_AUTHENTICATION_DATA: + if(command != CMD_CONNECT && command != CMD_CONNACK && command != CMD_AUTH){ + return MOSQ_ERR_PROTOCOL; + } + break; + + case MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER: + case MQTT_PROP_SERVER_KEEP_ALIVE: + case MQTT_PROP_RESPONSE_INFORMATION: + case MQTT_PROP_MAXIMUM_QOS: + case MQTT_PROP_RETAIN_AVAILABLE: + case MQTT_PROP_WILDCARD_SUB_AVAILABLE: + case MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE: + case MQTT_PROP_SHARED_SUB_AVAILABLE: + if(command != CMD_CONNACK){ + return MOSQ_ERR_PROTOCOL; + } + break; + + case MQTT_PROP_WILL_DELAY_INTERVAL: + if(command != CMD_WILL){ + return MOSQ_ERR_PROTOCOL; + } + break; + + case MQTT_PROP_REQUEST_PROBLEM_INFORMATION: + case MQTT_PROP_REQUEST_RESPONSE_INFORMATION: + if(command != CMD_CONNECT){ + return MOSQ_ERR_PROTOCOL; + } + break; + + case MQTT_PROP_SERVER_REFERENCE: + if(command != CMD_CONNACK && command != CMD_DISCONNECT){ + return MOSQ_ERR_PROTOCOL; + } + break; + + case MQTT_PROP_REASON_STRING: + if(command == CMD_CONNECT || command == CMD_PUBLISH || command == CMD_SUBSCRIBE || command == CMD_UNSUBSCRIBE){ + return MOSQ_ERR_PROTOCOL; + } + break; + + case MQTT_PROP_RECEIVE_MAXIMUM: + case MQTT_PROP_TOPIC_ALIAS_MAXIMUM: + case MQTT_PROP_MAXIMUM_PACKET_SIZE: + if(command != CMD_CONNECT && command != CMD_CONNACK){ + return MOSQ_ERR_PROTOCOL; + } + break; + + case MQTT_PROP_TOPIC_ALIAS: + if(command != CMD_PUBLISH){ + return MOSQ_ERR_PROTOCOL; + } + break; + + case MQTT_PROP_USER_PROPERTY: + break; + + default: + return MOSQ_ERR_PROTOCOL; + } + return MOSQ_ERR_SUCCESS; +} + + +const char *mosquitto_property_identifier_to_string(int identifier) +{ + switch(identifier){ + case MQTT_PROP_PAYLOAD_FORMAT_INDICATOR: + return "payload-format-indicator"; + case MQTT_PROP_MESSAGE_EXPIRY_INTERVAL: + return "message-expiry-interval"; + case MQTT_PROP_CONTENT_TYPE: + return "content-type"; + case MQTT_PROP_RESPONSE_TOPIC: + return "response-topic"; + case MQTT_PROP_CORRELATION_DATA: + return "correlation-data"; + case MQTT_PROP_SUBSCRIPTION_IDENTIFIER: + return "subscription-identifier"; + case MQTT_PROP_SESSION_EXPIRY_INTERVAL: + return "session-expiry-interval"; + case MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER: + return "assigned-client-identifier"; + case MQTT_PROP_SERVER_KEEP_ALIVE: + return "server-keep-alive"; + case MQTT_PROP_AUTHENTICATION_METHOD: + return "authentication-method"; + case MQTT_PROP_AUTHENTICATION_DATA: + return "authentication-data"; + case MQTT_PROP_REQUEST_PROBLEM_INFORMATION: + return "request-problem-information"; + case MQTT_PROP_WILL_DELAY_INTERVAL: + return "will-delay-interval"; + case MQTT_PROP_REQUEST_RESPONSE_INFORMATION: + return "request-response-information"; + case MQTT_PROP_RESPONSE_INFORMATION: + return "response-information"; + case MQTT_PROP_SERVER_REFERENCE: + return "server-reference"; + case MQTT_PROP_REASON_STRING: + return "reason-string"; + case MQTT_PROP_RECEIVE_MAXIMUM: + return "receive-maximum"; + case MQTT_PROP_TOPIC_ALIAS_MAXIMUM: + return "topic-alias-maximum"; + case MQTT_PROP_TOPIC_ALIAS: + return "topic-alias"; + case MQTT_PROP_MAXIMUM_QOS: + return "maximum-qos"; + case MQTT_PROP_RETAIN_AVAILABLE: + return "retain-available"; + case MQTT_PROP_USER_PROPERTY: + return "user-property"; + case MQTT_PROP_MAXIMUM_PACKET_SIZE: + return "maximum-packet-size"; + case MQTT_PROP_WILDCARD_SUB_AVAILABLE: + return "wildcard-subscription-available"; + case MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE: + return "subscription-identifier-available"; + case MQTT_PROP_SHARED_SUB_AVAILABLE: + return "shared-subscription-available"; + default: + return NULL; + } +} + + +int mosquitto_string_to_property_info(const char *propname, int *identifier, int *type) +{ + if(!propname) return MOSQ_ERR_INVAL; + + if(!strcasecmp(propname, "payload-format-indicator")){ + *identifier = MQTT_PROP_PAYLOAD_FORMAT_INDICATOR; + *type = MQTT_PROP_TYPE_BYTE; + }else if(!strcasecmp(propname, "message-expiry-interval")){ + *identifier = MQTT_PROP_MESSAGE_EXPIRY_INTERVAL; + *type = MQTT_PROP_TYPE_INT32; + }else if(!strcasecmp(propname, "content-type")){ + *identifier = MQTT_PROP_CONTENT_TYPE; + *type = MQTT_PROP_TYPE_STRING; + }else if(!strcasecmp(propname, "response-topic")){ + *identifier = MQTT_PROP_RESPONSE_TOPIC; + *type = MQTT_PROP_TYPE_STRING; + }else if(!strcasecmp(propname, "correlation-data")){ + *identifier = MQTT_PROP_CORRELATION_DATA; + *type = MQTT_PROP_TYPE_BINARY; + }else if(!strcasecmp(propname, "subscription-identifier")){ + *identifier = MQTT_PROP_SUBSCRIPTION_IDENTIFIER; + *type = MQTT_PROP_TYPE_VARINT; + }else if(!strcasecmp(propname, "session-expiry-interval")){ + *identifier = MQTT_PROP_SESSION_EXPIRY_INTERVAL; + *type = MQTT_PROP_TYPE_INT32; + }else if(!strcasecmp(propname, "assigned-client-identifier")){ + *identifier = MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER; + *type = MQTT_PROP_TYPE_STRING; + }else if(!strcasecmp(propname, "server-keep-alive")){ + *identifier = MQTT_PROP_SERVER_KEEP_ALIVE; + *type = MQTT_PROP_TYPE_INT16; + }else if(!strcasecmp(propname, "authentication-method")){ + *identifier = MQTT_PROP_AUTHENTICATION_METHOD; + *type = MQTT_PROP_TYPE_STRING; + }else if(!strcasecmp(propname, "authentication-data")){ + *identifier = MQTT_PROP_AUTHENTICATION_DATA; + *type = MQTT_PROP_TYPE_BINARY; + }else if(!strcasecmp(propname, "request-problem-information")){ + *identifier = MQTT_PROP_REQUEST_PROBLEM_INFORMATION; + *type = MQTT_PROP_TYPE_BYTE; + }else if(!strcasecmp(propname, "will-delay-interval")){ + *identifier = MQTT_PROP_WILL_DELAY_INTERVAL; + *type = MQTT_PROP_TYPE_INT32; + }else if(!strcasecmp(propname, "request-response-information")){ + *identifier = MQTT_PROP_REQUEST_RESPONSE_INFORMATION; + *type = MQTT_PROP_TYPE_BYTE; + }else if(!strcasecmp(propname, "response-information")){ + *identifier = MQTT_PROP_RESPONSE_INFORMATION; + *type = MQTT_PROP_TYPE_STRING; + }else if(!strcasecmp(propname, "server-reference")){ + *identifier = MQTT_PROP_SERVER_REFERENCE; + *type = MQTT_PROP_TYPE_STRING; + }else if(!strcasecmp(propname, "reason-string")){ + *identifier = MQTT_PROP_REASON_STRING; + *type = MQTT_PROP_TYPE_STRING; + }else if(!strcasecmp(propname, "receive-maximum")){ + *identifier = MQTT_PROP_RECEIVE_MAXIMUM; + *type = MQTT_PROP_TYPE_INT16; + }else if(!strcasecmp(propname, "topic-alias-maximum")){ + *identifier = MQTT_PROP_TOPIC_ALIAS_MAXIMUM; + *type = MQTT_PROP_TYPE_INT16; + }else if(!strcasecmp(propname, "topic-alias")){ + *identifier = MQTT_PROP_TOPIC_ALIAS; + *type = MQTT_PROP_TYPE_INT16; + }else if(!strcasecmp(propname, "maximum-qos")){ + *identifier = MQTT_PROP_MAXIMUM_QOS; + *type = MQTT_PROP_TYPE_BYTE; + }else if(!strcasecmp(propname, "retain-available")){ + *identifier = MQTT_PROP_RETAIN_AVAILABLE; + *type = MQTT_PROP_TYPE_BYTE; + }else if(!strcasecmp(propname, "user-property")){ + *identifier = MQTT_PROP_USER_PROPERTY; + *type = MQTT_PROP_TYPE_STRING_PAIR; + }else if(!strcasecmp(propname, "maximum-packet-size")){ + *identifier = MQTT_PROP_MAXIMUM_PACKET_SIZE; + *type = MQTT_PROP_TYPE_INT32; + }else if(!strcasecmp(propname, "wildcard-subscription-available")){ + *identifier = MQTT_PROP_WILDCARD_SUB_AVAILABLE; + *type = MQTT_PROP_TYPE_BYTE; + }else if(!strcasecmp(propname, "subscription-identifier-available")){ + *identifier = MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE; + *type = MQTT_PROP_TYPE_BYTE; + }else if(!strcasecmp(propname, "shared-subscription-available")){ + *identifier = MQTT_PROP_SHARED_SUB_AVAILABLE; + *type = MQTT_PROP_TYPE_BYTE; + }else{ + return MOSQ_ERR_INVAL; + } + return MOSQ_ERR_SUCCESS; +} + + +static void property__add(mosquitto_property **proplist, struct mqtt5__property *prop) +{ + mosquitto_property *p; + + if(!(*proplist)){ + *proplist = prop; + } + + p = *proplist; + while(p->next){ + p = p->next; + } + p->next = prop; + prop->next = NULL; +} + + +int mosquitto_property_add_byte(mosquitto_property **proplist, int identifier, uint8_t value) +{ + mosquitto_property *prop; + + if(!proplist) return MOSQ_ERR_INVAL; + if(identifier != MQTT_PROP_PAYLOAD_FORMAT_INDICATOR + && identifier != MQTT_PROP_REQUEST_PROBLEM_INFORMATION + && identifier != MQTT_PROP_REQUEST_RESPONSE_INFORMATION + && identifier != MQTT_PROP_MAXIMUM_QOS + && identifier != MQTT_PROP_RETAIN_AVAILABLE + && identifier != MQTT_PROP_WILDCARD_SUB_AVAILABLE + && identifier != MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE + && identifier != MQTT_PROP_SHARED_SUB_AVAILABLE){ + return MOSQ_ERR_INVAL; + } + + prop = mosquitto__calloc(1, sizeof(mosquitto_property)); + if(!prop) return MOSQ_ERR_NOMEM; + + prop->client_generated = true; + prop->identifier = identifier; + prop->value.i8 = value; + + property__add(proplist, prop); + return MOSQ_ERR_SUCCESS; +} + + +int mosquitto_property_add_int16(mosquitto_property **proplist, int identifier, uint16_t value) +{ + mosquitto_property *prop; + + if(!proplist) return MOSQ_ERR_INVAL; + if(identifier != MQTT_PROP_SERVER_KEEP_ALIVE + && identifier != MQTT_PROP_RECEIVE_MAXIMUM + && identifier != MQTT_PROP_TOPIC_ALIAS_MAXIMUM + && identifier != MQTT_PROP_TOPIC_ALIAS){ + return MOSQ_ERR_INVAL; + } + + prop = mosquitto__calloc(1, sizeof(mosquitto_property)); + if(!prop) return MOSQ_ERR_NOMEM; + + prop->client_generated = true; + prop->identifier = identifier; + prop->value.i16 = value; + + property__add(proplist, prop); + return MOSQ_ERR_SUCCESS; +} + + +int mosquitto_property_add_int32(mosquitto_property **proplist, int identifier, uint32_t value) +{ + mosquitto_property *prop; + + if(!proplist) return MOSQ_ERR_INVAL; + if(identifier != MQTT_PROP_MESSAGE_EXPIRY_INTERVAL + && identifier != MQTT_PROP_SESSION_EXPIRY_INTERVAL + && identifier != MQTT_PROP_WILL_DELAY_INTERVAL + && identifier != MQTT_PROP_MAXIMUM_PACKET_SIZE){ + + return MOSQ_ERR_INVAL; + } + + prop = mosquitto__calloc(1, sizeof(mosquitto_property)); + if(!prop) return MOSQ_ERR_NOMEM; + + prop->client_generated = true; + prop->identifier = identifier; + prop->value.i32 = value; + + property__add(proplist, prop); + return MOSQ_ERR_SUCCESS; +} + + +int mosquitto_property_add_varint(mosquitto_property **proplist, int identifier, uint32_t value) +{ + mosquitto_property *prop; + + if(!proplist || value > 268435455) return MOSQ_ERR_INVAL; + if(identifier != MQTT_PROP_SUBSCRIPTION_IDENTIFIER) return MOSQ_ERR_INVAL; + + prop = mosquitto__calloc(1, sizeof(mosquitto_property)); + if(!prop) return MOSQ_ERR_NOMEM; + + prop->client_generated = true; + prop->identifier = identifier; + prop->value.varint = value; + + property__add(proplist, prop); + return MOSQ_ERR_SUCCESS; +} + + +int mosquitto_property_add_binary(mosquitto_property **proplist, int identifier, const void *value, uint16_t len) +{ + mosquitto_property *prop; + + if(!proplist) return MOSQ_ERR_INVAL; + if(identifier != MQTT_PROP_CORRELATION_DATA + && identifier != MQTT_PROP_AUTHENTICATION_DATA){ + + return MOSQ_ERR_INVAL; + } + + prop = mosquitto__calloc(1, sizeof(mosquitto_property)); + if(!prop) return MOSQ_ERR_NOMEM; + + prop->client_generated = true; + prop->identifier = identifier; + + if(len){ + prop->value.bin.v = mosquitto__malloc(len); + if(!prop->value.bin.v){ + mosquitto__free(prop); + return MOSQ_ERR_NOMEM; + } + + memcpy(prop->value.bin.v, value, len); + prop->value.bin.len = len; + } + + property__add(proplist, prop); + return MOSQ_ERR_SUCCESS; +} + + +int mosquitto_property_add_string(mosquitto_property **proplist, int identifier, const char *value) +{ + mosquitto_property *prop; + size_t slen = 0; + + if(!proplist) return MOSQ_ERR_INVAL; + if(value){ + slen = strlen(value); + if(mosquitto_validate_utf8(value, (int)slen)) return MOSQ_ERR_MALFORMED_UTF8; + } + + if(identifier != MQTT_PROP_CONTENT_TYPE + && identifier != MQTT_PROP_RESPONSE_TOPIC + && identifier != MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER + && identifier != MQTT_PROP_AUTHENTICATION_METHOD + && identifier != MQTT_PROP_RESPONSE_INFORMATION + && identifier != MQTT_PROP_SERVER_REFERENCE + && identifier != MQTT_PROP_REASON_STRING){ + + return MOSQ_ERR_INVAL; + } + + prop = mosquitto__calloc(1, sizeof(mosquitto_property)); + if(!prop) return MOSQ_ERR_NOMEM; + + prop->client_generated = true; + prop->identifier = identifier; + if(value && slen > 0){ + prop->value.s.v = mosquitto__strdup(value); + if(!prop->value.s.v){ + mosquitto__free(prop); + return MOSQ_ERR_NOMEM; + } + prop->value.s.len = (uint16_t)slen; + } + + property__add(proplist, prop); + return MOSQ_ERR_SUCCESS; +} + + +int mosquitto_property_add_string_pair(mosquitto_property **proplist, int identifier, const char *name, const char *value) +{ + mosquitto_property *prop; + size_t slen_name = 0, slen_value = 0; + + if(!proplist) return MOSQ_ERR_INVAL; + if(identifier != MQTT_PROP_USER_PROPERTY) return MOSQ_ERR_INVAL; + if(name){ + slen_name = strlen(name); + if(mosquitto_validate_utf8(name, (int)slen_name)) return MOSQ_ERR_MALFORMED_UTF8; + } + if(value){ + if(mosquitto_validate_utf8(value, (int)slen_value)) return MOSQ_ERR_MALFORMED_UTF8; + } + + prop = mosquitto__calloc(1, sizeof(mosquitto_property)); + if(!prop) return MOSQ_ERR_NOMEM; + + prop->client_generated = true; + prop->identifier = identifier; + + if(name){ + prop->name.v = mosquitto__strdup(name); + if(!prop->name.v){ + mosquitto__free(prop); + return MOSQ_ERR_NOMEM; + } + prop->name.len = (uint16_t)strlen(name); + } + + if(value){ + prop->value.s.v = mosquitto__strdup(value); + if(!prop->value.s.v){ + mosquitto__free(prop->name.v); + mosquitto__free(prop); + return MOSQ_ERR_NOMEM; + } + prop->value.s.len = (uint16_t)strlen(value); + } + + property__add(proplist, prop); + return MOSQ_ERR_SUCCESS; +} + +int mosquitto_property_check_all(int command, const mosquitto_property *properties) +{ + const mosquitto_property *p, *tail; + int rc; + + p = properties; + + while(p){ + /* Validity checks */ + if(p->identifier == MQTT_PROP_REQUEST_PROBLEM_INFORMATION + || p->identifier == MQTT_PROP_PAYLOAD_FORMAT_INDICATOR + || p->identifier == MQTT_PROP_REQUEST_RESPONSE_INFORMATION + || p->identifier == MQTT_PROP_MAXIMUM_QOS + || p->identifier == MQTT_PROP_RETAIN_AVAILABLE + || p->identifier == MQTT_PROP_WILDCARD_SUB_AVAILABLE + || p->identifier == MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE + || p->identifier == MQTT_PROP_SHARED_SUB_AVAILABLE){ + + if(p->value.i8 > 1){ + return MOSQ_ERR_PROTOCOL; + } + }else if(p->identifier == MQTT_PROP_MAXIMUM_PACKET_SIZE){ + if( p->value.i32 == 0){ + return MOSQ_ERR_PROTOCOL; + } + }else if(p->identifier == MQTT_PROP_RECEIVE_MAXIMUM + || p->identifier == MQTT_PROP_TOPIC_ALIAS){ + + if(p->value.i16 == 0){ + return MOSQ_ERR_PROTOCOL; + } + } + + /* Check for properties on incorrect commands */ + rc = mosquitto_property_check_command(command, p->identifier); + if(rc) return rc; + + /* Check for duplicates */ + if(p->identifier != MQTT_PROP_USER_PROPERTY){ + tail = p->next; + while(tail){ + if(p->identifier == tail->identifier){ + return MOSQ_ERR_DUPLICATE_PROPERTY; + } + tail = tail->next; + } + } + + p = p->next; + } + + return MOSQ_ERR_SUCCESS; +} + +static const mosquitto_property *property__get_property(const mosquitto_property *proplist, int identifier, bool skip_first) +{ + const mosquitto_property *p; + bool is_first = true; + + p = proplist; + + while(p){ + if(p->identifier == identifier){ + if(!is_first || !skip_first){ + return p; + } + is_first = false; + } + p = p->next; + } + return NULL; +} + + +int mosquitto_property_identifier(const mosquitto_property *property) +{ + if(property == NULL) return 0; + + return property->identifier; +} + + +const mosquitto_property *mosquitto_property_next(const mosquitto_property *proplist) +{ + if(proplist == NULL) return NULL; + + return proplist->next; +} + + +const mosquitto_property *mosquitto_property_read_byte(const mosquitto_property *proplist, int identifier, uint8_t *value, bool skip_first) +{ + const mosquitto_property *p; + if(!proplist) return NULL; + + p = property__get_property(proplist, identifier, skip_first); + if(!p) return NULL; + if(p->identifier != MQTT_PROP_PAYLOAD_FORMAT_INDICATOR + && p->identifier != MQTT_PROP_REQUEST_PROBLEM_INFORMATION + && p->identifier != MQTT_PROP_REQUEST_RESPONSE_INFORMATION + && p->identifier != MQTT_PROP_MAXIMUM_QOS + && p->identifier != MQTT_PROP_RETAIN_AVAILABLE + && p->identifier != MQTT_PROP_WILDCARD_SUB_AVAILABLE + && p->identifier != MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE + && p->identifier != MQTT_PROP_SHARED_SUB_AVAILABLE){ + return NULL; + } + + if(value) *value = p->value.i8; + + return p; +} + + +const mosquitto_property *mosquitto_property_read_int16(const mosquitto_property *proplist, int identifier, uint16_t *value, bool skip_first) +{ + const mosquitto_property *p; + if(!proplist) return NULL; + + p = property__get_property(proplist, identifier, skip_first); + if(!p) return NULL; + if(p->identifier != MQTT_PROP_SERVER_KEEP_ALIVE + && p->identifier != MQTT_PROP_RECEIVE_MAXIMUM + && p->identifier != MQTT_PROP_TOPIC_ALIAS_MAXIMUM + && p->identifier != MQTT_PROP_TOPIC_ALIAS){ + return NULL; + } + + if(value) *value = p->value.i16; + + return p; +} + + +const mosquitto_property *mosquitto_property_read_int32(const mosquitto_property *proplist, int identifier, uint32_t *value, bool skip_first) +{ + const mosquitto_property *p; + if(!proplist) return NULL; + + p = property__get_property(proplist, identifier, skip_first); + if(!p) return NULL; + if(p->identifier != MQTT_PROP_MESSAGE_EXPIRY_INTERVAL + && p->identifier != MQTT_PROP_SESSION_EXPIRY_INTERVAL + && p->identifier != MQTT_PROP_WILL_DELAY_INTERVAL + && p->identifier != MQTT_PROP_MAXIMUM_PACKET_SIZE){ + + return NULL; + } + + if(value) *value = p->value.i32; + + return p; +} + + +const mosquitto_property *mosquitto_property_read_varint(const mosquitto_property *proplist, int identifier, uint32_t *value, bool skip_first) +{ + const mosquitto_property *p; + if(!proplist) return NULL; + + p = property__get_property(proplist, identifier, skip_first); + if(!p) return NULL; + if(p->identifier != MQTT_PROP_SUBSCRIPTION_IDENTIFIER){ + return NULL; + } + + if(value) *value = p->value.varint; + + return p; +} + + +const mosquitto_property *mosquitto_property_read_binary(const mosquitto_property *proplist, int identifier, void **value, uint16_t *len, bool skip_first) +{ + const mosquitto_property *p; + if(!proplist || (value && !len) || (!value && len)) return NULL; + + if(value) *value = NULL; + + p = property__get_property(proplist, identifier, skip_first); + if(!p) return NULL; + if(p->identifier != MQTT_PROP_CORRELATION_DATA + && p->identifier != MQTT_PROP_AUTHENTICATION_DATA){ + + return NULL; + } + + if(value){ + *len = p->value.bin.len; + *value = calloc(1, *len + 1U); + if(!(*value)) return NULL; + + memcpy(*value, p->value.bin.v, *len); + } + + return p; +} + + +const mosquitto_property *mosquitto_property_read_string(const mosquitto_property *proplist, int identifier, char **value, bool skip_first) +{ + const mosquitto_property *p; + if(!proplist) return NULL; + + p = property__get_property(proplist, identifier, skip_first); + if(!p) return NULL; + if(p->identifier != MQTT_PROP_CONTENT_TYPE + && p->identifier != MQTT_PROP_RESPONSE_TOPIC + && p->identifier != MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER + && p->identifier != MQTT_PROP_AUTHENTICATION_METHOD + && p->identifier != MQTT_PROP_RESPONSE_INFORMATION + && p->identifier != MQTT_PROP_SERVER_REFERENCE + && p->identifier != MQTT_PROP_REASON_STRING){ + + return NULL; + } + + if(value){ + *value = calloc(1, (size_t)p->value.s.len+1); + if(!(*value)) return NULL; + + memcpy(*value, p->value.s.v, p->value.s.len); + } + + return p; +} + + +const mosquitto_property *mosquitto_property_read_string_pair(const mosquitto_property *proplist, int identifier, char **name, char **value, bool skip_first) +{ + const mosquitto_property *p; + if(!proplist) return NULL; + + if(name) *name = NULL; + if(value) *value = NULL; + + p = property__get_property(proplist, identifier, skip_first); + if(!p) return NULL; + if(p->identifier != MQTT_PROP_USER_PROPERTY) return NULL; + + if(name){ + *name = calloc(1, (size_t)p->name.len+1); + if(!(*name)) return NULL; + memcpy(*name, p->name.v, p->name.len); + } + + if(value){ + *value = calloc(1, (size_t)p->value.s.len+1); + if(!(*value)){ + if(name){ + free(*name); + *name = NULL; + } + return NULL; + } + memcpy(*value, p->value.s.v, p->value.s.len); + } + + return p; +} + + +int mosquitto_property_copy_all(mosquitto_property **dest, const mosquitto_property *src) +{ + mosquitto_property *pnew, *plast = NULL; + + if(!src) return MOSQ_ERR_SUCCESS; + if(!dest) return MOSQ_ERR_INVAL; + + *dest = NULL; + + while(src){ + pnew = calloc(1, sizeof(mosquitto_property)); + if(!pnew){ + mosquitto_property_free_all(dest); + return MOSQ_ERR_NOMEM; + } + if(plast){ + plast->next = pnew; + }else{ + *dest = pnew; + } + plast = pnew; + + pnew->identifier = src->identifier; + switch(pnew->identifier){ + case MQTT_PROP_PAYLOAD_FORMAT_INDICATOR: + case MQTT_PROP_REQUEST_PROBLEM_INFORMATION: + case MQTT_PROP_REQUEST_RESPONSE_INFORMATION: + case MQTT_PROP_MAXIMUM_QOS: + case MQTT_PROP_RETAIN_AVAILABLE: + case MQTT_PROP_WILDCARD_SUB_AVAILABLE: + case MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE: + case MQTT_PROP_SHARED_SUB_AVAILABLE: + pnew->value.i8 = src->value.i8; + break; + + case MQTT_PROP_SERVER_KEEP_ALIVE: + case MQTT_PROP_RECEIVE_MAXIMUM: + case MQTT_PROP_TOPIC_ALIAS_MAXIMUM: + case MQTT_PROP_TOPIC_ALIAS: + pnew->value.i16 = src->value.i16; + break; + + case MQTT_PROP_MESSAGE_EXPIRY_INTERVAL: + case MQTT_PROP_SESSION_EXPIRY_INTERVAL: + case MQTT_PROP_WILL_DELAY_INTERVAL: + case MQTT_PROP_MAXIMUM_PACKET_SIZE: + pnew->value.i32 = src->value.i32; + break; + + case MQTT_PROP_SUBSCRIPTION_IDENTIFIER: + pnew->value.varint = src->value.varint; + break; + + case MQTT_PROP_CONTENT_TYPE: + case MQTT_PROP_RESPONSE_TOPIC: + case MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER: + case MQTT_PROP_AUTHENTICATION_METHOD: + case MQTT_PROP_RESPONSE_INFORMATION: + case MQTT_PROP_SERVER_REFERENCE: + case MQTT_PROP_REASON_STRING: + pnew->value.s.len = src->value.s.len; + pnew->value.s.v = strdup(src->value.s.v); + if(!pnew->value.s.v){ + mosquitto_property_free_all(dest); + return MOSQ_ERR_NOMEM; + } + break; + + case MQTT_PROP_AUTHENTICATION_DATA: + case MQTT_PROP_CORRELATION_DATA: + pnew->value.bin.len = src->value.bin.len; + pnew->value.bin.v = malloc(pnew->value.bin.len); + if(!pnew->value.bin.v){ + mosquitto_property_free_all(dest); + return MOSQ_ERR_NOMEM; + } + memcpy(pnew->value.bin.v, src->value.bin.v, pnew->value.bin.len); + break; + + case MQTT_PROP_USER_PROPERTY: + pnew->value.s.len = src->value.s.len; + pnew->value.s.v = strdup(src->value.s.v); + if(!pnew->value.s.v){ + mosquitto_property_free_all(dest); + return MOSQ_ERR_NOMEM; + } + + pnew->name.len = src->name.len; + pnew->name.v = strdup(src->name.v); + if(!pnew->name.v){ + mosquitto_property_free_all(dest); + return MOSQ_ERR_NOMEM; + } + break; + + default: + mosquitto_property_free_all(dest); + return MOSQ_ERR_INVAL; + } + + src = src->next; + } + + return MOSQ_ERR_SUCCESS; +} diff --git a/libs/mosquitto/src/property_mosq.h b/libs/mosquitto/src/property_mosq.h new file mode 100644 index 0000000..bd5e2b6 --- /dev/null +++ b/libs/mosquitto/src/property_mosq.h @@ -0,0 +1,54 @@ +/* +Copyright (c) 2018-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 PROPERTY_MOSQ_H +#define PROPERTY_MOSQ_H + +#include "mosquitto_internal.h" +#include "mosquitto.h" + +struct mqtt__string { + char *v; + uint16_t len; +}; + +struct mqtt5__property { + struct mqtt5__property *next; + union { + uint8_t i8; + uint16_t i16; + uint32_t i32; + uint32_t varint; + struct mqtt__string bin; + struct mqtt__string s; + } value; + struct mqtt__string name; + int32_t identifier; + bool client_generated; +}; + + +int property__read_all(int command, struct mosquitto__packet *packet, mosquitto_property **property); +int property__write_all(struct mosquitto__packet *packet, const mosquitto_property *property, bool write_len); +void property__free(mosquitto_property **property); + +unsigned int property__get_length(const mosquitto_property *property); +unsigned int property__get_length_all(const mosquitto_property *property); + +unsigned int property__get_remaining_length(const mosquitto_property *props); + +#endif diff --git a/libs/mosquitto/src/read_handle.c b/libs/mosquitto/src/read_handle.c index 0c90b50..eafa65b 100644 --- a/libs/mosquitto/src/read_handle.c +++ b/libs/mosquitto/src/read_handle.c @@ -1,15 +1,17 @@ /* -Copyright (c) 2009-2019 Roger Light +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. */ @@ -24,7 +26,7 @@ and the Eclipse Distribution License is available at #include "logging_mosq.h" #include "memory_mosq.h" #include "messages_mosq.h" -#include "mqtt3_protocol.h" +#include "mqtt_protocol.h" #include "net_mosq.h" #include "packet_mosq.h" #include "read_handle.h" @@ -37,26 +39,30 @@ int handle__packet(struct mosquitto *mosq) assert(mosq); switch((mosq->in_packet.command)&0xF0){ - case PINGREQ: + case CMD_PINGREQ: return handle__pingreq(mosq); - case PINGRESP: + case CMD_PINGRESP: return handle__pingresp(mosq); - case PUBACK: + case CMD_PUBACK: return handle__pubackcomp(mosq, "PUBACK"); - case PUBCOMP: + case CMD_PUBCOMP: return handle__pubackcomp(mosq, "PUBCOMP"); - case PUBLISH: + case CMD_PUBLISH: return handle__publish(mosq); - case PUBREC: + case CMD_PUBREC: return handle__pubrec(mosq); - case PUBREL: - return handle__pubrel(NULL, mosq); - case CONNACK: + case CMD_PUBREL: + return handle__pubrel(mosq); + case CMD_CONNACK: return handle__connack(mosq); - case SUBACK: + case CMD_SUBACK: return handle__suback(mosq); - case UNSUBACK: + case CMD_UNSUBACK: return handle__unsuback(mosq); + case CMD_DISCONNECT: + return handle__disconnect(mosq); + case CMD_AUTH: + return handle__auth(mosq); default: /* If we don't recognise the command, return an error straight away. */ log__printf(mosq, MOSQ_LOG_ERR, "Error: Unrecognised command %d\n", (mosq->in_packet.command)&0xF0); diff --git a/libs/mosquitto/src/read_handle.h b/libs/mosquitto/src/read_handle.h index ab0e261..3340ae1 100644 --- a/libs/mosquitto/src/read_handle.h +++ b/libs/mosquitto/src/read_handle.h @@ -1,15 +1,17 @@ /* -Copyright (c) 2010-2019 Roger Light +Copyright (c) 2010-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. */ @@ -22,15 +24,17 @@ struct mosquitto_db; int handle__pingreq(struct mosquitto *mosq); int handle__pingresp(struct mosquitto *mosq); #ifdef WITH_BROKER -int handle__pubackcomp(struct mosquitto_db *db, struct mosquitto *mosq, const char *type); +int handle__pubackcomp(struct mosquitto *mosq, const char *type); #else int handle__packet(struct mosquitto *mosq); int handle__connack(struct mosquitto *mosq); +int handle__disconnect(struct mosquitto *mosq); int handle__pubackcomp(struct mosquitto *mosq, const char *type); int handle__publish(struct mosquitto *mosq); +int handle__auth(struct mosquitto *mosq); #endif int handle__pubrec(struct mosquitto *mosq); -int handle__pubrel(struct mosquitto_db *db, struct mosquitto *mosq); +int handle__pubrel(struct mosquitto *mosq); int handle__suback(struct mosquitto *mosq); int handle__unsuback(struct mosquitto *mosq); diff --git a/libs/mosquitto/src/send_connect.c b/libs/mosquitto/src/send_connect.c index 7268712..0976d49 100644 --- a/libs/mosquitto/src/send_connect.c +++ b/libs/mosquitto/src/send_connect.c @@ -1,15 +1,17 @@ /* -Copyright (c) 2009-2019 Roger Light +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. */ @@ -27,22 +29,28 @@ and the Eclipse Distribution License is available at #include "memory_mosq.h" #include "mosquitto.h" #include "mosquitto_internal.h" -#include "mqtt3_protocol.h" +#include "mqtt_protocol.h" #include "packet_mosq.h" +#include "property_mosq.h" +#include "send_mosq.h" -int send__connect(struct mosquitto *mosq, uint16_t keepalive, bool clean_session) +int send__connect(struct mosquitto *mosq, uint16_t keepalive, bool clean_session, const mosquitto_property *properties) { struct mosquitto__packet *packet = NULL; - int payloadlen; + uint32_t payloadlen; uint8_t will = 0; uint8_t byte; int rc; uint8_t version; char *clientid, *username, *password; - int headerlen; + uint32_t headerlen; + uint32_t proplen = 0, varbytes; + mosquitto_property *local_props = NULL; + uint16_t receive_maximum; assert(mosq); - assert(mosq->id); + + if(mosq->protocol == mosq_p_mqtt31 && !mosq->id) return MOSQ_ERR_PROTOCOL; #if defined(WITH_BROKER) && defined(WITH_BRIDGE) if(mosq->bridge){ @@ -60,12 +68,29 @@ int send__connect(struct mosquitto *mosq, uint16_t keepalive, bool clean_session password = mosq->password; #endif - if(mosq->protocol == mosq_p_mqtt31){ - version = MQTT_PROTOCOL_V31; - headerlen = 12; + if(mosq->protocol == mosq_p_mqtt5){ + /* Generate properties from options */ + if(!mosquitto_property_read_int16(properties, MQTT_PROP_RECEIVE_MAXIMUM, &receive_maximum, false)){ + rc = mosquitto_property_add_int16(&local_props, MQTT_PROP_RECEIVE_MAXIMUM, mosq->msgs_in.inflight_maximum); + if(rc) return rc; + }else{ + mosq->msgs_in.inflight_maximum = receive_maximum; + mosq->msgs_in.inflight_quota = receive_maximum; + } + + version = MQTT_PROTOCOL_V5; + headerlen = 10; + proplen = 0; + proplen += property__get_length_all(properties); + proplen += property__get_length_all(local_props); + varbytes = packet__varint_bytes(proplen); + headerlen += proplen + varbytes; }else if(mosq->protocol == mosq_p_mqtt311){ version = MQTT_PROTOCOL_V311; headerlen = 10; + }else if(mosq->protocol == mosq_p_mqtt31){ + version = MQTT_PROTOCOL_V31; + headerlen = 12; }else{ return MOSQ_ERR_INVAL; } @@ -73,22 +98,44 @@ int send__connect(struct mosquitto *mosq, uint16_t keepalive, bool clean_session packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet)); if(!packet) return MOSQ_ERR_NOMEM; - payloadlen = 2+strlen(clientid); + if(clientid){ + payloadlen = (uint32_t)(2U+strlen(clientid)); + }else{ + payloadlen = 2U; + } +#ifdef WITH_BROKER + if(mosq->will && (mosq->bridge == NULL || mosq->bridge->notifications_local_only == false)){ +#else if(mosq->will){ +#endif will = 1; - assert(mosq->will->topic); + assert(mosq->will->msg.topic); - payloadlen += 2+strlen(mosq->will->topic) + 2+mosq->will->payloadlen; + payloadlen += (uint32_t)(2+strlen(mosq->will->msg.topic) + 2+(uint32_t)mosq->will->msg.payloadlen); + if(mosq->protocol == mosq_p_mqtt5){ + payloadlen += property__get_remaining_length(mosq->will->properties); + } } - if(username){ - payloadlen += 2+strlen(username); - if(password){ - payloadlen += 2+strlen(password); + + /* After this check we can be sure that the username and password are + * always valid for the current protocol, so there is no need to check + * username before checking password. */ + if(mosq->protocol == mosq_p_mqtt31 || mosq->protocol == mosq_p_mqtt311){ + if(password != NULL && username == NULL){ + mosquitto__free(packet); + return MOSQ_ERR_INVAL; } } - packet->command = CONNECT; - packet->remaining_length = headerlen+payloadlen; + if(username){ + payloadlen += (uint32_t)(2+strlen(username)); + } + if(password){ + payloadlen += (uint32_t)(2+strlen(password)); + } + + packet->command = CMD_CONNECT; + packet->remaining_length = headerlen + payloadlen; rc = packet__alloc(packet); if(rc){ mosquitto__free(packet); @@ -97,50 +144,70 @@ int send__connect(struct mosquitto *mosq, uint16_t keepalive, bool clean_session /* Variable header */ if(version == MQTT_PROTOCOL_V31){ - packet__write_string(packet, PROTOCOL_NAME_v31, strlen(PROTOCOL_NAME_v31)); - }else if(version == MQTT_PROTOCOL_V311){ - packet__write_string(packet, PROTOCOL_NAME_v311, strlen(PROTOCOL_NAME_v311)); + packet__write_string(packet, PROTOCOL_NAME_v31, (uint16_t)strlen(PROTOCOL_NAME_v31)); + }else{ + packet__write_string(packet, PROTOCOL_NAME, (uint16_t)strlen(PROTOCOL_NAME)); } #if defined(WITH_BROKER) && defined(WITH_BRIDGE) - if(mosq->bridge && mosq->bridge->try_private && mosq->bridge->try_private_accepted){ + if(mosq->bridge && mosq->bridge->protocol_version != mosq_p_mqtt5 && mosq->bridge->try_private && mosq->bridge->try_private_accepted){ version |= 0x80; }else{ } #endif packet__write_byte(packet, version); - byte = (clean_session&0x1)<<1; + byte = (uint8_t)((clean_session&0x1)<<1); if(will){ - byte = byte | ((mosq->will->retain&0x1)<<5) | ((mosq->will->qos&0x3)<<3) | ((will&0x1)<<2); + byte = byte | (uint8_t)(((mosq->will->msg.qos&0x3)<<3) | ((will&0x1)<<2)); + if(mosq->retain_available){ + byte |= (uint8_t)((mosq->will->msg.retain&0x1)<<5); + } } if(username){ byte = byte | 0x1<<7; - if(mosq->password){ - byte = byte | 0x1<<6; - } + } + if(mosq->password){ + byte = byte | 0x1<<6; } packet__write_byte(packet, byte); packet__write_uint16(packet, keepalive); + if(mosq->protocol == mosq_p_mqtt5){ + /* Write properties */ + packet__write_varint(packet, proplen); + property__write_all(packet, properties, false); + property__write_all(packet, local_props, false); + } + mosquitto_property_free_all(&local_props); + /* Payload */ - packet__write_string(packet, clientid, strlen(clientid)); + if(clientid){ + packet__write_string(packet, clientid, (uint16_t)strlen(clientid)); + }else{ + packet__write_uint16(packet, 0); + } if(will){ - packet__write_string(packet, mosq->will->topic, strlen(mosq->will->topic)); - packet__write_string(packet, (const char *)mosq->will->payload, mosq->will->payloadlen); + if(mosq->protocol == mosq_p_mqtt5){ + /* Write will properties */ + property__write_all(packet, mosq->will->properties, true); + } + packet__write_string(packet, mosq->will->msg.topic, (uint16_t)strlen(mosq->will->msg.topic)); + packet__write_string(packet, (const char *)mosq->will->msg.payload, (uint16_t)mosq->will->msg.payloadlen); } + if(username){ - packet__write_string(packet, username, strlen(username)); - if(password){ - packet__write_string(packet, password, strlen(password)); - } + packet__write_string(packet, username, (uint16_t)strlen(username)); + } + if(password){ + packet__write_string(packet, password, (uint16_t)strlen(password)); } mosq->keepalive = keepalive; #ifdef WITH_BROKER # ifdef WITH_BRIDGE - log__printf(mosq, MOSQ_LOG_DEBUG, "Bridge %s sending CONNECT", clientid); + log__printf(mosq, MOSQ_LOG_DEBUG, "Bridge %s sending CONNECT", SAFE_PRINT(clientid)); # endif #else - log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending CONNECT", clientid); + log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending CONNECT", SAFE_PRINT(clientid)); #endif return packet__queue(mosq, packet); } diff --git a/libs/mosquitto/src/send_disconnect.c b/libs/mosquitto/src/send_disconnect.c index 554e29d..83ec2ea 100644 --- a/libs/mosquitto/src/send_disconnect.c +++ b/libs/mosquitto/src/send_disconnect.c @@ -1,15 +1,17 @@ /* -Copyright (c) 2009-2019 Roger Light +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. */ @@ -25,20 +27,58 @@ and the Eclipse Distribution License is available at #include "mosquitto.h" #include "mosquitto_internal.h" #include "logging_mosq.h" -#include "mqtt3_protocol.h" +#include "memory_mosq.h" +#include "mqtt_protocol.h" +#include "packet_mosq.h" +#include "property_mosq.h" #include "send_mosq.h" -int send__disconnect(struct mosquitto *mosq) +int send__disconnect(struct mosquitto *mosq, uint8_t reason_code, const mosquitto_property *properties) { + struct mosquitto__packet *packet = NULL; + int rc; + assert(mosq); #ifdef WITH_BROKER -# ifdef WITH_BRIDGE - log__printf(mosq, MOSQ_LOG_DEBUG, "Bridge %s sending DISCONNECT", mosq->id); -# endif +# ifdef WITH_BRIDGE + if(mosq->bridge){ + log__printf(mosq, MOSQ_LOG_DEBUG, "Bridge %s sending DISCONNECT", SAFE_PRINT(mosq->id)); + }else +# else + { + log__printf(mosq, MOSQ_LOG_DEBUG, "Sending DISCONNECT to %s (rc%d)", SAFE_PRINT(mosq->id), reason_code); + } +# endif #else - log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending DISCONNECT", mosq->id); + log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending DISCONNECT", SAFE_PRINT(mosq->id)); #endif - return send__simple_command(mosq, DISCONNECT); + assert(mosq); + packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet)); + if(!packet) return MOSQ_ERR_NOMEM; + + packet->command = CMD_DISCONNECT; + if(mosq->protocol == mosq_p_mqtt5 && (reason_code != 0 || properties)){ + packet->remaining_length = 1; + if(properties){ + packet->remaining_length += property__get_remaining_length(properties); + } + }else{ + packet->remaining_length = 0; + } + + rc = packet__alloc(packet); + if(rc){ + mosquitto__free(packet); + return rc; + } + if(mosq->protocol == mosq_p_mqtt5 && (reason_code != 0 || properties)){ + packet__write_byte(packet, reason_code); + if(properties){ + property__write_all(packet, properties, true); + } + } + + return packet__queue(mosq, packet); } diff --git a/libs/mosquitto/src/send_mosq.c b/libs/mosquitto/src/send_mosq.c index 49914df..a834649 100644 --- a/libs/mosquitto/src/send_mosq.c +++ b/libs/mosquitto/src/send_mosq.c @@ -1,15 +1,17 @@ /* -Copyright (c) 2009-2019 Roger Light +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. */ @@ -30,10 +32,11 @@ and the Eclipse Distribution License is available at #include "mosquitto.h" #include "mosquitto_internal.h" #include "logging_mosq.h" -#include "mqtt3_protocol.h" +#include "mqtt_protocol.h" #include "memory_mosq.h" #include "net_mosq.h" #include "packet_mosq.h" +#include "property_mosq.h" #include "send_mosq.h" #include "time_mosq.h" #include "util_mosq.h" @@ -43,11 +46,11 @@ int send__pingreq(struct mosquitto *mosq) int rc; assert(mosq); #ifdef WITH_BROKER - log__printf(NULL, MOSQ_LOG_DEBUG, "Sending PINGREQ to %s", mosq->id); + log__printf(NULL, MOSQ_LOG_DEBUG, "Sending PINGREQ to %s", SAFE_PRINT(mosq->id)); #else - log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending PINGREQ", mosq->id); + log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending PINGREQ", SAFE_PRINT(mosq->id)); #endif - rc = send__simple_command(mosq, PINGREQ); + rc = send__simple_command(mosq, CMD_PINGREQ); if(rc == MOSQ_ERR_SUCCESS){ mosq->ping_t = mosquitto_time(); } @@ -57,56 +60,65 @@ int send__pingreq(struct mosquitto *mosq) int send__pingresp(struct mosquitto *mosq) { #ifdef WITH_BROKER - if(mosq) log__printf(NULL, MOSQ_LOG_DEBUG, "Sending PINGRESP to %s", mosq->id); + log__printf(NULL, MOSQ_LOG_DEBUG, "Sending PINGRESP to %s", SAFE_PRINT(mosq->id)); #else - if(mosq) log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending PINGRESP", mosq->id); + log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending PINGRESP", SAFE_PRINT(mosq->id)); #endif - return send__simple_command(mosq, PINGRESP); + return send__simple_command(mosq, CMD_PINGRESP); } -int send__puback(struct mosquitto *mosq, uint16_t mid) +int send__puback(struct mosquitto *mosq, uint16_t mid, uint8_t reason_code, const mosquitto_property *properties) { #ifdef WITH_BROKER - if(mosq) log__printf(NULL, MOSQ_LOG_DEBUG, "Sending PUBACK to %s (Mid: %d)", mosq->id, mid); + log__printf(NULL, MOSQ_LOG_DEBUG, "Sending PUBACK to %s (m%d, rc%d)", SAFE_PRINT(mosq->id), mid, reason_code); #else - if(mosq) log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending PUBACK (Mid: %d)", mosq->id, mid); + log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending PUBACK (m%d, rc%d)", SAFE_PRINT(mosq->id), mid, reason_code); #endif - return send__command_with_mid(mosq, PUBACK, mid, false); + util__increment_receive_quota(mosq); + /* We don't use Reason String or User Property yet. */ + return send__command_with_mid(mosq, CMD_PUBACK, mid, false, reason_code, properties); } -int send__pubcomp(struct mosquitto *mosq, uint16_t mid) +int send__pubcomp(struct mosquitto *mosq, uint16_t mid, const mosquitto_property *properties) { #ifdef WITH_BROKER - if(mosq) log__printf(NULL, MOSQ_LOG_DEBUG, "Sending PUBCOMP to %s (Mid: %d)", mosq->id, mid); + log__printf(NULL, MOSQ_LOG_DEBUG, "Sending PUBCOMP to %s (m%d)", SAFE_PRINT(mosq->id), mid); #else - if(mosq) log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending PUBCOMP (Mid: %d)", mosq->id, mid); + log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending PUBCOMP (m%d)", SAFE_PRINT(mosq->id), mid); #endif - return send__command_with_mid(mosq, PUBCOMP, mid, false); + util__increment_receive_quota(mosq); + /* We don't use Reason String or User Property yet. */ + return send__command_with_mid(mosq, CMD_PUBCOMP, mid, false, 0, properties); } -int send__pubrec(struct mosquitto *mosq, uint16_t mid) +int send__pubrec(struct mosquitto *mosq, uint16_t mid, uint8_t reason_code, const mosquitto_property *properties) { #ifdef WITH_BROKER - if(mosq) log__printf(NULL, MOSQ_LOG_DEBUG, "Sending PUBREC to %s (Mid: %d)", mosq->id, mid); + log__printf(NULL, MOSQ_LOG_DEBUG, "Sending PUBREC to %s (m%d, rc%d)", SAFE_PRINT(mosq->id), mid, reason_code); #else - if(mosq) log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending PUBREC (Mid: %d)", mosq->id, mid); + log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending PUBREC (m%d, rc%d)", SAFE_PRINT(mosq->id), mid, reason_code); #endif - return send__command_with_mid(mosq, PUBREC, mid, false); + if(reason_code >= 0x80 && mosq->protocol == mosq_p_mqtt5){ + util__increment_receive_quota(mosq); + } + /* We don't use Reason String or User Property yet. */ + return send__command_with_mid(mosq, CMD_PUBREC, mid, false, reason_code, properties); } -int send__pubrel(struct mosquitto *mosq, uint16_t mid) +int send__pubrel(struct mosquitto *mosq, uint16_t mid, const mosquitto_property *properties) { #ifdef WITH_BROKER - if(mosq) log__printf(NULL, MOSQ_LOG_DEBUG, "Sending PUBREL to %s (Mid: %d)", mosq->id, mid); + log__printf(NULL, MOSQ_LOG_DEBUG, "Sending PUBREL to %s (m%d)", SAFE_PRINT(mosq->id), mid); #else - if(mosq) log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending PUBREL (Mid: %d)", mosq->id, mid); + log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending PUBREL (m%d)", SAFE_PRINT(mosq->id), mid); #endif - return send__command_with_mid(mosq, PUBREL|2, mid, false); + /* We don't use Reason String or User Property yet. */ + return send__command_with_mid(mosq, CMD_PUBREL|2, mid, false, 0, properties); } /* For PUBACK, PUBCOMP, PUBREC, and PUBREL */ -int send__command_with_mid(struct mosquitto *mosq, uint8_t command, uint16_t mid, bool dup) +int send__command_with_mid(struct mosquitto *mosq, uint8_t command, uint16_t mid, bool dup, uint8_t reason_code, const mosquitto_property *properties) { struct mosquitto__packet *packet = NULL; int rc; @@ -120,14 +132,33 @@ int send__command_with_mid(struct mosquitto *mosq, uint8_t command, uint16_t mid packet->command |= 8; } packet->remaining_length = 2; + + if(mosq->protocol == mosq_p_mqtt5){ + if(reason_code != 0 || properties){ + packet->remaining_length += 1; + } + + if(properties){ + packet->remaining_length += property__get_remaining_length(properties); + } + } + rc = packet__alloc(packet); if(rc){ mosquitto__free(packet); return rc; } - packet->payload[packet->pos+0] = MOSQ_MSB(mid); - packet->payload[packet->pos+1] = MOSQ_LSB(mid); + packet__write_uint16(packet, mid); + + if(mosq->protocol == mosq_p_mqtt5){ + if(reason_code != 0 || properties){ + packet__write_byte(packet, reason_code); + } + if(properties){ + property__write_all(packet, properties, true); + } + } return packet__queue(mosq, packet); } diff --git a/libs/mosquitto/src/send_mosq.h b/libs/mosquitto/src/send_mosq.h index c96462c..85b51c3 100644 --- a/libs/mosquitto/src/send_mosq.h +++ b/libs/mosquitto/src/send_mosq.h @@ -1,15 +1,17 @@ /* -Copyright (c) 2010-2019 Roger Light +Copyright (c) 2010-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. */ @@ -17,21 +19,22 @@ and the Eclipse Distribution License is available at #define SEND_MOSQ_H #include "mosquitto.h" +#include "property_mosq.h" int send__simple_command(struct mosquitto *mosq, uint8_t command); -int send__command_with_mid(struct mosquitto *mosq, uint8_t command, uint16_t mid, bool dup); -int send__real_publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint32_t payloadlen, const void *payload, int qos, bool retain, bool dup); +int send__command_with_mid(struct mosquitto *mosq, uint8_t command, uint16_t mid, bool dup, uint8_t reason_code, const mosquitto_property *properties); +int send__real_publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint32_t payloadlen, const void *payload, uint8_t qos, bool retain, bool dup, const mosquitto_property *cmsg_props, const mosquitto_property *store_props, uint32_t expiry_interval); -int send__connect(struct mosquitto *mosq, uint16_t keepalive, bool clean_session); -int send__disconnect(struct mosquitto *mosq); +int send__connect(struct mosquitto *mosq, uint16_t keepalive, bool clean_session, const mosquitto_property *properties); +int send__disconnect(struct mosquitto *mosq, uint8_t reason_code, const mosquitto_property *properties); int send__pingreq(struct mosquitto *mosq); int send__pingresp(struct mosquitto *mosq); -int send__puback(struct mosquitto *mosq, uint16_t mid); -int send__pubcomp(struct mosquitto *mosq, uint16_t mid); -int send__publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint32_t payloadlen, const void *payload, int qos, bool retain, bool dup); -int send__pubrec(struct mosquitto *mosq, uint16_t mid); -int send__pubrel(struct mosquitto *mosq, uint16_t mid); -int send__subscribe(struct mosquitto *mosq, int *mid, const char *topic, uint8_t topic_qos); -int send__unsubscribe(struct mosquitto *mosq, int *mid, const char *topic); +int send__puback(struct mosquitto *mosq, uint16_t mid, uint8_t reason_code, const mosquitto_property *properties); +int send__pubcomp(struct mosquitto *mosq, uint16_t mid, const mosquitto_property *properties); +int send__publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint32_t payloadlen, const void *payload, uint8_t qos, bool retain, bool dup, const mosquitto_property *cmsg_props, const mosquitto_property *store_props, uint32_t expiry_interval); +int send__pubrec(struct mosquitto *mosq, uint16_t mid, uint8_t reason_code, const mosquitto_property *properties); +int send__pubrel(struct mosquitto *mosq, uint16_t mid, const mosquitto_property *properties); +int send__subscribe(struct mosquitto *mosq, int *mid, int topic_count, char *const *const topic, int topic_qos, const mosquitto_property *properties); +int send__unsubscribe(struct mosquitto *mosq, int *mid, int topic_count, char *const *const topic, const mosquitto_property *properties); #endif diff --git a/libs/mosquitto/src/send_publish.c b/libs/mosquitto/src/send_publish.c index 8ca3e34..1b5ffda 100644 --- a/libs/mosquitto/src/send_publish.c +++ b/libs/mosquitto/src/send_publish.c @@ -1,15 +1,17 @@ /* -Copyright (c) 2009-2019 Roger Light +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. */ @@ -29,14 +31,15 @@ and the Eclipse Distribution License is available at #include "mosquitto.h" #include "mosquitto_internal.h" #include "logging_mosq.h" -#include "mqtt3_protocol.h" +#include "mqtt_protocol.h" #include "memory_mosq.h" #include "net_mosq.h" #include "packet_mosq.h" +#include "property_mosq.h" #include "send_mosq.h" -int send__publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint32_t payloadlen, const void *payload, int qos, bool retain, bool dup) +int send__publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint32_t payloadlen, const void *payload, uint8_t qos, bool retain, bool dup, const mosquitto_property *cmsg_props, const mosquitto_property *store_props, uint32_t expiry_interval) { #ifdef WITH_BROKER size_t len; @@ -50,7 +53,6 @@ int send__publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint3 #endif #endif assert(mosq); - assert(topic); #if defined(WITH_BROKER) && defined(WITH_WEBSOCKETS) if(mosq->sock == INVALID_SOCKET && !mosq->wsi) return MOSQ_ERR_NO_CONN; @@ -58,6 +60,10 @@ int send__publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint3 if(mosq->sock == INVALID_SOCKET) return MOSQ_ERR_NO_CONN; #endif + if(!mosq->retain_available){ + retain = false; + } + #ifdef WITH_BROKER if(mosq->listener && mosq->listener->mount_point){ len = strlen(mosq->listener->mount_point); @@ -108,9 +114,9 @@ int send__publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint3 mosquitto__free(mapped_topic); mapped_topic = topic_temp; } - log__printf(NULL, MOSQ_LOG_DEBUG, "Sending PUBLISH to %s (d%d, q%d, r%d, m%d, '%s', ... (%ld bytes))", mosq->id, dup, qos, retain, mid, mapped_topic, (long)payloadlen); + log__printf(NULL, MOSQ_LOG_DEBUG, "Sending PUBLISH to %s (d%d, q%d, r%d, m%d, '%s', ... (%ld bytes))", SAFE_PRINT(mosq->id), dup, qos, retain, mid, mapped_topic, (long)payloadlen); G_PUB_BYTES_SENT_INC(payloadlen); - rc = send__real_publish(mosq, mid, mapped_topic, payloadlen, payload, qos, retain, dup); + rc = send__real_publish(mosq, mid, mapped_topic, payloadlen, payload, qos, retain, dup, cmsg_props, store_props, expiry_interval); mosquitto__free(mapped_topic); return rc; } @@ -118,32 +124,69 @@ int send__publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint3 } } #endif - log__printf(NULL, MOSQ_LOG_DEBUG, "Sending PUBLISH to %s (d%d, q%d, r%d, m%d, '%s', ... (%ld bytes))", mosq->id, dup, qos, retain, mid, topic, (long)payloadlen); + log__printf(NULL, MOSQ_LOG_DEBUG, "Sending PUBLISH to %s (d%d, q%d, r%d, m%d, '%s', ... (%ld bytes))", SAFE_PRINT(mosq->id), dup, qos, retain, mid, topic, (long)payloadlen); G_PUB_BYTES_SENT_INC(payloadlen); #else - log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending PUBLISH (d%d, q%d, r%d, m%d, '%s', ... (%ld bytes))", mosq->id, dup, qos, retain, mid, topic, (long)payloadlen); + log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending PUBLISH (d%d, q%d, r%d, m%d, '%s', ... (%ld bytes))", SAFE_PRINT(mosq->id), dup, qos, retain, mid, topic, (long)payloadlen); #endif - return send__real_publish(mosq, mid, topic, payloadlen, payload, qos, retain, dup); + return send__real_publish(mosq, mid, topic, payloadlen, payload, qos, retain, dup, cmsg_props, store_props, expiry_interval); } -int send__real_publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint32_t payloadlen, const void *payload, int qos, bool retain, bool dup) +int send__real_publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint32_t payloadlen, const void *payload, uint8_t qos, bool retain, bool dup, const mosquitto_property *cmsg_props, const mosquitto_property *store_props, uint32_t expiry_interval) { struct mosquitto__packet *packet = NULL; - int packetlen; + unsigned int packetlen; + unsigned int proplen = 0, varbytes; int rc; + mosquitto_property expiry_prop; assert(mosq); - assert(topic); - packetlen = 2+strlen(topic) + payloadlen; + if(topic){ + packetlen = 2+(unsigned int)strlen(topic) + payloadlen; + }else{ + packetlen = 2 + payloadlen; + } if(qos > 0) packetlen += 2; /* For message id */ + if(mosq->protocol == mosq_p_mqtt5){ + proplen = 0; + proplen += property__get_length_all(cmsg_props); + proplen += property__get_length_all(store_props); + if(expiry_interval > 0){ + expiry_prop.next = NULL; + expiry_prop.value.i32 = expiry_interval; + expiry_prop.identifier = MQTT_PROP_MESSAGE_EXPIRY_INTERVAL; + expiry_prop.client_generated = false; + + proplen += property__get_length_all(&expiry_prop); + } + + varbytes = packet__varint_bytes(proplen); + if(varbytes > 4){ + /* FIXME - Properties too big, don't publish any - should remove some first really */ + cmsg_props = NULL; + store_props = NULL; + expiry_interval = 0; + }else{ + packetlen += proplen + varbytes; + } + } + if(packet__check_oversize(mosq, packetlen)){ +#ifdef WITH_BROKER + log__printf(NULL, MOSQ_LOG_NOTICE, "Dropping too large outgoing PUBLISH for %s (%d bytes)", SAFE_PRINT(mosq->id), packetlen); +#else + log__printf(NULL, MOSQ_LOG_NOTICE, "Dropping too large outgoing PUBLISH (%d bytes)", packetlen); +#endif + return MOSQ_ERR_OVERSIZE_PACKET; + } + packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet)); if(!packet) return MOSQ_ERR_NOMEM; packet->mid = mid; - packet->command = PUBLISH | ((dup&0x1)<<3) | (qos<<1) | retain; + packet->command = (uint8_t)(CMD_PUBLISH | (uint8_t)((dup&0x1)<<3) | (uint8_t)(qos<<1) | retain); packet->remaining_length = packetlen; rc = packet__alloc(packet); if(rc){ @@ -151,11 +194,24 @@ int send__real_publish(struct mosquitto *mosq, uint16_t mid, const char *topic, return rc; } /* Variable header (topic string) */ - packet__write_string(packet, topic, strlen(topic)); + if(topic){ + packet__write_string(packet, topic, (uint16_t)strlen(topic)); + }else{ + packet__write_uint16(packet, 0); + } if(qos > 0){ packet__write_uint16(packet, mid); } + if(mosq->protocol == mosq_p_mqtt5){ + packet__write_varint(packet, proplen); + property__write_all(packet, cmsg_props, false); + property__write_all(packet, store_props, false); + if(expiry_interval > 0){ + property__write_all(packet, &expiry_prop, false); + } + } + /* Payload */ if(payloadlen){ packet__write_bytes(packet, payload, payloadlen); diff --git a/libs/mosquitto/src/send_subscribe.c b/libs/mosquitto/src/send_subscribe.c index bf2f5a0..40e1d43 100644 --- a/libs/mosquitto/src/send_subscribe.c +++ b/libs/mosquitto/src/send_subscribe.c @@ -1,15 +1,17 @@ /* -Copyright (c) 2009-2019 Roger Light +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. */ @@ -27,28 +29,42 @@ and the Eclipse Distribution License is available at #include "mosquitto_internal.h" #include "logging_mosq.h" #include "memory_mosq.h" -#include "mqtt3_protocol.h" +#include "mqtt_protocol.h" #include "packet_mosq.h" +#include "property_mosq.h" +#include "send_mosq.h" #include "util_mosq.h" -int send__subscribe(struct mosquitto *mosq, int *mid, const char *topic, uint8_t topic_qos) +int send__subscribe(struct mosquitto *mosq, int *mid, int topic_count, char *const *const topic, int topic_qos, const mosquitto_property *properties) { - /* FIXME - only deals with a single topic */ struct mosquitto__packet *packet = NULL; uint32_t packetlen; uint16_t local_mid; int rc; + int i; + size_t tlen; assert(mosq); assert(topic); + packetlen = 2; + if(mosq->protocol == mosq_p_mqtt5){ + packetlen += property__get_remaining_length(properties); + } + for(i=0; i UINT16_MAX){ + return MOSQ_ERR_INVAL; + } + packetlen += 2U+(uint16_t)tlen + 1U; + } + packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet)); if(!packet) return MOSQ_ERR_NOMEM; - packetlen = 2 + 2+strlen(topic) + 1; - packet->command = SUBSCRIBE | (1<<1); + packet->command = CMD_SUBSCRIBE | (1<<1); packet->remaining_length = packetlen; rc = packet__alloc(packet); if(rc){ @@ -61,16 +77,24 @@ int send__subscribe(struct mosquitto *mosq, int *mid, const char *topic, uint8_t if(mid) *mid = (int)local_mid; packet__write_uint16(packet, local_mid); + if(mosq->protocol == mosq_p_mqtt5){ + property__write_all(packet, properties, true); + } + /* Payload */ - packet__write_string(packet, topic, strlen(topic)); - packet__write_byte(packet, topic_qos); + for(i=0; iid, local_mid, topic, topic_qos); + log__printf(mosq, MOSQ_LOG_DEBUG, "Bridge %s sending SUBSCRIBE (Mid: %d, Topic: %s, QoS: %d, Options: 0x%02x)", SAFE_PRINT(mosq->id), local_mid, topic[0], topic_qos&0x03, topic_qos&0xFC); # endif #else - log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending SUBSCRIBE (Mid: %d, Topic: %s, QoS: %d)", mosq->id, local_mid, topic, topic_qos); + for(i=0; iid), local_mid, topic[i], topic_qos&0x03, topic_qos&0xFC); + } #endif return packet__queue(mosq, packet); diff --git a/libs/mosquitto/src/send_unsubscribe.c b/libs/mosquitto/src/send_unsubscribe.c index 5a94693..0806b81 100644 --- a/libs/mosquitto/src/send_unsubscribe.c +++ b/libs/mosquitto/src/send_unsubscribe.c @@ -1,15 +1,17 @@ /* -Copyright (c) 2009-2019 Roger Light +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. */ @@ -26,28 +28,42 @@ and the Eclipse Distribution License is available at #include "mosquitto.h" #include "logging_mosq.h" #include "memory_mosq.h" -#include "mqtt3_protocol.h" +#include "mqtt_protocol.h" #include "packet_mosq.h" +#include "property_mosq.h" +#include "send_mosq.h" #include "util_mosq.h" -int send__unsubscribe(struct mosquitto *mosq, int *mid, const char *topic) +int send__unsubscribe(struct mosquitto *mosq, int *mid, int topic_count, char *const *const topic, const mosquitto_property *properties) { - /* FIXME - only deals with a single topic */ struct mosquitto__packet *packet = NULL; uint32_t packetlen; uint16_t local_mid; int rc; + int i; + size_t tlen; assert(mosq); assert(topic); + packetlen = 2; + for(i=0; i UINT16_MAX){ + return MOSQ_ERR_INVAL; + } + packetlen += 2U+(uint16_t)tlen; + } + packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet)); if(!packet) return MOSQ_ERR_NOMEM; - packetlen = 2 + 2+strlen(topic); + if(mosq->protocol == mosq_p_mqtt5){ + packetlen += property__get_remaining_length(properties); + } - packet->command = UNSUBSCRIBE | (1<<1); + packet->command = CMD_UNSUBSCRIBE | (1<<1); packet->remaining_length = packetlen; rc = packet__alloc(packet); if(rc){ @@ -60,15 +76,26 @@ int send__unsubscribe(struct mosquitto *mosq, int *mid, const char *topic) if(mid) *mid = (int)local_mid; packet__write_uint16(packet, local_mid); + if(mosq->protocol == mosq_p_mqtt5){ + /* We don't use User Property yet. */ + property__write_all(packet, properties, true); + } + /* Payload */ - packet__write_string(packet, topic, strlen(topic)); + for(i=0; iid, local_mid, topic); + for(i=0; iid), local_mid, topic[i]); + } # endif #else - log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending UNSUBSCRIBE (Mid: %d, Topic: %s)", mosq->id, local_mid, topic); + for(i=0; iid), local_mid, topic[i]); + } #endif return packet__queue(mosq, packet); } diff --git a/libs/mosquitto/src/socks_mosq.c b/libs/mosquitto/src/socks_mosq.c index 8a099de..a7a0e03 100644 --- a/libs/mosquitto/src/socks_mosq.c +++ b/libs/mosquitto/src/socks_mosq.c @@ -1,15 +1,17 @@ /* -Copyright (c) 2014-2019 Roger Light +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. */ @@ -21,13 +23,14 @@ and the Eclipse Distribution License is available at #include #ifdef WIN32 # include -#elif __QNX__ +#elif defined(__QNX__) # include +# include # include #else # include #endif -#ifdef __FreeBSD__ +#if defined(__FreeBSD__) || defined(__OpenBSD__) # include # include #endif @@ -37,32 +40,34 @@ and the Eclipse Distribution License is available at #include "net_mosq.h" #include "packet_mosq.h" #include "send_mosq.h" - -#define SOCKS_AUTH_NONE 0x00 -#define SOCKS_AUTH_GSS 0x01 -#define SOCKS_AUTH_USERPASS 0x02 -#define SOCKS_AUTH_NO_ACCEPTABLE 0xFF - -#define SOCKS_ATYPE_IP_V4 1 /* four bytes */ -#define SOCKS_ATYPE_DOMAINNAME 3 /* one byte length, followed by fqdn no null, 256 max chars */ -#define SOCKS_ATYPE_IP_V6 4 /* 16 bytes */ - -#define SOCKS_REPLY_SUCCEEDED 0x00 -#define SOCKS_REPLY_GENERAL_FAILURE 0x01 -#define SOCKS_REPLY_CONNECTION_NOT_ALLOWED 0x02 -#define SOCKS_REPLY_NETWORK_UNREACHABLE 0x03 -#define SOCKS_REPLY_HOST_UNREACHABLE 0x04 -#define SOCKS_REPLY_CONNECTION_REFUSED 0x05 -#define SOCKS_REPLY_TTL_EXPIRED 0x06 -#define SOCKS_REPLY_COMMAND_NOT_SUPPORTED 0x07 -#define SOCKS_REPLY_ADDRESS_TYPE_NOT_SUPPORTED 0x08 +#include "socks_mosq.h" +#include "util_mosq.h" + +#define SOCKS_AUTH_NONE 0x00U +#define SOCKS_AUTH_GSS 0x01U +#define SOCKS_AUTH_USERPASS 0x02U +#define SOCKS_AUTH_NO_ACCEPTABLE 0xFFU + +#define SOCKS_ATYPE_IP_V4 1U /* four bytes */ +#define SOCKS_ATYPE_DOMAINNAME 3U /* one byte length, followed by fqdn no null, 256 max chars */ +#define SOCKS_ATYPE_IP_V6 4U /* 16 bytes */ + +#define SOCKS_REPLY_SUCCEEDED 0x00U +#define SOCKS_REPLY_GENERAL_FAILURE 0x01U +#define SOCKS_REPLY_CONNECTION_NOT_ALLOWED 0x02U +#define SOCKS_REPLY_NETWORK_UNREACHABLE 0x03U +#define SOCKS_REPLY_HOST_UNREACHABLE 0x04U +#define SOCKS_REPLY_CONNECTION_REFUSED 0x05U +#define SOCKS_REPLY_TTL_EXPIRED 0x06U +#define SOCKS_REPLY_COMMAND_NOT_SUPPORTED 0x07U +#define SOCKS_REPLY_ADDRESS_TYPE_NOT_SUPPORTED 0x08U int mosquitto_socks5_set(struct mosquitto *mosq, const char *host, int port, const char *username, const char *password) { #ifdef WITH_SOCKS if(!mosq) return MOSQ_ERR_INVAL; if(!host || strlen(host) > 256) return MOSQ_ERR_INVAL; - if(port < 1 || port > 65535) return MOSQ_ERR_INVAL; + if(port < 1 || port > UINT16_MAX) return MOSQ_ERR_INVAL; mosquitto__free(mosq->socks5_host); mosq->socks5_host = NULL; @@ -72,7 +77,7 @@ int mosquitto_socks5_set(struct mosquitto *mosq, const char *host, int port, con return MOSQ_ERR_NOMEM; } - mosq->socks5_port = port; + mosq->socks5_port = (uint16_t)port; mosquitto__free(mosq->socks5_username); mosq->socks5_username = NULL; @@ -81,12 +86,18 @@ int mosquitto_socks5_set(struct mosquitto *mosq, const char *host, int port, con mosq->socks5_password = NULL; if(username){ + if(strlen(username) > UINT8_MAX){ + return MOSQ_ERR_INVAL; + } mosq->socks5_username = mosquitto__strdup(username); if(!mosq->socks5_username){ return MOSQ_ERR_NOMEM; } if(password){ + if(strlen(password) > UINT8_MAX){ + return MOSQ_ERR_INVAL; + } mosq->socks5_password = mosquitto__strdup(password); if(!mosq->socks5_password){ mosquitto__free(mosq->socks5_username); @@ -97,6 +108,12 @@ int mosquitto_socks5_set(struct mosquitto *mosq, const char *host, int port, con return MOSQ_ERR_SUCCESS; #else + UNUSED(mosq); + UNUSED(host); + UNUSED(port); + UNUSED(username); + UNUSED(password); + return MOSQ_ERR_NOT_SUPPORTED; #endif } @@ -105,15 +122,18 @@ int mosquitto_socks5_set(struct mosquitto *mosq, const char *host, int port, con int socks5__send(struct mosquitto *mosq) { struct mosquitto__packet *packet; - int slen; - int ulen, plen; + size_t slen; + uint8_t ulen, plen; struct in_addr addr_ipv4; struct in6_addr addr_ipv6; int ipv4_pton_result; int ipv6_pton_result; + enum mosquitto_client_state state; - if(mosq->state == mosq_cs_socks5_new){ + state = mosquitto__get_state(mosq); + + if(state == mosq_cs_socks5_new){ packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet)); if(!packet) return MOSQ_ERR_NOMEM; @@ -134,9 +154,7 @@ int socks5__send(struct mosquitto *mosq) packet->payload[2] = SOCKS_AUTH_NONE; } - pthread_mutex_lock(&mosq->state_mutex); - mosq->state = mosq_cs_socks5_start; - pthread_mutex_unlock(&mosq->state_mutex); + mosquitto__set_state(mosq, mosq_cs_socks5_start); mosq->in_packet.pos = 0; mosq->in_packet.packet_length = 2; @@ -149,7 +167,7 @@ int socks5__send(struct mosquitto *mosq) } return packet__queue(mosq, packet); - }else if(mosq->state == mosq_cs_socks5_auth_ok){ + }else if(state == mosq_cs_socks5_auth_ok){ packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet)); if(!packet) return MOSQ_ERR_NOMEM; @@ -183,9 +201,10 @@ int socks5__send(struct mosquitto *mosq) }else{ slen = strlen(mosq->host); if(slen > UCHAR_MAX){ + mosquitto__free(packet); return MOSQ_ERR_NOMEM; } - packet->packet_length = 7 + slen; + packet->packet_length = 7U + (uint32_t)slen; packet->payload = mosquitto__malloc(sizeof(uint8_t)*packet->packet_length); if(!packet->payload){ mosquitto__free(packet); @@ -201,9 +220,7 @@ int socks5__send(struct mosquitto *mosq) packet->payload[1] = 0x01; packet->payload[2] = 0x00; - pthread_mutex_lock(&mosq->state_mutex); - mosq->state = mosq_cs_socks5_request; - pthread_mutex_unlock(&mosq->state_mutex); + mosquitto__set_state(mosq, mosq_cs_socks5_request); mosq->in_packet.pos = 0; mosq->in_packet.packet_length = 5; @@ -216,13 +233,13 @@ int socks5__send(struct mosquitto *mosq) } return packet__queue(mosq, packet); - }else if(mosq->state == mosq_cs_socks5_send_userpass){ + }else if(state == mosq_cs_socks5_send_userpass){ packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet)); if(!packet) return MOSQ_ERR_NOMEM; - ulen = strlen(mosq->socks5_username); - plen = strlen(mosq->socks5_password); - packet->packet_length = 3 + ulen + plen; + ulen = (uint8_t)strlen(mosq->socks5_username); + plen = (uint8_t)strlen(mosq->socks5_password); + packet->packet_length = 3U + ulen + plen; packet->payload = mosquitto__malloc(sizeof(uint8_t)*packet->packet_length); @@ -232,9 +249,7 @@ int socks5__send(struct mosquitto *mosq) packet->payload[2+ulen] = plen; memcpy(&(packet->payload[3+ulen]), mosq->socks5_password, plen); - pthread_mutex_lock(&mosq->state_mutex); - mosq->state = mosq_cs_socks5_userpass_reply; - pthread_mutex_unlock(&mosq->state_mutex); + mosquitto__set_state(mosq, mosq_cs_socks5_userpass_reply); mosq->in_packet.pos = 0; mosq->in_packet.packet_length = 2; @@ -256,13 +271,15 @@ int socks5__read(struct mosquitto *mosq) ssize_t len; uint8_t *payload; uint8_t i; + enum mosquitto_client_state state; - if(mosq->state == mosq_cs_socks5_start){ + state = mosquitto__get_state(mosq); + if(state == mosq_cs_socks5_start){ while(mosq->in_packet.to_process > 0){ len = net__read(mosq, &(mosq->in_packet.payload[mosq->in_packet.pos]), mosq->in_packet.to_process); if(len > 0){ - mosq->in_packet.pos += len; - mosq->in_packet.to_process -= len; + mosq->in_packet.pos += (uint32_t)len; + mosq->in_packet.to_process -= (uint32_t)len; }else{ #ifdef WIN32 errno = WSAGetLastError(); @@ -289,22 +306,22 @@ int socks5__read(struct mosquitto *mosq) switch(mosq->in_packet.payload[1]){ case SOCKS_AUTH_NONE: packet__cleanup(&mosq->in_packet); - mosq->state = mosq_cs_socks5_auth_ok; + mosquitto__set_state(mosq, mosq_cs_socks5_auth_ok); return socks5__send(mosq); case SOCKS_AUTH_USERPASS: packet__cleanup(&mosq->in_packet); - mosq->state = mosq_cs_socks5_send_userpass; + mosquitto__set_state(mosq, mosq_cs_socks5_send_userpass); return socks5__send(mosq); default: packet__cleanup(&mosq->in_packet); return MOSQ_ERR_AUTH; } - }else if(mosq->state == mosq_cs_socks5_userpass_reply){ + }else if(state == mosq_cs_socks5_userpass_reply){ while(mosq->in_packet.to_process > 0){ len = net__read(mosq, &(mosq->in_packet.payload[mosq->in_packet.pos]), mosq->in_packet.to_process); if(len > 0){ - mosq->in_packet.pos += len; - mosq->in_packet.to_process -= len; + mosq->in_packet.pos += (uint32_t)len; + mosq->in_packet.to_process -= (uint32_t)len; }else{ #ifdef WIN32 errno = WSAGetLastError(); @@ -330,7 +347,7 @@ int socks5__read(struct mosquitto *mosq) } if(mosq->in_packet.payload[1] == 0){ packet__cleanup(&mosq->in_packet); - mosq->state = mosq_cs_socks5_auth_ok; + mosquitto__set_state(mosq, mosq_cs_socks5_auth_ok); return socks5__send(mosq); }else{ i = mosq->in_packet.payload[1]; @@ -355,12 +372,12 @@ int socks5__read(struct mosquitto *mosq) } return MOSQ_ERR_PROXY; } - }else if(mosq->state == mosq_cs_socks5_request){ + }else if(state == mosq_cs_socks5_request){ while(mosq->in_packet.to_process > 0){ len = net__read(mosq, &(mosq->in_packet.payload[mosq->in_packet.pos]), mosq->in_packet.to_process); if(len > 0){ - mosq->in_packet.pos += len; - mosq->in_packet.to_process -= len; + mosq->in_packet.pos += (uint32_t)len; + mosq->in_packet.to_process -= (uint32_t)len; }else{ #ifdef WIN32 errno = WSAGetLastError(); @@ -390,7 +407,7 @@ int socks5__read(struct mosquitto *mosq) mosq->in_packet.to_process += 16+2-1; /* 16 bytes IPv6, 2 bytes port, -1 byte because we've already read the first byte */ mosq->in_packet.packet_length += 16+2-1; }else if(mosq->in_packet.payload[3] == SOCKS_ATYPE_DOMAINNAME){ - if(mosq->in_packet.payload[4] > 0 && mosq->in_packet.payload[4] <= 255){ + if(mosq->in_packet.payload[4] > 0){ mosq->in_packet.to_process += mosq->in_packet.payload[4]; mosq->in_packet.packet_length += mosq->in_packet.payload[4]; } @@ -405,13 +422,6 @@ int socks5__read(struct mosquitto *mosq) packet__cleanup(&mosq->in_packet); return MOSQ_ERR_NOMEM; } - payload = mosquitto__realloc(mosq->in_packet.payload, mosq->in_packet.packet_length); - if(payload){ - mosq->in_packet.payload = payload; - }else{ - packet__cleanup(&mosq->in_packet); - return MOSQ_ERR_NOMEM; - } return MOSQ_ERR_SUCCESS; } @@ -423,16 +433,16 @@ int socks5__read(struct mosquitto *mosq) if(mosq->in_packet.payload[1] == 0){ /* Auth passed */ packet__cleanup(&mosq->in_packet); - mosq->state = mosq_cs_new; + mosquitto__set_state(mosq, mosq_cs_new); if(mosq->socks5_host){ int rc = net__socket_connect_step3(mosq, mosq->host); if(rc) return rc; } - return send__connect(mosq, mosq->keepalive, mosq->clean_session); + return send__connect(mosq, mosq->keepalive, mosq->clean_start, NULL); }else{ i = mosq->in_packet.payload[1]; packet__cleanup(&mosq->in_packet); - mosq->state = mosq_cs_socks5_new; + mosquitto__set_state(mosq, mosq_cs_socks5_new); switch(i){ case SOCKS_REPLY_CONNECTION_NOT_ALLOWED: return MOSQ_ERR_AUTH; diff --git a/libs/mosquitto/src/socks_mosq.h b/libs/mosquitto/src/socks_mosq.h index 27b3dc3..d08c3c5 100644 --- a/libs/mosquitto/src/socks_mosq.h +++ b/libs/mosquitto/src/socks_mosq.h @@ -1,15 +1,17 @@ /* -Copyright (c) 2014-2019 Roger Light +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. */ diff --git a/libs/mosquitto/src/srv_mosq.c b/libs/mosquitto/src/srv_mosq.c index b830fcb..55d58ff 100644 --- a/libs/mosquitto/src/srv_mosq.c +++ b/libs/mosquitto/src/srv_mosq.c @@ -1,15 +1,17 @@ /* -Copyright (c) 2013-2019 Roger Light +Copyright (c) 2013-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. */ @@ -28,12 +30,16 @@ and the Eclipse Distribution License is available at #include "memory_mosq.h" #include "mosquitto_internal.h" #include "mosquitto.h" +#include "util_mosq.h" #ifdef WITH_SRV static void srv_callback(void *arg, int status, int timeouts, unsigned char *abuf, int alen) -{ +{ struct mosquitto *mosq = arg; struct ares_srv_reply *reply = NULL; + + UNUSED(timeouts); + if(status == ARES_SUCCESS){ status = ares_parse_srv_reply(abuf, alen, &reply); if(status == ARES_SUCCESS){ @@ -49,6 +55,11 @@ static void srv_callback(void *arg, int status, int timeouts, unsigned char *abu mosq->on_disconnect(mosq, mosq->userdata, MOSQ_ERR_LOOKUP); mosq->in_callback = false; } + if(mosq->on_disconnect_v5){ + mosq->in_callback = true; + mosq->on_disconnect_v5(mosq, mosq->userdata, MOSQ_ERR_LOOKUP, NULL); + mosq->in_callback = false; + } pthread_mutex_unlock(&mosq->callback_mutex); } } @@ -61,6 +72,12 @@ int mosquitto_connect_srv(struct mosquitto *mosq, const char *host, int keepaliv int rc; if(!mosq) return MOSQ_ERR_INVAL; + UNUSED(bind_address); + + if(keepalive < 0 || keepalive > UINT16_MAX){ + return MOSQ_ERR_INVAL; + } + rc = ares_init(&mosq->achan); if(rc != ARES_SUCCESS){ return MOSQ_ERR_UNKNOWN; @@ -86,15 +103,18 @@ int mosquitto_connect_srv(struct mosquitto *mosq, const char *host, int keepaliv mosquitto__free(h); } - pthread_mutex_lock(&mosq->state_mutex); - mosq->state = mosq_cs_connect_srv; - pthread_mutex_unlock(&mosq->state_mutex); + mosquitto__set_state(mosq, mosq_cs_connect_srv); - mosq->keepalive = keepalive; + mosq->keepalive = (uint16_t)keepalive; return MOSQ_ERR_SUCCESS; #else + UNUSED(mosq); + UNUSED(host); + UNUSED(keepalive); + UNUSED(bind_address); + return MOSQ_ERR_NOT_SUPPORTED; #endif } diff --git a/libs/mosquitto/src/strings_mosq.c b/libs/mosquitto/src/strings_mosq.c new file mode 100644 index 0000000..292a1a7 --- /dev/null +++ b/libs/mosquitto/src/strings_mosq.c @@ -0,0 +1,235 @@ +/* +Copyright (c) 2010-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 "mosquitto.h" +#include "mqtt_protocol.h" + +const char *mosquitto_strerror(int mosq_errno) +{ + switch(mosq_errno){ + case MOSQ_ERR_AUTH_CONTINUE: + return "Continue with authentication."; + case MOSQ_ERR_NO_SUBSCRIBERS: + return "No subscribers."; + case MOSQ_ERR_SUB_EXISTS: + return "Subscription already exists."; + case MOSQ_ERR_CONN_PENDING: + return "Connection pending."; + case MOSQ_ERR_SUCCESS: + return "No error."; + case MOSQ_ERR_NOMEM: + return "Out of memory."; + case MOSQ_ERR_PROTOCOL: + return "A network protocol error occurred when communicating with the broker."; + case MOSQ_ERR_INVAL: + return "Invalid arguments provided."; + case MOSQ_ERR_NO_CONN: + return "The client is not currently connected."; + case MOSQ_ERR_CONN_REFUSED: + return "The connection was refused."; + case MOSQ_ERR_NOT_FOUND: + return "Message not found (internal error)."; + case MOSQ_ERR_CONN_LOST: + return "The connection was lost."; + case MOSQ_ERR_TLS: + return "A TLS error occurred."; + case MOSQ_ERR_PAYLOAD_SIZE: + return "Payload too large."; + case MOSQ_ERR_NOT_SUPPORTED: + return "This feature is not supported."; + case MOSQ_ERR_AUTH: + return "Authorisation failed."; + case MOSQ_ERR_ACL_DENIED: + return "Access denied by ACL."; + case MOSQ_ERR_UNKNOWN: + return "Unknown error."; + case MOSQ_ERR_ERRNO: + return strerror(errno); + case MOSQ_ERR_EAI: + return "Lookup error."; + case MOSQ_ERR_PROXY: + return "Proxy error."; + case MOSQ_ERR_MALFORMED_UTF8: + return "Malformed UTF-8"; + case MOSQ_ERR_DUPLICATE_PROPERTY: + return "Duplicate property in property list"; + case MOSQ_ERR_TLS_HANDSHAKE: + return "TLS handshake failed."; + case MOSQ_ERR_QOS_NOT_SUPPORTED: + return "Requested QoS not supported on server."; + case MOSQ_ERR_OVERSIZE_PACKET: + return "Packet larger than supported by the server."; + case MOSQ_ERR_OCSP: + return "OCSP error."; + default: + return "Unknown error."; + } +} + +const char *mosquitto_connack_string(int connack_code) +{ + switch(connack_code){ + case 0: + return "Connection Accepted."; + case 1: + return "Connection Refused: unacceptable protocol version."; + case 2: + return "Connection Refused: identifier rejected."; + case 3: + return "Connection Refused: broker unavailable."; + case 4: + return "Connection Refused: bad user name or password."; + case 5: + return "Connection Refused: not authorised."; + default: + return "Connection Refused: unknown reason."; + } +} + +const char *mosquitto_reason_string(int reason_code) +{ + switch(reason_code){ + case MQTT_RC_SUCCESS: + return "Success"; + case MQTT_RC_GRANTED_QOS1: + return "Granted QoS 1"; + case MQTT_RC_GRANTED_QOS2: + return "Granted QoS 2"; + case MQTT_RC_DISCONNECT_WITH_WILL_MSG: + return "Disconnect with Will Message"; + case MQTT_RC_NO_MATCHING_SUBSCRIBERS: + return "No matching subscribers"; + case MQTT_RC_NO_SUBSCRIPTION_EXISTED: + return "No subscription existed"; + case MQTT_RC_CONTINUE_AUTHENTICATION: + return "Continue authentication"; + case MQTT_RC_REAUTHENTICATE: + return "Re-authenticate"; + + case MQTT_RC_UNSPECIFIED: + return "Unspecified error"; + case MQTT_RC_MALFORMED_PACKET: + return "Malformed Packet"; + case MQTT_RC_PROTOCOL_ERROR: + return "Protocol Error"; + case MQTT_RC_IMPLEMENTATION_SPECIFIC: + return "Implementation specific error"; + case MQTT_RC_UNSUPPORTED_PROTOCOL_VERSION: + return "Unsupported Protocol Version"; + case MQTT_RC_CLIENTID_NOT_VALID: + return "Client Identifier not valid"; + case MQTT_RC_BAD_USERNAME_OR_PASSWORD: + return "Bad User Name or Password"; + case MQTT_RC_NOT_AUTHORIZED: + return "Not authorized"; + case MQTT_RC_SERVER_UNAVAILABLE: + return "Server unavailable"; + case MQTT_RC_SERVER_BUSY: + return "Server busy"; + case MQTT_RC_BANNED: + return "Banned"; + case MQTT_RC_SERVER_SHUTTING_DOWN: + return "Server shutting down"; + case MQTT_RC_BAD_AUTHENTICATION_METHOD: + return "Bad authentication method"; + case MQTT_RC_KEEP_ALIVE_TIMEOUT: + return "Keep Alive timeout"; + case MQTT_RC_SESSION_TAKEN_OVER: + return "Session taken over"; + case MQTT_RC_TOPIC_FILTER_INVALID: + return "Topic Filter invalid"; + case MQTT_RC_TOPIC_NAME_INVALID: + return "Topic Name invalid"; + case MQTT_RC_PACKET_ID_IN_USE: + return "Packet Identifier in use"; + case MQTT_RC_PACKET_ID_NOT_FOUND: + return "Packet Identifier not found"; + case MQTT_RC_RECEIVE_MAXIMUM_EXCEEDED: + return "Receive Maximum exceeded"; + case MQTT_RC_TOPIC_ALIAS_INVALID: + return "Topic Alias invalid"; + case MQTT_RC_PACKET_TOO_LARGE: + return "Packet too large"; + case MQTT_RC_MESSAGE_RATE_TOO_HIGH: + return "Message rate too high"; + case MQTT_RC_QUOTA_EXCEEDED: + return "Quota exceeded"; + case MQTT_RC_ADMINISTRATIVE_ACTION: + return "Administrative action"; + case MQTT_RC_PAYLOAD_FORMAT_INVALID: + return "Payload format invalid"; + case MQTT_RC_RETAIN_NOT_SUPPORTED: + return "Retain not supported"; + case MQTT_RC_QOS_NOT_SUPPORTED: + return "QoS not supported"; + case MQTT_RC_USE_ANOTHER_SERVER: + return "Use another server"; + case MQTT_RC_SERVER_MOVED: + return "Server moved"; + case MQTT_RC_SHARED_SUBS_NOT_SUPPORTED: + return "Shared Subscriptions not supported"; + case MQTT_RC_CONNECTION_RATE_EXCEEDED: + return "Connection rate exceeded"; + case MQTT_RC_MAXIMUM_CONNECT_TIME: + return "Maximum connect time"; + case MQTT_RC_SUBSCRIPTION_IDS_NOT_SUPPORTED: + return "Subscription identifiers not supported"; + case MQTT_RC_WILDCARD_SUBS_NOT_SUPPORTED: + return "Wildcard Subscriptions not supported"; + default: + return "Unknown reason"; + } +} + + +int mosquitto_string_to_command(const char *str, int *cmd) +{ + if(!strcasecmp(str, "connect")){ + *cmd = CMD_CONNECT; + }else if(!strcasecmp(str, "connack")){ + *cmd = CMD_CONNACK; + }else if(!strcasecmp(str, "publish")){ + *cmd = CMD_PUBLISH; + }else if(!strcasecmp(str, "puback")){ + *cmd = CMD_PUBACK; + }else if(!strcasecmp(str, "pubrec")){ + *cmd = CMD_PUBREC; + }else if(!strcasecmp(str, "pubrel")){ + *cmd = CMD_PUBREL; + }else if(!strcasecmp(str, "pubcomp")){ + *cmd = CMD_PUBCOMP; + }else if(!strcasecmp(str, "subscribe")){ + *cmd = CMD_SUBSCRIBE; + }else if(!strcasecmp(str, "unsubscribe")){ + *cmd = CMD_UNSUBSCRIBE; + }else if(!strcasecmp(str, "disconnect")){ + *cmd = CMD_DISCONNECT; + }else if(!strcasecmp(str, "auth")){ + *cmd = CMD_AUTH; + }else if(!strcasecmp(str, "will")){ + *cmd = CMD_WILL; + }else{ + return MOSQ_ERR_INVAL; + } + return MOSQ_ERR_SUCCESS; +} diff --git a/libs/mosquitto/src/thread_mosq.c b/libs/mosquitto/src/thread_mosq.c index 39f6e35..a792bc1 100644 --- a/libs/mosquitto/src/thread_mosq.c +++ b/libs/mosquitto/src/thread_mosq.c @@ -1,15 +1,17 @@ /* -Copyright (c) 2011-2019 Roger Light +Copyright (c) 2011-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. */ @@ -20,30 +22,47 @@ and the Eclipse Distribution License is available at #include #endif +#if defined(WITH_THREADING) +#if defined(__linux__) || defined(__NetBSD__) +# include +#elif defined(__FreeBSD__) || defined(__OpenBSD__) +# include +#endif +#endif + #include "mosquitto_internal.h" #include "net_mosq.h" +#include "util_mosq.h" void *mosquitto__thread_main(void *obj); int mosquitto_loop_start(struct mosquitto *mosq) { -#ifdef WITH_THREADING +#if defined(WITH_THREADING) if(!mosq || mosq->threaded != mosq_ts_none) return MOSQ_ERR_INVAL; mosq->threaded = mosq_ts_self; if(!pthread_create(&mosq->thread_id, NULL, mosquitto__thread_main, mosq)){ +#if defined(__linux__) + pthread_setname_np(mosq->thread_id, "mosquitto loop"); +#elif defined(__NetBSD__) + pthread_setname_np(mosq->thread_id, "%s", "mosquitto loop"); +#elif defined(__FreeBSD__) || defined(__OpenBSD__) + pthread_set_name_np(mosq->thread_id, "mosquitto loop"); +#endif return MOSQ_ERR_SUCCESS; }else{ return MOSQ_ERR_ERRNO; } #else + UNUSED(mosq); return MOSQ_ERR_NOT_SUPPORTED; #endif } int mosquitto_loop_stop(struct mosquitto *mosq, bool force) { -#ifdef WITH_THREADING +#if defined(WITH_THREADING) # ifndef WITH_BROKER char sockpair_data = 0; # endif @@ -61,16 +80,20 @@ int mosquitto_loop_stop(struct mosquitto *mosq, bool force) send(mosq->sockpairW, &sockpair_data, 1, 0); #endif } - + +#ifdef HAVE_PTHREAD_CANCEL if(force){ pthread_cancel(mosq->thread_id); } +#endif pthread_join(mosq->thread_id, NULL); mosq->thread_id = pthread_self(); mosq->threaded = mosq_ts_none; return MOSQ_ERR_SUCCESS; #else + UNUSED(mosq); + UNUSED(force); return MOSQ_ERR_NOT_SUPPORTED; #endif } @@ -79,7 +102,6 @@ int mosquitto_loop_stop(struct mosquitto *mosq, bool force) void *mosquitto__thread_main(void *obj) { struct mosquitto *mosq = obj; - int state; #ifndef WIN32 struct timespec ts; ts.tv_sec = 0; @@ -89,10 +111,7 @@ void *mosquitto__thread_main(void *obj) if(!mosq) return NULL; do{ - pthread_mutex_lock(&mosq->state_mutex); - state = mosq->state; - pthread_mutex_unlock(&mosq->state_mutex); - if(state == mosq_cs_new){ + if(mosquitto__get_state(mosq) == mosq_cs_new){ #ifdef WIN32 Sleep(10); #else @@ -103,10 +122,6 @@ void *mosquitto__thread_main(void *obj) } }while(1); - if(state == mosq_cs_connect_async){ - mosquitto_reconnect(mosq); - } - if(!mosq->keepalive){ /* Sleep for a day if keepalive disabled. */ mosquitto_loop_forever(mosq, 1000*86400, 1); @@ -114,6 +129,9 @@ void *mosquitto__thread_main(void *obj) /* Sleep for our keepalive value. publish() etc. will wake us up. */ mosquitto_loop_forever(mosq, mosq->keepalive*1000, 1); } + if(mosq->threaded == mosq_ts_self){ + mosq->threaded = mosq_ts_none; + } return obj; } diff --git a/libs/mosquitto/src/time_mosq.c b/libs/mosquitto/src/time_mosq.c index c66d985..3a9362c 100644 --- a/libs/mosquitto/src/time_mosq.c +++ b/libs/mosquitto/src/time_mosq.c @@ -1,15 +1,17 @@ /* -Copyright (c) 2013-2019 Roger Light +Copyright (c) 2013-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. */ @@ -22,7 +24,9 @@ and the Eclipse Distribution License is available at #endif #ifdef WIN32 +#if !(defined(_MSC_VER) && _MSC_VER <= 1500) # define _WIN32_WINNT _WIN32_WINNT_VISTA +#endif # include #else # include diff --git a/libs/mosquitto/src/time_mosq.h b/libs/mosquitto/src/time_mosq.h index 75758a8..93d4fc2 100644 --- a/libs/mosquitto/src/time_mosq.h +++ b/libs/mosquitto/src/time_mosq.h @@ -1,15 +1,17 @@ /* -Copyright (c) 2013-2019 Roger Light +Copyright (c) 2013-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. */ diff --git a/libs/mosquitto/src/tls_mosq.c b/libs/mosquitto/src/tls_mosq.c index c2f1204..940df07 100644 --- a/libs/mosquitto/src/tls_mosq.c +++ b/libs/mosquitto/src/tls_mosq.c @@ -1,23 +1,25 @@ /* -Copyright (c) 2013-2019 Roger Light +Copyright (c) 2013-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. */ -#ifdef WITH_TLS - #include "config.h" +#ifdef WITH_TLS + #ifdef WIN32 # include # include @@ -56,7 +58,11 @@ int mosquitto__server_certificate_verify(int preverify_ok, X509_STORE_CTX *ctx) mosq = SSL_get_ex_data(ssl, tls_ex_index_mosq); if(!mosq) return 0; - if(mosq->tls_insecure == false){ + if(mosq->tls_insecure == false +#ifndef WITH_BROKER + && mosq->port != 0 /* no hostname checking for unix sockets */ +#endif + ){ if(X509_STORE_CTX_get_error_depth(ctx) == 0){ /* FIXME - use X509_check_host() etc. for sufficiently new openssl (>=1.1.x) */ cert = X509_STORE_CTX_get_current_cert(ctx); @@ -78,10 +84,10 @@ int mosquitto__server_certificate_verify(int preverify_ok, X509_STORE_CTX *ctx) } } -int mosquitto__cmp_hostname_wildcard(char *certname, const char *hostname) +static int mosquitto__cmp_hostname_wildcard(char *certname, const char *hostname) { - int i; - int len; + size_t i; + size_t len; if(!certname || !hostname){ return 1; diff --git a/libs/mosquitto/src/tls_mosq.h b/libs/mosquitto/src/tls_mosq.h index 78daf4e..ae175d9 100644 --- a/libs/mosquitto/src/tls_mosq.h +++ b/libs/mosquitto/src/tls_mosq.h @@ -1,15 +1,17 @@ /* -Copyright (c) 2013-2019 Roger Light +Copyright (c) 2013-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. */ @@ -26,6 +28,7 @@ and the Eclipse Distribution License is available at #ifdef WITH_TLS #include +#include int mosquitto__server_certificate_verify(int preverify_ok, X509_STORE_CTX *ctx); int mosquitto__verify_certificate_hostname(X509 *cert, const char *hostname); diff --git a/libs/mosquitto/src/utf8_mosq.c b/libs/mosquitto/src/utf8_mosq.c index 901936e..cc5abc1 100644 --- a/libs/mosquitto/src/utf8_mosq.c +++ b/libs/mosquitto/src/utf8_mosq.c @@ -1,15 +1,17 @@ /* -Copyright (c) 2016-2019 Roger Light +Copyright (c) 2016-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. */ @@ -45,11 +47,11 @@ int mosquitto_validate_utf8(const char *str, int len) codelen = 2; codepoint = (ustr[i] & 0x1F); }else if((ustr[i] & 0xF0) == 0xE0){ - // 1110xxxx - 3 byte sequence + /* 1110xxxx - 3 byte sequence */ codelen = 3; codepoint = (ustr[i] & 0x0F); }else if((ustr[i] & 0xF8) == 0xF0){ - // 11110xxx - 4 byte sequence + /* 11110xxx - 4 byte sequence */ if(ustr[i] > 0xF4){ /* Invalid, this would produce values > 0x10FFFF. */ return MOSQ_ERR_MALFORMED_UTF8; @@ -73,20 +75,36 @@ int mosquitto_validate_utf8(const char *str, int len) } codepoint = (codepoint<<6) | (ustr[i] & 0x3F); } - + /* Check for UTF-16 high/low surrogates */ if(codepoint >= 0xD800 && codepoint <= 0xDFFF){ return MOSQ_ERR_MALFORMED_UTF8; } /* Check for overlong or out of range encodings */ - if(codelen == 2 && codepoint < 0x0080){ - return MOSQ_ERR_MALFORMED_UTF8; - }else if(codelen == 3 && codepoint < 0x0800){ + /* Checking codelen == 2 isn't necessary here, because it is already + * covered above in the C0 and C1 checks. + * if(codelen == 2 && codepoint < 0x0080){ + * return MOSQ_ERR_MALFORMED_UTF8; + * }else + */ + if(codelen == 3 && codepoint < 0x0800){ return MOSQ_ERR_MALFORMED_UTF8; }else if(codelen == 4 && (codepoint < 0x10000 || codepoint > 0x10FFFF)){ return MOSQ_ERR_MALFORMED_UTF8; } + + /* Check for non-characters */ + if(codepoint >= 0xFDD0 && codepoint <= 0xFDEF){ + return MOSQ_ERR_MALFORMED_UTF8; + } + if((codepoint & 0xFFFF) == 0xFFFE || (codepoint & 0xFFFF) == 0xFFFF){ + return MOSQ_ERR_MALFORMED_UTF8; + } + /* Check for control characters */ + if(codepoint <= 0x001F || (codepoint >= 0x007F && codepoint <= 0x009F)){ + return MOSQ_ERR_MALFORMED_UTF8; + } } return MOSQ_ERR_SUCCESS; } diff --git a/libs/mosquitto/src/uthash.h b/libs/mosquitto/src/uthash.h new file mode 100644 index 0000000..76bdca6 --- /dev/null +++ b/libs/mosquitto/src/uthash.h @@ -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 --git a/libs/mosquitto/src/util_mosq.c b/libs/mosquitto/src/util_mosq.c index 25bd61d..fda6980 100644 --- a/libs/mosquitto/src/util_mosq.c +++ b/libs/mosquitto/src/util_mosq.c @@ -1,15 +1,17 @@ /* -Copyright (c) 2009-2019 Roger Light +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. */ @@ -17,6 +19,7 @@ and the Eclipse Distribution License is available at #include "config.h" #include +#include #include #ifdef WIN32 @@ -28,8 +31,16 @@ and the Eclipse Distribution License is available at # include #endif +#if !defined(WITH_TLS) && defined(__linux__) && defined(__GLIBC__) +# if __GLIBC_PREREQ(2, 25) +# include +# define HAVE_GETRANDOM 1 +# endif +#endif + #ifdef WITH_TLS # include +# include #endif #ifdef WITH_BROKER @@ -48,20 +59,23 @@ and the Eclipse Distribution License is available at #include #endif -#ifdef WITH_BROKER -int mosquitto__check_keepalive(struct mosquitto_db *db, struct mosquitto *mosq) -#else int mosquitto__check_keepalive(struct mosquitto *mosq) -#endif { time_t next_msg_out; time_t last_msg_in; - time_t now = mosquitto_time(); + time_t now; #ifndef WITH_BROKER int rc; #endif + enum mosquitto_client_state state; assert(mosq); +#ifdef WITH_BROKER + now = db.now_s; +#else + now = mosquitto_time(); +#endif + #if defined(WITH_BROKER) && defined(WITH_BRIDGE) /* Check if a lazy bridge should be timed out due to idle. */ if(mosq->bridge && mosq->bridge->start_type == bst_lazy @@ -69,7 +83,7 @@ int mosquitto__check_keepalive(struct mosquitto *mosq) && now - mosq->next_msg_out - mosq->keepalive >= mosq->bridge->idle_timeout){ log__printf(NULL, MOSQ_LOG_NOTICE, "Bridge connection %s has exceeded idle timeout, disconnecting.", mosq->id); - net__socket_close(db, mosq); + net__socket_close(mosq); return MOSQ_ERR_SUCCESS; } #endif @@ -80,7 +94,8 @@ int mosquitto__check_keepalive(struct mosquitto *mosq) if(mosq->keepalive && mosq->sock != INVALID_SOCKET && (now >= next_msg_out || now - last_msg_in >= mosq->keepalive)){ - if(mosq->state == mosq_cs_connected && mosq->ping_t == 0){ + state = mosquitto__get_state(mosq); + if(state == mosq_cs_active && mosq->ping_t == 0){ send__pingreq(mosq); /* Reset last msg times to give the server time to send a pingresp */ pthread_mutex_lock(&mosq->msgtime_mutex); @@ -89,22 +104,26 @@ int mosquitto__check_keepalive(struct mosquitto *mosq) pthread_mutex_unlock(&mosq->msgtime_mutex); }else{ #ifdef WITH_BROKER - net__socket_close(db, mosq); + net__socket_close(mosq); #else net__socket_close(mosq); - pthread_mutex_lock(&mosq->state_mutex); - if(mosq->state == mosq_cs_disconnecting){ + state = mosquitto__get_state(mosq); + if(state == mosq_cs_disconnecting){ rc = MOSQ_ERR_SUCCESS; }else{ rc = MOSQ_ERR_KEEPALIVE; } - pthread_mutex_unlock(&mosq->state_mutex); pthread_mutex_lock(&mosq->callback_mutex); if(mosq->on_disconnect){ mosq->in_callback = true; mosq->on_disconnect(mosq, mosq->userdata, rc); mosq->in_callback = false; } + if(mosq->on_disconnect_v5){ + mosq->in_callback = true; + mosq->on_disconnect_v5(mosq, mosq->userdata, rc, NULL); + mosq->in_callback = false; + } pthread_mutex_unlock(&mosq->callback_mutex); return rc; @@ -135,239 +154,32 @@ uint16_t mosquitto__mid_generate(struct mosquitto *mosq) return mid; } -/* Check that a topic used for publishing is valid. - * Search for + or # in a topic. Return MOSQ_ERR_INVAL if found. - * Also returns MOSQ_ERR_INVAL if the topic string is too long. - * Returns MOSQ_ERR_SUCCESS if everything is fine. - */ -int mosquitto_pub_topic_check(const char *str) -{ - int len = 0; - while(str && str[0]){ - if(str[0] == '+' || str[0] == '#'){ - return MOSQ_ERR_INVAL; - } - len++; - str = &str[1]; - } - if(len > 65535) return MOSQ_ERR_INVAL; - - return MOSQ_ERR_SUCCESS; -} - -int mosquitto_pub_topic_check2(const char *str, size_t len) -{ - int i; - - if(len > 65535) return MOSQ_ERR_INVAL; - - for(i=0; i 65535) return MOSQ_ERR_INVAL; - - return MOSQ_ERR_SUCCESS; -} -int mosquitto_sub_topic_check2(const char *str, size_t len) -{ - char c = '\0'; - int i; - - if(len > 65535) return MOSQ_ERR_INVAL; - - for(i=0; i 0 && sub[spos-1] != '/'){ - return MOSQ_ERR_INVAL; - } - /* Check for bad "foo+" or "foo+/a" subscription */ - if(spos < sublen-1 && sub[spos+1] != '/'){ - return MOSQ_ERR_INVAL; - } - spos++; - while(tpos < topiclen && topic[tpos] != '/'){ - tpos++; - } - if(tpos == topiclen && spos == sublen){ - *result = true; - return MOSQ_ERR_SUCCESS; - } - }else if(sub[spos] == '#'){ - if(spos > 0 && sub[spos-1] != '/'){ - return MOSQ_ERR_INVAL; - } - multilevel_wildcard = true; - if(spos+1 != sublen){ - return MOSQ_ERR_INVAL; - }else{ - *result = true; - return MOSQ_ERR_SUCCESS; - } - }else{ - /* Check for e.g. foo/bar matching foo/+/# */ - if(spos > 0 - && spos+2 == sublen - && tpos == topiclen - && sub[spos-1] == '+' - && sub[spos] == '/' - && sub[spos+1] == '#') - { - *result = true; - multilevel_wildcard = true; - return MOSQ_ERR_SUCCESS; - } - - for(i=spos; i 0 && sub[spos-1] != '/'){ - return MOSQ_ERR_INVAL; - } - spos++; - *result = true; - return MOSQ_ERR_SUCCESS; - } - } + sha = mosquitto__malloc(SHA_DIGEST_LENGTH); + if(!sha){ + return MOSQ_ERR_NOMEM; } - if(multilevel_wildcard == false && (tpos < topiclen || spos < sublen)){ - *result = false; - } - + memcpy(sha, tmp, SHA_DIGEST_LENGTH); + *bin = sha; return MOSQ_ERR_SUCCESS; } -#ifdef FINAL_WITH_TLS_PSK int mosquitto__hex2bin(const char *hex, unsigned char *bin, int bin_max_len) { BIGNUM *bn = NULL; int len; int leading_zero = 0; int start = 0; - int i = 0; + size_t i = 0; /* Count the number of leading zero */ for(i=0; i 4096){ - return NULL; - }else{ - if (restrict_read) { - HANDLE hfile; - SECURITY_ATTRIBUTES sec; - EXPLICIT_ACCESS ea; - PACL pacl = NULL; - char username[UNLEN + 1]; - int ulen = UNLEN; - SECURITY_DESCRIPTOR sd; - DWORD dwCreationDisposition; - - switch(mode[0]){ - case 'a': - dwCreationDisposition = OPEN_ALWAYS; - break; - case 'r': - dwCreationDisposition = OPEN_EXISTING; - break; - case 'w': - dwCreationDisposition = CREATE_ALWAYS; - break; - default: - return NULL; - } + if(mosq->msgs_in.inflight_quota < mosq->msgs_in.inflight_maximum){ + mosq->msgs_in.inflight_quota++; + } +} - GetUserName(username, &ulen); - if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) { - return NULL; - } - BuildExplicitAccessWithName(&ea, username, GENERIC_ALL, SET_ACCESS, NO_INHERITANCE); - if (SetEntriesInAcl(1, &ea, NULL, &pacl) != ERROR_SUCCESS) { - return NULL; - } - if (!SetSecurityDescriptorDacl(&sd, TRUE, pacl, FALSE)) { - LocalFree(pacl); - return NULL; - } +void util__increment_send_quota(struct mosquitto *mosq) +{ + if(mosq->msgs_out.inflight_quota < mosq->msgs_out.inflight_maximum){ + mosq->msgs_out.inflight_quota++; + } +} + + +void util__decrement_receive_quota(struct mosquitto *mosq) +{ + if(mosq->msgs_in.inflight_quota > 0){ + mosq->msgs_in.inflight_quota--; + } +} - sec.nLength = sizeof(SECURITY_ATTRIBUTES); - sec.bInheritHandle = FALSE; - sec.lpSecurityDescriptor = &sd; +void util__decrement_send_quota(struct mosquitto *mosq) +{ + if(mosq->msgs_out.inflight_quota > 0){ + mosq->msgs_out.inflight_quota--; + } +} - hfile = CreateFile(buf, GENERIC_READ | GENERIC_WRITE, 0, - &sec, - dwCreationDisposition, - FILE_ATTRIBUTE_NORMAL, - NULL); - LocalFree(pacl); +int util__random_bytes(void *bytes, int count) +{ + int rc = MOSQ_ERR_UNKNOWN; - int fd = _open_osfhandle((intptr_t)hfile, 0); - if (fd < 0) { - return NULL; - } +#ifdef WITH_TLS + if(RAND_bytes(bytes, count) == 1){ + rc = MOSQ_ERR_SUCCESS; + } +#elif defined(HAVE_GETRANDOM) + if(getrandom(bytes, (size_t)count, 0) == count){ + rc = MOSQ_ERR_SUCCESS; + } +#elif defined(WIN32) + HCRYPTPROV provider; - FILE *fptr = _fdopen(fd, mode); - if (!fptr) { - _close(fd); - return NULL; - } - return fptr; + if(!CryptAcquireContext(&provider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)){ + return MOSQ_ERR_UNKNOWN; + } - }else { - return fopen(buf, mode); - } + if(CryptGenRandom(provider, count, bytes)){ + rc = MOSQ_ERR_SUCCESS; } -#else - if (restrict_read) { - FILE *fptr; - mode_t old_mask; - old_mask = umask(0077); - fptr = fopen(path, mode); - umask(old_mask); + CryptReleaseContext(provider, 0); +#else + int i; - return fptr; - }else{ - return fopen(path, mode); + for(i=0; istate_mutex); +#ifdef WITH_BROKER + if(mosq->state != mosq_cs_disused) #endif + { + mosq->state = state; + } + pthread_mutex_unlock(&mosq->state_mutex); + + return MOSQ_ERR_SUCCESS; +} + +enum mosquitto_client_state mosquitto__get_state(struct mosquitto *mosq) +{ + enum mosquitto_client_state state; + + pthread_mutex_lock(&mosq->state_mutex); + state = mosq->state; + pthread_mutex_unlock(&mosq->state_mutex); + + return state; } diff --git a/libs/mosquitto/src/util_mosq.h b/libs/mosquitto/src/util_mosq.h index c2e67fd..7d99344 100644 --- a/libs/mosquitto/src/util_mosq.h +++ b/libs/mosquitto/src/util_mosq.h @@ -1,15 +1,17 @@ /* -Copyright (c) 2009-2019 Roger Light +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. */ @@ -25,16 +27,23 @@ and the Eclipse Distribution License is available at # include "mosquitto_broker_internal.h" #endif -#ifdef WITH_BROKER -int mosquitto__check_keepalive(struct mosquitto_db *db, struct mosquitto *mosq); -#else int mosquitto__check_keepalive(struct mosquitto *mosq); -#endif uint16_t mosquitto__mid_generate(struct mosquitto *mosq); -FILE *mosquitto__fopen(const char *path, const char *mode, bool restrict_read); -#ifdef FINAL_WITH_TLS_PSK +int mosquitto__set_state(struct mosquitto *mosq, enum mosquitto_client_state state); +enum mosquitto_client_state mosquitto__get_state(struct mosquitto *mosq); + +#ifdef WITH_TLS +int mosquitto__hex2bin_sha1(const char *hex, unsigned char **bin); int mosquitto__hex2bin(const char *hex, unsigned char *bin, int bin_max_len); #endif +int util__random_bytes(void *bytes, int count); + +void util__increment_receive_quota(struct mosquitto *mosq); +void util__increment_send_quota(struct mosquitto *mosq); +void util__decrement_receive_quota(struct mosquitto *mosq); +void util__decrement_send_quota(struct mosquitto *mosq); + + #endif diff --git a/libs/mosquitto/src/util_topic.c b/libs/mosquitto/src/util_topic.c new file mode 100644 index 0000000..e29102f --- /dev/null +++ b/libs/mosquitto/src/util_topic.c @@ -0,0 +1,446 @@ +/* +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 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 + +#ifdef WIN32 +# include +# include +# include +# include +#else +# include +#endif + + +#ifdef WITH_BROKER +#include "mosquitto_broker_internal.h" +#endif + +#include "mosquitto.h" +#include "memory_mosq.h" +#include "net_mosq.h" +#include "send_mosq.h" +#include "time_mosq.h" +#include "tls_mosq.h" +#include "util_mosq.h" + +/* Check that a topic used for publishing is valid. + * Search for + or # in a topic. Return MOSQ_ERR_INVAL if found. + * Also returns MOSQ_ERR_INVAL if the topic string is too long. + * Returns MOSQ_ERR_SUCCESS if everything is fine. + */ +int mosquitto_pub_topic_check(const char *str) +{ + int len = 0; +#ifdef WITH_BROKER + int hier_count = 0; +#endif + + if(str == NULL){ + return MOSQ_ERR_INVAL; + } + + while(str && str[0]){ + if(str[0] == '+' || str[0] == '#'){ + return MOSQ_ERR_INVAL; + } +#ifdef WITH_BROKER + else if(str[0] == '/'){ + hier_count++; + } +#endif + len++; + str = &str[1]; + } + if(len > 65535) return MOSQ_ERR_INVAL; +#ifdef WITH_BROKER + if(hier_count > TOPIC_HIERARCHY_LIMIT) return MOSQ_ERR_INVAL; +#endif + + return MOSQ_ERR_SUCCESS; +} + +int mosquitto_pub_topic_check2(const char *str, size_t len) +{ + size_t i; +#ifdef WITH_BROKER + int hier_count = 0; +#endif + + if(str == NULL || len > 65535){ + return MOSQ_ERR_INVAL; + } + + for(i=0; i TOPIC_HIERARCHY_LIMIT) return MOSQ_ERR_INVAL; +#endif + + return MOSQ_ERR_SUCCESS; +} + +/* Check that a topic used for subscriptions is valid. + * Search for + or # in a topic, check they aren't in invalid positions such as + * foo/#/bar, foo/+bar or foo/bar#. + * Return MOSQ_ERR_INVAL if invalid position found. + * Also returns MOSQ_ERR_INVAL if the topic string is too long. + * Returns MOSQ_ERR_SUCCESS if everything is fine. + */ +int mosquitto_sub_topic_check(const char *str) +{ + char c = '\0'; + int len = 0; +#ifdef WITH_BROKER + int hier_count = 0; +#endif + + if(str == NULL){ + return MOSQ_ERR_INVAL; + } + + while(str[0]){ + if(str[0] == '+'){ + if((c != '\0' && c != '/') || (str[1] != '\0' && str[1] != '/')){ + return MOSQ_ERR_INVAL; + } + }else if(str[0] == '#'){ + if((c != '\0' && c != '/') || str[1] != '\0'){ + return MOSQ_ERR_INVAL; + } + } +#ifdef WITH_BROKER + else if(str[0] == '/'){ + hier_count++; + } +#endif + len++; + c = str[0]; + str = &str[1]; + } + if(len > 65535) return MOSQ_ERR_INVAL; +#ifdef WITH_BROKER + if(hier_count > TOPIC_HIERARCHY_LIMIT) return MOSQ_ERR_INVAL; +#endif + + return MOSQ_ERR_SUCCESS; +} + +int mosquitto_sub_topic_check2(const char *str, size_t len) +{ + char c = '\0'; + size_t i; +#ifdef WITH_BROKER + int hier_count = 0; +#endif + + if(str == NULL || len > 65535){ + return MOSQ_ERR_INVAL; + } + + for(i=0; i TOPIC_HIERARCHY_LIMIT) return MOSQ_ERR_INVAL; +#endif + + return MOSQ_ERR_SUCCESS; +} + +/* Does a topic match a subscription? */ +int mosquitto_topic_matches_sub(const char *sub, const char *topic, bool *result) +{ + size_t spos; + + if(!result) return MOSQ_ERR_INVAL; + *result = false; + + if(!sub || !topic || sub[0] == 0 || topic[0] == 0){ + return MOSQ_ERR_INVAL; + } + + if((sub[0] == '$' && topic[0] != '$') + || (topic[0] == '$' && sub[0] != '$')){ + + return MOSQ_ERR_SUCCESS; + } + + spos = 0; + + while(sub[0] != 0){ + if(topic[0] == '+' || topic[0] == '#'){ + return MOSQ_ERR_INVAL; + } + if(sub[0] != topic[0] || topic[0] == 0){ /* Check for wildcard matches */ + if(sub[0] == '+'){ + /* Check for bad "+foo" or "a/+foo" subscription */ + if(spos > 0 && sub[-1] != '/'){ + return MOSQ_ERR_INVAL; + } + /* Check for bad "foo+" or "foo+/a" subscription */ + if(sub[1] != 0 && sub[1] != '/'){ + return MOSQ_ERR_INVAL; + } + spos++; + sub++; + while(topic[0] != 0 && topic[0] != '/'){ + if(topic[0] == '+' || topic[0] == '#'){ + return MOSQ_ERR_INVAL; + } + topic++; + } + if(topic[0] == 0 && sub[0] == 0){ + *result = true; + return MOSQ_ERR_SUCCESS; + } + }else if(sub[0] == '#'){ + /* Check for bad "foo#" subscription */ + if(spos > 0 && sub[-1] != '/'){ + return MOSQ_ERR_INVAL; + } + /* Check for # not the final character of the sub, e.g. "#foo" */ + if(sub[1] != 0){ + return MOSQ_ERR_INVAL; + }else{ + while(topic[0] != 0){ + if(topic[0] == '+' || topic[0] == '#'){ + return MOSQ_ERR_INVAL; + } + topic++; + } + *result = true; + return MOSQ_ERR_SUCCESS; + } + }else{ + /* Check for e.g. foo/bar matching foo/+/# */ + if(topic[0] == 0 + && spos > 0 + && sub[-1] == '+' + && sub[0] == '/' + && sub[1] == '#') + { + *result = true; + return MOSQ_ERR_SUCCESS; + } + + /* There is no match at this point, but is the sub invalid? */ + while(sub[0] != 0){ + if(sub[0] == '#' && sub[1] != 0){ + return MOSQ_ERR_INVAL; + } + spos++; + sub++; + } + + /* Valid input, but no match */ + return MOSQ_ERR_SUCCESS; + } + }else{ + /* sub[spos] == topic[tpos] */ + if(topic[1] == 0){ + /* Check for e.g. foo matching foo/# */ + if(sub[1] == '/' + && sub[2] == '#' + && sub[3] == 0){ + *result = true; + return MOSQ_ERR_SUCCESS; + } + } + spos++; + sub++; + topic++; + if(sub[0] == 0 && topic[0] == 0){ + *result = true; + return MOSQ_ERR_SUCCESS; + }else if(topic[0] == 0 && sub[0] == '+' && sub[1] == 0){ + if(spos > 0 && sub[-1] != '/'){ + return MOSQ_ERR_INVAL; + } + spos++; + sub++; + *result = true; + return MOSQ_ERR_SUCCESS; + } + } + } + if((topic[0] != 0 || sub[0] != 0)){ + *result = false; + } + while(topic[0] != 0){ + if(topic[0] == '+' || topic[0] == '#'){ + return MOSQ_ERR_INVAL; + } + topic++; + } + + return MOSQ_ERR_SUCCESS; +} + +/* Does a topic match a subscription? */ +int mosquitto_topic_matches_sub2(const char *sub, size_t sublen, const char *topic, size_t topiclen, bool *result) +{ + size_t spos, tpos; + + if(!result) return MOSQ_ERR_INVAL; + *result = false; + + if(!sub || !topic || !sublen || !topiclen){ + return MOSQ_ERR_INVAL; + } + + if((sub[0] == '$' && topic[0] != '$') + || (topic[0] == '$' && sub[0] != '$')){ + + return MOSQ_ERR_SUCCESS; + } + + spos = 0; + tpos = 0; + + while(spos < sublen){ + if(tpos < topiclen && (topic[tpos] == '+' || topic[tpos] == '#')){ + return MOSQ_ERR_INVAL; + } + if(tpos == topiclen || sub[spos] != topic[tpos]){ + if(sub[spos] == '+'){ + /* Check for bad "+foo" or "a/+foo" subscription */ + if(spos > 0 && sub[spos-1] != '/'){ + return MOSQ_ERR_INVAL; + } + /* Check for bad "foo+" or "foo+/a" subscription */ + if(spos+1 < sublen && sub[spos+1] != '/'){ + return MOSQ_ERR_INVAL; + } + spos++; + while(tpos < topiclen && topic[tpos] != '/'){ + if(topic[tpos] == '+' || topic[tpos] == '#'){ + return MOSQ_ERR_INVAL; + } + tpos++; + } + if(tpos == topiclen && spos == sublen){ + *result = true; + return MOSQ_ERR_SUCCESS; + } + }else if(sub[spos] == '#'){ + /* Check for bad "foo#" subscription */ + if(spos > 0 && sub[spos-1] != '/'){ + return MOSQ_ERR_INVAL; + } + /* Check for # not the final character of the sub, e.g. "#foo" */ + if(spos+1 < sublen){ + return MOSQ_ERR_INVAL; + }else{ + while(tpos < topiclen){ + if(topic[tpos] == '+' || topic[tpos] == '#'){ + return MOSQ_ERR_INVAL; + } + tpos++; + } + *result = true; + return MOSQ_ERR_SUCCESS; + } + }else{ + /* Check for e.g. foo/bar matching foo/+/# */ + if(tpos == topiclen + && spos > 0 + && sub[spos-1] == '+' + && sub[spos] == '/' + && spos+1 < sublen + && sub[spos+1] == '#') + { + *result = true; + return MOSQ_ERR_SUCCESS; + } + + /* There is no match at this point, but is the sub invalid? */ + while(spos < sublen){ + if(sub[spos] == '#' && spos+1 < sublen){ + return MOSQ_ERR_INVAL; + } + spos++; + } + + /* Valid input, but no match */ + return MOSQ_ERR_SUCCESS; + } + }else{ + /* sub[spos] == topic[tpos] */ + if(tpos+1 == topiclen){ + /* Check for e.g. foo matching foo/# */ + if(spos+3 == sublen + && sub[spos+1] == '/' + && sub[spos+2] == '#'){ + *result = true; + return MOSQ_ERR_SUCCESS; + } + } + spos++; + tpos++; + if(spos == sublen && tpos == topiclen){ + *result = true; + return MOSQ_ERR_SUCCESS; + }else if(tpos == topiclen && sub[spos] == '+' && spos+1 == sublen){ + if(spos > 0 && sub[spos-1] != '/'){ + return MOSQ_ERR_INVAL; + } + spos++; + *result = true; + return MOSQ_ERR_SUCCESS; + } + } + } + if(tpos < topiclen || spos < sublen){ + *result = false; + } + while(tpos < topiclen){ + if(topic[tpos] == '+' || topic[tpos] == '#'){ + return MOSQ_ERR_INVAL; + } + tpos++; + } + + return MOSQ_ERR_SUCCESS; +} diff --git a/libs/mosquitto/src/utlist.h b/libs/mosquitto/src/utlist.h new file mode 100644 index 0000000..5bb1ac9 --- /dev/null +++ b/libs/mosquitto/src/utlist.h @@ -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 + +/* + * This file contains macros to manipulate singly and doubly-linked lists. + * + * 1. LL_ macros: singly-linked lists. + * 2. DL_ macros: doubly-linked lists. + * 3. CDL_ macros: circular doubly-linked lists. + * + * To use singly-linked lists, your structure must have a "next" pointer. + * To use doubly-linked lists, your structure must "prev" and "next" pointers. + * Either way, the pointer to the head of the list must be initialized to NULL. + * + * ----------------.EXAMPLE ------------------------- + * struct item { + * int id; + * struct item *prev, *next; + * } + * + * struct item *list = NULL: + * + * int main() { + * struct item *item; + * ... allocate and populate item ... + * DL_APPEND(list, item); + * } + * -------------------------------------------------- + * + * For doubly-linked lists, the append and delete macros are O(1) + * For singly-linked lists, append and delete are O(n) but prepend is O(1) + * The sort macro is O(n log(n)) for all types of single/double/circular lists. + */ + +/* 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(LDECLTYPE) && !defined(NO_DECLTYPE) +#if defined(_MSC_VER) /* MS compiler */ +#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ +#define LDECLTYPE(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 LDECLTYPE(x) __typeof(x) +#endif +#endif + +/* for VS2008 we use some workarounds to get around the lack of decltype, + * namely, we always reassign our tmp variable to the list head if we need + * to dereference its prev/next pointers, and save/restore the real head.*/ +#ifdef NO_DECLTYPE +#define IF_NO_DECLTYPE(x) x +#define LDECLTYPE(x) char* +#define UTLIST_SV(elt,list) _tmp = (char*)(list); {char **_alias = (char**)&(list); *_alias = (elt); } +#define UTLIST_NEXT(elt,list,next) ((char*)((list)->next)) +#define UTLIST_NEXTASGN(elt,list,to,next) { char **_alias = (char**)&((list)->next); *_alias=(char*)(to); } +/* #define UTLIST_PREV(elt,list,prev) ((char*)((list)->prev)) */ +#define UTLIST_PREVASGN(elt,list,to,prev) { char **_alias = (char**)&((list)->prev); *_alias=(char*)(to); } +#define UTLIST_RS(list) { char **_alias = (char**)&(list); *_alias=_tmp; } +#define UTLIST_CASTASGN(a,b) { char **_alias = (char**)&(a); *_alias=(char*)(b); } +#else +#define IF_NO_DECLTYPE(x) +#define UTLIST_SV(elt,list) +#define UTLIST_NEXT(elt,list,next) ((elt)->next) +#define UTLIST_NEXTASGN(elt,list,to,next) ((elt)->next)=(to) +/* #define UTLIST_PREV(elt,list,prev) ((elt)->prev) */ +#define UTLIST_PREVASGN(elt,list,to,prev) ((elt)->prev)=(to) +#define UTLIST_RS(list) +#define UTLIST_CASTASGN(a,b) (a)=(b) +#endif + +/****************************************************************************** + * The sort macro is an adaptation of Simon Tatham's O(n log(n)) mergesort * + * Unwieldy variable names used here to avoid shadowing passed-in variables. * + *****************************************************************************/ +#define LL_SORT(list, cmp) \ + LL_SORT2(list, cmp, next) + +#define LL_SORT2(list, cmp, next) \ +do { \ + LDECLTYPE(list) _ls_p; \ + LDECLTYPE(list) _ls_q; \ + LDECLTYPE(list) _ls_e; \ + LDECLTYPE(list) _ls_tail; \ + IF_NO_DECLTYPE(LDECLTYPE(list) _tmp;) \ + int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ + if (list) { \ + _ls_insize = 1; \ + _ls_looping = 1; \ + while (_ls_looping) { \ + UTLIST_CASTASGN(_ls_p,list); \ + (list) = NULL; \ + _ls_tail = NULL; \ + _ls_nmerges = 0; \ + while (_ls_p) { \ + _ls_nmerges++; \ + _ls_q = _ls_p; \ + _ls_psize = 0; \ + for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ + _ls_psize++; \ + UTLIST_SV(_ls_q,list); _ls_q = UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); \ + if (!_ls_q) break; \ + } \ + _ls_qsize = _ls_insize; \ + while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ + if (_ls_psize == 0) { \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ + } else if (_ls_qsize == 0 || !_ls_q) { \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ + } else if (cmp(_ls_p,_ls_q) <= 0) { \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ + } else { \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ + } \ + if (_ls_tail) { \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_ls_e,next); UTLIST_RS(list); \ + } else { \ + UTLIST_CASTASGN(list,_ls_e); \ + } \ + _ls_tail = _ls_e; \ + } \ + _ls_p = _ls_q; \ + } \ + if (_ls_tail) { \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,NULL,next); UTLIST_RS(list); \ + } \ + if (_ls_nmerges <= 1) { \ + _ls_looping=0; \ + } \ + _ls_insize *= 2; \ + } \ + } \ +} while (0) + + +#define DL_SORT(list, cmp) \ + DL_SORT2(list, cmp, prev, next) + +#define DL_SORT2(list, cmp, prev, next) \ +do { \ + LDECLTYPE(list) _ls_p; \ + LDECLTYPE(list) _ls_q; \ + LDECLTYPE(list) _ls_e; \ + LDECLTYPE(list) _ls_tail; \ + IF_NO_DECLTYPE(LDECLTYPE(list) _tmp;) \ + int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ + if (list) { \ + _ls_insize = 1; \ + _ls_looping = 1; \ + while (_ls_looping) { \ + UTLIST_CASTASGN(_ls_p,list); \ + (list) = NULL; \ + _ls_tail = NULL; \ + _ls_nmerges = 0; \ + while (_ls_p) { \ + _ls_nmerges++; \ + _ls_q = _ls_p; \ + _ls_psize = 0; \ + for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ + _ls_psize++; \ + UTLIST_SV(_ls_q,list); _ls_q = UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); \ + if (!_ls_q) break; \ + } \ + _ls_qsize = _ls_insize; \ + while ((_ls_psize > 0) || ((_ls_qsize > 0) && _ls_q)) { \ + if (_ls_psize == 0) { \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ + } else if ((_ls_qsize == 0) || (!_ls_q)) { \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ + } else if (cmp(_ls_p,_ls_q) <= 0) { \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ + } else { \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ + } \ + if (_ls_tail) { \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_ls_e,next); UTLIST_RS(list); \ + } else { \ + UTLIST_CASTASGN(list,_ls_e); \ + } \ + UTLIST_SV(_ls_e,list); UTLIST_PREVASGN(_ls_e,list,_ls_tail,prev); UTLIST_RS(list); \ + _ls_tail = _ls_e; \ + } \ + _ls_p = _ls_q; \ + } \ + UTLIST_CASTASGN((list)->prev, _ls_tail); \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,NULL,next); UTLIST_RS(list); \ + if (_ls_nmerges <= 1) { \ + _ls_looping=0; \ + } \ + _ls_insize *= 2; \ + } \ + } \ +} while (0) + +#define CDL_SORT(list, cmp) \ + CDL_SORT2(list, cmp, prev, next) + +#define CDL_SORT2(list, cmp, prev, next) \ +do { \ + LDECLTYPE(list) _ls_p; \ + LDECLTYPE(list) _ls_q; \ + LDECLTYPE(list) _ls_e; \ + LDECLTYPE(list) _ls_tail; \ + LDECLTYPE(list) _ls_oldhead; \ + LDECLTYPE(list) _tmp; \ + int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ + if (list) { \ + _ls_insize = 1; \ + _ls_looping = 1; \ + while (_ls_looping) { \ + UTLIST_CASTASGN(_ls_p,list); \ + UTLIST_CASTASGN(_ls_oldhead,list); \ + (list) = NULL; \ + _ls_tail = NULL; \ + _ls_nmerges = 0; \ + while (_ls_p) { \ + _ls_nmerges++; \ + _ls_q = _ls_p; \ + _ls_psize = 0; \ + for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ + _ls_psize++; \ + UTLIST_SV(_ls_q,list); \ + if (UTLIST_NEXT(_ls_q,list,next) == _ls_oldhead) { \ + _ls_q = NULL; \ + } else { \ + _ls_q = UTLIST_NEXT(_ls_q,list,next); \ + } \ + UTLIST_RS(list); \ + if (!_ls_q) break; \ + } \ + _ls_qsize = _ls_insize; \ + while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ + if (_ls_psize == 0) { \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ + if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ + } else if (_ls_qsize == 0 || !_ls_q) { \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ + if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ + } else if (cmp(_ls_p,_ls_q) <= 0) { \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ + if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ + } else { \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ + if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ + } \ + if (_ls_tail) { \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_ls_e,next); UTLIST_RS(list); \ + } else { \ + UTLIST_CASTASGN(list,_ls_e); \ + } \ + UTLIST_SV(_ls_e,list); UTLIST_PREVASGN(_ls_e,list,_ls_tail,prev); UTLIST_RS(list); \ + _ls_tail = _ls_e; \ + } \ + _ls_p = _ls_q; \ + } \ + UTLIST_CASTASGN((list)->prev,_ls_tail); \ + UTLIST_CASTASGN(_tmp,list); \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_tmp,next); UTLIST_RS(list); \ + if (_ls_nmerges <= 1) { \ + _ls_looping=0; \ + } \ + _ls_insize *= 2; \ + } \ + } \ +} while (0) + +/****************************************************************************** + * singly linked list macros (non-circular) * + *****************************************************************************/ +#define LL_PREPEND(head,add) \ + LL_PREPEND2(head,add,next) + +#define LL_PREPEND2(head,add,next) \ +do { \ + (add)->next = (head); \ + (head) = (add); \ +} while (0) + +#define LL_CONCAT(head1,head2) \ + LL_CONCAT2(head1,head2,next) + +#define LL_CONCAT2(head1,head2,next) \ +do { \ + LDECLTYPE(head1) _tmp; \ + if (head1) { \ + _tmp = (head1); \ + while (_tmp->next) { _tmp = _tmp->next; } \ + _tmp->next=(head2); \ + } else { \ + (head1)=(head2); \ + } \ +} while (0) + +#define LL_APPEND(head,add) \ + LL_APPEND2(head,add,next) + +#define LL_APPEND2(head,add,next) \ +do { \ + LDECLTYPE(head) _tmp; \ + (add)->next=NULL; \ + if (head) { \ + _tmp = (head); \ + while (_tmp->next) { _tmp = _tmp->next; } \ + _tmp->next=(add); \ + } else { \ + (head)=(add); \ + } \ +} while (0) + +#define LL_INSERT_INORDER(head,add,cmp) \ + LL_INSERT_INORDER2(head,add,cmp,next) + +#define LL_INSERT_INORDER2(head,add,cmp,next) \ +do { \ + LDECLTYPE(head) _tmp; \ + if (head) { \ + LL_LOWER_BOUND2(head, _tmp, add, cmp, next); \ + LL_APPEND_ELEM2(head, _tmp, add, next); \ + } else { \ + (head) = (add); \ + (head)->next = NULL; \ + } \ +} while (0) + +#define LL_LOWER_BOUND(head,elt,like,cmp) \ + LL_LOWER_BOUND2(head,elt,like,cmp,next) + +#define LL_LOWER_BOUND2(head,elt,like,cmp,next) \ + do { \ + if ((head) == NULL || (cmp(head, like)) >= 0) { \ + (elt) = NULL; \ + } else { \ + for ((elt) = (head); (elt)->next != NULL; (elt) = (elt)->next) { \ + if (cmp((elt)->next, like) >= 0) { \ + break; \ + } \ + } \ + } \ + } while (0) + +#define LL_DELETE(head,del) \ + LL_DELETE2(head,del,next) + +#define LL_DELETE2(head,del,next) \ +do { \ + LDECLTYPE(head) _tmp; \ + if ((head) == (del)) { \ + (head)=(head)->next; \ + } else { \ + _tmp = (head); \ + while (_tmp->next && (_tmp->next != (del))) { \ + _tmp = _tmp->next; \ + } \ + if (_tmp->next) { \ + _tmp->next = (del)->next; \ + } \ + } \ +} while (0) + +#define LL_COUNT(head,el,counter) \ + LL_COUNT2(head,el,counter,next) \ + +#define LL_COUNT2(head,el,counter,next) \ +do { \ + (counter) = 0; \ + LL_FOREACH2(head,el,next) { ++(counter); } \ +} while (0) + +#define LL_FOREACH(head,el) \ + LL_FOREACH2(head,el,next) + +#define LL_FOREACH2(head,el,next) \ + for ((el) = (head); el; (el) = (el)->next) + +#define LL_FOREACH_SAFE(head,el,tmp) \ + LL_FOREACH_SAFE2(head,el,tmp,next) + +#define LL_FOREACH_SAFE2(head,el,tmp,next) \ + for ((el) = (head); (el) && ((tmp) = (el)->next, 1); (el) = (tmp)) + +#define LL_SEARCH_SCALAR(head,out,field,val) \ + LL_SEARCH_SCALAR2(head,out,field,val,next) + +#define LL_SEARCH_SCALAR2(head,out,field,val,next) \ +do { \ + LL_FOREACH2(head,out,next) { \ + if ((out)->field == (val)) break; \ + } \ +} while (0) + +#define LL_SEARCH(head,out,elt,cmp) \ + LL_SEARCH2(head,out,elt,cmp,next) + +#define LL_SEARCH2(head,out,elt,cmp,next) \ +do { \ + LL_FOREACH2(head,out,next) { \ + if ((cmp(out,elt))==0) break; \ + } \ +} while (0) + +#define LL_REPLACE_ELEM2(head, el, add, next) \ +do { \ + LDECLTYPE(head) _tmp; \ + assert((head) != NULL); \ + assert((el) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el)->next; \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + _tmp = (head); \ + while (_tmp->next && (_tmp->next != (el))) { \ + _tmp = _tmp->next; \ + } \ + if (_tmp->next) { \ + _tmp->next = (add); \ + } \ + } \ +} while (0) + +#define LL_REPLACE_ELEM(head, el, add) \ + LL_REPLACE_ELEM2(head, el, add, next) + +#define LL_PREPEND_ELEM2(head, el, add, next) \ +do { \ + if (el) { \ + LDECLTYPE(head) _tmp; \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el); \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + _tmp = (head); \ + while (_tmp->next && (_tmp->next != (el))) { \ + _tmp = _tmp->next; \ + } \ + if (_tmp->next) { \ + _tmp->next = (add); \ + } \ + } \ + } else { \ + LL_APPEND2(head, add, next); \ + } \ +} while (0) \ + +#define LL_PREPEND_ELEM(head, el, add) \ + LL_PREPEND_ELEM2(head, el, add, next) + +#define LL_APPEND_ELEM2(head, el, add, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el)->next; \ + (el)->next = (add); \ + } else { \ + LL_PREPEND2(head, add, next); \ + } \ +} while (0) \ + +#define LL_APPEND_ELEM(head, el, add) \ + LL_APPEND_ELEM2(head, el, add, next) + +#ifdef NO_DECLTYPE +/* Here are VS2008 / NO_DECLTYPE replacements for a few functions */ + +#undef LL_CONCAT2 +#define LL_CONCAT2(head1,head2,next) \ +do { \ + char *_tmp; \ + if (head1) { \ + _tmp = (char*)(head1); \ + while ((head1)->next) { (head1) = (head1)->next; } \ + (head1)->next = (head2); \ + UTLIST_RS(head1); \ + } else { \ + (head1)=(head2); \ + } \ +} while (0) + +#undef LL_APPEND2 +#define LL_APPEND2(head,add,next) \ +do { \ + if (head) { \ + (add)->next = head; /* use add->next as a temp variable */ \ + while ((add)->next->next) { (add)->next = (add)->next->next; } \ + (add)->next->next=(add); \ + } else { \ + (head)=(add); \ + } \ + (add)->next=NULL; \ +} while (0) + +#undef LL_INSERT_INORDER2 +#define LL_INSERT_INORDER2(head,add,cmp,next) \ +do { \ + if ((head) == NULL || (cmp(head, add)) >= 0) { \ + (add)->next = (head); \ + (head) = (add); \ + } else { \ + char *_tmp = (char*)(head); \ + while ((head)->next != NULL && (cmp((head)->next, add)) < 0) { \ + (head) = (head)->next; \ + } \ + (add)->next = (head)->next; \ + (head)->next = (add); \ + UTLIST_RS(head); \ + } \ +} while (0) + +#undef LL_DELETE2 +#define LL_DELETE2(head,del,next) \ +do { \ + if ((head) == (del)) { \ + (head)=(head)->next; \ + } else { \ + char *_tmp = (char*)(head); \ + while ((head)->next && ((head)->next != (del))) { \ + (head) = (head)->next; \ + } \ + if ((head)->next) { \ + (head)->next = ((del)->next); \ + } \ + UTLIST_RS(head); \ + } \ +} while (0) + +#undef LL_REPLACE_ELEM2 +#define LL_REPLACE_ELEM2(head, el, add, next) \ +do { \ + assert((head) != NULL); \ + assert((el) != NULL); \ + assert((add) != NULL); \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + (add)->next = head; \ + while ((add)->next->next && ((add)->next->next != (el))) { \ + (add)->next = (add)->next->next; \ + } \ + if ((add)->next->next) { \ + (add)->next->next = (add); \ + } \ + } \ + (add)->next = (el)->next; \ +} while (0) + +#undef LL_PREPEND_ELEM2 +#define LL_PREPEND_ELEM2(head, el, add, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + (add)->next = (head); \ + while ((add)->next->next && ((add)->next->next != (el))) { \ + (add)->next = (add)->next->next; \ + } \ + if ((add)->next->next) { \ + (add)->next->next = (add); \ + } \ + } \ + (add)->next = (el); \ + } else { \ + LL_APPEND2(head, add, next); \ + } \ +} while (0) \ + +#endif /* NO_DECLTYPE */ + +/****************************************************************************** + * doubly linked list macros (non-circular) * + *****************************************************************************/ +#define DL_PREPEND(head,add) \ + DL_PREPEND2(head,add,prev,next) + +#define DL_PREPEND2(head,add,prev,next) \ +do { \ + (add)->next = (head); \ + if (head) { \ + (add)->prev = (head)->prev; \ + (head)->prev = (add); \ + } else { \ + (add)->prev = (add); \ + } \ + (head) = (add); \ +} while (0) + +#define DL_APPEND(head,add) \ + DL_APPEND2(head,add,prev,next) + +#define DL_APPEND2(head,add,prev,next) \ +do { \ + if (head) { \ + (add)->prev = (head)->prev; \ + (head)->prev->next = (add); \ + (head)->prev = (add); \ + (add)->next = NULL; \ + } else { \ + (head)=(add); \ + (head)->prev = (head); \ + (head)->next = NULL; \ + } \ +} while (0) + +#define DL_INSERT_INORDER(head,add,cmp) \ + DL_INSERT_INORDER2(head,add,cmp,prev,next) + +#define DL_INSERT_INORDER2(head,add,cmp,prev,next) \ +do { \ + LDECLTYPE(head) _tmp; \ + if (head) { \ + DL_LOWER_BOUND2(head, _tmp, add, cmp, next); \ + DL_APPEND_ELEM2(head, _tmp, add, prev, next); \ + } else { \ + (head) = (add); \ + (head)->prev = (head); \ + (head)->next = NULL; \ + } \ +} while (0) + +#define DL_LOWER_BOUND(head,elt,like,cmp) \ + DL_LOWER_BOUND2(head,elt,like,cmp,next) + +#define DL_LOWER_BOUND2(head,elt,like,cmp,next) \ +do { \ + if ((head) == NULL || (cmp(head, like)) >= 0) { \ + (elt) = NULL; \ + } else { \ + for ((elt) = (head); (elt)->next != NULL; (elt) = (elt)->next) { \ + if ((cmp((elt)->next, like)) >= 0) { \ + break; \ + } \ + } \ + } \ +} while (0) + +#define DL_CONCAT(head1,head2) \ + DL_CONCAT2(head1,head2,prev,next) + +#define DL_CONCAT2(head1,head2,prev,next) \ +do { \ + LDECLTYPE(head1) _tmp; \ + if (head2) { \ + if (head1) { \ + UTLIST_CASTASGN(_tmp, (head2)->prev); \ + (head2)->prev = (head1)->prev; \ + (head1)->prev->next = (head2); \ + UTLIST_CASTASGN((head1)->prev, _tmp); \ + } else { \ + (head1)=(head2); \ + } \ + } \ +} while (0) + +#define DL_DELETE(head,del) \ + DL_DELETE2(head,del,prev,next) + +#define DL_DELETE2(head,del,prev,next) \ +do { \ + assert((head) != NULL); \ + assert((del)->prev != NULL); \ + if ((del)->prev == (del)) { \ + (head)=NULL; \ + } else if ((del)==(head)) { \ + (del)->next->prev = (del)->prev; \ + (head) = (del)->next; \ + } else { \ + (del)->prev->next = (del)->next; \ + if ((del)->next) { \ + (del)->next->prev = (del)->prev; \ + } else { \ + (head)->prev = (del)->prev; \ + } \ + } \ +} while (0) + +#define DL_COUNT(head,el,counter) \ + DL_COUNT2(head,el,counter,next) \ + +#define DL_COUNT2(head,el,counter,next) \ +do { \ + (counter) = 0; \ + DL_FOREACH2(head,el,next) { ++(counter); } \ +} while (0) + +#define DL_FOREACH(head,el) \ + DL_FOREACH2(head,el,next) + +#define DL_FOREACH2(head,el,next) \ + for ((el) = (head); el; (el) = (el)->next) + +/* this version is safe for deleting the elements during iteration */ +#define DL_FOREACH_SAFE(head,el,tmp) \ + DL_FOREACH_SAFE2(head,el,tmp,next) + +#define DL_FOREACH_SAFE2(head,el,tmp,next) \ + for ((el) = (head); (el) && ((tmp) = (el)->next, 1); (el) = (tmp)) + +/* these are identical to their singly-linked list counterparts */ +#define DL_SEARCH_SCALAR LL_SEARCH_SCALAR +#define DL_SEARCH LL_SEARCH +#define DL_SEARCH_SCALAR2 LL_SEARCH_SCALAR2 +#define DL_SEARCH2 LL_SEARCH2 + +#define DL_REPLACE_ELEM2(head, el, add, prev, next) \ +do { \ + assert((head) != NULL); \ + assert((el) != NULL); \ + assert((add) != NULL); \ + if ((head) == (el)) { \ + (head) = (add); \ + (add)->next = (el)->next; \ + if ((el)->next == NULL) { \ + (add)->prev = (add); \ + } else { \ + (add)->prev = (el)->prev; \ + (add)->next->prev = (add); \ + } \ + } else { \ + (add)->next = (el)->next; \ + (add)->prev = (el)->prev; \ + (add)->prev->next = (add); \ + if ((el)->next == NULL) { \ + (head)->prev = (add); \ + } else { \ + (add)->next->prev = (add); \ + } \ + } \ +} while (0) + +#define DL_REPLACE_ELEM(head, el, add) \ + DL_REPLACE_ELEM2(head, el, add, prev, next) + +#define DL_PREPEND_ELEM2(head, el, add, prev, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el); \ + (add)->prev = (el)->prev; \ + (el)->prev = (add); \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + (add)->prev->next = (add); \ + } \ + } else { \ + DL_APPEND2(head, add, prev, next); \ + } \ +} while (0) \ + +#define DL_PREPEND_ELEM(head, el, add) \ + DL_PREPEND_ELEM2(head, el, add, prev, next) + +#define DL_APPEND_ELEM2(head, el, add, prev, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el)->next; \ + (add)->prev = (el); \ + (el)->next = (add); \ + if ((add)->next) { \ + (add)->next->prev = (add); \ + } else { \ + (head)->prev = (add); \ + } \ + } else { \ + DL_PREPEND2(head, add, prev, next); \ + } \ +} while (0) \ + +#define DL_APPEND_ELEM(head, el, add) \ + DL_APPEND_ELEM2(head, el, add, prev, next) + +#ifdef NO_DECLTYPE +/* Here are VS2008 / NO_DECLTYPE replacements for a few functions */ + +#undef DL_INSERT_INORDER2 +#define DL_INSERT_INORDER2(head,add,cmp,prev,next) \ +do { \ + if ((head) == NULL) { \ + (add)->prev = (add); \ + (add)->next = NULL; \ + (head) = (add); \ + } else if ((cmp(head, add)) >= 0) { \ + (add)->prev = (head)->prev; \ + (add)->next = (head); \ + (head)->prev = (add); \ + (head) = (add); \ + } else { \ + char *_tmp = (char*)(head); \ + while ((head)->next && (cmp((head)->next, add)) < 0) { \ + (head) = (head)->next; \ + } \ + (add)->prev = (head); \ + (add)->next = (head)->next; \ + (head)->next = (add); \ + UTLIST_RS(head); \ + if ((add)->next) { \ + (add)->next->prev = (add); \ + } else { \ + (head)->prev = (add); \ + } \ + } \ +} while (0) +#endif /* NO_DECLTYPE */ + +/****************************************************************************** + * circular doubly linked list macros * + *****************************************************************************/ +#define CDL_APPEND(head,add) \ + CDL_APPEND2(head,add,prev,next) + +#define CDL_APPEND2(head,add,prev,next) \ +do { \ + if (head) { \ + (add)->prev = (head)->prev; \ + (add)->next = (head); \ + (head)->prev = (add); \ + (add)->prev->next = (add); \ + } else { \ + (add)->prev = (add); \ + (add)->next = (add); \ + (head) = (add); \ + } \ +} while (0) + +#define CDL_PREPEND(head,add) \ + CDL_PREPEND2(head,add,prev,next) + +#define CDL_PREPEND2(head,add,prev,next) \ +do { \ + if (head) { \ + (add)->prev = (head)->prev; \ + (add)->next = (head); \ + (head)->prev = (add); \ + (add)->prev->next = (add); \ + } else { \ + (add)->prev = (add); \ + (add)->next = (add); \ + } \ + (head) = (add); \ +} while (0) + +#define CDL_INSERT_INORDER(head,add,cmp) \ + CDL_INSERT_INORDER2(head,add,cmp,prev,next) + +#define CDL_INSERT_INORDER2(head,add,cmp,prev,next) \ +do { \ + LDECLTYPE(head) _tmp; \ + if (head) { \ + CDL_LOWER_BOUND2(head, _tmp, add, cmp, next); \ + CDL_APPEND_ELEM2(head, _tmp, add, prev, next); \ + } else { \ + (head) = (add); \ + (head)->next = (head); \ + (head)->prev = (head); \ + } \ +} while (0) + +#define CDL_LOWER_BOUND(head,elt,like,cmp) \ + CDL_LOWER_BOUND2(head,elt,like,cmp,next) + +#define CDL_LOWER_BOUND2(head,elt,like,cmp,next) \ +do { \ + if ((head) == NULL || (cmp(head, like)) >= 0) { \ + (elt) = NULL; \ + } else { \ + for ((elt) = (head); (elt)->next != (head); (elt) = (elt)->next) { \ + if ((cmp((elt)->next, like)) >= 0) { \ + break; \ + } \ + } \ + } \ +} while (0) + +#define CDL_DELETE(head,del) \ + CDL_DELETE2(head,del,prev,next) + +#define CDL_DELETE2(head,del,prev,next) \ +do { \ + if (((head)==(del)) && ((head)->next == (head))) { \ + (head) = NULL; \ + } else { \ + (del)->next->prev = (del)->prev; \ + (del)->prev->next = (del)->next; \ + if ((del) == (head)) (head)=(del)->next; \ + } \ +} while (0) + +#define CDL_COUNT(head,el,counter) \ + CDL_COUNT2(head,el,counter,next) \ + +#define CDL_COUNT2(head, el, counter,next) \ +do { \ + (counter) = 0; \ + CDL_FOREACH2(head,el,next) { ++(counter); } \ +} while (0) + +#define CDL_FOREACH(head,el) \ + CDL_FOREACH2(head,el,next) + +#define CDL_FOREACH2(head,el,next) \ + for ((el)=(head);el;(el)=(((el)->next==(head)) ? NULL : (el)->next)) + +#define CDL_FOREACH_SAFE(head,el,tmp1,tmp2) \ + CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next) + +#define CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next) \ + for ((el) = (head), (tmp1) = (head) ? (head)->prev : NULL; \ + (el) && ((tmp2) = (el)->next, 1); \ + (el) = ((el) == (tmp1) ? NULL : (tmp2))) + +#define CDL_SEARCH_SCALAR(head,out,field,val) \ + CDL_SEARCH_SCALAR2(head,out,field,val,next) + +#define CDL_SEARCH_SCALAR2(head,out,field,val,next) \ +do { \ + CDL_FOREACH2(head,out,next) { \ + if ((out)->field == (val)) break; \ + } \ +} while (0) + +#define CDL_SEARCH(head,out,elt,cmp) \ + CDL_SEARCH2(head,out,elt,cmp,next) + +#define CDL_SEARCH2(head,out,elt,cmp,next) \ +do { \ + CDL_FOREACH2(head,out,next) { \ + if ((cmp(out,elt))==0) break; \ + } \ +} while (0) + +#define CDL_REPLACE_ELEM2(head, el, add, prev, next) \ +do { \ + assert((head) != NULL); \ + assert((el) != NULL); \ + assert((add) != NULL); \ + if ((el)->next == (el)) { \ + (add)->next = (add); \ + (add)->prev = (add); \ + (head) = (add); \ + } else { \ + (add)->next = (el)->next; \ + (add)->prev = (el)->prev; \ + (add)->next->prev = (add); \ + (add)->prev->next = (add); \ + if ((head) == (el)) { \ + (head) = (add); \ + } \ + } \ +} while (0) + +#define CDL_REPLACE_ELEM(head, el, add) \ + CDL_REPLACE_ELEM2(head, el, add, prev, next) + +#define CDL_PREPEND_ELEM2(head, el, add, prev, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el); \ + (add)->prev = (el)->prev; \ + (el)->prev = (add); \ + (add)->prev->next = (add); \ + if ((head) == (el)) { \ + (head) = (add); \ + } \ + } else { \ + CDL_APPEND2(head, add, prev, next); \ + } \ +} while (0) + +#define CDL_PREPEND_ELEM(head, el, add) \ + CDL_PREPEND_ELEM2(head, el, add, prev, next) + +#define CDL_APPEND_ELEM2(head, el, add, prev, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el)->next; \ + (add)->prev = (el); \ + (el)->next = (add); \ + (add)->next->prev = (add); \ + } else { \ + CDL_PREPEND2(head, add, prev, next); \ + } \ +} while (0) + +#define CDL_APPEND_ELEM(head, el, add) \ + CDL_APPEND_ELEM2(head, el, add, prev, next) + +#ifdef NO_DECLTYPE +/* Here are VS2008 / NO_DECLTYPE replacements for a few functions */ + +#undef CDL_INSERT_INORDER2 +#define CDL_INSERT_INORDER2(head,add,cmp,prev,next) \ +do { \ + if ((head) == NULL) { \ + (add)->prev = (add); \ + (add)->next = (add); \ + (head) = (add); \ + } else if ((cmp(head, add)) >= 0) { \ + (add)->prev = (head)->prev; \ + (add)->next = (head); \ + (add)->prev->next = (add); \ + (head)->prev = (add); \ + (head) = (add); \ + } else { \ + char *_tmp = (char*)(head); \ + while ((char*)(head)->next != _tmp && (cmp((head)->next, add)) < 0) { \ + (head) = (head)->next; \ + } \ + (add)->prev = (head); \ + (add)->next = (head)->next; \ + (add)->next->prev = (add); \ + (head)->next = (add); \ + UTLIST_RS(head); \ + } \ +} while (0) +#endif /* NO_DECLTYPE */ + +#endif /* UTLIST_H */ diff --git a/libs/mosquitto/src/will_mosq.c b/libs/mosquitto/src/will_mosq.c index 531d704..ba73abe 100644 --- a/libs/mosquitto/src/will_mosq.c +++ b/libs/mosquitto/src/will_mosq.c @@ -1,15 +1,17 @@ /* -Copyright (c) 2010-2019 Roger Light +Copyright (c) 2010-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. */ @@ -28,59 +30,76 @@ and the Eclipse Distribution License is available at #include "logging_mosq.h" #include "messages_mosq.h" #include "memory_mosq.h" -#include "mqtt3_protocol.h" +#include "mqtt_protocol.h" #include "net_mosq.h" #include "read_handle.h" #include "send_mosq.h" #include "util_mosq.h" +#include "will_mosq.h" -int will__set(struct mosquitto *mosq, const char *topic, int payloadlen, const void *payload, int qos, bool retain) +int will__set(struct mosquitto *mosq, const char *topic, int payloadlen, const void *payload, int qos, bool retain, mosquitto_property *properties) { int rc = MOSQ_ERR_SUCCESS; + mosquitto_property *p; if(!mosq || !topic) return MOSQ_ERR_INVAL; - if(payloadlen < 0 || payloadlen > MQTT_MAX_PAYLOAD) return MOSQ_ERR_PAYLOAD_SIZE; + if(payloadlen < 0 || payloadlen > (int)MQTT_MAX_PAYLOAD) return MOSQ_ERR_PAYLOAD_SIZE; if(payloadlen > 0 && !payload) return MOSQ_ERR_INVAL; if(mosquitto_pub_topic_check(topic)) return MOSQ_ERR_INVAL; - if(mosquitto_validate_utf8(topic, strlen(topic))) return MOSQ_ERR_MALFORMED_UTF8; + if(mosquitto_validate_utf8(topic, (uint16_t)strlen(topic))) return MOSQ_ERR_MALFORMED_UTF8; + + if(properties){ + if(mosq->protocol != mosq_p_mqtt5){ + return MOSQ_ERR_NOT_SUPPORTED; + } + p = properties; + while(p){ + rc = mosquitto_property_check_command(CMD_WILL, p->identifier); + if(rc) return rc; + p = p->next; + } + } if(mosq->will){ - mosquitto__free(mosq->will->topic); - mosquitto__free(mosq->will->payload); + mosquitto__free(mosq->will->msg.topic); + mosquitto__free(mosq->will->msg.payload); + mosquitto_property_free_all(&mosq->will->properties); mosquitto__free(mosq->will); } - mosq->will = mosquitto__calloc(1, sizeof(struct mosquitto_message)); + mosq->will = mosquitto__calloc(1, sizeof(struct mosquitto_message_all)); if(!mosq->will) return MOSQ_ERR_NOMEM; - mosq->will->topic = mosquitto__strdup(topic); - if(!mosq->will->topic){ + mosq->will->msg.topic = mosquitto__strdup(topic); + if(!mosq->will->msg.topic){ rc = MOSQ_ERR_NOMEM; goto cleanup; } - mosq->will->payloadlen = payloadlen; - if(mosq->will->payloadlen > 0){ + mosq->will->msg.payloadlen = payloadlen; + if(mosq->will->msg.payloadlen > 0){ if(!payload){ rc = MOSQ_ERR_INVAL; goto cleanup; } - mosq->will->payload = mosquitto__malloc(sizeof(char)*mosq->will->payloadlen); - if(!mosq->will->payload){ + mosq->will->msg.payload = mosquitto__malloc(sizeof(char)*(unsigned int)mosq->will->msg.payloadlen); + if(!mosq->will->msg.payload){ rc = MOSQ_ERR_NOMEM; goto cleanup; } - memcpy(mosq->will->payload, payload, payloadlen); + memcpy(mosq->will->msg.payload, payload, (unsigned int)payloadlen); } - mosq->will->qos = qos; - mosq->will->retain = retain; + mosq->will->msg.qos = qos; + mosq->will->msg.retain = retain; + + mosq->will->properties = properties; return MOSQ_ERR_SUCCESS; cleanup: if(mosq->will){ - mosquitto__free(mosq->will->topic); - mosquitto__free(mosq->will->payload); + mosquitto__free(mosq->will->msg.topic); + mosquitto__free(mosq->will->msg.payload); mosquitto__free(mosq->will); mosq->will = NULL; @@ -93,14 +112,17 @@ int will__clear(struct mosquitto *mosq) { if(!mosq->will) return MOSQ_ERR_SUCCESS; - mosquitto__free(mosq->will->topic); - mosq->will->topic = NULL; + mosquitto__free(mosq->will->msg.topic); + mosq->will->msg.topic = NULL; + + mosquitto__free(mosq->will->msg.payload); + mosq->will->msg.payload = NULL; - mosquitto__free(mosq->will->payload); - mosq->will->payload = NULL; + mosquitto_property_free_all(&mosq->will->properties); mosquitto__free(mosq->will); mosq->will = NULL; + mosq->will_delay_interval = 0; return MOSQ_ERR_SUCCESS; } diff --git a/libs/mosquitto/src/will_mosq.h b/libs/mosquitto/src/will_mosq.h index eb5d483..ad5573b 100644 --- a/libs/mosquitto/src/will_mosq.h +++ b/libs/mosquitto/src/will_mosq.h @@ -1,15 +1,17 @@ /* -Copyright (c) 2010-2019 Roger Light +Copyright (c) 2010-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. */ @@ -20,7 +22,7 @@ and the Eclipse Distribution License is available at #include "mosquitto.h" #include "mosquitto_internal.h" -int will__set(struct mosquitto *mosq, const char *topic, int payloadlen, const void *payload, int qos, bool retain); +int will__set(struct mosquitto *mosq, const char *topic, int payloadlen, const void *payload, int qos, bool retain, mosquitto_property *properties); int will__clear(struct mosquitto *mosq); #endif