diff options
author | George Hazan <ghazan@miranda.im> | 2019-12-23 13:53:27 +0300 |
---|---|---|
committer | George Hazan <ghazan@miranda.im> | 2019-12-23 13:53:27 +0300 |
commit | a29c9194cf2d9f11839ea833872beab754e19527 (patch) | |
tree | 2a1ff6475dd77d41a1e6bf6acfb5be479c5543eb /libs/libmosquitto/src | |
parent | c1358991022919836c77d630ee9a3719fff86ed0 (diff) |
libmosquitto - a helper for MQTT clients
Diffstat (limited to 'libs/libmosquitto/src')
59 files changed, 10941 insertions, 0 deletions
diff --git a/libs/libmosquitto/src/actions.c b/libs/libmosquitto/src/actions.c new file mode 100644 index 0000000000..a5bf673fa9 --- /dev/null +++ b/libs/libmosquitto/src/actions.c @@ -0,0 +1,260 @@ +/* +Copyright (c) 2010-2019 Roger Light <roger@atchoo.org> + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#include <string.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 "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; + const mosquitto_property *p; + const mosquitto_property *outgoing_properties = NULL; + mosquitto_property local_property; + bool have_topic_alias; + int rc; + int 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->maximum_qos) return MOSQ_ERR_QOS_NOT_SUPPORTED; + + 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, tlen)) return MOSQ_ERR_MALFORMED_UTF8; + if(payloadlen < 0 || payloadlen > MQTT_MAX_PAYLOAD) return MOSQ_ERR_PAYLOAD_SIZE; + if(mosquitto_pub_topic_check(topic) != MOSQ_ERR_SUCCESS){ + return MOSQ_ERR_INVAL; + } + } + + if(mosq->maximum_packet_size > 0){ + remaining_length = 1 + 2+tlen + 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); + if(mid){ + *mid = local_mid; + } + + if(qos == 0){ + return send__publish(mosq, local_mid, topic, payloadlen, payload, qos, retain, false, outgoing_properties, NULL, 0); + }else{ + message = mosquitto__calloc(1, sizeof(struct mosquitto_message_all)); + if(!message) return MOSQ_ERR_NOMEM; + + message->next = NULL; + message->timestamp = mosquitto_time(); + message->msg.mid = local_mid; + if(topic){ + message->msg.topic = mosquitto__strdup(topic); + if(!message->msg.topic){ + message__cleanup(&message); + return MOSQ_ERR_NOMEM; + } + } + if(payloadlen){ + message->msg.payloadlen = payloadlen; + message->msg.payload = mosquitto__malloc(payloadlen*sizeof(uint8_t)); + if(!message->msg.payload){ + message__cleanup(&message); + return MOSQ_ERR_NOMEM; + } + memcpy(message->msg.payload, payload, payloadlen*sizeof(uint8_t)); + }else{ + message->msg.payloadlen = 0; + message->msg.payload = NULL; + } + message->msg.qos = qos; + message->msg.retain = retain; + message->dup = false; + + pthread_mutex_lock(&mosq->msgs_out.mutex); + message->state = mosq_ms_invalid; + message__queue(mosq, message, mosq_md_out); + pthread_mutex_unlock(&mosq->msgs_out.mutex); + return MOSQ_ERR_SUCCESS; + } +} + + +int mosquitto_subscribe(struct mosquitto *mosq, int *mid, const char *sub, int qos) +{ + 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(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; i<sub_count; i++){ + if(mosquitto_sub_topic_check(sub[i])) return MOSQ_ERR_INVAL; + slen = strlen(sub[i]); + if(mosquitto_validate_utf8(sub[i], slen)) return MOSQ_ERR_MALFORMED_UTF8; + remaining_length += 2+slen + 1; + } + + if(mosq->maximum_packet_size > 0){ + remaining_length += 2 + property__get_length_all(outgoing_properties); + if(packet__check_oversize(mosq, remaining_length)){ + return MOSQ_ERR_OVERSIZE_PACKET; + } + } + + 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(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; i<sub_count; i++){ + if(mosquitto_sub_topic_check(sub[i])) return MOSQ_ERR_INVAL; + slen = strlen(sub[i]); + if(mosquitto_validate_utf8(sub[i], slen)) return MOSQ_ERR_MALFORMED_UTF8; + remaining_length += 2+slen; + } + + if(mosq->maximum_packet_size > 0){ + remaining_length += 2 + property__get_length_all(outgoing_properties); + if(packet__check_oversize(mosq, remaining_length)){ + return MOSQ_ERR_OVERSIZE_PACKET; + } + } + + return send__unsubscribe(mosq, mid, sub_count, sub, outgoing_properties); +} + diff --git a/libs/libmosquitto/src/callbacks.c b/libs/libmosquitto/src/callbacks.c new file mode 100644 index 0000000000..17f5161bff --- /dev/null +++ b/libs/libmosquitto/src/callbacks.c @@ -0,0 +1,120 @@ +/* +Copyright (c) 2010-2019 Roger Light <roger@atchoo.org> + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#include "mosquitto.h" +#include "mosquitto_internal.h" + + +void mosquitto_connect_callback_set(struct mosquitto *mosq, void (*on_connect)(struct mosquitto *, void *, int)) +{ + pthread_mutex_lock(&mosq->callback_mutex); + mosq->on_connect = on_connect; + pthread_mutex_unlock(&mosq->callback_mutex); +} + +void mosquitto_connect_with_flags_callback_set(struct mosquitto *mosq, void (*on_connect)(struct mosquitto *, void *, int, int)) +{ + pthread_mutex_lock(&mosq->callback_mutex); + mosq->on_connect_with_flags = on_connect; + 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); + mosq->on_disconnect = on_disconnect; + 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); + mosq->on_publish = on_publish; + 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); + mosq->on_message = on_message; + 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); + mosq->on_subscribe = on_subscribe; + 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); + mosq->on_unsubscribe = on_unsubscribe; + 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); + mosq->on_log = on_log; + pthread_mutex_unlock(&mosq->log_callback_mutex); +} + diff --git a/libs/libmosquitto/src/connect.c b/libs/libmosquitto/src/connect.c new file mode 100644 index 0000000000..7543d4af74 --- /dev/null +++ b/libs/libmosquitto/src/connect.c @@ -0,0 +1,330 @@ +/* +Copyright (c) 2010-2019 Roger Light <roger@atchoo.org> + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#include <string.h> + +#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, const mosquitto_property *properties); +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, const char *bind_address) +{ + int i; + int rc; + + if(!mosq) return MOSQ_ERR_INVAL; + if(!host || port <= 0) return MOSQ_ERR_INVAL; + + if(mosq->id == NULL && (mosq->protocol == mosq_p_mqtt31 || mosq->protocol == mosq_p_mqtt311)){ + 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] = '/'; + + rc = util__random_bytes(&mosq->id[5], 18); + if(rc) return rc; + + for(i=5; i<23; i++){ + mosq->id[i] = alphanum[(mosq->id[i]&0x7F)%(sizeof(alphanum)-1)]; + } + } + + mosquitto__free(mosq->host); + mosq->host = mosquitto__strdup(host); + if(!mosq->host) return MOSQ_ERR_NOMEM; + mosq->port = port; + + mosquitto__free(mosq->bind_address); + if(bind_address){ + mosq->bind_address = mosquitto__strdup(bind_address); + if(!mosq->bind_address) return MOSQ_ERR_NOMEM; + } + + mosq->keepalive = keepalive; + mosq->msgs_in.inflight_quota = mosq->msgs_in.inflight_maximum; + mosq->msgs_out.inflight_quota = mosq->msgs_out.inflight_maximum; + + 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; + } + + 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; +} + + +int mosquitto_connect(struct mosquitto *mosq, const char *host, int port, int keepalive) +{ + return mosquitto_connect_bind(mosq, host, port, keepalive, NULL); +} + + +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; + + if(properties){ + rc = mosquitto_property_check_all(CMD_CONNECT, properties); + if(rc) return rc; + } + + rc = mosquitto__connect_init(mosq, host, port, keepalive, bind_address); + if(rc) return rc; + + pthread_mutex_lock(&mosq->state_mutex); + mosq->state = mosq_cs_new; + pthread_mutex_unlock(&mosq->state_mutex); + + return mosquitto__reconnect(mosq, true, properties); +} + + +int mosquitto_connect_async(struct mosquitto *mosq, const char *host, int port, int keepalive) +{ + return mosquitto_connect_bind_async(mosq, host, port, keepalive, NULL); +} + + +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; + + pthread_mutex_lock(&mosq->state_mutex); + mosq->state = mosq_cs_connect_async; + pthread_mutex_unlock(&mosq->state_mutex); + + return mosquitto__reconnect(mosq, false, NULL); +} + + +int mosquitto_reconnect_async(struct mosquitto *mosq) +{ + return mosquitto__reconnect(mosq, false, NULL); +} + + +int mosquitto_reconnect(struct mosquitto *mosq) +{ + return mosquitto__reconnect(mosq, true, NULL); +} + + +static int mosquitto__reconnect(struct mosquitto *mosq, bool blocking, const mosquitto_property *properties) +{ + 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; + if(mosq->protocol != mosq_p_mqtt5 && properties) return MOSQ_ERR_NOT_SUPPORTED; + + 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_CONNECT, outgoing_properties); + if(rc) return rc; + } + + 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; + } + pthread_mutex_unlock(&mosq->state_mutex); + + pthread_mutex_lock(&mosq->msgtime_mutex); + mosq->last_msg_in = mosquitto_time(); + mosq->next_msg_out = mosq->last_msg_in + mosq->keepalive; + pthread_mutex_unlock(&mosq->msgtime_mutex); + + 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); + + message__reconnect_reset(mosq); + + if(mosq->sock != INVALID_SOCKET){ + net__socket_close(mosq); //close socket + } + +#ifdef WITH_SOCKS + if(mosq->socks5_host){ + rc = net__socket_connect(mosq, mosq->socks5_host, mosq->socks5_port, mosq->bind_address, 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){ + return rc; + } + +#ifdef WITH_SOCKS + if(mosq->socks5_host){ + return socks5__send(mosq); + }else +#endif + { + return send__connect(mosq, mosq->keepalive, mosq->clean_start, outgoing_properties); + } +} + + +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(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; + } + + 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, reason_code, outgoing_properties); +} + + +void do_client_disconnect(struct mosquitto *mosq, int reason_code, const mosquitto_property *properties) +{ + pthread_mutex_lock(&mosq->state_mutex); + mosq->state = mosq_cs_disconnecting; + pthread_mutex_unlock(&mosq->state_mutex); + + 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; + } + } + 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/libmosquitto/src/dummypthread.h b/libs/libmosquitto/src/dummypthread.h new file mode 100644 index 0000000000..4207f3d68c --- /dev/null +++ b/libs/libmosquitto/src/dummypthread.h @@ -0,0 +1,13 @@ +#ifndef DUMMYPTHREAD_H +#define DUMMYPTHREAD_H + +#define pthread_create(A, B, C, D) +#define pthread_join(A, B) +#define pthread_cancel(A) + +#define pthread_mutex_init(A, B) +#define pthread_mutex_destroy(A) +#define pthread_mutex_lock(A) +#define pthread_mutex_unlock(A) + +#endif diff --git a/libs/libmosquitto/src/handle_auth.c b/libs/libmosquitto/src/handle_auth.c new file mode 100644 index 0000000000..5b84d3de31 --- /dev/null +++ b/libs/libmosquitto/src/handle_auth.c @@ -0,0 +1,49 @@ +/* +Copyright (c) 2018 Roger Light <roger@atchoo.org> + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#include <stdio.h> +#include <string.h> + +#include "logging_mosq.h" +#include "mosquitto_internal.h" +#include "mqtt_protocol.h" +#include "packet_mosq.h" +#include "property_mosq.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", mosq->id); + + if(mosq->protocol != mosq_p_mqtt5){ + return MOSQ_ERR_PROTOCOL; + } + + 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/libmosquitto/src/handle_connack.c b/libs/libmosquitto/src/handle_connack.c new file mode 100644 index 0000000000..19b6a66160 --- /dev/null +++ b/libs/libmosquitto/src/handle_connack.c @@ -0,0 +1,110 @@ +/* +Copyright (c) 2009-2019 Roger Light <roger@atchoo.org> + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#include <assert.h> + +#include "mosquitto.h" +#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) +{ + uint8_t connect_flags; + uint8_t reason_code; + int rc; + mosquitto_property *properties = NULL; + char *clientid = NULL; + + assert(mosq); + 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) 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_MAXIMUM_QOS, &mosq->maximum_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; + + log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received CONNACK (%d)", mosq->id, reason_code); + pthread_mutex_lock(&mosq->callback_mutex); + if(mosq->on_connect){ + mosq->in_callback = true; + 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, 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); + 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; + } + pthread_mutex_unlock(&mosq->state_mutex); + message__retry_check(mosq); + return MOSQ_ERR_SUCCESS; + case 1: + case 2: + case 3: + case 4: + case 5: + return MOSQ_ERR_CONN_REFUSED; + default: + return MOSQ_ERR_PROTOCOL; + } +} + diff --git a/libs/libmosquitto/src/handle_disconnect.c b/libs/libmosquitto/src/handle_disconnect.c new file mode 100644 index 0000000000..0ec1c8b3cb --- /dev/null +++ b/libs/libmosquitto/src/handle_disconnect.c @@ -0,0 +1,62 @@ +/* +Copyright (c) 2009-2018 Roger Light <roger@atchoo.org> + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#include <stdio.h> +#include <string.h> + +#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 "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; + } + + 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/libmosquitto/src/handle_ping.c b/libs/libmosquitto/src/handle_ping.c new file mode 100644 index 0000000000..56d89a3c3f --- /dev/null +++ b/libs/libmosquitto/src/handle_ping.c @@ -0,0 +1,70 @@ +/* +Copyright (c) 2009-2019 Roger Light <roger@atchoo.org> + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#include <assert.h> +#include <stdio.h> +#include <string.h> + +#ifdef WITH_BROKER +# include "mosquitto_broker_internal.h" +#endif + +#include "mosquitto.h" +#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 "read_handle.h" +#include "send_mosq.h" +#include "util_mosq.h" + +int handle__pingreq(struct mosquitto *mosq) +{ + assert(mosq); + + if(mosq->state != mosq_cs_connected){ + return MOSQ_ERR_PROTOCOL; + } + +#ifdef WITH_BROKER + log__printf(NULL, MOSQ_LOG_DEBUG, "Received PINGREQ from %s", mosq->id); +#else + log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received PINGREQ", mosq->id); +#endif + return send__pingresp(mosq); +} + +int handle__pingresp(struct mosquitto *mosq) +{ + assert(mosq); + + if(mosq->state != mosq_cs_connected){ + 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); +#else + log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received PINGRESP", mosq->id); +#endif + return MOSQ_ERR_SUCCESS; +} + diff --git a/libs/libmosquitto/src/handle_pubackcomp.c b/libs/libmosquitto/src/handle_pubackcomp.c new file mode 100644 index 0000000000..67921494e2 --- /dev/null +++ b/libs/libmosquitto/src/handle_pubackcomp.c @@ -0,0 +1,118 @@ +/* +Copyright (c) 2009-2019 Roger Light <roger@atchoo.org> + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#include <assert.h> +#include <stdio.h> +#include <string.h> + +#ifdef WITH_BROKER +# include "mosquitto_broker_internal.h" +#endif + +#include "mosquitto.h" +#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 "read_handle.h" +#include "send_mosq.h" +#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(mosq->state != mosq_cs_connected){ + return MOSQ_ERR_PROTOCOL; + } + + 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 */ + 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(mosq->in_packet.remaining_length > 3){ + rc = property__read_all(CMD_PUBACK, &mosq->in_packet, &properties); + if(rc) return rc; + } + } + +#ifdef WITH_BROKER + log__printf(NULL, MOSQ_LOG_DEBUG, "Received %s from %s (Mid: %d, RC:%d)", type, 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(db, 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, mosq->id, mid); + return MOSQ_ERR_SUCCESS; + }else{ + return rc; + } +#else + log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received %s (Mid: %d, RC:%d)", mosq->id, type, mid, reason_code); + + rc = message__delete(mosq, mid, mosq_md_out, qos); + if(rc){ + return rc; + }else{ + /* Only inform the client the message has been sent once. */ + pthread_mutex_lock(&mosq->callback_mutex); + if(mosq->on_publish){ + mosq->in_callback = true; + mosq->on_publish(mosq, mosq->userdata, mid); + mosq->in_callback = false; + } + 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); + } + 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/libmosquitto/src/handle_publish.c b/libs/libmosquitto/src/handle_publish.c new file mode 100644 index 0000000000..8c93e3cefb --- /dev/null +++ b/libs/libmosquitto/src/handle_publish.c @@ -0,0 +1,167 @@ +/* +Copyright (c) 2009-2019 Roger Light <roger@atchoo.org> + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#include <assert.h> +#include <string.h> + +#include "mosquitto.h" +#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 "send_mosq.h" +#include "time_mosq.h" +#include "util_mosq.h" + + +int handle__publish(struct mosquitto *mosq) +{ + uint8_t header; + struct mosquitto_message_all *message; + int rc = 0; + uint16_t mid; + int slen; + mosquitto_property *properties = NULL; + + assert(mosq); + + if(mosq->state != mosq_cs_connected){ + return MOSQ_ERR_PROTOCOL; + } + + message = mosquitto__calloc(1, sizeof(struct mosquitto_message_all)); + if(!message) return MOSQ_ERR_NOMEM; + + header = mosq->in_packet.command; + + message->dup = (header & 0x08)>>3; + message->msg.qos = (header & 0x06)>>1; + message->msg.retain = (header & 0x01); + + rc = packet__read_string(&mosq->in_packet, &message->msg.topic, &slen); + if(rc){ + message__cleanup(&message); + return rc; + } + if(!slen){ + message__cleanup(&message); + return MOSQ_ERR_PROTOCOL; + } + + 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; + } + + if(mosq->protocol == mosq_p_mqtt5){ + rc = property__read_all(CMD_PUBLISH, &mosq->in_packet, &properties); + if(rc) return rc; + } + + message->msg.payloadlen = 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)); + 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); + 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, + message->msg.mid, message->msg.topic, + (long)message->msg.payloadlen); + + message->timestamp = mosquitto_time(); + switch(message->msg.qos){ + case 0: + 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 MOSQ_ERR_SUCCESS; + case 1: + util__decrement_receive_quota(mosq); + rc = send__puback(mosq, message->msg.mid, 0); + 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: + util__decrement_receive_quota(mosq); + rc = send__pubrec(mosq, message->msg.mid, 0); + 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->msgs_in.mutex); + mosquitto_property_free_all(&properties); + return rc; + default: + message__cleanup(&message); + mosquitto_property_free_all(&properties); + return MOSQ_ERR_PROTOCOL; + } +} + diff --git a/libs/libmosquitto/src/handle_pubrec.c b/libs/libmosquitto/src/handle_pubrec.c new file mode 100644 index 0000000000..81f1b4962a --- /dev/null +++ b/libs/libmosquitto/src/handle_pubrec.c @@ -0,0 +1,110 @@ +/* +Copyright (c) 2009-2019 Roger Light <roger@atchoo.org> + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#include <assert.h> +#include <stdio.h> +#include <string.h> + +#ifdef WITH_BROKER +# include "mosquitto_broker_internal.h" +#endif + +#include "mosquitto.h" +#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 "read_handle.h" +#include "send_mosq.h" +#include "util_mosq.h" + +int handle__pubrec(struct mosquitto_db *db, struct mosquitto *mosq) +{ + uint8_t reason_code = 0; + uint16_t mid; + int rc; + mosquitto_property *properties = NULL; + + assert(mosq); + + if(mosq->state != mosq_cs_connected){ + 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(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); + } + } + +#ifdef WITH_BROKER + log__printf(NULL, MOSQ_LOG_DEBUG, "Received PUBREC from %s (Mid: %d)", 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(db, mosq, mid, mosq_ms_wait_for_pubrec, 2); + } +#else + UNUSED(db); + + log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received PUBREC (Mid: %d)", 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); + }else if(rc != MOSQ_ERR_SUCCESS){ + return rc; + } + rc = send__pubrel(mosq, mid); + if(rc) return rc; + + return MOSQ_ERR_SUCCESS; +} + diff --git a/libs/libmosquitto/src/handle_pubrel.c b/libs/libmosquitto/src/handle_pubrel.c new file mode 100644 index 0000000000..d4a90652d2 --- /dev/null +++ b/libs/libmosquitto/src/handle_pubrel.c @@ -0,0 +1,126 @@ +/* +Copyright (c) 2009-2019 Roger Light <roger@atchoo.org> + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#include <assert.h> +#include <stdio.h> +#include <string.h> + +#ifdef WITH_BROKER +# include "mosquitto_broker_internal.h" +#endif + +#include "mosquitto.h" +#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 "read_handle.h" +#include "send_mosq.h" +#include "util_mosq.h" + + +int handle__pubrel(struct mosquitto_db *db, 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->state != mosq_cs_connected){ + return MOSQ_ERR_PROTOCOL; + } + + 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(mosq->in_packet.remaining_length > 3){ + rc = property__read_all(CMD_PUBREL, &mosq->in_packet, &properties); + if(rc) return rc; + } + } + +#ifdef WITH_BROKER + log__printf(NULL, MOSQ_LOG_DEBUG, "Received PUBREL from %s (Mid: %d)", 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(db, mosq, mid); + if(rc == MOSQ_ERR_PROTOCOL){ + return rc; + }else if(rc != MOSQ_ERR_SUCCESS){ + /* Message not found. Still send a PUBCOMP anyway because this could be + * due to a repeated PUBREL after a client has reconnected. */ + } + + rc = send__pubcomp(mosq, mid); + if(rc) return rc; +#else + UNUSED(db); + + log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received PUBREL (Mid: %d)", mosq->id, mid); + + rc = send__pubcomp(mosq, mid); + if(rc){ + message__remove(mosq, mid, mosq_md_in, &message, 2); + return rc; + } + + rc = message__remove(mosq, mid, mosq_md_in, &message, 2); + if(rc){ + return rc; + }else{ + /* Only pass the message on if we have removed it from the queue - this + * prevents multiple callbacks for the same message. */ + pthread_mutex_lock(&mosq->callback_mutex); + if(mosq->on_message){ + mosq->in_callback = true; + mosq->on_message(mosq, mosq->userdata, &message->msg); + mosq->in_callback = false; + } + 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); + mosquitto_property_free_all(&properties); + message__cleanup(&message); + } +#endif + + return MOSQ_ERR_SUCCESS; +} + diff --git a/libs/libmosquitto/src/handle_suback.c b/libs/libmosquitto/src/handle_suback.c new file mode 100644 index 0000000000..dadebcd79f --- /dev/null +++ b/libs/libmosquitto/src/handle_suback.c @@ -0,0 +1,98 @@ +/* +Copyright (c) 2009-2019 Roger Light <roger@atchoo.org> + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#include <assert.h> + +#ifdef WITH_BROKER +# include "mosquitto_broker_internal.h" +#endif + +#include "mosquitto.h" +#include "mosquitto_internal.h" +#include "logging_mosq.h" +#include "memory_mosq.h" +#include "mqtt_protocol.h" +#include "packet_mosq.h" +#include "property_mosq.h" + + +int handle__suback(struct mosquitto *mosq) +{ + uint16_t mid; + uint8_t qos; + int *granted_qos; + int qos_count; + int i = 0; + int rc; + mosquitto_property *properties = NULL; + + assert(mosq); + + if(mosq->state != mosq_cs_connected){ + return MOSQ_ERR_PROTOCOL; + } + +#ifdef WITH_BROKER + log__printf(NULL, MOSQ_LOG_DEBUG, "Received SUBACK from %s", mosq->id); +#else + log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received SUBACK", 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; + while(mosq->in_packet.pos < mosq->in_packet.remaining_length){ + rc = packet__read_byte(&mosq->in_packet, &qos); + if(rc){ + mosquitto__free(granted_qos); + return rc; + } + granted_qos[i] = (int)qos; + i++; + } +#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); + + return MOSQ_ERR_SUCCESS; +} + diff --git a/libs/libmosquitto/src/handle_unsuback.c b/libs/libmosquitto/src/handle_unsuback.c new file mode 100644 index 0000000000..3ced0e5280 --- /dev/null +++ b/libs/libmosquitto/src/handle_unsuback.c @@ -0,0 +1,87 @@ +/* +Copyright (c) 2009-2019 Roger Light <roger@atchoo.org> + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#include <assert.h> +#include <stdio.h> +#include <string.h> + +#ifdef WITH_BROKER +# include "mosquitto_broker_internal.h" +#endif + +#include "mosquitto.h" +#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" +#include "send_mosq.h" +#include "util_mosq.h" + + +int handle__unsuback(struct mosquitto *mosq) +{ + uint16_t mid; + int rc; + mosquitto_property *properties = NULL; + + assert(mosq); + + if(mosq->state != mosq_cs_connected){ + return MOSQ_ERR_PROTOCOL; + } + +#ifdef WITH_BROKER + log__printf(NULL, MOSQ_LOG_DEBUG, "Received UNSUBACK from %s", mosq->id); +#else + log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received UNSUBACK", 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_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/libmosquitto/src/helpers.c b/libs/libmosquitto/src/helpers.c new file mode 100644 index 0000000000..0f60501edc --- /dev/null +++ b/libs/libmosquitto/src/helpers.c @@ -0,0 +1,227 @@ +/* +Copyright (c) 2016-2019 Roger Light <roger@atchoo.org> + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#include <errno.h> +#include <stdbool.h> + +#include "mosquitto.h" +#include "mosquitto_internal.h" + +struct userdata__callback { + const char *topic; + int (*callback)(struct mosquitto *, void *, const struct mosquitto_message *); + void *userdata; + int qos; + int rc; +}; + +struct userdata__simple { + struct mosquitto_message *messages; + int max_msg_count; + int message_count; + bool want_retained; +}; + + +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); +} + + +static void on_message_callback(struct mosquitto *mosq, void *obj, const struct mosquitto_message *message) +{ + int rc; + struct userdata__callback *userdata = obj; + + rc = userdata->callback(mosq, userdata->userdata, message); + if(rc){ + mosquitto_disconnect(mosq); + } +} + +static int on_message_simple(struct mosquitto *mosq, void *obj, const struct mosquitto_message *message) +{ + struct userdata__simple *userdata = obj; + int rc; + + if(userdata->max_msg_count == 0){ + return 0; + } + + /* Don't process stale retained messages if 'want_retained' was false */ + if(!userdata->want_retained && message->retain){ + return 0; + } + + userdata->max_msg_count--; + + rc = mosquitto_message_copy(&userdata->messages[userdata->message_count], message); + if(rc){ + return rc; + } + userdata->message_count++; + if(userdata->max_msg_count == 0){ + mosquitto_disconnect(mosq); + } + return 0; +} + + +libmosq_EXPORT int mosquitto_subscribe_simple( + struct mosquitto_message **messages, + int msg_count, + bool want_retained, + const char *topic, + int qos, + const char *host, + int port, + const char *client_id, + int keepalive, + bool clean_session, + const char *username, + const char *password, + const struct libmosquitto_will *will, + const struct libmosquitto_tls *tls) +{ + struct userdata__simple userdata; + int rc; + int i; + + if(!topic || msg_count < 1 || !messages){ + return MOSQ_ERR_INVAL; + } + + *messages = NULL; + + userdata.messages = calloc(sizeof(struct mosquitto_message), msg_count); + if(!userdata.messages){ + return MOSQ_ERR_NOMEM; + } + userdata.message_count = 0; + userdata.max_msg_count = msg_count; + userdata.want_retained = want_retained; + + rc = mosquitto_subscribe_callback( + on_message_simple, &userdata, + topic, qos, + host, port, + client_id, keepalive, clean_session, + username, password, + will, tls); + + if(!rc && userdata.max_msg_count == 0){ + *messages = userdata.messages; + return MOSQ_ERR_SUCCESS; + }else{ + for(i=0; i<msg_count; i++){ + mosquitto_message_free_contents(&userdata.messages[i]); + } + free(userdata.messages); + userdata.messages = NULL; + return rc; + } +} + + +libmosq_EXPORT int mosquitto_subscribe_callback( + int (*callback)(struct mosquitto *, void *, const struct mosquitto_message *), + void *userdata, + const char *topic, + int qos, + const char *host, + int port, + const char *client_id, + int keepalive, + bool clean_session, + const char *username, + const char *password, + const struct libmosquitto_will *will, + const struct libmosquitto_tls *tls) +{ + struct mosquitto *mosq; + struct userdata__callback cb_userdata; + int rc; + + if(!callback || !topic){ + return MOSQ_ERR_INVAL; + } + + cb_userdata.topic = topic; + cb_userdata.qos = qos; + cb_userdata.rc = 0; + cb_userdata.userdata = userdata; + cb_userdata.callback = callback; + + mosq = mosquitto_new(client_id, clean_session, &cb_userdata); + if(!mosq){ + return MOSQ_ERR_NOMEM; + } + + if(will){ + rc = mosquitto_will_set(mosq, will->topic, will->payloadlen, will->payload, will->qos, will->retain); + if(rc){ + mosquitto_destroy(mosq); + return rc; + } + } + if(username){ + rc = mosquitto_username_pw_set(mosq, username, password); + if(rc){ + mosquitto_destroy(mosq); + return rc; + } + } + if(tls){ + rc = mosquitto_tls_set(mosq, tls->cafile, tls->capath, tls->certfile, tls->keyfile, tls->pw_callback); + if(rc){ + mosquitto_destroy(mosq); + return rc; + } + rc = mosquitto_tls_opts_set(mosq, tls->cert_reqs, tls->tls_version, tls->ciphers); + if(rc){ + mosquitto_destroy(mosq); + return rc; + } + } + + mosquitto_connect_callback_set(mosq, on_connect); + mosquitto_message_callback_set(mosq, on_message_callback); + + rc = mosquitto_connect(mosq, host, port, keepalive); + if(rc){ + mosquitto_destroy(mosq); + return rc; + } + 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; +} + diff --git a/libs/libmosquitto/src/logging_mosq.c b/libs/libmosquitto/src/logging_mosq.c new file mode 100644 index 0000000000..eab6356239 --- /dev/null +++ b/libs/libmosquitto/src/logging_mosq.c @@ -0,0 +1,59 @@ +/* +Copyright (c) 2009-2019 Roger Light <roger@atchoo.org> + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#include <assert.h> +#include <stdarg.h> +#include <stdio.h> +#include <string.h> + +#include "mosquitto_internal.h" +#include "mosquitto.h" +#include "memory_mosq.h" + +int log__printf(struct mosquitto *mosq, int priority, const char *fmt, ...) +{ + va_list va; + char *s; + int len; + + assert(mosq); + assert(fmt); + + pthread_mutex_lock(&mosq->log_callback_mutex); + if(mosq->on_log){ + len = strlen(fmt) + 500; + s = mosquitto__malloc(len*sizeof(char)); + if(!s){ + pthread_mutex_unlock(&mosq->log_callback_mutex); + return MOSQ_ERR_NOMEM; + } + + va_start(va, fmt); + vsnprintf(s, len, fmt, va); + va_end(va); + s[len-1] = '\0'; /* Ensure string is null terminated. */ + + mosq->on_log(mosq, mosq->userdata, priority, s); + + mosquitto__free(s); + } + pthread_mutex_unlock(&mosq->log_callback_mutex); + + return MOSQ_ERR_SUCCESS; +} + diff --git a/libs/libmosquitto/src/logging_mosq.h b/libs/libmosquitto/src/logging_mosq.h new file mode 100644 index 0000000000..c3cc29d9ee --- /dev/null +++ b/libs/libmosquitto/src/logging_mosq.h @@ -0,0 +1,23 @@ +/* +Copyright (c) 2009-2019 Roger Light <roger@atchoo.org> + +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 LOGGING_MOSQ_H +#define LOGGING_MOSQ_H + +#include "mosquitto.h" + +int log__printf(struct mosquitto *mosq, int priority, const char *fmt, ...); + +#endif diff --git a/libs/libmosquitto/src/loop.c b/libs/libmosquitto/src/loop.c new file mode 100644 index 0000000000..2342c945aa --- /dev/null +++ b/libs/libmosquitto/src/loop.c @@ -0,0 +1,390 @@ +/* +Copyright (c) 2010-2019 Roger Light <roger@atchoo.org> + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#include <errno.h> +#ifndef WIN32 +#include <sys/select.h> +#include <time.h> +#endif + +#include "mosquitto.h" +#include "mosquitto_internal.h" +#include "net_mosq.h" +#include "packet_mosq.h" +#include "socks_mosq.h" +#include "tls_mosq.h" +#include "util_mosq.h" + +#if !defined(WIN32) && !defined(__SYMBIAN32__) +#define HAVE_PSELECT +#endif + +int mosquitto_loop(struct mosquitto *mosq, int timeout, int max_packets) +{ +#ifdef HAVE_PSELECT + struct timespec local_timeout; +#else + struct timeval local_timeout; +#endif + fd_set readfds, writefds; + int fdcount; + int rc; + char pairbuf; + int maxfd = 0; + time_t now; + + if(!mosq || max_packets < 1) return MOSQ_ERR_INVAL; +#ifndef WIN32 + if(mosq->sock >= FD_SETSIZE || mosq->sockpairR >= FD_SETSIZE){ + return MOSQ_ERR_INVAL; + } +#endif + + FD_ZERO(&readfds); + FD_ZERO(&writefds); + if(mosq->sock != INVALID_SOCKET){ + maxfd = mosq->sock; + FD_SET(mosq->sock, &readfds); + pthread_mutex_lock(&mosq->current_out_packet_mutex); + pthread_mutex_lock(&mosq->out_packet_mutex); + if(mosq->out_packet || mosq->current_out_packet){ + FD_SET(mosq->sock, &writefds); + } +#ifdef WITH_TLS + if(mosq->ssl){ + if(mosq->want_write){ + FD_SET(mosq->sock, &writefds); + }else if(mosq->want_connect){ + /* Remove possible FD_SET from above, we don't want to check + * for writing if we are still connecting, unless want_write is + * definitely set. The presence of outgoing packets does not + * matter yet. */ + FD_CLR(mosq->sock, &writefds); + } + } +#endif + pthread_mutex_unlock(&mosq->out_packet_mutex); + pthread_mutex_unlock(&mosq->current_out_packet_mutex); + }else{ +#ifdef WITH_SRV + if(mosq->achan){ + pthread_mutex_lock(&mosq->state_mutex); + if(mosq->state == 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; +#endif + } + if(mosq->sockpairR != INVALID_SOCKET){ + /* 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){ + maxfd = mosq->sockpairR; + } + } + + if(timeout < 0){ + timeout = 1000; + } + + now = mosquitto_time(); + if(mosq->next_msg_out && now + timeout/1000 > mosq->next_msg_out){ + timeout = (mosq->next_msg_out - now)*1000; + } + + if(timeout < 0){ + /* There has been a delay somewhere which means we should have already + * sent a message. */ + timeout = 0; + } + + local_timeout.tv_sec = timeout/1000; +#ifdef HAVE_PSELECT + local_timeout.tv_nsec = (timeout-local_timeout.tv_sec*1000)*1e6; +#else + local_timeout.tv_usec = (timeout-local_timeout.tv_sec*1000)*1000; +#endif + +#ifdef HAVE_PSELECT + fdcount = pselect(maxfd+1, &readfds, &writefds, NULL, &local_timeout, NULL); +#else + fdcount = select(maxfd+1, &readfds, &writefds, 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->sock != INVALID_SOCKET){ + if(FD_ISSET(mosq->sock, &readfds)){ + rc = mosquitto_loop_read(mosq, max_packets); + if(rc || mosq->sock == INVALID_SOCKET){ + return rc; + } + } + 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 + /* Fake write possible, to stimulate output write even though + * we didn't ask for it, because at that point the publish or + * other command wasn't present. */ + if(mosq->sock != INVALID_SOCKET) + FD_SET(mosq->sock, &writefds); + } + if(mosq->sock != INVALID_SOCKET && FD_ISSET(mosq->sock, &writefds)){ +#ifdef WITH_TLS + if(mosq->want_connect){ + rc = net__socket_connect_tls(mosq); + if(rc) return rc; + }else +#endif + { + rc = mosquitto_loop_write(mosq, max_packets); + if(rc || mosq->sock == INVALID_SOCKET){ + return rc; + } + } + } + } +#ifdef WITH_SRV + if(mosq->achan){ + ares_process(mosq->achan, &readfds, &writefds); + } +#endif + } + return mosquitto_loop_misc(mosq); +} + + +int mosquitto_loop_forever(struct mosquitto *mosq, int timeout, int max_packets) +{ + int run = 1; + int rc; + unsigned int reconnects = 0; + unsigned long reconnect_delay; +#ifndef WIN32 + struct timespec req, rem; +#endif + + if(!mosq) return MOSQ_ERR_INVAL; + + if(mosq->state == mosq_cs_connect_async){ + mosquitto_reconnect(mosq); + } + + while(run){ + do{ + 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){ + case MOSQ_ERR_NOMEM: + case MOSQ_ERR_PROTOCOL: + case MOSQ_ERR_INVAL: + case MOSQ_ERR_NOT_FOUND: + case MOSQ_ERR_TLS: + case MOSQ_ERR_PAYLOAD_SIZE: + case MOSQ_ERR_NOT_SUPPORTED: + case MOSQ_ERR_AUTH: + case MOSQ_ERR_ACL_DENIED: + case MOSQ_ERR_UNKNOWN: + case MOSQ_ERR_EAI: + case MOSQ_ERR_PROXY: + return rc; + case MOSQ_ERR_ERRNO: + break; + } + if(errno == EPROTO){ + return rc; + } + do{ + rc = MOSQ_ERR_SUCCESS; + pthread_mutex_lock(&mosq->state_mutex); + if(mosq->state == mosq_cs_disconnecting){ + 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); + }else{ + reconnect_delay = mosq->reconnect_delay*(reconnects+1); + } + }else{ + reconnect_delay = mosq->reconnect_delay; + } + + if(reconnect_delay > mosq->reconnect_delay_max){ + reconnect_delay = mosq->reconnect_delay_max; + }else{ + 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 + + pthread_mutex_lock(&mosq->state_mutex); + if(mosq->state == mosq_cs_disconnecting){ + run = 0; + pthread_mutex_unlock(&mosq->state_mutex); + }else{ + pthread_mutex_unlock(&mosq->state_mutex); + rc = mosquitto_reconnect(mosq); + } + } + }while(run && rc != MOSQ_ERR_SUCCESS); + } + return rc; +} + + +int mosquitto_loop_misc(struct mosquitto *mosq) +{ + if(!mosq) return MOSQ_ERR_INVAL; + if(mosq->sock == INVALID_SOCKET) return MOSQ_ERR_NO_CONN; + + return mosquitto__check_keepalive(mosq); +} + + +static int mosquitto__loop_rc_handle(struct mosquitto *mosq, int rc) +{ + if(rc){ + net__socket_close(mosq); + pthread_mutex_lock(&mosq->state_mutex); + if(mosq->state == mosq_cs_disconnecting){ + 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; +} + + +int mosquitto_loop_read(struct mosquitto *mosq, int max_packets) +{ + int rc; + int i; + if(max_packets < 1) return MOSQ_ERR_INVAL; + +#ifdef WITH_TLS + if(mosq->want_connect){ + return net__socket_connect_tls(mosq); + } +#endif + + 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->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 + * have QoS > 0. We should try to deal with that many in this loop in order + * to keep up. */ + for(i=0; i<max_packets || SSL_DATA_PENDING(mosq); i++){ +#ifdef WITH_SOCKS + if(mosq->socks5_host){ + rc = socks5__read(mosq); + }else +#endif + { + rc = packet__read(mosq); + } + if(rc || errno == EAGAIN || errno == COMPAT_EWOULDBLOCK){ + return mosquitto__loop_rc_handle(mosq, rc); + } + } + return rc; +} + + +int mosquitto_loop_write(struct mosquitto *mosq, int max_packets) +{ + int rc; + int i; + if(max_packets < 1) return MOSQ_ERR_INVAL; + + 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->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 + * have QoS > 0. We should try to deal with that many in this loop in order + * to keep up. */ + for(i=0; i<max_packets; i++){ + rc = packet__write(mosq); + if(rc || errno == EAGAIN || errno == COMPAT_EWOULDBLOCK){ + return mosquitto__loop_rc_handle(mosq, rc); + } + } + return rc; +} + diff --git a/libs/libmosquitto/src/memory_mosq.c b/libs/libmosquitto/src/memory_mosq.c new file mode 100644 index 0000000000..a9b01e5477 --- /dev/null +++ b/libs/libmosquitto/src/memory_mosq.c @@ -0,0 +1,160 @@ +/* +Copyright (c) 2009-2019 Roger Light <roger@atchoo.org> + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#include <stdlib.h> +#include <string.h> + +#include "memory_mosq.h" + +#ifdef REAL_WITH_MEMORY_TRACKING +# if defined(__APPLE__) +# include <malloc/malloc.h> +# define malloc_usable_size malloc_size +# elif defined(__FreeBSD__) +# include <malloc_np.h> +# else +# include <malloc.h> +# endif +#endif + +#ifdef REAL_WITH_MEMORY_TRACKING +static unsigned long memcount = 0; +static unsigned long max_memcount = 0; +#endif + +#ifdef WITH_BROKER +static size_t mem_limit = 0; +void memory__set_limit(size_t lim) +{ + mem_limit = lim; +} +#endif + +void *mosquitto__calloc(size_t nmemb, size_t size) +{ +#ifdef REAL_WITH_MEMORY_TRACKING + if(mem_limit && memcount + size > mem_limit){ + return NULL; + } +#endif + void *mem = calloc(nmemb, size); + +#ifdef REAL_WITH_MEMORY_TRACKING + if(mem){ + memcount += malloc_usable_size(mem); + if(memcount > max_memcount){ + max_memcount = memcount; + } + } +#endif + + return mem; +} + +void mosquitto__free(void *mem) +{ +#ifdef REAL_WITH_MEMORY_TRACKING + if(!mem){ + return; + } + memcount -= malloc_usable_size(mem); +#endif + free(mem); +} + +void *mosquitto__malloc(size_t size) +{ +#ifdef REAL_WITH_MEMORY_TRACKING + if(mem_limit && memcount + size > mem_limit){ + return NULL; + } +#endif + void *mem = malloc(size); + +#ifdef REAL_WITH_MEMORY_TRACKING + if(mem){ + memcount += malloc_usable_size(mem); + if(memcount > max_memcount){ + max_memcount = memcount; + } + } +#endif + + return mem; +} + +#ifdef REAL_WITH_MEMORY_TRACKING +unsigned long mosquitto__memory_used(void) +{ + return memcount; +} + +unsigned long mosquitto__max_memory_used(void) +{ + return max_memcount; +} +#endif + +void *mosquitto__realloc(void *ptr, size_t size) +{ +#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); + } +#endif + mem = realloc(ptr, size); + +#ifdef REAL_WITH_MEMORY_TRACKING + if(mem){ + memcount += malloc_usable_size(mem); + if(memcount > max_memcount){ + max_memcount = memcount; + } + } +#endif + + return mem; +} + +char *mosquitto__strdup(const char *s) +{ +#ifdef REAL_WITH_MEMORY_TRACKING + if(mem_limit && memcount + strlen(s) > mem_limit){ + return NULL; + } +#endif + char *str = strdup(s); + +#ifdef REAL_WITH_MEMORY_TRACKING + if(str){ + memcount += malloc_usable_size(str); + if(memcount > max_memcount){ + max_memcount = memcount; + } + } +#endif + + return str; +} + diff --git a/libs/libmosquitto/src/memory_mosq.h b/libs/libmosquitto/src/memory_mosq.h new file mode 100644 index 0000000000..63386a2115 --- /dev/null +++ b/libs/libmosquitto/src/memory_mosq.h @@ -0,0 +1,41 @@ +/* +Copyright (c) 2010-2019 Roger Light <roger@atchoo.org> + +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 MEMORY_MOSQ_H +#define MEMORY_MOSQ_H + +#include <stdio.h> +#include <sys/types.h> + +#if defined(WITH_MEMORY_TRACKING) && defined(WITH_BROKER) && defined(__GLIBC__) +#define REAL_WITH_MEMORY_TRACKING +#endif + +void *mosquitto__calloc(size_t nmemb, size_t size); +void mosquitto__free(void *mem); +void *mosquitto__malloc(size_t size); +#ifdef REAL_WITH_MEMORY_TRACKING +unsigned long mosquitto__memory_used(void); +unsigned long mosquitto__max_memory_used(void); +#endif +void *mosquitto__realloc(void *ptr, size_t size); +char *mosquitto__strdup(const char *s); + +#ifdef WITH_BROKER +void memory__set_limit(size_t lim); +#endif + +#endif diff --git a/libs/libmosquitto/src/messages_mosq.c b/libs/libmosquitto/src/messages_mosq.c new file mode 100644 index 0000000000..bf77f5e64b --- /dev/null +++ b/libs/libmosquitto/src/messages_mosq.c @@ -0,0 +1,348 @@ +/* +Copyright (c) 2010-2019 Roger Light <roger@atchoo.org> + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#include <assert.h> +#include <stdlib.h> +#include <string.h> +#include <utlist.h> + +#include "mosquitto_internal.h" +#include "mosquitto.h" +#include "memory_mosq.h" +#include "messages_mosq.h" +#include "send_mosq.h" +#include "time_mosq.h" +#include "util_mosq.h" + +void message__cleanup(struct mosquitto_message_all **message) +{ + struct mosquitto_message_all *msg; + + if(!message || !*message) return; + + msg = *message; + + mosquitto__free(msg->msg.topic); + mosquitto__free(msg->msg.payload); + mosquitto__free(msg); +} + +void message__cleanup_all(struct mosquitto *mosq) +{ + struct mosquitto_message_all *tail, *tmp; + + assert(mosq); + + DL_FOREACH_SAFE(mosq->msgs_in.inflight, tail, tmp){ + DL_DELETE(mosq->msgs_in.inflight, tail); + message__cleanup(&tail); + } + DL_FOREACH_SAFE(mosq->msgs_out.inflight, tail, tmp){ + DL_DELETE(mosq->msgs_out.inflight, tail); + message__cleanup(&tail); + } +} + +int mosquitto_message_copy(struct mosquitto_message *dst, const struct mosquitto_message *src) +{ + if(!dst || !src) return MOSQ_ERR_INVAL; + + dst->mid = src->mid; + dst->topic = mosquitto__strdup(src->topic); + if(!dst->topic) return MOSQ_ERR_NOMEM; + dst->qos = src->qos; + dst->retain = src->retain; + if(src->payloadlen){ + dst->payload = mosquitto__calloc(src->payloadlen+1, sizeof(uint8_t)); + if(!dst->payload){ + mosquitto__free(dst->topic); + return MOSQ_ERR_NOMEM; + } + memcpy(dst->payload, src->payload, src->payloadlen); + dst->payloadlen = src->payloadlen; + }else{ + dst->payloadlen = 0; + dst->payload = NULL; + } + return MOSQ_ERR_SUCCESS; +} + +int message__delete(struct mosquitto *mosq, uint16_t mid, enum mosquitto_msg_direction dir, int qos) +{ + struct mosquitto_message_all *message; + int rc; + assert(mosq); + + rc = message__remove(mosq, mid, dir, &message, qos); + if(rc == MOSQ_ERR_SUCCESS){ + message__cleanup(&message); + } + return rc; +} + +void mosquitto_message_free(struct mosquitto_message **message) +{ + struct mosquitto_message *msg; + + if(!message || !*message) return; + + msg = *message; + + mosquitto__free(msg->topic); + mosquitto__free(msg->payload); + mosquitto__free(msg); +} + +void mosquitto_message_free_contents(struct mosquitto_message *message) +{ + if(!message) return; + + mosquitto__free(message->topic); + mosquitto__free(message->payload); +} + +int message__queue(struct mosquitto *mosq, struct mosquitto_message_all *message, enum mosquitto_msg_direction dir) +{ + /* mosq->*_message_mutex should be locked before entering this function */ + assert(mosq); + assert(message); + assert(message->msg.qos != 0); + + if(dir == mosq_md_out){ + DL_APPEND(mosq->msgs_out.inflight, message); + mosq->msgs_out.queue_len++; + }else{ + DL_APPEND(mosq->msgs_in.inflight, message); + mosq->msgs_in.queue_len++; + } + + return message__release_to_inflight(mosq, dir); +} + +void message__reconnect_reset(struct mosquitto *mosq) +{ + struct mosquitto_message_all *message, *tmp; + assert(mosq); + + 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){ + 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); + } + } + pthread_mutex_unlock(&mosq->msgs_in.mutex); + + + 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++; + + message->timestamp = 0; + if(mosq->msgs_out.inflight_quota != 0){ + util__decrement_send_quota(mosq); + 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. */ + } + }else{ + message->state = mosq_ms_invalid; + } + } + 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, cur->msg.mid, cur->msg.topic, cur->msg.payloadlen, cur->msg.payload, cur->msg.qos, cur->msg.retain, cur->dup, NULL, 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, *tmp; + bool found = false; + assert(mosq); + assert(message); + + if(dir == mosq_md_out){ + 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->msgs_out.mutex); + return MOSQ_ERR_PROTOCOL; + } + DL_DELETE(mosq->msgs_out.inflight, cur); + + *message = cur; + mosq->msgs_out.queue_len--; + found = true; + break; + } + } + pthread_mutex_unlock(&mosq->msgs_out.mutex); + if(found){ + return MOSQ_ERR_SUCCESS; + }else{ + return MOSQ_ERR_NOT_FOUND; + } + }else{ + 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->msgs_in.mutex); + return MOSQ_ERR_PROTOCOL; + } + DL_DELETE(mosq->msgs_in.inflight, cur); + *message = cur; + mosq->msgs_in.queue_len--; + found = true; + break; + } + } + + pthread_mutex_unlock(&mosq->msgs_in.mutex); + if(found){ + return MOSQ_ERR_SUCCESS; + }else{ + return MOSQ_ERR_NOT_FOUND; + } + } +} + +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(&mosq->msgs_out.mutex); +#endif + + DL_FOREACH(mosq->msgs_out.inflight, msg){ + switch(msg->state){ + case mosq_ms_publish_qos1: + case mosq_ms_publish_qos2: + msg->timestamp = now; + msg->dup = true; + send__publish(mosq, msg->msg.mid, msg->msg.topic, msg->msg.payloadlen, msg->msg.payload, msg->msg.qos, msg->msg.retain, msg->dup, NULL, NULL, 0); + break; + case mosq_ms_wait_for_pubrel: + msg->timestamp = now; + msg->dup = true; + send__pubrec(mosq, msg->msg.mid, 0); + break; + case mosq_ms_resend_pubrel: + case mosq_ms_wait_for_pubcomp: + msg->timestamp = now; + msg->dup = true; + send__pubrel(mosq, msg->msg.mid); + break; + default: + break; + } + } +#ifdef WITH_THREADING + pthread_mutex_unlock(&mosq->msgs_out.mutex); +#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, *tmp; + assert(mosq); + + 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->msgs_out.mutex); + return MOSQ_ERR_PROTOCOL; + } + message->state = state; + message->timestamp = mosquitto_time(); + pthread_mutex_unlock(&mosq->msgs_out.mutex); + return MOSQ_ERR_SUCCESS; + } + } + 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->send_maximum = max_inflight_messages; + + return MOSQ_ERR_SUCCESS; +} + diff --git a/libs/libmosquitto/src/messages_mosq.h b/libs/libmosquitto/src/messages_mosq.h new file mode 100644 index 0000000000..1951bfc79c --- /dev/null +++ b/libs/libmosquitto/src/messages_mosq.h @@ -0,0 +1,32 @@ +/* +Copyright (c) 2010-2019 Roger Light <roger@atchoo.org> + +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 MESSAGES_MOSQ_H +#define MESSAGES_MOSQ_H + +#include "mosquitto_internal.h" +#include "mosquitto.h" + +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); +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); + +#endif diff --git a/libs/libmosquitto/src/mosquitto.c b/libs/libmosquitto/src/mosquitto.c new file mode 100644 index 0000000000..4b5768f38b --- /dev/null +++ b/libs/libmosquitto/src/mosquitto.c @@ -0,0 +1,612 @@ +/* +Copyright (c) 2010-2019 Roger Light <roger@atchoo.org> + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#include <errno.h> +#include <signal.h> +#include <string.h> +#ifndef WIN32 +#include <sys/time.h> +#endif + +#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" + + +void mosquitto__destroy(struct mosquitto *mosq); + +int mosquitto_lib_version(int *major, int *minor, int *revision) +{ + if(major) *major = LIBMOSQUITTO_MAJOR; + if(minor) *minor = LIBMOSQUITTO_MINOR; + if(revision) *revision = LIBMOSQUITTO_REVISION; + return LIBMOSQUITTO_VERSION_NUMBER; +} + +int mosquitto_lib_init(void) +{ +#ifdef WIN32 + srand(GetTickCount64()); +#elif _POSIX_TIMERS>0 && defined(_POSIX_MONOTONIC_CLOCK) + struct timespec tp; + + clock_gettime(CLOCK_MONOTONIC, &tp); + srand(tp.tv_nsec); +#elif defined(__APPLE__) + uint64_t ticks; + + ticks = mach_absolute_time(); + srand((unsigned int)ticks); +#else + struct timeval tv; + + gettimeofday(&tv, NULL); + srand(tv.tv_sec*1000 + tv.tv_usec/1000); +#endif + + return net__init(); +} + +int mosquitto_lib_cleanup(void) +{ + net__cleanup(); + + return MOSQ_ERR_SUCCESS; +} + +struct mosquitto *mosquitto_new(const char *id, bool clean_start, void *userdata) +{ + struct mosquitto *mosq = NULL; + int rc; + + if(clean_start == false && id == NULL){ + errno = EINVAL; + return NULL; + } + +#ifndef WIN32 + signal(SIGPIPE, SIG_IGN); +#endif + + 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_start, userdata); + if(rc){ + mosquitto_destroy(mosq); + if(rc == MOSQ_ERR_INVAL){ + errno = EINVAL; + }else if(rc == MOSQ_ERR_NOMEM){ + errno = ENOMEM; + } + return NULL; + } + }else{ + errno = ENOMEM; + } + return mosq; +} + +int mosquitto_reinitialise(struct mosquitto *mosq, const char *id, bool clean_start, void *userdata) +{ + if(!mosq) return MOSQ_ERR_INVAL; + + if(clean_start == false && id == NULL){ + return MOSQ_ERR_INVAL; + } + + mosquitto__destroy(mosq); + memset(mosq, 0, sizeof(struct mosquitto)); + + if(userdata){ + mosq->userdata = userdata; + }else{ + mosq->userdata = mosq; + } + mosq->protocol = mosq_p_mqtt311; + mosq->sock = INVALID_SOCKET; + mosq->sockpairR = INVALID_SOCKET; + mosq->sockpairW = INVALID_SOCKET; + mosq->keepalive = 60; + mosq->clean_start = clean_start; + if(id){ + if(STREMPTY(id)){ + return MOSQ_ERR_INVAL; + } + if(mosquitto_validate_utf8(id, strlen(id))){ + return MOSQ_ERR_MALFORMED_UTF8; + } + mosq->id = mosquitto__strdup(id); + } + mosq->in_packet.payload = NULL; + packet__cleanup(&mosq->in_packet); + mosq->out_packet = NULL; + 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->maximum_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; + mosq->on_message = NULL; + mosq->on_subscribe = NULL; + mosq->on_unsubscribe = NULL; + mosq->host = NULL; + mosq->port = 1883; + mosq->in_callback = false; + mosq->reconnect_delay = 1; + mosq->reconnect_delay_max = 1; + mosq->reconnect_exponential_backoff = false; + mosq->threaded = mosq_ts_none; +#ifdef WITH_TLS + mosq->ssl = NULL; + mosq->ssl_ctx = NULL; + 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); + pthread_mutex_init(&mosq->log_callback_mutex, NULL); + pthread_mutex_init(&mosq->state_mutex, NULL); + 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->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 + + return MOSQ_ERR_SUCCESS; +} + + +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 + * and so the mutexes need destroying. If mosq->id is NULL, the mutexes + * haven't been initialised. */ + pthread_mutex_destroy(&mosq->callback_mutex); + pthread_mutex_destroy(&mosq->log_callback_mutex); + pthread_mutex_destroy(&mosq->state_mutex); + 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->msgs_in.mutex); + pthread_mutex_destroy(&mosq->msgs_out.mutex); + pthread_mutex_destroy(&mosq->mid_mutex); + } +#endif + if(mosq->sock != INVALID_SOCKET){ + net__socket_close(mosq); + } + message__cleanup_all(mosq); + will__clear(mosq); +#ifdef WITH_TLS + if(mosq->ssl){ + SSL_free(mosq->ssl); + } + if(mosq->ssl_ctx){ + SSL_CTX_free(mosq->ssl_ctx); + } + mosquitto__free(mosq->tls_cafile); + mosquitto__free(mosq->tls_capath); + mosquitto__free(mosq->tls_certfile); + mosquitto__free(mosq->tls_keyfile); + if(mosq->tls_pw_callback) mosq->tls_pw_callback = NULL; + mosquitto__free(mosq->tls_version); + 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); + mosq->address = NULL; + + mosquitto__free(mosq->id); + mosq->id = NULL; + + mosquitto__free(mosq->username); + mosq->username = NULL; + + mosquitto__free(mosq->password); + mosq->password = NULL; + + mosquitto__free(mosq->host); + mosq->host = NULL; + + 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; + } + + packet__cleanup(packet); + mosquitto__free(packet); + } + + packet__cleanup(&mosq->in_packet); + 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; + } +} + +void mosquitto_destroy(struct mosquitto *mosq) +{ + if(!mosq) return; + + mosquitto__destroy(mosq); + mosquitto__free(mosq); +} + +int mosquitto_socket(struct mosquitto *mosq) +{ + if(!mosq) return INVALID_SOCKET; + return mosq->sock; +} + + +bool mosquitto_want_write(struct mosquitto *mosq) +{ + bool result = false; + if(mosq->out_packet || mosq->current_out_packet){ + result = true; + } +#ifdef WITH_TLS + if(mosq->ssl){ + if (mosq->want_write) { + result = true; + }else if(mosq->want_connect){ + result = false; + } + } +#endif + return result; +} + + +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 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"; + 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; +} + + +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; + + if(!subtopic || !topics || !count) return MOSQ_ERR_INVAL; + + len = strlen(subtopic); + + for(i=0; i<len; i++){ + if(subtopic[i] == '/'){ + if(i > len-1){ + /* Separator at end of line */ + }else{ + hier_count++; + } + } + } + + (*topics) = mosquitto__calloc(hier_count, sizeof(char *)); + if(!(*topics)) return MOSQ_ERR_NOMEM; + + start = 0; + stop = 0; + hier = 0; + + for(i=0; i<len+1; i++){ + if(subtopic[i] == '/' || subtopic[i] == '\0'){ + stop = i; + if(start != stop){ + tlen = stop-start + 1; + (*topics)[hier] = mosquitto__calloc(tlen, sizeof(char)); + if(!(*topics)[hier]){ + for(j=0; j<hier; j++){ + mosquitto__free((*topics)[j]); + } + mosquitto__free((*topics)); + return MOSQ_ERR_NOMEM; + } + for(j=start; j<stop; j++){ + (*topics)[hier][j-start] = subtopic[j]; + } + } + start = i+1; + hier++; + } + } + + *count = hier_count; + + return MOSQ_ERR_SUCCESS; +} + +int mosquitto_sub_topic_tokens_free(char ***topics, int count) +{ + int i; + + if(!topics || !(*topics) || count<1) return MOSQ_ERR_INVAL; + + for(i=0; i<count; i++){ + mosquitto__free((*topics)[i]); + } + mosquitto__free(*topics); + + return MOSQ_ERR_SUCCESS; +} + diff --git a/libs/libmosquitto/src/mosquitto_internal.h b/libs/libmosquitto/src/mosquitto_internal.h new file mode 100644 index 0000000000..ff656a4375 --- /dev/null +++ b/libs/libmosquitto/src/mosquitto_internal.h @@ -0,0 +1,358 @@ +/* +Copyright (c) 2010-2019 Roger Light <roger@atchoo.org> + +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. + Tatsuzo Osawa - Add epoll. +*/ + +#ifndef MOSQUITTO_INTERNAL_H +#define MOSQUITTO_INTERNAL_H + +#include "config.h" + +#ifdef WIN32 +# include <winsock2.h> +#endif + +#ifdef WITH_TLS +# include <openssl/ssl.h> +#else +# include <time.h> +#endif +#include <stdlib.h> + +#if defined(WITH_THREADING) && !defined(WITH_BROKER) +# include <pthread.h> +#else +# include <dummypthread.h> +#endif + +#ifdef WITH_SRV +# include <ares.h> +#endif + +#ifdef WIN32 +# if _MSC_VER < 1600 + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; + typedef unsigned long long uint64_t; +# else +# include <stdint.h> +# endif +#else +# include <stdint.h> +#endif + +#include "mosquitto.h" +#include "time_mosq.h" +#ifdef WITH_BROKER +# ifdef __linux__ +# include <netdb.h> +# endif +# include "uthash.h" +struct mosquitto_client_msg; +#endif + +#ifdef WIN32 +typedef SOCKET mosq_sock_t; +#else +typedef int mosq_sock_t; +#endif + +enum mosquitto_msg_direction { + mosq_md_in = 0, + mosq_md_out = 1 +}; + +enum mosquitto_msg_state { + mosq_ms_invalid = 0, + mosq_ms_publish_qos0 = 1, + mosq_ms_publish_qos1 = 2, + mosq_ms_wait_for_puback = 3, + mosq_ms_publish_qos2 = 4, + mosq_ms_wait_for_pubrec = 5, + mosq_ms_resend_pubrel = 6, + mosq_ms_wait_for_pubrel = 7, + mosq_ms_resend_pubcomp = 8, + mosq_ms_wait_for_pubcomp = 9, + mosq_ms_send_pubrec = 10, + mosq_ms_queued = 11 +}; + +enum mosquitto_client_state { + mosq_cs_new = 0, + mosq_cs_connected = 1, + mosq_cs_disconnecting = 2, + mosq_cs_connect_async = 3, + mosq_cs_connect_pending = 4, + mosq_cs_connect_srv = 5, + mosq_cs_disconnect_ws = 6, + mosq_cs_disconnected = 7, + mosq_cs_socks5_new = 8, + mosq_cs_socks5_start = 9, + mosq_cs_socks5_request = 10, + mosq_cs_socks5_reply = 11, + mosq_cs_socks5_auth_ok = 12, + 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_mqtt5 = 5, +}; + +enum mosquitto__threaded_state { + mosq_ts_none, /* No threads in use */ + mosq_ts_self, /* Threads started by libmosquitto */ + mosq_ts_external /* Threads started by external code */ +}; + +enum mosquitto__transport { + mosq_t_invalid = 0, + mosq_t_tcp = 1, + mosq_t_ws = 2, + 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; + uint32_t remaining_mult; + uint32_t remaining_length; + uint32_t packet_length; + uint32_t to_process; + uint32_t pos; + uint16_t mid; + uint8_t command; + int8_t remaining_count; +}; + +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; + unsigned long msg_bytes; + unsigned long msg_bytes12; + int msg_count; + int msg_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 { + mosq_sock_t sock; +#ifndef WITH_BROKER + mosq_sock_t sockpairR, sockpairW; +#endif +#if defined(__GLIBC__) && defined(WITH_ADNS) + struct gaicb *adns; /* For getaddrinfo_a */ +#endif + enum mosquitto__protocol protocol; + char *address; + char *id; + char *username; + char *password; + uint16_t keepalive; + uint16_t last_mid; + enum mosquitto_client_state state; + time_t last_msg_in; + time_t next_msg_out; + time_t ping_t; + struct mosquitto__packet in_packet; + struct mosquitto__packet *current_out_packet; + struct mosquitto__packet *out_packet; + struct mosquitto_message_all *will; + struct mosquitto__alias *aliases; + struct will_delay_list *will_delay_entry; + uint32_t maximum_packet_size; + int alias_count; + uint32_t will_delay_interval; + time_t will_delay_time; +#ifdef WITH_TLS + SSL *ssl; + SSL_CTX *ssl_ctx; + char *tls_cafile; + char *tls_capath; + char *tls_certfile; + char *tls_keyfile; + int (*tls_pw_callback)(char *buf, int size, int rwflag, void *userdata); + char *tls_version; + char *tls_ciphers; + char *tls_psk; + char *tls_psk_identity; + int tls_cert_reqs; + bool tls_insecure; + bool ssl_ctx_defaults; + bool tls_ocsp_required; + char *tls_engine; + char *tls_engine_kpass_sha1; + enum mosquitto__keyform tls_keyform; + char *tls_alpn; +#endif + bool want_write; + bool want_connect; +#if defined(WITH_THREADING) && !defined(WITH_BROKER) + pthread_mutex_t callback_mutex; + pthread_mutex_t log_callback_mutex; + pthread_mutex_t msgtime_mutex; + pthread_mutex_t out_packet_mutex; + pthread_mutex_t current_out_packet_mutex; + pthread_mutex_t state_mutex; + pthread_mutex_t mid_mutex; + pthread_t thread_id; +#endif + bool clean_start; + uint32_t session_expiry_interval; + time_t session_expiry_time; +#ifdef WITH_BROKER + bool removed_from_by_id; /* True if removed from by_id hash */ + bool is_dropping; + bool is_bridge; + struct mosquitto__bridge *bridge; + struct mosquitto_msg_data msgs_in; + struct mosquitto_msg_data msgs_out; + struct mosquitto__acl_user *acl_list; + struct mosquitto__listener *listener; + struct mosquitto__packet *out_packet_last; + struct mosquitto__subhier **subs; + struct mosquitto__subshared_ref **shared_subs; + char *auth_method; + int sub_count; + int shared_sub_count; + int pollfd_index; +# 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; + char *socks5_username; + char *socks5_password; +# endif + void *userdata; + bool in_callback; + 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)(); + char *host; + int port; + char *bind_address; + unsigned int reconnect_delay; + unsigned int reconnect_delay_max; + bool reconnect_exponential_backoff; + char threaded; + struct mosquitto__packet *out_packet_last; +# ifdef WITH_SRV + ares_channel achan; +# endif + uint16_t send_maximum; + uint16_t receive_maximum; +#endif + uint8_t maximum_qos; + +#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; +#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/libmosquitto/src/mqtt_protocol.h b/libs/libmosquitto/src/mqtt_protocol.h new file mode 100644 index 0000000000..672d50b767 --- /dev/null +++ b/libs/libmosquitto/src/mqtt_protocol.h @@ -0,0 +1,158 @@ +/* +Copyright (c) 2009-2018 Roger Light <roger@atchoo.org> + +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 MQTT_PROTOCOL_H +#define MQTT_PROTOCOL_H + +#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 0x10 +#define CMD_CONNACK 0x20 +#define CMD_PUBLISH 0x30 +#define CMD_PUBACK 0x40 +#define CMD_PUBREC 0x50 +#define CMD_PUBREL 0x60 +#define CMD_PUBCOMP 0x70 +#define CMD_SUBSCRIBE 0x80 +#define CMD_SUBACK 0x90 +#define CMD_UNSUBSCRIBE 0xA0 +#define CMD_UNSUBACK 0xB0 +#define CMD_PINGREQ 0xC0 +#define CMD_PINGRESP 0xD0 +#define CMD_DISCONNECT 0xE0 +#define CMD_AUTH 0xF0 + +/* Mosquitto only: for distinguishing CONNECT and WILL properties */ +#define CMD_WILL 0x100 + +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 { + 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 { + 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 { + 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 268435455 + +#endif diff --git a/libs/libmosquitto/src/net_mosq.c b/libs/libmosquitto/src/net_mosq.c new file mode 100644 index 0000000000..f207e32aea --- /dev/null +++ b/libs/libmosquitto/src/net_mosq.c @@ -0,0 +1,1090 @@ +/* +Copyright (c) 2009-2019 Roger Light <roger@atchoo.org> + +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. +*/ + +#define _GNU_SOURCE +#include "config.h" + +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#ifndef WIN32 +#define _GNU_SOURCE +#include <netdb.h> +#include <sys/socket.h> +#include <unistd.h> +#else +#include <winsock2.h> +#include <ws2tcpip.h> +#endif + +#ifdef __ANDROID__ +#include <linux/in.h> +#include <linux/in6.h> +#include <sys/endian.h> +#endif + +#ifdef HAVE_NETINET_IN_H +# include <netinet/in.h> +#endif + +#ifdef __QNX__ +#include <net/netbyte.h> +#endif + +#ifdef WITH_TLS +#include <openssl/conf.h> +#include <openssl/engine.h> +#include <openssl/err.h> +#include <openssl/ui.h> +#include <tls_mosq.h> +#endif + +#ifdef WITH_BROKER +# include "mosquitto_broker_internal.h" +# ifdef WITH_WEBSOCKETS +# include <libwebsockets.h> +# endif +#else +# include "read_handle.h" +#endif + +#include "logging_mosq.h" +#include "memory_mosq.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; + +/* 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) +{ +#ifdef WIN32 + WSADATA wsaData; + if(WSAStartup(MAKEWORD(2,2), &wsaData) != 0){ + return MOSQ_ERR_UNKNOWN; + } +#endif + +#ifdef WITH_SRV + 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(); +# 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); + } +#endif + return MOSQ_ERR_SUCCESS; +} + +void net__cleanup(void) +{ +#ifdef WITH_TLS +# if OPENSSL_VERSION_NUMBER < 0x10100000L + CRYPTO_cleanup_all_ex_data(); + ERR_free_strings(); + ERR_remove_thread_state(NULL); + EVP_cleanup(); + +# if !defined(OPENSSL_NO_ENGINE) + ENGINE_cleanup(); +# endif +# endif + + CONF_modules_unload(1); + cleanup_ui_method(); +#endif + +#ifdef WITH_SRV + ares_library_cleanup(); +#endif + +#ifdef WIN32 + WSACleanup(); +#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; + + assert(mosq); +#ifdef WITH_TLS +#ifdef WITH_WEBSOCKETS + if(!mosq->wsi) +#endif + { + if(mosq->ssl){ + SSL_shutdown(mosq->ssl); + SSL_free(mosq->ssl); + mosq->ssl = NULL; + } + } +#endif + +#ifdef WITH_WEBSOCKETS + if(mosq->wsi) + { + if(mosq->state != mosq_cs_disconnecting){ + context__set_state(mosq, mosq_cs_disconnect_ws); + } + libwebsocket_callback_on_writable(mosq->ws_context, mosq->wsi); + }else +#endif + { + if(mosq->sock != INVALID_SOCKET){ +#ifdef WITH_BROKER + HASH_DELETE(hh_sock, db->contexts_by_sock, mosq); +#endif + rc = COMPAT_CLOSE(mosq->sock); + mosq->sock = INVALID_SOCKET; + } + } + +#ifdef WITH_BROKER + if(mosq->listener){ + mosq->listener->client_count--; + } +#endif + + return rc; +} + + +#ifdef FINAL_WITH_TLS_PSK +static unsigned int psk_client_callback(SSL *ssl, const char *hint, + char *identity, unsigned int max_identity_len, + unsigned char *psk, unsigned int max_psk_len) +{ + 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); + if (len < 0) return 0; + return len; +} +#endif + +#if defined(WITH_BROKER) && defined(__GLIBC__) && defined(WITH_ADNS) +/* Async connect, part 1 (dns lookup) */ +int net__try_connect_step1(struct mosquitto *mosq, const char *host) +{ + int s; + void *sevp = NULL; + struct addrinfo *hints; + + if(mosq->adns){ + gai_cancel(mosq->adns); + mosquitto__free((struct addrinfo *)mosq->adns->ar_request); + mosquitto__free(mosq->adns); + } + mosq->adns = mosquitto__calloc(1, sizeof(struct gaicb)); + if(!mosq->adns){ + return MOSQ_ERR_NOMEM; + } + + hints = mosquitto__calloc(1, sizeof(struct addrinfo)); + if(!hints){ + mosquitto__free(mosq->adns); + mosq->adns = NULL; + return MOSQ_ERR_NOMEM; + } + + hints->ai_family = AF_UNSPEC; + hints->ai_socktype = SOCK_STREAM; + + mosq->adns->ar_name = host; + mosq->adns->ar_request = hints; + + s = getaddrinfo_a(GAI_NOWAIT, &mosq->adns, 1, sevp); + if(s){ + errno = s; + if(mosq->adns){ + mosquitto__free((struct addrinfo *)mosq->adns->ar_request); + mosquitto__free(mosq->adns); + mosq->adns = NULL; + } + return MOSQ_ERR_EAI; + } + + return MOSQ_ERR_SUCCESS; +} + +/* Async connect part 2, the connection. */ +int net__try_connect_step2(struct mosquitto *mosq, uint16_t port, mosq_sock_t *sock) +{ + struct addrinfo *ainfo, *rp; + int rc; + + ainfo = mosq->adns->ar_result; + + for(rp = ainfo; rp != NULL; rp = rp->ai_next){ + *sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if(*sock == INVALID_SOCKET) continue; + + if(rp->ai_family == AF_INET){ + ((struct sockaddr_in *)rp->ai_addr)->sin_port = htons(port); + }else if(rp->ai_family == AF_INET6){ + ((struct sockaddr_in6 *)rp->ai_addr)->sin6_port = htons(port); + }else{ + COMPAT_CLOSE(*sock); + *sock = INVALID_SOCKET; + continue; + } + + /* Set non-blocking */ + if(net__socket_nonblock(sock)){ + continue; + } + + rc = connect(*sock, rp->ai_addr, rp->ai_addrlen); +#ifdef WIN32 + errno = WSAGetLastError(); +#endif + if(rc == 0 || errno == EINPROGRESS || errno == COMPAT_EWOULDBLOCK){ + if(rc < 0 && (errno == EINPROGRESS || errno == COMPAT_EWOULDBLOCK)){ + rc = MOSQ_ERR_CONN_PENDING; + } + + /* Set non-blocking */ + if(net__socket_nonblock(sock)){ + continue; + } + break; + } + + COMPAT_CLOSE(*sock); + *sock = INVALID_SOCKET; + } + freeaddrinfo(mosq->adns->ar_result); + mosq->adns->ar_result = NULL; + + mosquitto__free((struct addrinfo *)mosq->adns->ar_request); + mosquitto__free(mosq->adns); + mosq->adns = NULL; + + if(!rp){ + return MOSQ_ERR_ERRNO; + } + + return rc; +} + +#endif + + +int net__try_connect(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 + + *sock = INVALID_SOCKET; + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + s = getaddrinfo(host, NULL, &hints, &ainfo); + if(s){ + errno = s; + return MOSQ_ERR_EAI; + } + + if(bind_address){ + s = getaddrinfo(bind_address, NULL, &hints, &ainfo_bind); + if(s){ + freeaddrinfo(ainfo); + errno = s; + return MOSQ_ERR_EAI; + } + } + + for(rp = ainfo; rp != NULL; rp = rp->ai_next){ + *sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if(*sock == INVALID_SOCKET) continue; + + if(rp->ai_family == AF_INET){ + ((struct sockaddr_in *)rp->ai_addr)->sin_port = htons(port); + }else if(rp->ai_family == AF_INET6){ + ((struct sockaddr_in6 *)rp->ai_addr)->sin6_port = htons(port); + }else{ + COMPAT_CLOSE(*sock); + *sock = INVALID_SOCKET; + continue; + } + + if(bind_address){ + for(rp_bind = ainfo_bind; rp_bind != NULL; rp_bind = rp_bind->ai_next){ + if(bind(*sock, rp_bind->ai_addr, rp_bind->ai_addrlen) == 0){ + break; + } + } + if(!rp_bind){ + COMPAT_CLOSE(*sock); + *sock = INVALID_SOCKET; + continue; + } + } + + if(!blocking){ + /* Set non-blocking */ + if(net__socket_nonblock(sock)){ + continue; + } + } + + rc = connect(*sock, rp->ai_addr, rp->ai_addrlen); +#ifdef WIN32 + errno = WSAGetLastError(); +#endif + if(rc == 0 || errno == EINPROGRESS || errno == COMPAT_EWOULDBLOCK){ + if(rc < 0 && (errno == EINPROGRESS || errno == COMPAT_EWOULDBLOCK)){ + rc = MOSQ_ERR_CONN_PENDING; + } + + if(blocking){ + /* Set non-blocking */ + if(net__socket_nonblock(sock)){ + continue; + } + } + break; + } + + COMPAT_CLOSE(*sock); + *sock = INVALID_SOCKET; + } + freeaddrinfo(ainfo); + if(bind_address){ + freeaddrinfo(ainfo_bind); + } + if(!rp){ + return MOSQ_ERR_ERRNO; + } + return rc; +} + + +#ifdef WITH_TLS +void net__print_ssl_error(struct mosquitto *mosq) +{ + char ebuf[256]; + unsigned long e; + + e = ERR_get_error(); + while(e){ + log__printf(mosq, MOSQ_LOG_ERR, "OpenSSL Error: %s", ERR_error_string(e, ebuf)); + e = ERR_get_error(); + } +} + + +int net__socket_connect_tls(struct mosquitto *mosq) +{ + int ret, err; + + ERR_clear_error(); + long res; + 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); + if (err == SSL_ERROR_SYSCALL) { + mosq->want_connect = true; + return MOSQ_ERR_SUCCESS; + } + if(err == SSL_ERROR_WANT_READ){ + mosq->want_connect = true; + /* We always try to read anyway */ + }else if(err == SSL_ERROR_WANT_WRITE){ + mosq->want_write = true; + mosq->want_connect = true; + }else{ + net__print_ssl_error(mosq); + + COMPAT_CLOSE(mosq->sock); + mosq->sock = INVALID_SOCKET; + net__print_ssl_error(mosq); + return MOSQ_ERR_TLS; + } + }else{ + mosq->want_connect = false; + } + return MOSQ_ERR_SUCCESS; +} +#endif + + +#ifdef WITH_TLS +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(mosq->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."); + return MOSQ_ERR_INVAL; + } + } + + /* 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->ssl_ctx){ +#if OPENSSL_VERSION_NUMBER < 0x10100000L + mosq->ssl_ctx = SSL_CTX_new(SSLv23_client_method()); +#else + mosq->ssl_ctx = SSL_CTX_new(TLS_client_method()); +#endif + + 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; + } + } + + if(!mosq->tls_version){ + 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); + }else if(!strcmp(mosq->tls_version, "tlsv1.2")){ + SSL_CTX_set_options(mosq->ssl_ctx, SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_3); + }else if(!strcmp(mosq->tls_version, "tlsv1.1")){ + SSL_CTX_set_options(mosq->ssl_ctx, SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_2 | SSL_OP_NO_TLSv1_3); +#else + }else if(!strcmp(mosq->tls_version, "tlsv1.2")){ + 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 | SSL_OP_NO_TLSv1_2); +#endif + }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; + } + + /* 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 + 1); + } + +#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); + COMPAT_CLOSE(mosq->sock); + return MOSQ_ERR_TLS; + } + if(!ENGINE_init(engine)){ + log__printf(mosq, MOSQ_LOG_ERR, "Failed engine initialisation\n"); + ENGINE_free(engine); + COMPAT_CLOSE(mosq->sock); + 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); +#if !defined(OPENSSL_NO_ENGINE) + ENGINE_FINISH(engine); +#endif + COMPAT_CLOSE(mosq->sock); + mosq->sock = INVALID_SOCKET; + 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 +#if !defined(OPENSSL_NO_ENGINE) + ENGINE_FINISH(engine); +#endif + COMPAT_CLOSE(mosq->sock); + mosq->sock = INVALID_SOCKET; + net__print_ssl_error(mosq); + return MOSQ_ERR_TLS; + } + if(mosq->tls_cert_reqs == 0){ + SSL_CTX_set_verify(mosq->ssl_ctx, SSL_VERIFY_NONE, NULL); + }else{ + SSL_CTX_set_verify(mosq->ssl_ctx, SSL_VERIFY_PEER, mosquitto__server_certificate_verify); + } + + if(mosq->tls_pw_callback){ + SSL_CTX_set_default_passwd_cb(mosq->ssl_ctx, mosq->tls_pw_callback); + SSL_CTX_set_default_passwd_cb_userdata(mosq->ssl_ctx, mosq); + } + + if(mosq->tls_certfile){ + ret = SSL_CTX_use_certificate_chain_file(mosq->ssl_ctx, mosq->tls_certfile); + if(ret != 1){ +#ifdef WITH_BROKER + log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load client certificate, check bridge_certfile \"%s\".", mosq->tls_certfile); +#else + log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load client certificate \"%s\".", mosq->tls_certfile); +#endif +#if !defined(OPENSSL_NO_ENGINE) + ENGINE_FINISH(engine); +#endif + COMPAT_CLOSE(mosq->sock); + mosq->sock = INVALID_SOCKET; + net__print_ssl_error(mosq); + return MOSQ_ERR_TLS; + } + } + if(mosq->tls_keyfile){ + if(mosq->tls_keyform == mosq_k_engine){ +#if !defined(OPENSSL_NO_ENGINE) + UI_METHOD *ui_method = net__get_ui_method(); + if(mosq->tls_engine_kpass_sha1){ + if(!ENGINE_ctrl_cmd(engine, ENGINE_SECRET_MODE, ENGINE_SECRET_MODE_SHA, NULL, NULL, 0)){ + log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to set engine secret mode sha1"); + ENGINE_FINISH(engine); + COMPAT_CLOSE(mosq->sock); + 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); + COMPAT_CLOSE(mosq->sock); + net__print_ssl_error(mosq); + return MOSQ_ERR_TLS; + } + ui_method = NULL; + } + EVP_PKEY *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); + COMPAT_CLOSE(mosq->sock); + 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); + COMPAT_CLOSE(mosq->sock); + 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); +#else + log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load client key file \"%s\".", mosq->tls_keyfile); +#endif +#if !defined(OPENSSL_NO_ENGINE) + ENGINE_FINISH(engine); +#endif + COMPAT_CLOSE(mosq->sock); + mosq->sock = INVALID_SOCKET; + net__print_ssl_error(mosq); + 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."); +#if !defined(OPENSSL_NO_ENGINE) + ENGINE_FINISH(engine); +#endif + COMPAT_CLOSE(mosq->sock); + mosq->sock = INVALID_SOCKET; + net__print_ssl_error(mosq); + return MOSQ_ERR_TLS; + } + } +#ifdef FINAL_WITH_TLS_PSK + }else if(mosq->tls_psk){ + SSL_CTX_set_psk_client_callback(mosq->ssl_ctx, psk_client_callback); +#endif + } + } + + return MOSQ_ERR_SUCCESS; +} +#endif + + +int net__socket_connect_step3(struct mosquitto *mosq, const char *host) +{ +#ifdef WITH_TLS + BIO *bio; + + int rc = net__init_ssl_ctx(mosq); + if(rc) return rc; + + if(mosq->ssl_ctx){ + if(mosq->ssl){ + SSL_free(mosq->ssl); + } + mosq->ssl = SSL_new(mosq->ssl_ctx); + if(!mosq->ssl){ + COMPAT_CLOSE(mosq->sock); + mosq->sock = INVALID_SOCKET; + net__print_ssl_error(mosq); + return MOSQ_ERR_TLS; + } + + 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__print_ssl_error(mosq); + return MOSQ_ERR_TLS; + } + SSL_set_bio(mosq->ssl, bio, bio); + + /* + * required for the SNI resolving + */ + if(SSL_set_tlsext_host_name(mosq->ssl, host) != 1) { + COMPAT_CLOSE(mosq->sock); + mosq->sock = INVALID_SOCKET; + return MOSQ_ERR_TLS; + } + + if(net__socket_connect_tls(mosq)){ + return MOSQ_ERR_TLS; + } + + } +#endif + return MOSQ_ERR_SUCCESS; +} + +/* 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, rc2; + + if(!mosq || !host || !port) return MOSQ_ERR_INVAL; + + rc = net__try_connect(host, port, &sock, bind_address, blocking); + if(rc > 0) return rc; + + mosq->sock = sock; + +#if defined(WITH_SOCKS) && !defined(WITH_BROKER) + if(!mosq->socks5_host) +#endif + { + rc2 = net__socket_connect_step3(mosq, host); + if(rc2) return rc2; + } + + return MOSQ_ERR_SUCCESS; +} + + +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); + 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 + } + return (ssize_t )ret; + }else{ + /* Call normal read/recv */ + +#endif + +#ifndef WIN32 + return read(mosq->sock, buf, count); +#else + return recv(mosq->sock, buf, count, 0); +#endif + +#ifdef WITH_TLS + } +#endif +} + +ssize_t net__write(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){ + mosq->want_write = false; + ERR_clear_error(); + ret = SSL_write(mosq->ssl, buf, 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 + } + return (ssize_t )ret; + }else{ + /* Call normal write/send */ +#endif + +#ifndef WIN32 + return write(mosq->sock, buf, count); +#else + return send(mosq->sock, buf, count, 0); +#endif + +#ifdef WITH_TLS + } +#endif +} + + +int net__socket_nonblock(mosq_sock_t *sock) +{ +#ifndef WIN32 + int opt; + /* Set non-blocking */ + opt = fcntl(*sock, F_GETFL, 0); + if(opt == -1){ + COMPAT_CLOSE(*sock); + *sock = INVALID_SOCKET; + return MOSQ_ERR_ERRNO; + } + 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); + return MOSQ_ERR_ERRNO; + } +#else + unsigned long opt = 1; + if(ioctlsocket(*sock, FIONBIO, &opt)){ + COMPAT_CLOSE(*sock); + return MOSQ_ERR_ERRNO; + } +#endif + return MOSQ_ERR_SUCCESS; +} + + +#ifndef WITH_BROKER +int net__socketpair(mosq_sock_t *pairR, mosq_sock_t *pairW) +{ +#ifdef WIN32 + int family[2] = {AF_INET, AF_INET6}; + int i; + struct sockaddr_storage ss; + struct sockaddr_in *sa = (struct sockaddr_in *)&ss; + struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)&ss; + socklen_t ss_len; + mosq_sock_t spR, spW; + + mosq_sock_t listensock; + + *pairR = INVALID_SOCKET; + *pairW = INVALID_SOCKET; + + for(i=0; i<2; i++){ + memset(&ss, 0, sizeof(ss)); + if(family[i] == AF_INET){ + sa->sin_family = family[i]; + sa->sin_addr.s_addr = htonl(INADDR_LOOPBACK); + sa->sin_port = 0; + ss_len = sizeof(struct sockaddr_in); + }else if(family[i] == AF_INET6){ + sa6->sin6_family = family[i]; + sa6->sin6_addr = in6addr_loopback; + sa6->sin6_port = 0; + ss_len = sizeof(struct sockaddr_in6); + }else{ + return MOSQ_ERR_INVAL; + } + + listensock = socket(family[i], SOCK_STREAM, IPPROTO_TCP); + if(listensock == -1){ + continue; + } + + if(bind(listensock, (struct sockaddr *)&ss, ss_len) == -1){ + COMPAT_CLOSE(listensock); + continue; + } + + if(listen(listensock, 1) == -1){ + COMPAT_CLOSE(listensock); + continue; + } + memset(&ss, 0, sizeof(ss)); + ss_len = sizeof(ss); + if(getsockname(listensock, (struct sockaddr *)&ss, &ss_len) < 0){ + COMPAT_CLOSE(listensock); + continue; + } + + if(family[i] == AF_INET){ + sa->sin_family = family[i]; + sa->sin_addr.s_addr = htonl(INADDR_LOOPBACK); + ss_len = sizeof(struct sockaddr_in); + }else if(family[i] == AF_INET6){ + sa6->sin6_family = family[i]; + sa6->sin6_addr = in6addr_loopback; + ss_len = sizeof(struct sockaddr_in6); + } + + spR = socket(family[i], SOCK_STREAM, IPPROTO_TCP); + if(spR == -1){ + COMPAT_CLOSE(listensock); + continue; + } + if(net__socket_nonblock(&spR)){ + COMPAT_CLOSE(listensock); + continue; + } + if(connect(spR, (struct sockaddr *)&ss, ss_len) < 0){ +#ifdef WIN32 + errno = WSAGetLastError(); +#endif + if(errno != EINPROGRESS && errno != COMPAT_EWOULDBLOCK){ + COMPAT_CLOSE(spR); + COMPAT_CLOSE(listensock); + continue; + } + } + spW = accept(listensock, NULL, 0); + if(spW == -1){ +#ifdef WIN32 + errno = WSAGetLastError(); +#endif + if(errno != EINPROGRESS && errno != COMPAT_EWOULDBLOCK){ + COMPAT_CLOSE(spR); + COMPAT_CLOSE(listensock); + continue; + } + } + + if(net__socket_nonblock(&spW)){ + COMPAT_CLOSE(spR); + COMPAT_CLOSE(listensock); + continue; + } + COMPAT_CLOSE(listensock); + + *pairR = spR; + *pairW = spW; + return MOSQ_ERR_SUCCESS; + } + return MOSQ_ERR_UNKNOWN; +#else + int sv[2]; + + if(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1){ + return MOSQ_ERR_ERRNO; + } + if(net__socket_nonblock(&sv[0])){ + COMPAT_CLOSE(sv[1]); + return MOSQ_ERR_ERRNO; + } + if(net__socket_nonblock(&sv[1])){ + COMPAT_CLOSE(sv[0]); + return MOSQ_ERR_ERRNO; + } + *pairR = sv[0]; + *pairW = sv[1]; + return MOSQ_ERR_SUCCESS; +#endif +} +#endif diff --git a/libs/libmosquitto/src/net_mosq.h b/libs/libmosquitto/src/net_mosq.h new file mode 100644 index 0000000000..b843ebe388 --- /dev/null +++ b/libs/libmosquitto/src/net_mosq.h @@ -0,0 +1,82 @@ +/* +Copyright (c) 2010-2019 Roger Light <roger@atchoo.org> + +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 NET_MOSQ_H +#define NET_MOSQ_H + +#ifndef WIN32 +#include <unistd.h> +#else +#include <winsock2.h> +typedef SSIZE_T ssize_t; +#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_EWOULDBLOCK WSAEWOULDBLOCK +#else +# define COMPAT_CLOSE(a) close(a) +# define COMPAT_ECONNRESET ECONNRESET +# define COMPAT_EWOULDBLOCK EWOULDBLOCK +#endif + +/* For when not using winsock libraries. */ +#ifndef INVALID_SOCKET +#define INVALID_SOCKET -1 +#endif + +/* Macros for accessing the MSB and LSB of a uint16_t */ +#define MOSQ_MSB(A) (uint8_t)((A & 0xFF00) >> 8) +#define MOSQ_LSB(A) (uint8_t)(A & 0x00FF) + +int net__init(void); +void net__cleanup(void); + +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(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); +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); + +#ifdef WITH_TLS +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/libmosquitto/src/net_mosq_ocsp.c b/libs/libmosquitto/src/net_mosq_ocsp.c new file mode 100644 index 0000000000..32f2a3e321 --- /dev/null +++ b/libs/libmosquitto/src/net_mosq_ocsp.c @@ -0,0 +1,159 @@ +/* +Copyright (c) 2009-2014 Roger Light <roger@atchoo.org> +Copyright (c) 2017 Bayerische Motoren Werke Aktiengesellschaft (BMW AG), Dr. Lars Voelker <lars.voelker@bmw.de> + +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: + Dr. Lars Voelker, BMW AG +*/ + +/* +COPYRIGHT AND PERMISSION NOTICE of curl on which the ocsp code is based: + +Copyright (c) 1996 - 2016, Daniel Stenberg, <daniel@haxx.se>, 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. +*/ + +#ifdef WITH_TLS +#include <logging_mosq.h> +#include <mosquitto_internal.h> +#include <net_mosq.h> + +#include <openssl/safestack.h> +#include <openssl/tls1.h> +#include <openssl/ssl.h> +#include <openssl/ocsp.h> + +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 = 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/libmosquitto/src/options.c b/libs/libmosquitto/src/options.c new file mode 100644 index 0000000000..7bab736628 --- /dev/null +++ b/libs/libmosquitto/src/options.c @@ -0,0 +1,486 @@ +/* +Copyright (c) 2010-2019 Roger Light <roger@atchoo.org> + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#ifndef WIN32 +# include <strings.h> +#endif + +#include <string.h> + +#ifdef WITH_TLS +# ifdef WIN32 +# include <winsock2.h> +# endif +# include <openssl/engine.h> +#endif + +#include "mosquitto.h" +#include "mosquitto_internal.h" +#include "memory_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; + + if(properties){ + rc = mosquitto_property_check_all(CMD_WILL, properties); + if(rc) return rc; + } + + return will__set(mosq, topic, payloadlen, payload, qos, retain, properties); +} + + +int mosquitto_will_clear(struct mosquitto *mosq) +{ + if(!mosq) return MOSQ_ERR_INVAL; + return will__clear(mosq); +} + + +int mosquitto_username_pw_set(struct mosquitto *mosq, const char *username, const char *password) +{ + 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; + + mosquitto__free(mosq->password); + mosq->password = NULL; + + if(username){ + if(mosquitto_validate_utf8(username, strlen(username))){ + 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; + } + } + return MOSQ_ERR_SUCCESS; +} + + +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; + +} + + +int mosquitto_tls_set(struct mosquitto *mosq, const char *cafile, const char *capath, const char *certfile, const char *keyfile, int (*pw_callback)(char *buf, int size, int rwflag, void *userdata)) +{ +#ifdef WITH_TLS + FILE *fptr; + + if(!mosq || (!cafile && !capath) || (certfile && !keyfile) || (!certfile && keyfile)) return MOSQ_ERR_INVAL; + + mosquitto__free(mosq->tls_cafile); + mosq->tls_cafile = NULL; + if(cafile){ + fptr = mosquitto__fopen(cafile, "rt", false); + if(fptr){ + fclose(fptr); + }else{ + return MOSQ_ERR_INVAL; + } + mosq->tls_cafile = mosquitto__strdup(cafile); + + if(!mosq->tls_cafile){ + return MOSQ_ERR_NOMEM; + } + } + + mosquitto__free(mosq->tls_capath); + mosq->tls_capath = NULL; + if(capath){ + mosq->tls_capath = mosquitto__strdup(capath); + if(!mosq->tls_capath){ + return MOSQ_ERR_NOMEM; + } + } + + mosquitto__free(mosq->tls_certfile); + mosq->tls_certfile = NULL; + if(certfile){ + fptr = mosquitto__fopen(certfile, "rt", false); + if(fptr){ + fclose(fptr); + }else{ + mosquitto__free(mosq->tls_cafile); + mosq->tls_cafile = NULL; + + mosquitto__free(mosq->tls_capath); + mosq->tls_capath = NULL; + return MOSQ_ERR_INVAL; + } + mosq->tls_certfile = mosquitto__strdup(certfile); + if(!mosq->tls_certfile){ + return MOSQ_ERR_NOMEM; + } + } + + mosquitto__free(mosq->tls_keyfile); + mosq->tls_keyfile = NULL; + if(keyfile){ + fptr = mosquitto__fopen(keyfile, "rt", false); + if(fptr){ + fclose(fptr); + }else{ + mosquitto__free(mosq->tls_cafile); + mosq->tls_cafile = NULL; + + mosquitto__free(mosq->tls_capath); + mosq->tls_capath = NULL; + + mosquitto__free(mosq->tls_certfile); + mosq->tls_certfile = NULL; + return MOSQ_ERR_INVAL; + } + mosq->tls_keyfile = mosquitto__strdup(keyfile); + if(!mosq->tls_keyfile){ + return MOSQ_ERR_NOMEM; + } + } + + mosq->tls_pw_callback = pw_callback; + + + return MOSQ_ERR_SUCCESS; +#else + return MOSQ_ERR_NOT_SUPPORTED; + +#endif +} + + +int mosquitto_tls_opts_set(struct mosquitto *mosq, int cert_reqs, const char *tls_version, const char *ciphers) +{ +#ifdef WITH_TLS + if(!mosq) return MOSQ_ERR_INVAL; + + mosq->tls_cert_reqs = cert_reqs; + if(tls_version){ + 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; + }else{ + return MOSQ_ERR_INVAL; + } + }else{ + mosq->tls_version = mosquitto__strdup("tlsv1.2"); + if(!mosq->tls_version) return MOSQ_ERR_NOMEM; + } + if(ciphers){ + mosq->tls_ciphers = mosquitto__strdup(ciphers); + if(!mosq->tls_ciphers) return MOSQ_ERR_NOMEM; + }else{ + mosq->tls_ciphers = NULL; + } + + + return MOSQ_ERR_SUCCESS; +#else + return MOSQ_ERR_NOT_SUPPORTED; + +#endif +} + + +int mosquitto_tls_insecure_set(struct mosquitto *mosq, bool value) +{ +#ifdef WITH_TLS + if(!mosq) return MOSQ_ERR_INVAL; + mosq->tls_insecure = value; + return MOSQ_ERR_SUCCESS; +#else + 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: +#ifdef WITH_TLS +# if !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; +#endif +#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; + } +#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; + + default: + return MOSQ_ERR_INVAL; + } + + 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 + if(!mosq || !psk || !identity) return MOSQ_ERR_INVAL; + + /* Check for hex only digits */ + if(strspn(psk, "0123456789abcdefABCDEF") < strlen(psk)){ + return MOSQ_ERR_INVAL; + } + mosq->tls_psk = mosquitto__strdup(psk); + if(!mosq->tls_psk) return MOSQ_ERR_NOMEM; + + mosq->tls_psk_identity = mosquitto__strdup(identity); + if(!mosq->tls_psk_identity){ + mosquitto__free(mosq->tls_psk); + return MOSQ_ERR_NOMEM; + } + if(ciphers){ + mosq->tls_ciphers = mosquitto__strdup(ciphers); + if(!mosq->tls_ciphers) return MOSQ_ERR_NOMEM; + }else{ + mosq->tls_ciphers = NULL; + } + + return MOSQ_ERR_SUCCESS; +#else + return MOSQ_ERR_NOT_SUPPORTED; +#endif +} + + +int mosquitto_opts_set(struct mosquitto *mosq, enum mosq_opt_t option, void *value) +{ + int ival; + + if(!mosq || !value) return MOSQ_ERR_INVAL; + + switch(option){ + case MOSQ_OPT_PROTOCOL_VERSION: + ival = *((int *)value); + return mosquitto_int_option(mosq, option, ival); + 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); +#else + CRYPTO_add(&(mosq->ssl_ctx)->references, 1, CRYPTO_LOCK_SSL_CTX); +#endif + } + break; +#else + return MOSQ_ERR_NOT_SUPPORTED; +#endif + 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(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_RECEIVE_MAXIMUM: + if(value < 0 || value > 65535){ + return MOSQ_ERR_INVAL; + } + mosq->receive_maximum = value; + break; + + case MOSQ_OPT_SEND_MAXIMUM: + if(value < 0 || value > 65535){ + return MOSQ_ERR_INVAL; + } + mosq->send_maximum = 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 + return MOSQ_ERR_NOT_SUPPORTED; +#endif + + case MOSQ_OPT_TLS_OCSP_REQUIRED: +#ifdef WITH_TLS + mosq->tls_ocsp_required = (bool)value; +#else + return MOSQ_ERR_NOT_SUPPORTED; +#endif + 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 || !value) return MOSQ_ERR_INVAL; + + switch(option){ + 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); +#else + CRYPTO_add(&(mosq->ssl_ctx)->references, 1, CRYPTO_LOCK_SSL_CTX); +#endif + } + break; +#else + return MOSQ_ERR_NOT_SUPPORTED; +#endif + default: + return MOSQ_ERR_INVAL; + } + return MOSQ_ERR_SUCCESS; +} + + +void mosquitto_user_data_set(struct mosquitto *mosq, void *userdata) +{ + if(mosq){ + mosq->userdata = userdata; + } +} + +void *mosquitto_userdata(struct mosquitto *mosq) +{ + return mosq->userdata; +} diff --git a/libs/libmosquitto/src/packet_datatypes.c b/libs/libmosquitto/src/packet_datatypes.c new file mode 100644 index 0000000000..335cb9f02d --- /dev/null +++ b/libs/libmosquitto/src/packet_datatypes.c @@ -0,0 +1,271 @@ +/* +Copyright (c) 2009-2018 Roger Light <roger@atchoo.org> + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#include <assert.h> +#include <errno.h> +#include <string.h> + +#ifdef WITH_BROKER +# include "mosquitto_broker_internal.h" +# ifdef WITH_WEBSOCKETS +# include <libwebsockets.h> +# 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_PROTOCOL; + + *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_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_binary(struct mosquitto__packet *packet, uint8_t **data, int *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_PROTOCOL; + + *data = mosquitto__malloc(slen+1); + 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, int *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 = -1; + 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_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)); +} + + +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_PROTOCOL; + + 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, (word & 0xFF000000) >> 24); + packet__write_byte(packet, (word & 0x00FF0000) >> 16); + packet__write_byte(packet, (word & 0x0000FF00) >> 8); + packet__write_byte(packet, (word & 0x000000FF)); +} + + +int packet__read_varint(struct mosquitto__packet *packet, int32_t *word, int8_t *bytes) +{ + int i; + uint8_t byte; + int remaining_mult = 1; + int32_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_PROTOCOL; + }else{ + *word = lword; + if(bytes) (*bytes) = lbytes; + return MOSQ_ERR_SUCCESS; + } + } + }else{ + return MOSQ_ERR_PROTOCOL; + } + } + return MOSQ_ERR_PROTOCOL; +} + + +int packet__write_varint(struct mosquitto__packet *packet, int32_t word) +{ + uint8_t byte; + int count = 0; + + do{ + byte = 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_PROTOCOL; + } + return MOSQ_ERR_SUCCESS; +} + + +int packet__varint_bytes(int32_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/libmosquitto/src/packet_mosq.c b/libs/libmosquitto/src/packet_mosq.c new file mode 100644 index 0000000000..0f7af7426c --- /dev/null +++ b/libs/libmosquitto/src/packet_mosq.c @@ -0,0 +1,456 @@ +/* +Copyright (c) 2009-2019 Roger Light <roger@atchoo.org> + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#include <assert.h> +#include <errno.h> +#include <string.h> + +#ifdef WITH_BROKER +# include "mosquitto_broker_internal.h" +# ifdef WITH_WEBSOCKETS +# include <libwebsockets.h> +# 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" +# include "send_mosq.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__alloc(struct mosquitto__packet *packet) +{ + uint8_t remaining_bytes[5], byte; + uint32_t remaining_length; + int i; + + assert(packet); + + remaining_length = packet->remaining_length; + packet->payload = NULL; + packet->remaining_count = 0; + do{ + byte = remaining_length % 128; + remaining_length = remaining_length / 128; + /* If there are more digits to encode, set the top bit of this digit */ + if(remaining_length > 0){ + byte = byte | 0x80; + } + remaining_bytes[packet->remaining_count] = byte; + 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; +#ifdef WITH_WEBSOCKETS + packet->payload = mosquitto__malloc(sizeof(uint8_t)*packet->packet_length + LWS_SEND_BUFFER_PRE_PADDING + LWS_SEND_BUFFER_POST_PADDING); +#else + packet->payload = mosquitto__malloc(sizeof(uint8_t)*packet->packet_length); +#endif + if(!packet->payload) return MOSQ_ERR_NOMEM; + + packet->payload[0] = packet->command; + for(i=0; i<packet->remaining_count; i++){ + packet->payload[i+1] = remaining_bytes[i]; + } + packet->pos = 1 + packet->remaining_count; + + return MOSQ_ERR_SUCCESS; +} + +void packet__cleanup(struct mosquitto__packet *packet) +{ + if(!packet) return; + + /* Free data and reset values */ + packet->command = 0; + packet->remaining_count = 0; + packet->remaining_mult = 1; + packet->remaining_length = 0; + mosquitto__free(packet->payload); + packet->payload = NULL; + packet->to_process = 0; + packet->pos = 0; +} + +int packet__queue(struct mosquitto *mosq, struct mosquitto__packet *packet) +{ +#ifndef WITH_BROKER + char sockpair_data = 0; +#endif + assert(mosq); + assert(packet); + + packet->pos = 0; + packet->to_process = packet->packet_length; + + packet->next = NULL; + pthread_mutex_lock(&mosq->out_packet_mutex); + if(mosq->out_packet){ + mosq->out_packet_last->next = packet; + }else{ + mosq->out_packet = packet; + } + mosq->out_packet_last = packet; + 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); + return MOSQ_ERR_SUCCESS; + }else{ + return packet__write(mosq); + } +# else + return packet__write(mosq); +# endif +#else + + /* Write a single byte to sockpairW (connected to sockpairR) to break out + * of select() if in threaded mode. */ + if(mosq->sockpairW != INVALID_SOCKET){ +#ifndef WIN32 + if(write(mosq->sockpairW, &sockpair_data, 1)){ + } +#else + send(mosq->sockpairW, &sockpair_data, 1, 0); +#endif + } + + if(mosq->in_callback == false && mosq->threaded == mosq_ts_none){ + return packet__write(mosq); + }else{ + return MOSQ_ERR_SUCCESS; + } +#endif +} + + +int packet__check_oversize(struct mosquitto *mosq, uint32_t remaining_length) +{ + uint32_t len; + + if(mosq->maximum_packet_size == 0) return MOSQ_ERR_SUCCESS; + + len = remaining_length + packet__varint_bytes(remaining_length); + if(len > mosq->maximum_packet_size){ + return MOSQ_ERR_OVERSIZE_PACKET; + }else{ + return MOSQ_ERR_SUCCESS; + } +} + + +int packet__write(struct mosquitto *mosq) +{ + ssize_t write_length; + struct mosquitto__packet *packet; + + if(!mosq) return MOSQ_ERR_INVAL; + if(mosq->sock == INVALID_SOCKET) return MOSQ_ERR_NO_CONN; + + 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; + if(!mosq->out_packet){ + mosq->out_packet_last = NULL; + } + } + pthread_mutex_unlock(&mosq->out_packet_mutex); + +#if defined(WITH_TLS) && !defined(WITH_BROKER) + if((mosq->state == mosq_cs_connect_pending) || mosq->want_connect){ +#else + if(mosq->state == mosq_cs_connect_pending){ +#endif + pthread_mutex_unlock(&mosq->current_out_packet_mutex); + return MOSQ_ERR_SUCCESS; + } + + while(mosq->current_out_packet){ + packet = mosq->current_out_packet; + + while(packet->to_process > 0){ + 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; + }else{ +#ifdef WIN32 + errno = WSAGetLastError(); +#endif + if(errno == EAGAIN || errno == COMPAT_EWOULDBLOCK){ + pthread_mutex_unlock(&mosq->current_out_packet_mutex); + return MOSQ_ERR_SUCCESS; + }else{ + pthread_mutex_unlock(&mosq->current_out_packet_mutex); + switch(errno){ + case COMPAT_ECONNRESET: + return MOSQ_ERR_CONN_LOST; + default: + return MOSQ_ERR_ERRNO; + } + } + } + } + + G_MSGS_SENT_INC(1); + if(((packet->command)&0xF6) == CMD_PUBLISH){ + G_PUB_MSGS_SENT_INC(1); +#ifndef WITH_BROKER + pthread_mutex_lock(&mosq->callback_mutex); + if(mosq->on_publish){ + /* This is a QoS=0 message */ + mosq->in_callback = true; + mosq->on_publish(mosq, mosq->userdata, packet->mid); + mosq->in_callback = false; + } + if(mosq->on_publish_v5){ + /* This is a QoS=0 message */ + mosq->in_callback = true; + mosq->on_publish_v5(mosq, mosq->userdata, packet->mid, 0, NULL); + mosq->in_callback = false; + } + pthread_mutex_unlock(&mosq->callback_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 + } + + /* 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); + } + 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; + + if(!mosq){ + return MOSQ_ERR_INVAL; + } + if(mosq->sock == INVALID_SOCKET){ + return MOSQ_ERR_NO_CONN; + } + if(mosq->state == mosq_cs_connect_pending){ + return MOSQ_ERR_SUCCESS; + } + + /* This gets called if pselect() indicates that there is network data + * available - ie. at least one byte. What we do depends on what data we + * already have. + * If we've not got a command, attempt to read one and save it. This should + * always work because it's only a single byte. + * Then try to read the remaining length. This may fail because it is may + * be more than one byte - will need to save data pending next read if it + * does fail. + * Then try to read the remaining payload, where 'payload' here means the + * combined variable header and actual payload. This is the most likely to + * fail due to longer length, so save current data and current position. + * After all data is read, send to mosquitto__handle_packet() to deal with. + * Finally, free the memory and reset everything to starting conditions. + */ + if(!mosq->in_packet.command){ + read_length = net__read(mosq, &byte, 1); + if(read_length == 1){ + mosq->in_packet.command = byte; +#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) != CMD_CONNECT){ + return MOSQ_ERR_PROTOCOL; + } +#endif + }else{ + if(read_length == 0){ + return MOSQ_ERR_CONN_LOST; /* EOF */ + } +#ifdef WIN32 + errno = WSAGetLastError(); +#endif + if(errno == EAGAIN || errno == COMPAT_EWOULDBLOCK){ + return MOSQ_ERR_SUCCESS; + }else{ + switch(errno){ + case COMPAT_ECONNRESET: + return MOSQ_ERR_CONN_LOST; + default: + return MOSQ_ERR_ERRNO; + } + } + } + } + /* remaining_count is the number of bytes that the remaining_length + * parameter occupied in this incoming packet. We don't use it here as such + * (it is used when allocating an outgoing packet), but we must be able to + * determine whether all of the remaining_length parameter has been read. + * remaining_count has three states here: + * 0 means that we haven't read any remaining_length bytes + * <0 means we have read some remaining_length bytes but haven't finished + * >0 means we have finished reading the remaining_length bytes. + */ + if(mosq->in_packet.remaining_count <= 0){ + do{ + read_length = net__read(mosq, &byte, 1); + if(read_length == 1){ + mosq->in_packet.remaining_count--; + /* 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; + } + + 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 */ + } +#ifdef WIN32 + errno = WSAGetLastError(); +#endif + if(errno == EAGAIN || errno == COMPAT_EWOULDBLOCK){ + return MOSQ_ERR_SUCCESS; + }else{ + switch(errno){ + case COMPAT_ECONNRESET: + return MOSQ_ERR_CONN_LOST; + default: + return MOSQ_ERR_ERRNO; + } + } + } + }while((byte & 128) != 0); + /* We have finished reading remaining_length, so make remaining_count + * positive. */ + mosq->in_packet.remaining_count *= -1; + +#ifdef WITH_BROKER + if(db->config->max_packet_size > 0 && mosq->in_packet.remaining_length+1 > db->config->max_packet_size){ + log__printf(NULL, MOSQ_LOG_INFO, "Client %s sent too large packet %d, disconnecting.", mosq->id, mosq->in_packet.remaining_length+1); + 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; + } + mosq->in_packet.to_process = mosq->in_packet.remaining_length; + } + } + while(mosq->in_packet.to_process>0){ + 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; + }else{ +#ifdef WIN32 + errno = WSAGetLastError(); +#endif + if(errno == EAGAIN || errno == COMPAT_EWOULDBLOCK){ + if(mosq->in_packet.to_process > 1000){ + /* Update last_msg_in time if more than 1000 bytes left to + * receive. Helps when receiving large messages. + * 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. */ + pthread_mutex_lock(&mosq->msgtime_mutex); + mosq->last_msg_in = mosquitto_time(); + pthread_mutex_unlock(&mosq->msgtime_mutex); + } + return MOSQ_ERR_SUCCESS; + }else{ + switch(errno){ + case COMPAT_ECONNRESET: + return MOSQ_ERR_CONN_LOST; + default: + return MOSQ_ERR_ERRNO; + } + } + } + } + + /* All data for this packet is read. */ + mosq->in_packet.pos = 0; +#ifdef WITH_BROKER + G_MSGS_RECEIVED_INC(1); + 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 + + /* Free data and reset values */ + packet__cleanup(&mosq->in_packet); + + pthread_mutex_lock(&mosq->msgtime_mutex); + mosq->last_msg_in = mosquitto_time(); + pthread_mutex_unlock(&mosq->msgtime_mutex); + return rc; +} diff --git a/libs/libmosquitto/src/packet_mosq.h b/libs/libmosquitto/src/packet_mosq.h new file mode 100644 index 0000000000..5967f09406 --- /dev/null +++ b/libs/libmosquitto/src/packet_mosq.h @@ -0,0 +1,56 @@ +/* +Copyright (c) 2010-2019 Roger Light <roger@atchoo.org> + +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 PACKET_MOSQ_H +#define PACKET_MOSQ_H + +#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); +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_binary(struct mosquitto__packet *packet, uint8_t **data, int *length); +int packet__read_string(struct mosquitto__packet *packet, char **str, int *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, int32_t *word, int8_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, int32_t word); + +int packet__varint_bytes(int32_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/libmosquitto/src/property_mosq.c b/libs/libmosquitto/src/property_mosq.c new file mode 100644 index 0000000000..76158e584d --- /dev/null +++ b/libs/libmosquitto/src/property_mosq.c @@ -0,0 +1,1181 @@ +/* +Copyright (c) 2018 Roger Light <roger@atchoo.org> + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#include <assert.h> +#include <errno.h> +#include <string.h> + +#include "logging_mosq.h" +#include "memory_mosq.h" +#include "mqtt_protocol.h" +#include "packet_mosq.h" +#include "property_mosq.h" + + +int property__read(struct mosquitto__packet *packet, int32_t *len, mosquitto_property *property) +{ + int rc; + int32_t property_identifier; + uint8_t byte; + int8_t byte_count; + uint16_t uint16; + uint32_t uint32; + int32_t varint; + char *str1, *str2; + int 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 = 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; + int32_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; +} + + +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 3 + 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 3 + property->value.s.len; /* 1 + 2 bytes (len) + X bytes (string) */ + + /* string pair */ + case MQTT_PROP_USER_PROPERTY: + return 5 + property->value.s.len + property->name.len; /* 1 + 2*(2 bytes (len) + X bytes (string))*/ + + default: + return 0; + } + return 0; +} + + +int property__get_length_all(const mosquitto_property *property) +{ + const mosquitto_property *p; + int len = 0; + + p = property; + while(p){ + len += property__get_length(p); + p = p->next; + } + return len; +} + + +int property__write(struct mosquitto__packet *packet, const mosquitto_property *property) +{ + int rc; + + rc = packet__write_varint(packet, 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; +} + + +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; + + if(!proplist) return MOSQ_ERR_INVAL; + if(value){ + if(mosquitto_validate_utf8(value, strlen(value))) 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 && strlen(value)){ + prop->value.s.v = mosquitto__strdup(value); + if(!prop->value.s.v){ + mosquitto__free(prop); + return MOSQ_ERR_NOMEM; + } + prop->value.s.len = strlen(value); + } + + 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; + + if(!proplist) return MOSQ_ERR_INVAL; + if(identifier != MQTT_PROP_USER_PROPERTY) return MOSQ_ERR_INVAL; + if(name){ + if(mosquitto_validate_utf8(name, strlen(name))) return MOSQ_ERR_MALFORMED_UTF8; + } + if(value){ + if(mosquitto_validate_utf8(value, strlen(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 && strlen(name)){ + prop->name.v = mosquitto__strdup(name); + if(!prop->name.v){ + mosquitto__free(prop); + return MOSQ_ERR_NOMEM; + } + prop->name.len = strlen(name); + } + + if(value && strlen(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 = 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_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 */ + tail = p->next; + while(tail){ + if(p->identifier == tail->identifier + && p->identifier != MQTT_PROP_USER_PROPERTY){ + + return MOSQ_ERR_DUPLICATE_PROPERTY; + } + tail = tail->next; + } + + p = p->next; + } + + return MOSQ_ERR_SUCCESS; +} + +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; +} + + +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; + + 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 = malloc(*len); + 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, 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; + + 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, p->name.len+1); + if(!(*name)) return NULL; + memcpy(*name, p->name.v, p->name.len); + } + + if(value){ + *value = calloc(1, p->value.s.len+1); + if(!(*value)){ + if(name) free(*name); + 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/libmosquitto/src/property_mosq.h b/libs/libmosquitto/src/property_mosq.h new file mode 100644 index 0000000000..d965d8a3e6 --- /dev/null +++ b/libs/libmosquitto/src/property_mosq.h @@ -0,0 +1,50 @@ +/* +Copyright (c) 2018 Roger Light <roger@atchoo.org> + +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 PROPERTY_MOSQ_H +#define PROPERTY_MOSQ_H + +#include "mosquitto_internal.h" +#include "mosquitto.h" + +struct mqtt__string { + char *v; + int 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); + +int property__get_length(const mosquitto_property *property); +int property__get_length_all(const mosquitto_property *property); + +#endif diff --git a/libs/libmosquitto/src/read_handle.c b/libs/libmosquitto/src/read_handle.c new file mode 100644 index 0000000000..19c0c35b1e --- /dev/null +++ b/libs/libmosquitto/src/read_handle.c @@ -0,0 +1,70 @@ +/* +Copyright (c) 2009-2019 Roger Light <roger@atchoo.org> + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#include <assert.h> +#include <stdio.h> +#include <string.h> + +#include "mosquitto.h" +#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 "read_handle.h" +#include "send_mosq.h" +#include "time_mosq.h" +#include "util_mosq.h" + +int handle__packet(struct mosquitto *mosq) +{ + assert(mosq); + + switch((mosq->in_packet.command)&0xF0){ + case CMD_PINGREQ: + return handle__pingreq(mosq); + case CMD_PINGRESP: + return handle__pingresp(mosq); + case CMD_PUBACK: + return handle__pubackcomp(mosq, "PUBACK"); + case CMD_PUBCOMP: + return handle__pubackcomp(mosq, "PUBCOMP"); + case CMD_PUBLISH: + return handle__publish(mosq); + case CMD_PUBREC: + return handle__pubrec(NULL, mosq); + case CMD_PUBREL: + return handle__pubrel(NULL, mosq); + case CMD_CONNACK: + return handle__connack(mosq); + case CMD_SUBACK: + return handle__suback(mosq); + 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); + return MOSQ_ERR_PROTOCOL; + } +} + diff --git a/libs/libmosquitto/src/read_handle.h b/libs/libmosquitto/src/read_handle.h new file mode 100644 index 0000000000..2a6936d817 --- /dev/null +++ b/libs/libmosquitto/src/read_handle.h @@ -0,0 +1,40 @@ +/* +Copyright (c) 2010-2019 Roger Light <roger@atchoo.org> + +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 READ_HANDLE_H +#define READ_HANDLE_H + +#include "mosquitto.h" +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); +#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_db *db, struct mosquitto *mosq); +int handle__pubrel(struct mosquitto_db *db, struct mosquitto *mosq); +int handle__suback(struct mosquitto *mosq); +int handle__unsuback(struct mosquitto *mosq); + + +#endif diff --git a/libs/libmosquitto/src/send_connect.c b/libs/libmosquitto/src/send_connect.c new file mode 100644 index 0000000000..210f125a80 --- /dev/null +++ b/libs/libmosquitto/src/send_connect.c @@ -0,0 +1,204 @@ +/* +Copyright (c) 2009-2019 Roger Light <roger@atchoo.org> + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#include <assert.h> +#include <string.h> + +#ifdef WITH_BROKER +# include "mosquitto_broker_internal.h" +#endif + +#include "logging_mosq.h" +#include "memory_mosq.h" +#include "mosquitto.h" +#include "mosquitto_internal.h" +#include "mqtt_protocol.h" +#include "packet_mosq.h" +#include "property_mosq.h" + +int send__connect(struct mosquitto *mosq, uint16_t keepalive, bool clean_session, const mosquitto_property *properties) +{ + struct mosquitto__packet *packet = NULL; + int payloadlen; + uint8_t will = 0; + uint8_t byte; + int rc; + uint8_t version; + char *clientid, *username, *password; + int headerlen; + int proplen = 0, will_proplen, varbytes; + mosquitto_property *local_props = NULL; + uint16_t receive_maximum; + + assert(mosq); + + if(mosq->protocol == mosq_p_mqtt31 && !mosq->id) return MOSQ_ERR_PROTOCOL; + +#if defined(WITH_BROKER) && defined(WITH_BRIDGE) + if(mosq->bridge){ + clientid = mosq->bridge->remote_clientid; + username = mosq->bridge->remote_username; + password = mosq->bridge->remote_password; + }else{ + clientid = mosq->id; + username = mosq->username; + password = mosq->password; + } +#else + clientid = mosq->id; + username = mosq->username; + password = mosq->password; +#endif + + 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; + } + + packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet)); + if(!packet) return MOSQ_ERR_NOMEM; + + if(clientid){ + payloadlen = 2+strlen(clientid); + }else{ + payloadlen = 2; + } + if(mosq->will){ + will = 1; + assert(mosq->will->msg.topic); + + payloadlen += 2+strlen(mosq->will->msg.topic) + 2+mosq->will->msg.payloadlen; + if(mosq->protocol == mosq_p_mqtt5){ + will_proplen = property__get_length_all(mosq->will->properties); + varbytes = packet__varint_bytes(will_proplen); + payloadlen += will_proplen + varbytes; + } + } + + /* After this check we can be sure that the username and password are + * always valid for the current protocol, so there is no need to check + * username before checking password. */ + if(mosq->protocol == mosq_p_mqtt31 || mosq->protocol == mosq_p_mqtt311){ + if(password != NULL && username == NULL){ + return MOSQ_ERR_INVAL; + } + } + + if(username){ + payloadlen += 2+strlen(username); + } + if(password){ + payloadlen += 2+strlen(password); + } + + packet->command = CMD_CONNECT; + packet->remaining_length = headerlen + payloadlen; + rc = packet__alloc(packet); + if(rc){ + mosquitto__free(packet); + return rc; + } + + /* Variable header */ + if(version == MQTT_PROTOCOL_V31){ + packet__write_string(packet, PROTOCOL_NAME_v31, strlen(PROTOCOL_NAME_v31)); + }else{ + packet__write_string(packet, PROTOCOL_NAME, strlen(PROTOCOL_NAME)); + } +#if defined(WITH_BROKER) && defined(WITH_BRIDGE) + if(mosq->bridge && mosq->bridge->try_private && mosq->bridge->try_private_accepted){ + version |= 0x80; + }else{ + } +#endif + packet__write_byte(packet, version); + byte = (clean_session&0x1)<<1; + if(will){ + byte = byte | ((mosq->will->msg.retain&0x1)<<5) | ((mosq->will->msg.qos&0x3)<<3) | ((will&0x1)<<2); + } + if(username){ + byte = byte | 0x1<<7; + } + 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); + } + + /* Payload */ + if(clientid){ + packet__write_string(packet, clientid, strlen(clientid)); + }else{ + packet__write_uint16(packet, 0); + } + if(will){ + 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, strlen(mosq->will->msg.topic)); + packet__write_string(packet, (const char *)mosq->will->msg.payload, mosq->will->msg.payloadlen); + } + + if(username){ + packet__write_string(packet, username, strlen(username)); + } + if(password){ + packet__write_string(packet, password, strlen(password)); + } + + mosq->keepalive = keepalive; +#ifdef WITH_BROKER +# ifdef WITH_BRIDGE + log__printf(mosq, MOSQ_LOG_DEBUG, "Bridge %s sending CONNECT", clientid); +# endif +#else + log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending CONNECT", clientid); +#endif + return packet__queue(mosq, packet); +} + diff --git a/libs/libmosquitto/src/send_disconnect.c b/libs/libmosquitto/src/send_disconnect.c new file mode 100644 index 0000000000..3598988755 --- /dev/null +++ b/libs/libmosquitto/src/send_disconnect.c @@ -0,0 +1,85 @@ +/* +Copyright (c) 2009-2019 Roger Light <roger@atchoo.org> + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#include <assert.h> + +#ifdef WITH_BROKER +# include "mosquitto_broker_internal.h" +#endif + +#include "mosquitto.h" +#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 "send_mosq.h" + + +int send__disconnect(struct mosquitto *mosq, uint8_t reason_code, const mosquitto_property *properties) +{ + struct mosquitto__packet *packet = NULL; + int rc; + int proplen, varbytes; + + assert(mosq); +#ifdef WITH_BROKER +# ifdef WITH_BRIDGE + if(mosq->bridge){ + log__printf(mosq, MOSQ_LOG_DEBUG, "Bridge %s sending DISCONNECT", mosq->id); + }else +# else + { + log__printf(mosq, MOSQ_LOG_DEBUG, "Sending DISCONNECT to %s (rc%d)", mosq->id, reason_code); + } +# endif +#else + log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending DISCONNECT", mosq->id); +#endif + 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){ + proplen = property__get_length_all(properties); + varbytes = packet__varint_bytes(proplen); + packet->remaining_length += proplen + varbytes; + } + }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/libmosquitto/src/send_mosq.c b/libs/libmosquitto/src/send_mosq.c new file mode 100644 index 0000000000..27bc1fda17 --- /dev/null +++ b/libs/libmosquitto/src/send_mosq.c @@ -0,0 +1,188 @@ +/* +Copyright (c) 2009-2019 Roger Light <roger@atchoo.org> + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#include <assert.h> +#include <stdio.h> +#include <string.h> + +#ifdef WITH_BROKER +# include "mosquitto_broker_internal.h" +# include "sys_tree.h" +#else +# define G_PUB_BYTES_SENT_INC(A) +#endif + +#include "mosquitto.h" +#include "mosquitto_internal.h" +#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 "send_mosq.h" +#include "time_mosq.h" +#include "util_mosq.h" + +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); +#else + log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending PINGREQ", mosq->id); +#endif + rc = send__simple_command(mosq, CMD_PINGREQ); + if(rc == MOSQ_ERR_SUCCESS){ + mosq->ping_t = mosquitto_time(); + } + return rc; +} + +int send__pingresp(struct mosquitto *mosq) +{ +#ifdef WITH_BROKER + log__printf(NULL, MOSQ_LOG_DEBUG, "Sending PINGRESP to %s", mosq->id); +#else + log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending PINGRESP", mosq->id); +#endif + return send__simple_command(mosq, CMD_PINGRESP); +} + +int send__puback(struct mosquitto *mosq, uint16_t mid, uint8_t reason_code) +{ +#ifdef WITH_BROKER + log__printf(NULL, MOSQ_LOG_DEBUG, "Sending PUBACK to %s (m%d, rc%d)", mosq->id, mid, reason_code); +#else + log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending PUBACK (m%d, rc%d)", mosq->id, mid, reason_code); +#endif + 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, NULL); +} + +int send__pubcomp(struct mosquitto *mosq, uint16_t mid) +{ +#ifdef WITH_BROKER + log__printf(NULL, MOSQ_LOG_DEBUG, "Sending PUBCOMP to %s (m%d)", mosq->id, mid); +#else + log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending PUBCOMP (m%d)", mosq->id, mid); +#endif + 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, NULL); +} + + +int send__pubrec(struct mosquitto *mosq, uint16_t mid, uint8_t reason_code) +{ +#ifdef WITH_BROKER + log__printf(NULL, MOSQ_LOG_DEBUG, "Sending PUBREC to %s (m%d, rc%d)", mosq->id, mid, reason_code); +#else + log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending PUBREC (m%d, rc%d)", mosq->id, mid, reason_code); +#endif + 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, NULL); +} + +int send__pubrel(struct mosquitto *mosq, uint16_t mid) +{ +#ifdef WITH_BROKER + log__printf(NULL, MOSQ_LOG_DEBUG, "Sending PUBREL to %s (m%d)", mosq->id, mid); +#else + log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending PUBREL (m%d)", mosq->id, mid); +#endif + /* We don't use Reason String or User Property yet. */ + return send__command_with_mid(mosq, CMD_PUBREL|2, mid, false, 0, NULL); +} + +/* For PUBACK, PUBCOMP, PUBREC, and PUBREL */ +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; + int proplen, varbytes; + + assert(mosq); + packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet)); + if(!packet) return MOSQ_ERR_NOMEM; + + packet->command = command; + if(dup){ + packet->command |= 8; + } + packet->remaining_length = 2; + + if(mosq->protocol == mosq_p_mqtt5){ + if(reason_code != 0 || properties){ + packet->remaining_length += 1; + } + + if(properties){ + proplen = property__get_length_all(properties); + varbytes = packet__varint_bytes(proplen); + packet->remaining_length += varbytes + proplen; + } + } + + rc = packet__alloc(packet); + if(rc){ + mosquitto__free(packet); + return rc; + } + + 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); +} + +/* For DISCONNECT, PINGREQ and PINGRESP */ +int send__simple_command(struct mosquitto *mosq, uint8_t command) +{ + struct mosquitto__packet *packet = NULL; + int rc; + + assert(mosq); + packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet)); + if(!packet) return MOSQ_ERR_NOMEM; + + packet->command = command; + packet->remaining_length = 0; + + rc = packet__alloc(packet); + if(rc){ + mosquitto__free(packet); + return rc; + } + + return packet__queue(mosq, packet); +} + diff --git a/libs/libmosquitto/src/send_mosq.h b/libs/libmosquitto/src/send_mosq.h new file mode 100644 index 0000000000..4b76f2f952 --- /dev/null +++ b/libs/libmosquitto/src/send_mosq.h @@ -0,0 +1,38 @@ +/* +Copyright (c) 2010-2019 Roger Light <roger@atchoo.org> + +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 SEND_MOSQ_H +#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, 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, int 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, 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, uint8_t reason_code); +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, 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); +int send__pubrel(struct mosquitto *mosq, uint16_t mid); +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/libmosquitto/src/send_publish.c b/libs/libmosquitto/src/send_publish.c new file mode 100644 index 0000000000..f07c539d40 --- /dev/null +++ b/libs/libmosquitto/src/send_publish.c @@ -0,0 +1,215 @@ +/* +Copyright (c) 2009-2019 Roger Light <roger@atchoo.org> + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#include <assert.h> +#include <string.h> + +#ifdef WITH_BROKER +# include "mosquitto_broker_internal.h" +# include "sys_tree.h" +#else +# define G_PUB_BYTES_SENT_INC(A) +#endif + +#include "mosquitto.h" +#include "mosquitto_internal.h" +#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 "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, const mosquitto_property *cmsg_props, const mosquitto_property *store_props, uint32_t expiry_interval) +{ +#ifdef WITH_BROKER + size_t len; +#ifdef WITH_BRIDGE + int i; + struct mosquitto__bridge_topic *cur_topic; + bool match; + int rc; + char *mapped_topic = NULL; + char *topic_temp = NULL; +#endif +#endif + assert(mosq); + +#if defined(WITH_BROKER) && defined(WITH_WEBSOCKETS) + if(mosq->sock == INVALID_SOCKET && !mosq->wsi) return MOSQ_ERR_NO_CONN; +#else + if(mosq->sock == INVALID_SOCKET) return MOSQ_ERR_NO_CONN; +#endif + +#ifdef WITH_BROKER + if(mosq->listener && mosq->listener->mount_point){ + len = strlen(mosq->listener->mount_point); + if(len < strlen(topic)){ + topic += len; + }else{ + /* Invalid topic string. Should never happen, but silently swallow the message anyway. */ + return MOSQ_ERR_SUCCESS; + } + } +#ifdef WITH_BRIDGE + if(mosq->bridge && mosq->bridge->topics && mosq->bridge->topic_remapping){ + for(i=0; i<mosq->bridge->topic_count; i++){ + cur_topic = &mosq->bridge->topics[i]; + if((cur_topic->direction == bd_both || cur_topic->direction == bd_out) + && (cur_topic->remote_prefix || cur_topic->local_prefix)){ + /* Topic mapping required on this topic if the message matches */ + + rc = mosquitto_topic_matches_sub(cur_topic->local_topic, topic, &match); + if(rc){ + return rc; + } + if(match){ + mapped_topic = mosquitto__strdup(topic); + if(!mapped_topic) return MOSQ_ERR_NOMEM; + if(cur_topic->local_prefix){ + /* This prefix needs removing. */ + if(!strncmp(cur_topic->local_prefix, mapped_topic, strlen(cur_topic->local_prefix))){ + topic_temp = mosquitto__strdup(mapped_topic+strlen(cur_topic->local_prefix)); + mosquitto__free(mapped_topic); + if(!topic_temp){ + return MOSQ_ERR_NOMEM; + } + mapped_topic = topic_temp; + } + } + + if(cur_topic->remote_prefix){ + /* This prefix needs adding. */ + len = strlen(mapped_topic) + strlen(cur_topic->remote_prefix)+1; + topic_temp = mosquitto__malloc(len+1); + if(!topic_temp){ + mosquitto__free(mapped_topic); + return MOSQ_ERR_NOMEM; + } + snprintf(topic_temp, len, "%s%s", cur_topic->remote_prefix, mapped_topic); + topic_temp[len] = '\0'; + 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); + G_PUB_BYTES_SENT_INC(payloadlen); + 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; + } + } + } + } +#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); + 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); +#endif + + 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, const mosquitto_property *cmsg_props, const mosquitto_property *store_props, uint32_t expiry_interval) +{ + struct mosquitto__packet *packet = NULL; + int packetlen; + int proplen = 0, varbytes; + int rc; + mosquitto_property expiry_prop; + + assert(mosq); + + if(topic){ + packetlen = 2+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)", 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 = CMD_PUBLISH | ((dup&0x1)<<3) | (qos<<1) | retain; + packet->remaining_length = packetlen; + rc = packet__alloc(packet); + if(rc){ + mosquitto__free(packet); + return rc; + } + /* Variable header (topic string) */ + if(topic){ + packet__write_string(packet, topic, 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); + } + + return packet__queue(mosq, packet); +} diff --git a/libs/libmosquitto/src/send_subscribe.c b/libs/libmosquitto/src/send_subscribe.c new file mode 100644 index 0000000000..1f2760f985 --- /dev/null +++ b/libs/libmosquitto/src/send_subscribe.c @@ -0,0 +1,96 @@ +/* +Copyright (c) 2009-2019 Roger Light <roger@atchoo.org> + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#include <assert.h> +#include <string.h> + +#ifdef WITH_BROKER +# include "mosquitto_broker_internal.h" +#endif + +#include "mosquitto.h" +#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 "util_mosq.h" + + +int send__subscribe(struct mosquitto *mosq, int *mid, int topic_count, const char **topic, int topic_qos, const mosquitto_property *properties) +{ + struct mosquitto__packet *packet = NULL; + uint32_t packetlen; + uint16_t local_mid; + int rc; + int i; + int proplen, varbytes; + + assert(mosq); + assert(topic); + + packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet)); + if(!packet) return MOSQ_ERR_NOMEM; + + packetlen = 2; + if(mosq->protocol == mosq_p_mqtt5){ + proplen = property__get_length_all(properties); + varbytes = packet__varint_bytes(proplen); + packetlen += proplen + varbytes; + } + for(i=0; i<topic_count; i++){ + packetlen += 2+strlen(topic[i]) + 1; + } + + packet->command = CMD_SUBSCRIBE | (1<<1); + packet->remaining_length = packetlen; + rc = packet__alloc(packet); + if(rc){ + mosquitto__free(packet); + return rc; + } + + /* Variable header */ + local_mid = mosquitto__mid_generate(mosq); + 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 */ + for(i=0; i<topic_count; i++){ + packet__write_string(packet, topic[i], strlen(topic[i])); + packet__write_byte(packet, topic_qos); + } + +#ifdef WITH_BROKER +# ifdef WITH_BRIDGE + log__printf(mosq, MOSQ_LOG_DEBUG, "Bridge %s sending SUBSCRIBE (Mid: %d, Topic: %s, QoS: %d, Options: 0x%02x)", mosq->id, local_mid, topic[0], topic_qos&0x03, topic_qos&0xFC); +# endif +#else + for(i=0; i<topic_count; i++){ + log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending SUBSCRIBE (Mid: %d, Topic: %s, QoS: %d, Options: 0x%02x)", mosq->id, local_mid, topic[i], topic_qos&0x03, topic_qos&0xFC); + } +#endif + + return packet__queue(mosq, packet); +} + diff --git a/libs/libmosquitto/src/send_unsubscribe.c b/libs/libmosquitto/src/send_unsubscribe.c new file mode 100644 index 0000000000..e7317151a5 --- /dev/null +++ b/libs/libmosquitto/src/send_unsubscribe.c @@ -0,0 +1,99 @@ +/* +Copyright (c) 2009-2019 Roger Light <roger@atchoo.org> + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#include <assert.h> +#include <string.h> + +#ifdef WITH_BROKER +# include "mosquitto_broker_internal.h" +#endif + +#include "mosquitto.h" +#include "logging_mosq.h" +#include "memory_mosq.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, 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 proplen, varbytes; + int i; + + assert(mosq); + assert(topic); + + packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet)); + if(!packet) return MOSQ_ERR_NOMEM; + + packetlen = 2; + + for(i=0; i<topic_count; i++){ + packetlen += 2+strlen(topic[i]); + } + if(mosq->protocol == mosq_p_mqtt5){ + proplen = property__get_length_all(properties); + varbytes = packet__varint_bytes(proplen); + packetlen += proplen + varbytes; + } + + packet->command = CMD_UNSUBSCRIBE | (1<<1); + packet->remaining_length = packetlen; + rc = packet__alloc(packet); + if(rc){ + mosquitto__free(packet); + return rc; + } + + /* Variable header */ + local_mid = mosquitto__mid_generate(mosq); + 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 */ + for(i=0; i<topic_count; i++){ + packet__write_string(packet, topic[i], strlen(topic[i])); + } + +#ifdef WITH_BROKER +# ifdef WITH_BRIDGE + for(i=0; i<topic_count; i++){ + log__printf(mosq, MOSQ_LOG_DEBUG, "Bridge %s sending UNSUBSCRIBE (Mid: %d, Topic: %s)", mosq->id, local_mid, topic[i]); + } +# endif +#else + for(i=0; i<topic_count; i++){ + log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending UNSUBSCRIBE (Mid: %d, Topic: %s)", mosq->id, local_mid, topic[i]); + } +#endif + return packet__queue(mosq, packet); +} + diff --git a/libs/libmosquitto/src/socks_mosq.c b/libs/libmosquitto/src/socks_mosq.c new file mode 100644 index 0000000000..0df58a4b65 --- /dev/null +++ b/libs/libmosquitto/src/socks_mosq.c @@ -0,0 +1,460 @@ +/* +Copyright (c) 2014-2019 Roger Light <roger@atchoo.org> + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#include <errno.h> +#include <string.h> +#include <limits.h> +#ifdef WIN32 +# include <ws2tcpip.h> +#elif __QNX__ +# include <sys/socket.h> +# include <netinet/in.h> +#else +# include <arpa/inet.h> +#endif +#ifdef __FreeBSD__ +# include <sys/socket.h> +# include <netinet/in.h> +#endif + +#include "mosquitto_internal.h" +#include "memory_mosq.h" +#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 + +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; + + mosquitto__free(mosq->socks5_host); + mosq->socks5_host = NULL; + + mosq->socks5_host = mosquitto__strdup(host); + if(!mosq->socks5_host){ + return MOSQ_ERR_NOMEM; + } + + mosq->socks5_port = port; + + mosquitto__free(mosq->socks5_username); + mosq->socks5_username = NULL; + + mosquitto__free(mosq->socks5_password); + mosq->socks5_password = NULL; + + if(username){ + mosq->socks5_username = mosquitto__strdup(username); + if(!mosq->socks5_username){ + return MOSQ_ERR_NOMEM; + } + + if(password){ + mosq->socks5_password = mosquitto__strdup(password); + if(!mosq->socks5_password){ + mosquitto__free(mosq->socks5_username); + return MOSQ_ERR_NOMEM; + } + } + } + + return MOSQ_ERR_SUCCESS; +#else + return MOSQ_ERR_NOT_SUPPORTED; +#endif +} + +#ifdef WITH_SOCKS +int socks5__send(struct mosquitto *mosq) +{ + struct mosquitto__packet *packet; + int slen; + int ulen, plen; + + struct in_addr addr_ipv4; + struct in6_addr addr_ipv6; + int ipv4_pton_result; + int ipv6_pton_result; + + if(mosq->state == mosq_cs_socks5_new){ + packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet)); + if(!packet) return MOSQ_ERR_NOMEM; + + if(mosq->socks5_username){ + packet->packet_length = 4; + }else{ + packet->packet_length = 3; + } + packet->payload = mosquitto__malloc(sizeof(uint8_t)*packet->packet_length); + + packet->payload[0] = 0x05; + if(mosq->socks5_username){ + packet->payload[1] = 2; + packet->payload[2] = SOCKS_AUTH_NONE; + packet->payload[3] = SOCKS_AUTH_USERPASS; + }else{ + packet->payload[1] = 1; + packet->payload[2] = SOCKS_AUTH_NONE; + } + + pthread_mutex_lock(&mosq->state_mutex); + mosq->state = mosq_cs_socks5_start; + pthread_mutex_unlock(&mosq->state_mutex); + + mosq->in_packet.pos = 0; + mosq->in_packet.packet_length = 2; + mosq->in_packet.to_process = 2; + mosq->in_packet.payload = mosquitto__malloc(sizeof(uint8_t)*2); + if(!mosq->in_packet.payload){ + mosquitto__free(packet->payload); + mosquitto__free(packet); + return MOSQ_ERR_NOMEM; + } + + return packet__queue(mosq, packet); + }else if(mosq->state == mosq_cs_socks5_auth_ok){ + packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet)); + if(!packet) return MOSQ_ERR_NOMEM; + + ipv4_pton_result = inet_pton(AF_INET, mosq->host, &addr_ipv4); + ipv6_pton_result = inet_pton(AF_INET6, mosq->host, &addr_ipv6); + + if(ipv4_pton_result == 1){ + packet->packet_length = 10; + packet->payload = mosquitto__malloc(sizeof(uint8_t)*packet->packet_length); + if(!packet->payload){ + mosquitto__free(packet); + return MOSQ_ERR_NOMEM; + } + packet->payload[3] = SOCKS_ATYPE_IP_V4; + memcpy(&(packet->payload[4]), (const void*)&addr_ipv4, 4); + packet->payload[4+4] = MOSQ_MSB(mosq->port); + packet->payload[4+4+1] = MOSQ_LSB(mosq->port); + + }else if(ipv6_pton_result == 1){ + packet->packet_length = 22; + packet->payload = mosquitto__malloc(sizeof(uint8_t)*packet->packet_length); + if(!packet->payload){ + mosquitto__free(packet); + return MOSQ_ERR_NOMEM; + } + packet->payload[3] = SOCKS_ATYPE_IP_V6; + memcpy(&(packet->payload[4]), (const void*)&addr_ipv6, 16); + packet->payload[4+16] = MOSQ_MSB(mosq->port); + packet->payload[4+16+1] = MOSQ_LSB(mosq->port); + + }else{ + slen = strlen(mosq->host); + if(slen > UCHAR_MAX){ + return MOSQ_ERR_NOMEM; + } + packet->packet_length = 7 + slen; + packet->payload = mosquitto__malloc(sizeof(uint8_t)*packet->packet_length); + if(!packet->payload){ + mosquitto__free(packet); + return MOSQ_ERR_NOMEM; + } + packet->payload[3] = SOCKS_ATYPE_DOMAINNAME; + packet->payload[4] = (uint8_t)slen; + memcpy(&(packet->payload[5]), mosq->host, slen); + packet->payload[5+slen] = MOSQ_MSB(mosq->port); + packet->payload[6+slen] = MOSQ_LSB(mosq->port); + } + packet->payload[0] = 0x05; + 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); + + mosq->in_packet.pos = 0; + mosq->in_packet.packet_length = 5; + mosq->in_packet.to_process = 5; + mosq->in_packet.payload = mosquitto__malloc(sizeof(uint8_t)*5); + if(!mosq->in_packet.payload){ + mosquitto__free(packet->payload); + mosquitto__free(packet); + return MOSQ_ERR_NOMEM; + } + + return packet__queue(mosq, packet); + }else if(mosq->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; + packet->payload = mosquitto__malloc(sizeof(uint8_t)*packet->packet_length); + + + packet->payload[0] = 0x01; + packet->payload[1] = ulen; + memcpy(&(packet->payload[2]), mosq->socks5_username, ulen); + 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); + + mosq->in_packet.pos = 0; + mosq->in_packet.packet_length = 2; + mosq->in_packet.to_process = 2; + mosq->in_packet.payload = mosquitto__malloc(sizeof(uint8_t)*2); + if(!mosq->in_packet.payload){ + mosquitto__free(packet->payload); + mosquitto__free(packet); + return MOSQ_ERR_NOMEM; + } + + return packet__queue(mosq, packet); + } + return MOSQ_ERR_SUCCESS; +} + +int socks5__read(struct mosquitto *mosq) +{ + ssize_t len; + uint8_t *payload; + uint8_t i; + + if(mosq->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; + }else{ +#ifdef WIN32 + errno = WSAGetLastError(); +#endif + if(errno == EAGAIN || errno == COMPAT_EWOULDBLOCK){ + return MOSQ_ERR_SUCCESS; + }else{ + packet__cleanup(&mosq->in_packet); + switch(errno){ + case 0: + return MOSQ_ERR_PROXY; + case COMPAT_ECONNRESET: + return MOSQ_ERR_CONN_LOST; + default: + return MOSQ_ERR_ERRNO; + } + } + } + } + if(mosq->in_packet.payload[0] != 5){ + packet__cleanup(&mosq->in_packet); + return MOSQ_ERR_PROXY; + } + switch(mosq->in_packet.payload[1]){ + case SOCKS_AUTH_NONE: + packet__cleanup(&mosq->in_packet); + mosq->state = 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; + return socks5__send(mosq); + default: + packet__cleanup(&mosq->in_packet); + return MOSQ_ERR_AUTH; + } + }else if(mosq->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; + }else{ +#ifdef WIN32 + errno = WSAGetLastError(); +#endif + if(errno == EAGAIN || errno == COMPAT_EWOULDBLOCK){ + return MOSQ_ERR_SUCCESS; + }else{ + packet__cleanup(&mosq->in_packet); + switch(errno){ + case 0: + return MOSQ_ERR_PROXY; + case COMPAT_ECONNRESET: + return MOSQ_ERR_CONN_LOST; + default: + return MOSQ_ERR_ERRNO; + } + } + } + } + if(mosq->in_packet.payload[0] != 1){ + packet__cleanup(&mosq->in_packet); + return MOSQ_ERR_PROXY; + } + if(mosq->in_packet.payload[1] == 0){ + packet__cleanup(&mosq->in_packet); + mosq->state = mosq_cs_socks5_auth_ok; + return socks5__send(mosq); + }else{ + i = mosq->in_packet.payload[1]; + packet__cleanup(&mosq->in_packet); + switch(i){ + case SOCKS_REPLY_CONNECTION_NOT_ALLOWED: + return MOSQ_ERR_AUTH; + + case SOCKS_REPLY_NETWORK_UNREACHABLE: + case SOCKS_REPLY_HOST_UNREACHABLE: + case SOCKS_REPLY_CONNECTION_REFUSED: + return MOSQ_ERR_NO_CONN; + + case SOCKS_REPLY_GENERAL_FAILURE: + case SOCKS_REPLY_TTL_EXPIRED: + case SOCKS_REPLY_COMMAND_NOT_SUPPORTED: + case SOCKS_REPLY_ADDRESS_TYPE_NOT_SUPPORTED: + return MOSQ_ERR_PROXY; + + default: + return MOSQ_ERR_INVAL; + } + return MOSQ_ERR_PROXY; + } + }else if(mosq->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; + }else{ +#ifdef WIN32 + errno = WSAGetLastError(); +#endif + if(errno == EAGAIN || errno == COMPAT_EWOULDBLOCK){ + return MOSQ_ERR_SUCCESS; + }else{ + packet__cleanup(&mosq->in_packet); + switch(errno){ + case 0: + return MOSQ_ERR_PROXY; + case COMPAT_ECONNRESET: + return MOSQ_ERR_CONN_LOST; + default: + return MOSQ_ERR_ERRNO; + } + } + } + } + + if(mosq->in_packet.packet_length == 5){ + /* First part of the packet has been received, we now know what else to expect. */ + if(mosq->in_packet.payload[3] == SOCKS_ATYPE_IP_V4){ + mosq->in_packet.to_process += 4+2-1; /* 4 bytes IPv4, 2 bytes port, -1 byte because we've already read the first byte */ + mosq->in_packet.packet_length += 4+2-1; + }else if(mosq->in_packet.payload[3] == SOCKS_ATYPE_IP_V6){ + 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.to_process += mosq->in_packet.payload[4]; + mosq->in_packet.packet_length += mosq->in_packet.payload[4]; + } + }else{ + packet__cleanup(&mosq->in_packet); + return MOSQ_ERR_PROTOCOL; + } + 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; + } + 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; + } + + /* Entire packet is now read. */ + if(mosq->in_packet.payload[0] != 5){ + packet__cleanup(&mosq->in_packet); + return MOSQ_ERR_PROXY; + } + if(mosq->in_packet.payload[1] == 0){ + /* Auth passed */ + packet__cleanup(&mosq->in_packet); + mosq->state = 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_start, NULL); + }else{ + i = mosq->in_packet.payload[1]; + packet__cleanup(&mosq->in_packet); + mosq->state = mosq_cs_socks5_new; + switch(i){ + case SOCKS_REPLY_CONNECTION_NOT_ALLOWED: + return MOSQ_ERR_AUTH; + + case SOCKS_REPLY_NETWORK_UNREACHABLE: + case SOCKS_REPLY_HOST_UNREACHABLE: + case SOCKS_REPLY_CONNECTION_REFUSED: + return MOSQ_ERR_NO_CONN; + + case SOCKS_REPLY_GENERAL_FAILURE: + case SOCKS_REPLY_TTL_EXPIRED: + case SOCKS_REPLY_COMMAND_NOT_SUPPORTED: + case SOCKS_REPLY_ADDRESS_TYPE_NOT_SUPPORTED: + return MOSQ_ERR_PROXY; + + default: + return MOSQ_ERR_INVAL; + } + } + }else{ + return packet__read(mosq); + } + return MOSQ_ERR_SUCCESS; +} +#endif diff --git a/libs/libmosquitto/src/socks_mosq.h b/libs/libmosquitto/src/socks_mosq.h new file mode 100644 index 0000000000..27b3dc3f31 --- /dev/null +++ b/libs/libmosquitto/src/socks_mosq.h @@ -0,0 +1,23 @@ +/* +Copyright (c) 2014-2019 Roger Light <roger@atchoo.org> + +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 SOCKS_MOSQ_H +#define SOCKS_MOSQ_H + +int socks5__send(struct mosquitto *mosq); +int socks5__read(struct mosquitto *mosq); + +#endif diff --git a/libs/libmosquitto/src/srv_mosq.c b/libs/libmosquitto/src/srv_mosq.c new file mode 100644 index 0000000000..d08c2bd0cc --- /dev/null +++ b/libs/libmosquitto/src/srv_mosq.c @@ -0,0 +1,112 @@ +/* +Copyright (c) 2013-2019 Roger Light <roger@atchoo.org> + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#ifdef WITH_SRV +# include <ares.h> + +# include <arpa/nameser.h> +# include <stdio.h> +# include <string.h> +#endif + +#include "logging_mosq.h" +#include "memory_mosq.h" +#include "mosquitto_internal.h" +#include "mosquitto.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; + if(status == ARES_SUCCESS){ + status = ares_parse_srv_reply(abuf, alen, &reply); + if(status == ARES_SUCCESS){ + // FIXME - choose which answer to use based on rfc2782 page 3. */ + mosquitto_connect(mosq, reply->host, reply->port, mosq->keepalive); + } + }else{ + log__printf(mosq, MOSQ_LOG_ERR, "Error: SRV lookup failed (%d).", status); + /* FIXME - calling on_disconnect here isn't correct. */ + pthread_mutex_lock(&mosq->callback_mutex); + if(mosq->on_disconnect){ + mosq->in_callback = true; + 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); + } +} +#endif + +int mosquitto_connect_srv(struct mosquitto *mosq, const char *host, int keepalive, const char *bind_address) +{ +#ifdef WITH_SRV + char *h; + int rc; + if(!mosq) return MOSQ_ERR_INVAL; + + rc = ares_init(&mosq->achan); + if(rc != ARES_SUCCESS){ + return MOSQ_ERR_UNKNOWN; + } + + if(!host){ + // get local domain + }else{ +#ifdef WITH_TLS + if(mosq->tls_cafile || mosq->tls_capath || mosq->tls_psk){ + h = mosquitto__malloc(strlen(host) + strlen("_secure-mqtt._tcp.") + 1); + if(!h) return MOSQ_ERR_NOMEM; + sprintf(h, "_secure-mqtt._tcp.%s", host); + }else{ +#endif + h = mosquitto__malloc(strlen(host) + strlen("_mqtt._tcp.") + 1); + if(!h) return MOSQ_ERR_NOMEM; + sprintf(h, "_mqtt._tcp.%s", host); +#ifdef WITH_TLS + } +#endif + ares_search(mosq->achan, h, ns_c_in, ns_t_srv, srv_callback, mosq); + mosquitto__free(h); + } + + pthread_mutex_lock(&mosq->state_mutex); + mosq->state = mosq_cs_connect_srv; + pthread_mutex_unlock(&mosq->state_mutex); + + mosq->keepalive = 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/libmosquitto/src/stdafx.cxx b/libs/libmosquitto/src/stdafx.cxx new file mode 100644 index 0000000000..1647228cd0 --- /dev/null +++ b/libs/libmosquitto/src/stdafx.cxx @@ -0,0 +1,2 @@ + +#include "stdafx.h"
\ No newline at end of file diff --git a/libs/libmosquitto/src/stdafx.h b/libs/libmosquitto/src/stdafx.h new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/libs/libmosquitto/src/stdafx.h @@ -0,0 +1 @@ +#pragma once diff --git a/libs/libmosquitto/src/thread_mosq.c b/libs/libmosquitto/src/thread_mosq.c new file mode 100644 index 0000000000..7bde453d3d --- /dev/null +++ b/libs/libmosquitto/src/thread_mosq.c @@ -0,0 +1,133 @@ +/* +Copyright (c) 2011-2019 Roger Light <roger@atchoo.org> + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#ifndef WIN32 +#include <time.h> +#endif + +#include "mosquitto_internal.h" +#include "net_mosq.h" + +void *mosquitto__thread_main(void *obj); + +int mosquitto_loop_start(struct mosquitto *mosq) +{ +#if defined(WITH_THREADING) && defined(HAVE_PTHREAD_CANCEL) + 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)){ + return MOSQ_ERR_SUCCESS; + }else{ + return MOSQ_ERR_ERRNO; + } +#else + return MOSQ_ERR_NOT_SUPPORTED; +#endif +} + +int mosquitto_loop_stop(struct mosquitto *mosq, bool force) +{ +#if defined(WITH_THREADING) && defined(HAVE_PTHREAD_CANCEL) +# ifndef WITH_BROKER + char sockpair_data = 0; +# endif + + if(!mosq || mosq->threaded != mosq_ts_self) return MOSQ_ERR_INVAL; + + + /* Write a single byte to sockpairW (connected to sockpairR) to break out + * of select() if in threaded mode. */ + if(mosq->sockpairW != INVALID_SOCKET){ +#ifndef WIN32 + if(write(mosq->sockpairW, &sockpair_data, 1)){ + } +#else + send(mosq->sockpairW, &sockpair_data, 1, 0); +#endif + } + + if(force){ + pthread_cancel(mosq->thread_id); + } + pthread_join(mosq->thread_id, NULL); + mosq->thread_id = pthread_self(); + mosq->threaded = mosq_ts_none; + + return MOSQ_ERR_SUCCESS; +#else + return MOSQ_ERR_NOT_SUPPORTED; +#endif +} + +#ifdef WITH_THREADING +void *mosquitto__thread_main(void *obj) +{ + struct mosquitto *mosq = obj; + int state; +#ifndef WIN32 + struct timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = 10000000; +#endif + + 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){ +#ifdef WIN32 + Sleep(10); +#else + nanosleep(&ts, NULL); +#endif + }else{ + break; + } + }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); + }else{ + /* Sleep for our keepalive value. publish() etc. will wake us up. */ + mosquitto_loop_forever(mosq, mosq->keepalive*1000, 1); + } + + return obj; +} +#endif + +int mosquitto_threaded_set(struct mosquitto *mosq, bool threaded) +{ + if(!mosq) return MOSQ_ERR_INVAL; + + if(threaded){ + mosq->threaded = mosq_ts_external; + }else{ + mosq->threaded = mosq_ts_none; + } + + return MOSQ_ERR_SUCCESS; +} diff --git a/libs/libmosquitto/src/time_mosq.c b/libs/libmosquitto/src/time_mosq.c new file mode 100644 index 0000000000..c66d985444 --- /dev/null +++ b/libs/libmosquitto/src/time_mosq.c @@ -0,0 +1,61 @@ +/* +Copyright (c) 2013-2019 Roger Light <roger@atchoo.org> + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#ifdef __APPLE__ +#include <mach/mach.h> +#include <mach/mach_time.h> +#endif + +#ifdef WIN32 +# define _WIN32_WINNT _WIN32_WINNT_VISTA +# include <windows.h> +#else +# include <unistd.h> +#endif +#include <time.h> + +#include "mosquitto.h" +#include "time_mosq.h" + +time_t mosquitto_time(void) +{ +#ifdef WIN32 + return GetTickCount64()/1000; +#elif _POSIX_TIMERS>0 && defined(_POSIX_MONOTONIC_CLOCK) + struct timespec tp; + + clock_gettime(CLOCK_MONOTONIC, &tp); + return tp.tv_sec; +#elif defined(__APPLE__) + static mach_timebase_info_data_t tb; + uint64_t ticks; + uint64_t sec; + + ticks = mach_absolute_time(); + + if(tb.denom == 0){ + mach_timebase_info(&tb); + } + sec = ticks*tb.numer/tb.denom/1000000000; + + return (time_t)sec; +#else + return time(NULL); +#endif +} + diff --git a/libs/libmosquitto/src/time_mosq.h b/libs/libmosquitto/src/time_mosq.h new file mode 100644 index 0000000000..75758a8291 --- /dev/null +++ b/libs/libmosquitto/src/time_mosq.h @@ -0,0 +1,22 @@ +/* +Copyright (c) 2013-2019 Roger Light <roger@atchoo.org> + +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 TIME_MOSQ_H +#define TIME_MOSQ_H + +time_t mosquitto_time(void); + +#endif diff --git a/libs/libmosquitto/src/tls_mosq.c b/libs/libmosquitto/src/tls_mosq.c new file mode 100644 index 0000000000..c2f1204f42 --- /dev/null +++ b/libs/libmosquitto/src/tls_mosq.c @@ -0,0 +1,183 @@ +/* +Copyright (c) 2013-2019 Roger Light <roger@atchoo.org> + +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. +*/ + +#ifdef WITH_TLS + +#include "config.h" + +#ifdef WIN32 +# include <winsock2.h> +# include <ws2tcpip.h> +#else +# include <arpa/inet.h> +# include <sys/socket.h> +# include <strings.h> +#endif + +#include <string.h> +#include <openssl/conf.h> +#include <openssl/x509v3.h> +#include <openssl/ssl.h> + +#ifdef WITH_BROKER +# include "mosquitto_broker_internal.h" +#endif +#include "mosquitto_internal.h" +#include "logging_mosq.h" +#include "tls_mosq.h" + +extern int tls_ex_index_mosq; + +int mosquitto__server_certificate_verify(int preverify_ok, X509_STORE_CTX *ctx) +{ + /* Preverify should have already checked expiry, revocation. + * We need to verify the hostname. */ + struct mosquitto *mosq; + SSL *ssl; + X509 *cert; + + /* Always reject if preverify_ok has failed. */ + if(!preverify_ok) return 0; + + ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); + mosq = SSL_get_ex_data(ssl, tls_ex_index_mosq); + if(!mosq) return 0; + + if(mosq->tls_insecure == false){ + 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); + /* This is the peer certificate, all others are upwards in the chain. */ +#if defined(WITH_BROKER) + preverify_ok = mosquitto__verify_certificate_hostname(cert, mosq->bridge->addresses[mosq->bridge->cur_address].address); +#else + preverify_ok = mosquitto__verify_certificate_hostname(cert, mosq->host); +#endif + if (preverify_ok != 1) { + log__printf(mosq, MOSQ_LOG_ERR, "Error: host name verification failed."); + } + return preverify_ok; + }else{ + return preverify_ok; + } + }else{ + return preverify_ok; + } +} + +int mosquitto__cmp_hostname_wildcard(char *certname, const char *hostname) +{ + int i; + int len; + + if(!certname || !hostname){ + return 1; + } + + if(certname[0] == '*'){ + if(certname[1] != '.'){ + return 1; + } + certname += 2; + len = strlen(hostname); + for(i=0; i<len-1; i++){ + if(hostname[i] == '.'){ + hostname += i+1; + break; + } + } + return strcasecmp(certname, hostname); + }else{ + return strcasecmp(certname, hostname); + } +} + +/* This code is based heavily on the example provided in "Secure Programming + * Cookbook for C and C++". + */ +int mosquitto__verify_certificate_hostname(X509 *cert, const char *hostname) +{ + int i; + char name[256]; + X509_NAME *subj; + bool have_san_dns = false; + STACK_OF(GENERAL_NAME) *san; + const GENERAL_NAME *nval; + const unsigned char *data; + unsigned char ipv6_addr[16]; + unsigned char ipv4_addr[4]; + int ipv6_ok; + int ipv4_ok; + +#ifdef WIN32 + ipv6_ok = InetPton(AF_INET6, hostname, &ipv6_addr); + ipv4_ok = InetPton(AF_INET, hostname, &ipv4_addr); +#else + ipv6_ok = inet_pton(AF_INET6, hostname, &ipv6_addr); + ipv4_ok = inet_pton(AF_INET, hostname, &ipv4_addr); +#endif + + san = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); + if(san){ + for(i=0; i<sk_GENERAL_NAME_num(san); i++){ + nval = sk_GENERAL_NAME_value(san, i); + if(nval->type == GEN_DNS){ +#if OPENSSL_VERSION_NUMBER < 0x10100000L + data = ASN1_STRING_data(nval->d.dNSName); +#else + data = ASN1_STRING_get0_data(nval->d.dNSName); +#endif + if(data && !mosquitto__cmp_hostname_wildcard((char *)data, hostname)){ + sk_GENERAL_NAME_pop_free(san, GENERAL_NAME_free); + return 1; + } + have_san_dns = true; + }else if(nval->type == GEN_IPADD){ +#if OPENSSL_VERSION_NUMBER < 0x10100000L + data = ASN1_STRING_data(nval->d.iPAddress); +#else + data = ASN1_STRING_get0_data(nval->d.iPAddress); +#endif + if(nval->d.iPAddress->length == 4 && ipv4_ok){ + if(!memcmp(ipv4_addr, data, 4)){ + sk_GENERAL_NAME_pop_free(san, GENERAL_NAME_free); + return 1; + } + }else if(nval->d.iPAddress->length == 16 && ipv6_ok){ + if(!memcmp(ipv6_addr, data, 16)){ + sk_GENERAL_NAME_pop_free(san, GENERAL_NAME_free); + return 1; + } + } + } + } + sk_GENERAL_NAME_pop_free(san, GENERAL_NAME_free); + if(have_san_dns){ + /* Only check CN if subjectAltName DNS entry does not exist. */ + return 0; + } + } + + subj = X509_get_subject_name(cert); + if(X509_NAME_get_text_by_NID(subj, NID_commonName, name, sizeof(name)) > 0){ + name[sizeof(name) - 1] = '\0'; + if (!mosquitto__cmp_hostname_wildcard(name, hostname)) return 1; + } + return 0; +} + +#endif + diff --git a/libs/libmosquitto/src/tls_mosq.h b/libs/libmosquitto/src/tls_mosq.h new file mode 100644 index 0000000000..5a1cda54fc --- /dev/null +++ b/libs/libmosquitto/src/tls_mosq.h @@ -0,0 +1,36 @@ +/* +Copyright (c) 2013-2019 Roger Light <roger@atchoo.org> + +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 TLS_MOSQ_H +#define TLS_MOSQ_H + +#ifdef WITH_TLS +# define SSL_DATA_PENDING(A) ((A)->ssl && SSL_pending((A)->ssl)) +#else +# define SSL_DATA_PENDING(A) 0 +#endif + +#ifdef WITH_TLS + +#include <openssl/ssl.h> +#include <openssl/engine.h> + +int mosquitto__server_certificate_verify(int preverify_ok, X509_STORE_CTX *ctx); +int mosquitto__verify_certificate_hostname(X509 *cert, const char *hostname); + +#endif /* WITH_TLS */ + +#endif diff --git a/libs/libmosquitto/src/utf8_mosq.c b/libs/libmosquitto/src/utf8_mosq.c new file mode 100644 index 0000000000..875c1c7f27 --- /dev/null +++ b/libs/libmosquitto/src/utf8_mosq.c @@ -0,0 +1,109 @@ +/* +Copyright (c) 2016-2019 Roger Light <roger@atchoo.org> + +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. +*/ + +#include "config.h" + +#include <stdio.h> +#include "mosquitto.h" + +int mosquitto_validate_utf8(const char *str, int len) +{ + int i; + int j; + int codelen; + int codepoint; + const unsigned char *ustr = (const unsigned char *)str; + + if(!str) return MOSQ_ERR_INVAL; + if(len < 0 || len > 65536) return MOSQ_ERR_INVAL; + + for(i=0; i<len; i++){ + if(ustr[i] == 0){ + return MOSQ_ERR_MALFORMED_UTF8; + }else if(ustr[i] <= 0x7f){ + codelen = 1; + codepoint = ustr[i]; + }else if((ustr[i] & 0xE0) == 0xC0){ + /* 110xxxxx - 2 byte sequence */ + if(ustr[i] == 0xC0 || ustr[i] == 0xC1){ + /* Invalid bytes */ + return MOSQ_ERR_MALFORMED_UTF8; + } + codelen = 2; + codepoint = (ustr[i] & 0x1F); + }else if((ustr[i] & 0xF0) == 0xE0){ + // 1110xxxx - 3 byte sequence + codelen = 3; + codepoint = (ustr[i] & 0x0F); + }else if((ustr[i] & 0xF8) == 0xF0){ + // 11110xxx - 4 byte sequence + if(ustr[i] > 0xF4){ + /* Invalid, this would produce values > 0x10FFFF. */ + return MOSQ_ERR_MALFORMED_UTF8; + } + codelen = 4; + codepoint = (ustr[i] & 0x07); + }else{ + /* Unexpected continuation byte. */ + return MOSQ_ERR_MALFORMED_UTF8; + } + + /* Reconstruct full code point */ + if(i == len-codelen+1){ + /* Not enough data */ + return MOSQ_ERR_MALFORMED_UTF8; + } + for(j=0; j<codelen-1; j++){ + if((ustr[++i] & 0xC0) != 0x80){ + /* Not a continuation byte */ + return MOSQ_ERR_MALFORMED_UTF8; + } + 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 */ + /* 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/libmosquitto/src/util_mosq.c b/libs/libmosquitto/src/util_mosq.c new file mode 100644 index 0000000000..5e1065e7c4 --- /dev/null +++ b/libs/libmosquitto/src/util_mosq.c @@ -0,0 +1,354 @@ +/* +Copyright (c) 2009-2019 Roger Light <roger@atchoo.org> + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#include <assert.h> +#include <string.h> + +#ifdef WIN32 +# include <winsock2.h> +# include <aclapi.h> +# include <io.h> +# include <lmcons.h> +#else +# include <sys/stat.h> +#endif + +#if !defined(WITH_TLS) && defined(__linux__) && defined(__GLIBC__) +# if __GLIBC_PREREQ(2, 25) +# include <sys/random.h> +# define HAVE_GETRANDOM 1 +# endif +#endif + +#ifdef WITH_TLS +# include <openssl/bn.h> +# include <openssl/rand.h> +#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" + +#ifdef WITH_WEBSOCKETS +#include <libwebsockets.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 +{ + time_t next_msg_out; + time_t last_msg_in; + time_t now = mosquitto_time(); +#ifndef WITH_BROKER + int rc; +#endif + + assert(mosq); +#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 + && mosq->sock != INVALID_SOCKET + && 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); + return MOSQ_ERR_SUCCESS; + } +#endif + pthread_mutex_lock(&mosq->msgtime_mutex); + next_msg_out = mosq->next_msg_out; + last_msg_in = mosq->last_msg_in; + pthread_mutex_unlock(&mosq->msgtime_mutex); + if(mosq->keepalive && mosq->sock != INVALID_SOCKET && + (now >= next_msg_out || now - last_msg_in >= mosq->keepalive)){ + + if(mosq->state == mosq_cs_connected && mosq->ping_t == 0){ + send__pingreq(mosq); + /* Reset last msg times to give the server time to send a pingresp */ + pthread_mutex_lock(&mosq->msgtime_mutex); + mosq->last_msg_in = now; + mosq->next_msg_out = now + mosq->keepalive; + pthread_mutex_unlock(&mosq->msgtime_mutex); + }else{ +#ifdef WITH_BROKER + net__socket_close(db, mosq); +#else + net__socket_close(mosq); + pthread_mutex_lock(&mosq->state_mutex); + if(mosq->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; +#endif + } + } + return MOSQ_ERR_SUCCESS; +} + +uint16_t mosquitto__mid_generate(struct mosquitto *mosq) +{ + /* FIXME - this would be better with atomic increment, but this is safer + * for now for a bug fix release. + * + * If this is changed to use atomic increment, callers of this function + * will have to be aware that they may receive a 0 result, which may not be + * used as a mid. + */ + uint16_t mid; + assert(mosq); + + pthread_mutex_lock(&mosq->mid_mutex); + mosq->last_mid++; + if(mosq->last_mid == 0) mosq->last_mid++; + mid = mosq->last_mid; + pthread_mutex_unlock(&mosq->mid_mutex); + + return mid; +} + + +#ifdef WITH_TLS +int mosquitto__hex2bin_sha1(const char *hex, unsigned char **bin) +{ + unsigned char *sha, tmp[SHA_DIGEST_LENGTH]; + + if(mosquitto__hex2bin(hex, tmp, SHA_DIGEST_LENGTH) != SHA_DIGEST_LENGTH){ + return MOSQ_ERR_INVAL; + } + + sha = mosquitto__malloc(SHA_DIGEST_LENGTH); + memcpy(sha, tmp, SHA_DIGEST_LENGTH); + *bin = sha; + return MOSQ_ERR_SUCCESS; +} + +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; + size_t i = 0; + + /* Count the number of leading zero */ + for(i=0; i<strlen(hex); i=i+2) { + if(strncmp(hex + i, "00", 2) == 0) { + leading_zero++; + /* output leading zero to bin */ + bin[start++] = 0; + }else{ + break; + } + } + + if(BN_hex2bn(&bn, hex) == 0){ + if(bn) BN_free(bn); + return 0; + } + if(BN_num_bytes(bn) + leading_zero > bin_max_len){ + BN_free(bn); + return 0; + } + + len = BN_bn2bin(bn, bin + leading_zero); + BN_free(bn); + return len + leading_zero; +} +#endif + +FILE *mosquitto__fopen(const char *path, const char *mode, bool restrict_read) +{ +#ifdef WIN32 + char buf[4096]; + int rc; + rc = ExpandEnvironmentStrings(path, buf, 4096); + if(rc == 0 || rc > 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; + } + + 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; + } + + sec.nLength = sizeof(SECURITY_ATTRIBUTES); + sec.bInheritHandle = FALSE; + sec.lpSecurityDescriptor = &sd; + + hfile = CreateFile(buf, GENERIC_READ | GENERIC_WRITE, 0, + &sec, + dwCreationDisposition, + FILE_ATTRIBUTE_NORMAL, + NULL); + + LocalFree(pacl); + + int fd = _open_osfhandle((intptr_t)hfile, 0); + if (fd < 0) { + return NULL; + } + + FILE *fptr = _fdopen(fd, mode); + if (!fptr) { + _close(fd); + return NULL; + } + return fptr; + + }else { + return fopen(buf, mode); + } + } +#else + 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 +} + +void util__increment_receive_quota(struct mosquitto *mosq) +{ + if(mosq->msgs_in.inflight_quota < mosq->msgs_in.inflight_maximum){ + mosq->msgs_in.inflight_quota++; + } +} + +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--; + } +} + +void util__decrement_send_quota(struct mosquitto *mosq) +{ + if(mosq->msgs_out.inflight_quota > 0){ + mosq->msgs_out.inflight_quota--; + } +} + + +int util__random_bytes(void *bytes, int count) +{ + int rc = MOSQ_ERR_UNKNOWN; + +#ifdef WITH_TLS + if(RAND_bytes(bytes, count) == 1){ + rc = MOSQ_ERR_SUCCESS; + } +#elif defined(HAVE_GETRANDOM) + if(getrandom(bytes, count, 0) == count){ + rc = MOSQ_ERR_SUCCESS; + } +#elif defined(WIN32) + HCRYPTPROV provider; + + if(!CryptAcquireContext(&provider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)){ + return MOSQ_ERR_UNKNOWN; + } + + if(CryptGenRandom(provider, count, bytes)){ + rc = MOSQ_ERR_SUCCESS; + } + + CryptReleaseContext(provider, 0); +#else + int i; + + for(i=0; i<count; i++){ + ((uint8_t *)bytes)[i] = (uint8_t )(random()&0xFF); + } + rc = MOSQ_ERR_SUCCESS; +#endif + return rc; +} diff --git a/libs/libmosquitto/src/util_mosq.h b/libs/libmosquitto/src/util_mosq.h new file mode 100644 index 0000000000..9ee570f3e9 --- /dev/null +++ b/libs/libmosquitto/src/util_mosq.h @@ -0,0 +1,47 @@ +/* +Copyright (c) 2009-2019 Roger Light <roger@atchoo.org> + +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 UTIL_MOSQ_H +#define UTIL_MOSQ_H + +#include <stdio.h> + +#include "tls_mosq.h" +#include "mosquitto.h" +#include "mosquitto_internal.h" +#ifdef WITH_BROKER +# 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 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/libmosquitto/src/util_topic.c b/libs/libmosquitto/src/util_topic.c new file mode 100644 index 0000000000..67b78782ef --- /dev/null +++ b/libs/libmosquitto/src/util_topic.c @@ -0,0 +1,252 @@ +/* +Copyright (c) 2009-2018 Roger Light <roger@atchoo.org> + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#include <assert.h> +#include <string.h> + +#ifdef WIN32 +# include <winsock2.h> +# include <aclapi.h> +# include <io.h> +# include <lmcons.h> +#else +# include <sys/stat.h> +#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; + 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) +{ + size_t i; + + if(len > 65535) return MOSQ_ERR_INVAL; + + for(i=0; i<len; i++){ + if(str[i] == '+' || str[i] == '#'){ + return MOSQ_ERR_INVAL; + } + } + + 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; + while(str && 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; + } + } + len++; + c = str[0]; + str = &str[1]; + } + if(len > 65535) return MOSQ_ERR_INVAL; + + return MOSQ_ERR_SUCCESS; +} + +int mosquitto_sub_topic_check2(const char *str, size_t len) +{ + char c = '\0'; + size_t i; + + if(len > 65535) return MOSQ_ERR_INVAL; + + for(i=0; i<len; i++){ + if(str[i] == '+'){ + if((c != '\0' && c != '/') || (i<len-1 && str[i+1] != '/')){ + return MOSQ_ERR_INVAL; + } + }else if(str[i] == '#'){ + if((c != '\0' && c != '/') || i<len-1){ + return MOSQ_ERR_INVAL; + } + } + c = str[i]; + } + + return MOSQ_ERR_SUCCESS; +} + +int mosquitto_topic_matches_sub(const char *sub, const char *topic, bool *result) +{ + return mosquitto_topic_matches_sub2(sub, 0, topic, 0, result); +} + +/* 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; + + UNUSED(sublen); + UNUSED(topiclen); + + 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] != '/'){ + 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{ + *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; + } + + return MOSQ_ERR_SUCCESS; +} diff --git a/libs/libmosquitto/src/will_mosq.c b/libs/libmosquitto/src/will_mosq.c new file mode 100644 index 0000000000..f9f1f32a56 --- /dev/null +++ b/libs/libmosquitto/src/will_mosq.c @@ -0,0 +1,126 @@ +/* +Copyright (c) 2010-2019 Roger Light <roger@atchoo.org> + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#include <stdio.h> +#include <string.h> + +#ifdef WITH_BROKER +# include "mosquitto_broker_internal.h" +#endif + +#include "mosquitto.h" +#include "mosquitto_internal.h" +#include "logging_mosq.h" +#include "messages_mosq.h" +#include "memory_mosq.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, 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 && !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(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->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_all)); + if(!mosq->will) return MOSQ_ERR_NOMEM; + mosq->will->msg.topic = mosquitto__strdup(topic); + if(!mosq->will->msg.topic){ + rc = MOSQ_ERR_NOMEM; + goto cleanup; + } + mosq->will->msg.payloadlen = payloadlen; + if(mosq->will->msg.payloadlen > 0){ + if(!payload){ + rc = MOSQ_ERR_INVAL; + goto cleanup; + } + mosq->will->msg.payload = mosquitto__malloc(sizeof(char)*mosq->will->msg.payloadlen); + if(!mosq->will->msg.payload){ + rc = MOSQ_ERR_NOMEM; + goto cleanup; + } + + memcpy(mosq->will->msg.payload, payload, payloadlen); + } + 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->msg.topic); + mosquitto__free(mosq->will->msg.payload); + + mosquitto__free(mosq->will); + mosq->will = NULL; + } + + return rc; +} + +int will__clear(struct mosquitto *mosq) +{ + if(!mosq->will) return MOSQ_ERR_SUCCESS; + + mosquitto__free(mosq->will->msg.topic); + mosq->will->msg.topic = NULL; + + mosquitto__free(mosq->will->msg.payload); + mosq->will->msg.payload = NULL; + + mosquitto_property_free_all(&mosq->will->properties); + + mosquitto__free(mosq->will); + mosq->will = NULL; + + return MOSQ_ERR_SUCCESS; +} + diff --git a/libs/libmosquitto/src/will_mosq.h b/libs/libmosquitto/src/will_mosq.h new file mode 100644 index 0000000000..c96dad1a71 --- /dev/null +++ b/libs/libmosquitto/src/will_mosq.h @@ -0,0 +1,26 @@ +/* +Copyright (c) 2010-2019 Roger Light <roger@atchoo.org> + +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 WILL_MOSQ_H +#define WILL_MOSQ_H + +#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, mosquitto_property *properties); +int will__clear(struct mosquitto *mosq); + +#endif |