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 | |
parent | c1358991022919836c77d630ee9a3719fff86ed0 (diff) |
libmosquitto - a helper for MQTT clients
Diffstat (limited to 'libs/libmosquitto')
65 files changed, 16064 insertions, 0 deletions
diff --git a/libs/libmosquitto/include/config.h b/libs/libmosquitto/include/config.h new file mode 100644 index 0000000000..f717d00c8a --- /dev/null +++ b/libs/libmosquitto/include/config.h @@ -0,0 +1,69 @@ +#ifndef CONFIG_H +#define CONFIG_H +/* ============================================================ + * Platform options + * ============================================================ */ + +#ifdef __APPLE__ +# define __DARWIN_C_SOURCE +#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__SYMBIAN32__) || defined(__QNX__) +# define _XOPEN_SOURCE 700 +# define __BSD_VISIBLE 1 +# define HAVE_NETINET_IN_H +#else +# define _XOPEN_SOURCE 700 +# define _DEFAULT_SOURCE 1 +# define _POSIX_C_SOURCE 200809L +#endif + +#define _GNU_SOURCE + +#define OPENSSL_LOAD_CONF + +/* ============================================================ + * Compatibility defines + * ============================================================ */ +#if defined(_MSC_VER) && _MSC_VER < 1900 +# define snprintf sprintf_s +# define EPROTO ECONNABORTED +#endif + +#ifdef WIN32 +# ifndef strcasecmp +# define strcasecmp strcmpi +# endif +# define strtok_r strtok_s +# define strerror_r(e, b, l) strerror_s(b, l, e) +#endif + + +#define uthash_malloc(sz) mosquitto__malloc(sz) +#define uthash_free(ptr,sz) mosquitto__free(ptr) + + +#ifdef WITH_TLS +# include <openssl/opensslconf.h> +# if defined(WITH_TLS_PSK) && !defined(OPENSSL_NO_PSK) +# define FINAL_WITH_TLS_PSK +# endif +#endif + + +#ifdef __COVERITY__ +# include <stdint.h> +/* These are "wrong", but we don't use them so it doesn't matter */ +# define _Float32 uint32_t +# define _Float32x uint32_t +# define _Float64 uint64_t +# define _Float64x uint64_t +# define _Float128 uint64_t +#endif + +#define UNUSED(A) (void)(A) + +/* Android Bionic libpthread implementation doesn't have pthread_cancel */ +#ifndef ANDROID +# define HAVE_PTHREAD_CANCEL +#endif + +#endif diff --git a/libs/libmosquitto/include/mosquitto.h b/libs/libmosquitto/include/mosquitto.h new file mode 100644 index 0000000000..09bba66e08 --- /dev/null +++ b/libs/libmosquitto/include/mosquitto.h @@ -0,0 +1,2993 @@ +/* +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 MOSQUITTO_H +#define MOSQUITTO_H + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(WIN32) && !defined(WITH_BROKER) && !defined(LIBMOSQUITTO_STATIC) +# ifdef libmosquitto_EXPORTS +# define libmosq_EXPORT __declspec(dllexport) +# else +# define libmosq_EXPORT __declspec(dllimport) +# endif +#else +# define libmosq_EXPORT +#endif + +#if defined(_MSC_VER) && _MSC_VER < 1900 +# ifndef __cplusplus +# define bool char +# define true 1 +# define false 0 +# endif +#else +# ifndef __cplusplus +# include <stdbool.h> +# endif +#endif + +#include <stddef.h> +#include <stdint.h> + +#define LIBMOSQUITTO_MAJOR 1 +#define LIBMOSQUITTO_MINOR 6 +#define LIBMOSQUITTO_REVISION 3 +/* LIBMOSQUITTO_VERSION_NUMBER looks like 1002001 for e.g. version 1.2.1. */ +#define LIBMOSQUITTO_VERSION_NUMBER (LIBMOSQUITTO_MAJOR*1000000+LIBMOSQUITTO_MINOR*1000+LIBMOSQUITTO_REVISION) + +/* Log types */ +#define MOSQ_LOG_NONE 0 +#define MOSQ_LOG_INFO (1<<0) +#define MOSQ_LOG_NOTICE (1<<1) +#define MOSQ_LOG_WARNING (1<<2) +#define MOSQ_LOG_ERR (1<<3) +#define MOSQ_LOG_DEBUG (1<<4) +#define MOSQ_LOG_SUBSCRIBE (1<<5) +#define MOSQ_LOG_UNSUBSCRIBE (1<<6) +#define MOSQ_LOG_WEBSOCKETS (1<<7) +#define MOSQ_LOG_INTERNAL 0x80000000 +#define MOSQ_LOG_ALL 0x7FFFFFFF + +/* Error values */ +enum mosq_err_t { + MOSQ_ERR_AUTH_CONTINUE = -4, + MOSQ_ERR_NO_SUBSCRIBERS = -3, + MOSQ_ERR_SUB_EXISTS = -2, + MOSQ_ERR_CONN_PENDING = -1, + MOSQ_ERR_SUCCESS = 0, + MOSQ_ERR_NOMEM = 1, + MOSQ_ERR_PROTOCOL = 2, + MOSQ_ERR_INVAL = 3, + MOSQ_ERR_NO_CONN = 4, + MOSQ_ERR_CONN_REFUSED = 5, + MOSQ_ERR_NOT_FOUND = 6, + MOSQ_ERR_CONN_LOST = 7, + MOSQ_ERR_TLS = 8, + MOSQ_ERR_PAYLOAD_SIZE = 9, + MOSQ_ERR_NOT_SUPPORTED = 10, + MOSQ_ERR_AUTH = 11, + MOSQ_ERR_ACL_DENIED = 12, + MOSQ_ERR_UNKNOWN = 13, + MOSQ_ERR_ERRNO = 14, + MOSQ_ERR_EAI = 15, + MOSQ_ERR_PROXY = 16, + MOSQ_ERR_PLUGIN_DEFER = 17, + MOSQ_ERR_MALFORMED_UTF8 = 18, + MOSQ_ERR_KEEPALIVE = 19, + MOSQ_ERR_LOOKUP = 20, + MOSQ_ERR_MALFORMED_PACKET = 21, + MOSQ_ERR_DUPLICATE_PROPERTY = 22, + MOSQ_ERR_TLS_HANDSHAKE = 23, + MOSQ_ERR_QOS_NOT_SUPPORTED = 24, + MOSQ_ERR_OVERSIZE_PACKET = 25, + MOSQ_ERR_OCSP = 26, +}; + +/* Option values */ +enum mosq_opt_t { + MOSQ_OPT_PROTOCOL_VERSION = 1, + MOSQ_OPT_SSL_CTX = 2, + MOSQ_OPT_SSL_CTX_WITH_DEFAULTS = 3, + MOSQ_OPT_RECEIVE_MAXIMUM = 4, + MOSQ_OPT_SEND_MAXIMUM = 5, + MOSQ_OPT_TLS_KEYFORM = 6, + MOSQ_OPT_TLS_ENGINE = 7, + MOSQ_OPT_TLS_ENGINE_KPASS_SHA1 = 8, + MOSQ_OPT_TLS_OCSP_REQUIRED = 9, + MOSQ_OPT_TLS_ALPN = 10, +}; + + +/* MQTT specification restricts client ids to a maximum of 23 characters */ +#define MOSQ_MQTT_ID_MAX_LENGTH 23 + +#define MQTT_PROTOCOL_V31 3 +#define MQTT_PROTOCOL_V311 4 +#define MQTT_PROTOCOL_V5 5 + +struct mosquitto_message{ + int mid; + char *topic; + void *payload; + int payloadlen; + int qos; + bool retain; +}; + +struct mosquitto; +typedef struct mqtt5__property mosquitto_property; + +/* + * Topic: Threads + * libmosquitto provides thread safe operation, with the exception of + * <mosquitto_lib_init> which is not thread safe. + * + * If your application uses threads you must use <mosquitto_threaded_set> to + * tell the library this is the case, otherwise it makes some optimisations + * for the single threaded case that may result in unexpected behaviour for + * the multi threaded case. + */ +/*************************************************** + * Important note + * + * The following functions that deal with network operations will return + * MOSQ_ERR_SUCCESS on success, but this does not mean that the operation has + * taken place. An attempt will be made to write the network data, but if the + * socket is not available for writing at that time then the packet will not be + * sent. To ensure the packet is sent, call mosquitto_loop() (which must also + * be called to process incoming network data). + * This is especially important when disconnecting a client that has a will. If + * the broker does not receive the DISCONNECT command, it will assume that the + * client has disconnected unexpectedly and send the will. + * + * mosquitto_connect() + * mosquitto_disconnect() + * mosquitto_subscribe() + * mosquitto_unsubscribe() + * mosquitto_publish() + ***************************************************/ + + +/* ====================================================================== + * + * Section: Library version, init, and cleanup + * + * ====================================================================== */ +/* + * Function: mosquitto_lib_version + * + * Can be used to obtain version information for the mosquitto library. + * This allows the application to compare the library version against the + * version it was compiled against by using the LIBMOSQUITTO_MAJOR, + * LIBMOSQUITTO_MINOR and LIBMOSQUITTO_REVISION defines. + * + * Parameters: + * major - an integer pointer. If not NULL, the major version of the + * library will be returned in this variable. + * minor - an integer pointer. If not NULL, the minor version of the + * library will be returned in this variable. + * revision - an integer pointer. If not NULL, the revision of the library will + * be returned in this variable. + * + * Returns: + * LIBMOSQUITTO_VERSION_NUMBER, which is a unique number based on the major, + * minor and revision values. + * See Also: + * <mosquitto_lib_cleanup>, <mosquitto_lib_init> + */ +libmosq_EXPORT int mosquitto_lib_version(int *major, int *minor, int *revision); + +/* + * Function: mosquitto_lib_init + * + * Must be called before any other mosquitto functions. + * + * This function is *not* thread safe. + * + * Returns: + * MOSQ_ERR_SUCCESS - always + * + * See Also: + * <mosquitto_lib_cleanup>, <mosquitto_lib_version> + */ +libmosq_EXPORT int mosquitto_lib_init(void); + +/* + * Function: mosquitto_lib_cleanup + * + * Call to free resources associated with the library. + * + * Returns: + * MOSQ_ERR_SUCCESS - always + * + * See Also: + * <mosquitto_lib_init>, <mosquitto_lib_version> + */ +libmosq_EXPORT int mosquitto_lib_cleanup(void); + + +/* ====================================================================== + * + * Section: Client creation, destruction, and reinitialisation + * + * ====================================================================== */ +/* + * Function: mosquitto_new + * + * Create a new mosquitto client instance. + * + * Parameters: + * id - String to use as the client id. If NULL, a random client id + * will be generated. If id is NULL, clean_session must be true. + * clean_session - set to true to instruct the broker to clean all messages + * and subscriptions on disconnect, false to instruct it to + * keep them. See the man page mqtt(7) for more details. + * Note that a client will never discard its own outgoing + * messages on disconnect. Calling <mosquitto_connect> or + * <mosquitto_reconnect> will cause the messages to be resent. + * Use <mosquitto_reinitialise> to reset a client to its + * original state. + * Must be set to true if the id parameter is NULL. + * obj - A user pointer that will be passed as an argument to any + * callbacks that are specified. + * + * Returns: + * Pointer to a struct mosquitto on success. + * NULL on failure. Interrogate errno to determine the cause for the failure: + * - ENOMEM on out of memory. + * - EINVAL on invalid input parameters. + * + * See Also: + * <mosquitto_reinitialise>, <mosquitto_destroy>, <mosquitto_user_data_set> + */ +libmosq_EXPORT struct mosquitto *mosquitto_new(const char *id, bool clean_session, void *obj); + +/* + * Function: mosquitto_destroy + * + * Use to free memory associated with a mosquitto client instance. + * + * Parameters: + * mosq - a struct mosquitto pointer to free. + * + * See Also: + * <mosquitto_new>, <mosquitto_reinitialise> + */ +libmosq_EXPORT void mosquitto_destroy(struct mosquitto *mosq); + +/* + * Function: mosquitto_reinitialise + * + * This function allows an existing mosquitto client to be reused. Call on a + * mosquitto instance to close any open network connections, free memory + * and reinitialise the client with the new parameters. The end result is the + * same as the output of <mosquitto_new>. + * + * Parameters: + * mosq - a valid mosquitto instance. + * id - string to use as the client id. If NULL, a random client id + * will be generated. If id is NULL, clean_session must be true. + * clean_session - set to true to instruct the broker to clean all messages + * and subscriptions on disconnect, false to instruct it to + * keep them. See the man page mqtt(7) for more details. + * Must be set to true if the id parameter is NULL. + * obj - A user pointer that will be passed as an argument to any + * callbacks that are specified. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * + * See Also: + * <mosquitto_new>, <mosquitto_destroy> + */ +libmosq_EXPORT int mosquitto_reinitialise(struct mosquitto *mosq, const char *id, bool clean_session, void *obj); + + +/* ====================================================================== + * + * Section: Will + * + * ====================================================================== */ +/* + * Function: mosquitto_will_set + * + * Configure will information for a mosquitto instance. By default, clients do + * not have a will. This must be called before calling <mosquitto_connect>. + * + * Parameters: + * mosq - a valid mosquitto instance. + * topic - the topic on which to publish the will. + * payloadlen - the size of the payload (bytes). Valid values are between 0 and + * 268,435,455. + * payload - pointer to the data to send. If payloadlen > 0 this must be a + * valid memory location. + * qos - integer value 0, 1 or 2 indicating the Quality of Service to be + * used for the will. + * retain - set to true to make the will a retained message. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * MOSQ_ERR_PAYLOAD_SIZE - if payloadlen is too large. + * MOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8. + */ +libmosq_EXPORT int mosquitto_will_set(struct mosquitto *mosq, const char *topic, int payloadlen, const void *payload, int qos, bool retain); + +/* + * Function: mosquitto_will_set_v5 + * + * Configure will information for a mosquitto instance, with attached + * properties. By default, clients do not have a will. This must be called + * before calling <mosquitto_connect>. + * + * Parameters: + * mosq - a valid mosquitto instance. + * topic - the topic on which to publish the will. + * payloadlen - the size of the payload (bytes). Valid values are between 0 and + * 268,435,455. + * payload - pointer to the data to send. If payloadlen > 0 this must be a + * valid memory location. + * qos - integer value 0, 1 or 2 indicating the Quality of Service to be + * used for the will. + * retain - set to true to make the will a retained message. + * properties - list of MQTT 5 properties. Can be NULL. On success only, the + * property list becomes the property of libmosquitto once this + * function is called and will be freed by the library. The + * property list must be freed by the application on error. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * MOSQ_ERR_PAYLOAD_SIZE - if payloadlen is too large. + * MOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8. + * MOSQ_ERR_NOT_SUPPORTED - if properties is not NULL and the client is not + * using MQTT v5 + * MOSQ_ERR_PROTOCOL - if a property is invalid for use with wills. + * MOSQ_ERR_DUPLICATE_PROPERTY - if a property is duplicated where it is forbidden. + */ +libmosq_EXPORT int mosquitto_will_set_v5(struct mosquitto *mosq, const char *topic, int payloadlen, const void *payload, int qos, bool retain, mosquitto_property *properties); + +/* + * Function: mosquitto_will_clear + * + * Remove a previously configured will. This must be called before calling + * <mosquitto_connect>. + * + * Parameters: + * mosq - a valid mosquitto instance. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + */ +libmosq_EXPORT int mosquitto_will_clear(struct mosquitto *mosq); + + +/* ====================================================================== + * + * Section: Username and password + * + * ====================================================================== */ +/* + * Function: mosquitto_username_pw_set + * + * Configure username and password for a mosquitton instance. This is only + * supported by brokers that implement the MQTT spec v3.1. By default, no + * username or password will be sent. + * If username is NULL, the password argument is ignored. + * This must be called before calling mosquitto_connect(). + * + * This is must be called before calling <mosquitto_connect>. + * + * Parameters: + * mosq - a valid mosquitto instance. + * username - the username to send as a string, or NULL to disable + * authentication. + * password - the password to send as a string. Set to NULL when username is + * valid in order to send just a username. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + */ +libmosq_EXPORT int mosquitto_username_pw_set(struct mosquitto *mosq, const char *username, const char *password); + + +/* ====================================================================== + * + * Section: Connecting, reconnecting, disconnecting + * + * ====================================================================== */ +/* + * Function: mosquitto_connect + * + * Connect to an MQTT broker. + * + * Parameters: + * mosq - a valid mosquitto instance. + * host - the hostname or ip address of the broker to connect to. + * port - the network port to connect to. Usually 1883. + * keepalive - the number of seconds after which the broker should send a PING + * message to the client if no other messages have been exchanged + * in that time. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_ERRNO - if a system call returned an error. The variable errno + * contains the error code, even on Windows. + * Use strerror_r() where available or FormatMessage() on + * Windows. + * + * See Also: + * <mosquitto_connect_bind>, <mosquitto_connect_async>, <mosquitto_reconnect>, <mosquitto_disconnect>, <mosquitto_tls_set> + */ +libmosq_EXPORT int mosquitto_connect(struct mosquitto *mosq, const char *host, int port, int keepalive); + +/* + * Function: mosquitto_connect_bind + * + * Connect to an MQTT broker. This extends the functionality of + * <mosquitto_connect> by adding the bind_address parameter. Use this function + * if you need to restrict network communication over a particular interface. + * + * Parameters: + * mosq - a valid mosquitto instance. + * host - the hostname or ip address of the broker to connect to. + * port - the network port to connect to. Usually 1883. + * keepalive - the number of seconds after which the broker should send a PING + * message to the client if no other messages have been exchanged + * in that time. + * bind_address - the hostname or ip address of the local network interface to + * bind to. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_ERRNO - if a system call returned an error. The variable errno + * contains the error code, even on Windows. + * Use strerror_r() where available or FormatMessage() on + * Windows. + * + * See Also: + * <mosquitto_connect>, <mosquitto_connect_async>, <mosquitto_connect_bind_async> + */ +libmosq_EXPORT int mosquitto_connect_bind(struct mosquitto *mosq, const char *host, int port, int keepalive, const char *bind_address); + +/* + * Function: mosquitto_connect_bind_v5 + * + * Connect to an MQTT broker. This extends the functionality of + * <mosquitto_connect> by adding the bind_address parameter. Use this function + * if you need to restrict network communication over a particular interface. + * + * Parameters: + * mosq - a valid mosquitto instance. + * host - the hostname or ip address of the broker to connect to. + * port - the network port to connect to. Usually 1883. + * keepalive - the number of seconds after which the broker should send a PING + * message to the client if no other messages have been exchanged + * in that time. + * bind_address - the hostname or ip address of the local network interface to + * bind to. + * properties - the MQTT 5 properties for the connect (not for the Will). + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_ERRNO - if a system call returned an error. The variable errno + * contains the error code, even on Windows. + * Use strerror_r() where available or FormatMessage() on + * Windows. + * MOSQ_ERR_DUPLICATE_PROPERTY - if a property is duplicated where it is forbidden. + * MOSQ_ERR_PROTOCOL - if any property is invalid for use with CONNECT. + * + * See Also: + * <mosquitto_connect>, <mosquitto_connect_async>, <mosquitto_connect_bind_async> + */ +libmosq_EXPORT int mosquitto_connect_bind_v5(struct mosquitto *mosq, const char *host, int port, int keepalive, const char *bind_address, const mosquitto_property *properties); + +/* + * Function: mosquitto_connect_async + * + * Connect to an MQTT broker. This is a non-blocking call. If you use + * <mosquitto_connect_async> your client must use the threaded interface + * <mosquitto_loop_start>. If you need to use <mosquitto_loop>, you must use + * <mosquitto_connect> to connect the client. + * + * May be called before or after <mosquitto_loop_start>. + * + * Parameters: + * mosq - a valid mosquitto instance. + * host - the hostname or ip address of the broker to connect to. + * port - the network port to connect to. Usually 1883. + * keepalive - the number of seconds after which the broker should send a PING + * message to the client if no other messages have been exchanged + * in that time. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_ERRNO - if a system call returned an error. The variable errno + * contains the error code, even on Windows. + * Use strerror_r() where available or FormatMessage() on + * Windows. + * + * See Also: + * <mosquitto_connect_bind_async>, <mosquitto_connect>, <mosquitto_reconnect>, <mosquitto_disconnect>, <mosquitto_tls_set> + */ +libmosq_EXPORT int mosquitto_connect_async(struct mosquitto *mosq, const char *host, int port, int keepalive); + +/* + * Function: mosquitto_connect_bind_async + * + * Connect to an MQTT broker. This is a non-blocking call. If you use + * <mosquitto_connect_bind_async> your client must use the threaded interface + * <mosquitto_loop_start>. If you need to use <mosquitto_loop>, you must use + * <mosquitto_connect> to connect the client. + * + * This extends the functionality of <mosquitto_connect_async> by adding the + * bind_address parameter. Use this function if you need to restrict network + * communication over a particular interface. + * + * May be called before or after <mosquitto_loop_start>. + * + * Parameters: + * mosq - a valid mosquitto instance. + * host - the hostname or ip address of the broker to connect to. + * port - the network port to connect to. Usually 1883. + * keepalive - the number of seconds after which the broker should send a PING + * message to the client if no other messages have been exchanged + * in that time. + * bind_address - the hostname or ip address of the local network interface to + * bind to. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_ERRNO - if a system call returned an error. The variable errno + * contains the error code, even on Windows. + * Use strerror_r() where available or FormatMessage() on + * Windows. + * + * See Also: + * <mosquitto_connect_async>, <mosquitto_connect>, <mosquitto_connect_bind> + */ +libmosq_EXPORT int mosquitto_connect_bind_async(struct mosquitto *mosq, const char *host, int port, int keepalive, const char *bind_address); + +/* + * Function: mosquitto_connect_srv + * + * Connect to an MQTT broker. This is a non-blocking call. If you use + * <mosquitto_connect_async> your client must use the threaded interface + * <mosquitto_loop_start>. If you need to use <mosquitto_loop>, you must use + * <mosquitto_connect> to connect the client. + * + * This extends the functionality of <mosquitto_connect_async> by adding the + * bind_address parameter. Use this function if you need to restrict network + * communication over a particular interface. + * + * May be called before or after <mosquitto_loop_start>. + * + * Parameters: + * mosq - a valid mosquitto instance. + * host - the hostname or ip address of the broker to connect to. + * keepalive - the number of seconds after which the broker should send a PING + * message to the client if no other messages have been exchanged + * in that time. + * bind_address - the hostname or ip address of the local network interface to + * bind to. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_ERRNO - if a system call returned an error. The variable errno + * contains the error code, even on Windows. + * Use strerror_r() where available or FormatMessage() on + * Windows. + * + * See Also: + * <mosquitto_connect_async>, <mosquitto_connect>, <mosquitto_connect_bind> + */ +libmosq_EXPORT int mosquitto_connect_srv(struct mosquitto *mosq, const char *host, int keepalive, const char *bind_address); + +/* + * Function: mosquitto_reconnect + * + * Reconnect to a broker. + * + * This function provides an easy way of reconnecting to a broker after a + * connection has been lost. It uses the values that were provided in the + * <mosquitto_connect> call. It must not be called before + * <mosquitto_connect>. + * + * Parameters: + * mosq - a valid mosquitto instance. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_ERRNO - if a system call returned an error. The variable errno + * contains the error code, even on Windows. + * Use strerror_r() where available or FormatMessage() on + * Windows. + * + * See Also: + * <mosquitto_connect>, <mosquitto_disconnect>, <mosquitto_reconnect_async> + */ +libmosq_EXPORT int mosquitto_reconnect(struct mosquitto *mosq); + +/* + * Function: mosquitto_reconnect_async + * + * Reconnect to a broker. Non blocking version of <mosquitto_reconnect>. + * + * This function provides an easy way of reconnecting to a broker after a + * connection has been lost. It uses the values that were provided in the + * <mosquitto_connect> or <mosquitto_connect_async> calls. It must not be + * called before <mosquitto_connect>. + * + * Parameters: + * mosq - a valid mosquitto instance. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_ERRNO - if a system call returned an error. The variable errno + * contains the error code, even on Windows. + * Use strerror_r() where available or FormatMessage() on + * Windows. + * + * See Also: + * <mosquitto_connect>, <mosquitto_disconnect> + */ +libmosq_EXPORT int mosquitto_reconnect_async(struct mosquitto *mosq); + +/* + * Function: mosquitto_disconnect + * + * Disconnect from the broker. + * + * Parameters: + * mosq - a valid mosquitto instance. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. + */ +libmosq_EXPORT int mosquitto_disconnect(struct mosquitto *mosq); + +/* + * Function: mosquitto_disconnect_v5 + * + * Disconnect from the broker, with attached MQTT properties. + * + * Use <mosquitto_property_add_*> to create a list of properties, then attach + * them to this publish. Properties need freeing with + * <mosquitto_property_free_all>. + * + * Parameters: + * mosq - a valid mosquitto instance. + * reason_code - the disconnect reason code. + * properties - a valid mosquitto_property list, or NULL. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. + * MOSQ_ERR_DUPLICATE_PROPERTY - if a property is duplicated where it is forbidden. + * MOSQ_ERR_PROTOCOL - if any property is invalid for use with DISCONNECT. + */ +libmosq_EXPORT int mosquitto_disconnect_v5(struct mosquitto *mosq, int reason_code, const mosquitto_property *properties); + + +/* ====================================================================== + * + * Section: Publishing, subscribing, unsubscribing + * + * ====================================================================== */ +/* + * Function: mosquitto_publish + * + * Publish a message on a given topic. + * + * Parameters: + * mosq - a valid mosquitto instance. + * mid - pointer to an int. If not NULL, the function will set this + * to the message id of this particular message. This can be then + * used with the publish callback to determine when the message + * has been sent. + * Note that although the MQTT protocol doesn't use message ids + * for messages with QoS=0, libmosquitto assigns them message ids + * so they can be tracked with this parameter. + * topic - null terminated string of the topic to publish to. + * payloadlen - the size of the payload (bytes). Valid values are between 0 and + * 268,435,455. + * payload - pointer to the data to send. If payloadlen > 0 this must be a + * valid memory location. + * qos - integer value 0, 1 or 2 indicating the Quality of Service to be + * used for the message. + * retain - set to true to make the message retained. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. + * MOSQ_ERR_PROTOCOL - if there is a protocol error communicating with the + * broker. + * MOSQ_ERR_PAYLOAD_SIZE - if payloadlen is too large. + * MOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8 + * MOSQ_ERR_QOS_NOT_SUPPORTED - if the QoS is greater than that supported by + * the broker. + * MOSQ_ERR_OVERSIZE_PACKET - if the resulting packet would be larger than + * supported by the broker. + * + * See Also: + * <mosquitto_max_inflight_messages_set> + */ +libmosq_EXPORT int mosquitto_publish(struct mosquitto *mosq, int *mid, const char *topic, int payloadlen, const void *payload, int qos, bool retain); + + +/* + * Function: mosquitto_publish_v5 + * + * Publish a message on a given topic, with attached MQTT properties. + * + * Use <mosquitto_property_add_*> to create a list of properties, then attach + * them to this publish. Properties need freeing with + * <mosquitto_property_free_all>. + * + * Requires the mosquitto instance to be connected with MQTT 5. + * + * Parameters: + * mosq - a valid mosquitto instance. + * mid - pointer to an int. If not NULL, the function will set this + * to the message id of this particular message. This can be then + * used with the publish callback to determine when the message + * has been sent. + * Note that although the MQTT protocol doesn't use message ids + * for messages with QoS=0, libmosquitto assigns them message ids + * so they can be tracked with this parameter. + * topic - null terminated string of the topic to publish to. + * payloadlen - the size of the payload (bytes). Valid values are between 0 and + * 268,435,455. + * payload - pointer to the data to send. If payloadlen > 0 this must be a + * valid memory location. + * qos - integer value 0, 1 or 2 indicating the Quality of Service to be + * used for the message. + * retain - set to true to make the message retained. + * properties - a valid mosquitto_property list, or NULL. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. + * MOSQ_ERR_PROTOCOL - if there is a protocol error communicating with the + * broker. + * MOSQ_ERR_PAYLOAD_SIZE - if payloadlen is too large. + * MOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8 + * MOSQ_ERR_DUPLICATE_PROPERTY - if a property is duplicated where it is forbidden. + * MOSQ_ERR_PROTOCOL - if any property is invalid for use with PUBLISH. + * MOSQ_ERR_QOS_NOT_SUPPORTED - if the QoS is greater than that supported by + * the broker. + * MOSQ_ERR_OVERSIZE_PACKET - if the resulting packet would be larger than + * supported by the broker. + */ +libmosq_EXPORT int mosquitto_publish_v5( + struct mosquitto *mosq, + int *mid, + const char *topic, + int payloadlen, + const void *payload, + int qos, + bool retain, + const mosquitto_property *properties); + + +/* + * Function: mosquitto_subscribe + * + * Subscribe to a topic. + * + * Parameters: + * mosq - a valid mosquitto instance. + * mid - a pointer to an int. If not NULL, the function will set this to + * the message id of this particular message. This can be then used + * with the subscribe callback to determine when the message has been + * sent. + * sub - the subscription pattern. + * qos - the requested Quality of Service for this subscription. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. + * MOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8 + * MOSQ_ERR_OVERSIZE_PACKET - if the resulting packet would be larger than + * supported by the broker. + */ +libmosq_EXPORT int mosquitto_subscribe(struct mosquitto *mosq, int *mid, const char *sub, int qos); + +/* + * Function: mosquitto_subscribe_v5 + * + * Subscribe to a topic, with attached MQTT properties. + * + * Use <mosquitto_property_add_*> to create a list of properties, then attach + * them to this subscribe. Properties need freeing with + * <mosquitto_property_free_all>. + * + * Requires the mosquitto instance to be connected with MQTT 5. + * + * + * Parameters: + * mosq - a valid mosquitto instance. + * mid - a pointer to an int. If not NULL, the function will set this to + * the message id of this particular message. This can be then used + * with the subscribe callback to determine when the message has been + * sent. + * sub - the subscription pattern. + * qos - the requested Quality of Service for this subscription. + * options - options to apply to this subscription, OR'd together. Set to 0 to + * use the default options, otherwise choose from the list: + * MQTT_SUB_OPT_NO_LOCAL - with this option set, if this client + * publishes to a topic to which it is subscribed, the + * broker will not publish the message back to the + * client. + * MQTT_SUB_OPT_RETAIN_AS_PUBLISHED - with this option set, messages + * published for this subscription will keep the + * retain flag as was set by the publishing client. + * The default behaviour without this option set has + * the retain flag indicating whether a message is + * fresh/stale. + * MQTT_SUB_OPT_SEND_RETAIN_ALWAYS - with this option set, + * pre-existing retained messages are sent as soon as + * the subscription is made, even if the subscription + * already exists. This is the default behaviour, so + * it is not necessary to set this option. + * MQTT_SUB_OPT_SEND_RETAIN_NEW - with this option set, pre-existing + * retained messages for this subscription will be + * sent when the subscription is made, but only if the + * subscription does not already exist. + * MQTT_SUB_OPT_SEND_RETAIN_NEVER - with this option set, + * pre-existing retained messages will never be sent + * for this subscription. + * properties - a valid mosquitto_property list, or NULL. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. + * MOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8 + * MOSQ_ERR_DUPLICATE_PROPERTY - if a property is duplicated where it is forbidden. + * MOSQ_ERR_PROTOCOL - if any property is invalid for use with SUBSCRIBE. + * MOSQ_ERR_OVERSIZE_PACKET - if the resulting packet would be larger than + * supported by the broker. + */ +libmosq_EXPORT int mosquitto_subscribe_v5(struct mosquitto *mosq, int *mid, const char *sub, int qos, int options, const mosquitto_property *properties); + +/* + * Function: mosquitto_subscribe_multiple + * + * Subscribe to multiple topics. + * + * Parameters: + * mosq - a valid mosquitto instance. + * mid - a pointer to an int. If not NULL, the function will set this to + * the message id of this particular message. This can be then used + * with the subscribe callback to determine when the message has been + * sent. + * sub_count - the count of subscriptions to be made + * sub - array of sub_count pointers, each pointing to a subscription string. + * The "char *const *const" datatype ensures that neither the array of + * pointers nor the strings that they point to are mutable. If you aren't + * familiar with this, just think of it as a safer "char **", + * equivalent to "const char *" for a simple string pointer. + * qos - the requested Quality of Service for each subscription. + * options - options to apply to this subscription, OR'd together. Set to 0 to + * use the default options, otherwise choose from the list: + * MQTT_SUB_OPT_NO_LOCAL - with this option set, if this client + * publishes to a topic to which it is subscribed, the + * broker will not publish the message back to the + * client. + * MQTT_SUB_OPT_RETAIN_AS_PUBLISHED - with this option set, messages + * published for this subscription will keep the + * retain flag as was set by the publishing client. + * The default behaviour without this option set has + * the retain flag indicating whether a message is + * fresh/stale. + * MQTT_SUB_OPT_SEND_RETAIN_ALWAYS - with this option set, + * pre-existing retained messages are sent as soon as + * the subscription is made, even if the subscription + * already exists. This is the default behaviour, so + * it is not necessary to set this option. + * MQTT_SUB_OPT_SEND_RETAIN_NEW - with this option set, pre-existing + * retained messages for this subscription will be + * sent when the subscription is made, but only if the + * subscription does not already exist. + * MQTT_SUB_OPT_SEND_RETAIN_NEVER - with this option set, + * pre-existing retained messages will never be sent + * for this subscription. + * properties - a valid mosquitto_property list, or NULL. Only used with MQTT + * v5 clients. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. + * MOSQ_ERR_MALFORMED_UTF8 - if a topic is not valid UTF-8 + * MOSQ_ERR_OVERSIZE_PACKET - if the resulting packet would be larger than + * supported by the broker. + */ +libmosq_EXPORT int mosquitto_subscribe_multiple(struct mosquitto *mosq, int *mid, int sub_count, char *const *const sub, int qos, int options, const mosquitto_property *properties); + +/* + * Function: mosquitto_unsubscribe + * + * Unsubscribe from a topic. + * + * Parameters: + * mosq - a valid mosquitto instance. + * mid - a pointer to an int. If not NULL, the function will set this to + * the message id of this particular message. This can be then used + * with the unsubscribe callback to determine when the message has been + * sent. + * sub - the unsubscription pattern. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. + * MOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8 + * MOSQ_ERR_OVERSIZE_PACKET - if the resulting packet would be larger than + * supported by the broker. + */ +libmosq_EXPORT int mosquitto_unsubscribe(struct mosquitto *mosq, int *mid, const char *sub); + +/* + * Function: mosquitto_unsubscribe_v5 + * + * Unsubscribe from a topic, with attached MQTT properties. + * + * Parameters: + * mosq - a valid mosquitto instance. + * mid - a pointer to an int. If not NULL, the function will set this to + * the message id of this particular message. This can be then used + * with the unsubscribe callback to determine when the message has been + * sent. + * sub - the unsubscription pattern. + * properties - a valid mosquitto_property list, or NULL. Only used with MQTT + * v5 clients. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. + * MOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8 + * MOSQ_ERR_DUPLICATE_PROPERTY - if a property is duplicated where it is forbidden. + * MOSQ_ERR_PROTOCOL - if any property is invalid for use with UNSUBSCRIBE. + * MOSQ_ERR_OVERSIZE_PACKET - if the resulting packet would be larger than + * supported by the broker. + */ +libmosq_EXPORT int mosquitto_unsubscribe_v5(struct mosquitto *mosq, int *mid, const char *sub, const mosquitto_property *properties); + +/* + * Function: mosquitto_unsubscribe_multiple + * + * Unsubscribe from multiple topics. + * + * Parameters: + * mosq - a valid mosquitto instance. + * mid - a pointer to an int. If not NULL, the function will set this to + * the message id of this particular message. This can be then used + * with the subscribe callback to determine when the message has been + * sent. + * sub_count - the count of unsubscriptions to be made + * sub - array of sub_count pointers, each pointing to an unsubscription string. + * The "char *const *const" datatype ensures that neither the array of + * pointers nor the strings that they point to are mutable. If you aren't + * familiar with this, just think of it as a safer "char **", + * equivalent to "const char *" for a simple string pointer. + * properties - a valid mosquitto_property list, or NULL. Only used with MQTT + * v5 clients. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. + * MOSQ_ERR_MALFORMED_UTF8 - if a topic is not valid UTF-8 + * MOSQ_ERR_OVERSIZE_PACKET - if the resulting packet would be larger than + * supported by the broker. + */ +libmosq_EXPORT int mosquitto_unsubscribe_multiple(struct mosquitto *mosq, int *mid, int sub_count, char *const *const sub, const mosquitto_property *properties); + + +/* ====================================================================== + * + * Section: Struct mosquitto_message helper functions + * + * ====================================================================== */ +/* + * Function: mosquitto_message_copy + * + * Copy the contents of a mosquitto message to another message. + * Useful for preserving a message received in the on_message() callback. + * + * Parameters: + * dst - a pointer to a valid mosquitto_message struct to copy to. + * src - a pointer to a valid mosquitto_message struct to copy from. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * + * See Also: + * <mosquitto_message_free> + */ +libmosq_EXPORT int mosquitto_message_copy(struct mosquitto_message *dst, const struct mosquitto_message *src); + +/* + * Function: mosquitto_message_free + * + * Completely free a mosquitto_message struct. + * + * Parameters: + * message - pointer to a mosquitto_message pointer to free. + * + * See Also: + * <mosquitto_message_copy>, <mosquitto_message_free_contents> + */ +libmosq_EXPORT void mosquitto_message_free(struct mosquitto_message **message); + +/* + * Function: mosquitto_message_free_contents + * + * Free a mosquitto_message struct contents, leaving the struct unaffected. + * + * Parameters: + * message - pointer to a mosquitto_message struct to free its contents. + * + * See Also: + * <mosquitto_message_copy>, <mosquitto_message_free> + */ +libmosq_EXPORT void mosquitto_message_free_contents(struct mosquitto_message *message); + + +/* ====================================================================== + * + * Section: Network loop (managed by libmosquitto) + * + * ====================================================================== */ +/* + * Function: mosquitto_loop + * + * The main network loop for the client. You must call this frequently in order + * to keep communications between the client and broker working. If incoming + * data is present it will then be processed. Outgoing commands, from e.g. + * <mosquitto_publish>, are normally sent immediately that their function is + * called, but this is not always possible. <mosquitto_loop> will also attempt + * to send any remaining outgoing messages, which also includes commands that + * are part of the flow for messages with QoS>0. + * + * An alternative approach is to use <mosquitto_loop_start> to run the client + * loop in its own thread. + * + * This calls select() to monitor the client network socket. If you want to + * integrate mosquitto client operation with your own select() call, use + * <mosquitto_socket>, <mosquitto_loop_read>, <mosquitto_loop_write> and + * <mosquitto_loop_misc>. + * + * Threads: + * + * Parameters: + * mosq - a valid mosquitto instance. + * timeout - Maximum number of milliseconds to wait for network activity + * in the select() call before timing out. Set to 0 for instant + * return. Set negative to use the default of 1000ms. + * max_packets - this parameter is currently unused and should be set to 1 for + * future compatibility. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. + * MOSQ_ERR_CONN_LOST - if the connection to the broker was lost. + * MOSQ_ERR_PROTOCOL - if there is a protocol error communicating with the + * broker. + * MOSQ_ERR_ERRNO - if a system call returned an error. The variable errno + * contains the error code, even on Windows. + * Use strerror_r() where available or FormatMessage() on + * Windows. + * See Also: + * <mosquitto_loop_forever>, <mosquitto_loop_start>, <mosquitto_loop_stop> + */ +libmosq_EXPORT int mosquitto_loop(struct mosquitto *mosq, int timeout, int max_packets); + +/* + * Function: mosquitto_loop_forever + * + * This function call loop() for you in an infinite blocking loop. It is useful + * for the case where you only want to run the MQTT client loop in your + * program. + * + * It handles reconnecting in case server connection is lost. If you call + * mosquitto_disconnect() in a callback it will return. + * + * Parameters: + * mosq - a valid mosquitto instance. + * timeout - Maximum number of milliseconds to wait for network activity + * in the select() call before timing out. Set to 0 for instant + * return. Set negative to use the default of 1000ms. + * max_packets - this parameter is currently unused and should be set to 1 for + * future compatibility. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. + * MOSQ_ERR_CONN_LOST - if the connection to the broker was lost. + * MOSQ_ERR_PROTOCOL - if there is a protocol error communicating with the + * broker. + * MOSQ_ERR_ERRNO - if a system call returned an error. The variable errno + * contains the error code, even on Windows. + * Use strerror_r() where available or FormatMessage() on + * Windows. + * + * See Also: + * <mosquitto_loop>, <mosquitto_loop_start> + */ +libmosq_EXPORT int mosquitto_loop_forever(struct mosquitto *mosq, int timeout, int max_packets); + +/* + * Function: mosquitto_loop_start + * + * This is part of the threaded client interface. Call this once to start a new + * thread to process network traffic. This provides an alternative to + * repeatedly calling <mosquitto_loop> yourself. + * + * Parameters: + * mosq - a valid mosquitto instance. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOT_SUPPORTED - if thread support is not available. + * + * See Also: + * <mosquitto_connect_async>, <mosquitto_loop>, <mosquitto_loop_forever>, <mosquitto_loop_stop> + */ +libmosq_EXPORT int mosquitto_loop_start(struct mosquitto *mosq); + +/* + * Function: mosquitto_loop_stop + * + * This is part of the threaded client interface. Call this once to stop the + * network thread previously created with <mosquitto_loop_start>. This call + * will block until the network thread finishes. For the network thread to end, + * you must have previously called <mosquitto_disconnect> or have set the force + * parameter to true. + * + * Parameters: + * mosq - a valid mosquitto instance. + * force - set to true to force thread cancellation. If false, + * <mosquitto_disconnect> must have already been called. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOT_SUPPORTED - if thread support is not available. + * + * See Also: + * <mosquitto_loop>, <mosquitto_loop_start> + */ +libmosq_EXPORT int mosquitto_loop_stop(struct mosquitto *mosq, bool force); + + +/* ====================================================================== + * + * Section: Network loop (for use in other event loops) + * + * ====================================================================== */ +/* + * Function: mosquitto_loop_read + * + * Carry out network read operations. + * This should only be used if you are not using mosquitto_loop() and are + * monitoring the client network socket for activity yourself. + * + * Parameters: + * mosq - a valid mosquitto instance. + * max_packets - this parameter is currently unused and should be set to 1 for + * future compatibility. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. + * MOSQ_ERR_CONN_LOST - if the connection to the broker was lost. + * MOSQ_ERR_PROTOCOL - if there is a protocol error communicating with the + * broker. + * MOSQ_ERR_ERRNO - if a system call returned an error. The variable errno + * contains the error code, even on Windows. + * Use strerror_r() where available or FormatMessage() on + * Windows. + * + * See Also: + * <mosquitto_socket>, <mosquitto_loop_write>, <mosquitto_loop_misc> + */ +libmosq_EXPORT int mosquitto_loop_read(struct mosquitto *mosq, int max_packets); + +/* + * Function: mosquitto_loop_write + * + * Carry out network write operations. + * This should only be used if you are not using mosquitto_loop() and are + * monitoring the client network socket for activity yourself. + * + * Parameters: + * mosq - a valid mosquitto instance. + * max_packets - this parameter is currently unused and should be set to 1 for + * future compatibility. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. + * MOSQ_ERR_CONN_LOST - if the connection to the broker was lost. + * MOSQ_ERR_PROTOCOL - if there is a protocol error communicating with the + * broker. + * MOSQ_ERR_ERRNO - if a system call returned an error. The variable errno + * contains the error code, even on Windows. + * Use strerror_r() where available or FormatMessage() on + * Windows. + * + * See Also: + * <mosquitto_socket>, <mosquitto_loop_read>, <mosquitto_loop_misc>, <mosquitto_want_write> + */ +libmosq_EXPORT int mosquitto_loop_write(struct mosquitto *mosq, int max_packets); + +/* + * Function: mosquitto_loop_misc + * + * Carry out miscellaneous operations required as part of the network loop. + * This should only be used if you are not using mosquitto_loop() and are + * monitoring the client network socket for activity yourself. + * + * This function deals with handling PINGs and checking whether messages need + * to be retried, so should be called fairly frequently. + * + * Parameters: + * mosq - a valid mosquitto instance. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. + * + * See Also: + * <mosquitto_socket>, <mosquitto_loop_read>, <mosquitto_loop_write> + */ +libmosq_EXPORT int mosquitto_loop_misc(struct mosquitto *mosq); + + +/* ====================================================================== + * + * Section: Network loop (helper functions) + * + * ====================================================================== */ +/* + * Function: mosquitto_socket + * + * Return the socket handle for a mosquitto instance. Useful if you want to + * include a mosquitto client in your own select() calls. + * + * Parameters: + * mosq - a valid mosquitto instance. + * + * Returns: + * The socket for the mosquitto client or -1 on failure. + */ +libmosq_EXPORT int mosquitto_socket(struct mosquitto *mosq); + +/* + * Function: mosquitto_want_write + * + * Returns true if there is data ready to be written on the socket. + * + * Parameters: + * mosq - a valid mosquitto instance. + * + * See Also: + * <mosquitto_socket>, <mosquitto_loop_read>, <mosquitto_loop_write> + */ +libmosq_EXPORT bool mosquitto_want_write(struct mosquitto *mosq); + +/* + * Function: mosquitto_threaded_set + * + * Used to tell the library that your application is using threads, but not + * using <mosquitto_loop_start>. The library operates slightly differently when + * not in threaded mode in order to simplify its operation. If you are managing + * your own threads and do not use this function you will experience crashes + * due to race conditions. + * + * When using <mosquitto_loop_start>, this is set automatically. + * + * Parameters: + * mosq - a valid mosquitto instance. + * threaded - true if your application is using threads, false otherwise. + */ +libmosq_EXPORT int mosquitto_threaded_set(struct mosquitto *mosq, bool threaded); + + +/* ====================================================================== + * + * Section: Client options + * + * ====================================================================== */ +/* + * Function: mosquitto_opts_set + * + * Used to set options for the client. + * + * This function is deprecated, the replacement <mosquitto_int_option> and + * <mosquitto_void_option> functions should be used instead. + * + * Parameters: + * mosq - a valid mosquitto instance. + * option - the option to set. + * value - the option specific value. + * + * Options: + * MOSQ_OPT_PROTOCOL_VERSION + * Value must be an int, set to either MQTT_PROTOCOL_V31 or + * MQTT_PROTOCOL_V311. Must be set before the client connects. + * Defaults to MQTT_PROTOCOL_V31. + * + * MOSQ_OPT_SSL_CTX + * Pass an openssl SSL_CTX to be used when creating TLS connections + * rather than libmosquitto creating its own. This must be called + * before connecting to have any effect. If you use this option, the + * onus is on you to ensure that you are using secure settings. + * Setting to NULL means that libmosquitto will use its own SSL_CTX + * if TLS is to be used. + * This option is only available for openssl 1.1.0 and higher. + * + * MOSQ_OPT_SSL_CTX_WITH_DEFAULTS + * Value must be an int set to 1 or 0. If set to 1, then the user + * specified SSL_CTX passed in using MOSQ_OPT_SSL_CTX will have the + * default options applied to it. This means that you only need to + * change the values that are relevant to you. If you use this + * option then you must configure the TLS options as normal, i.e. + * you should use <mosquitto_tls_set> to configure the cafile/capath + * as a minimum. + * This option is only available for openssl 1.1.0 and higher. + */ +libmosq_EXPORT int mosquitto_opts_set(struct mosquitto *mosq, enum mosq_opt_t option, void *value); + +/* + * Function: mosquitto_int_option + * + * Used to set integer options for the client. + * + * Parameters: + * mosq - a valid mosquitto instance. + * option - the option to set. + * value - the option specific value. + * + * Options: + * MOSQ_OPT_PROTOCOL_VERSION + * Value must be set to either MQTT_PROTOCOL_V31, + * MQTT_PROTOCOL_V311, or MQTT_PROTOCOL_V5. Must be set before the + * client connects. Defaults to MQTT_PROTOCOL_V311. + * + * MOSQ_OPT_RECEIVE_MAXIMUM + * Value can be set between 1 and 65535 inclusive, and represents + * the maximum number of incoming QoS 1 and QoS 2 messages that this + * client wants to process at once. Defaults to 20. This option is + * not valid for MQTT v3.1 or v3.1.1 clients. + * Note that if the MQTT_PROP_RECEIVE_MAXIMUM property is in the + * proplist passed to mosquitto_connect_v5(), then that property + * will override this option. Using this option is the recommended + * method however. + * + * MOSQ_OPT_SEND_MAXIMUM + * Value can be set between 1 and 65535 inclusive, and represents + * the maximum number of outgoing QoS 1 and QoS 2 messages that this + * client will attempt to have "in flight" at once. Defaults to 20. + * This option is not valid for MQTT v3.1 or v3.1.1 clients. + * Note that if the broker being connected to sends a + * MQTT_PROP_RECEIVE_MAXIMUM property that has a lower value than + * this option, then the broker provided value will be used. + * + * MOSQ_OPT_SSL_CTX_WITH_DEFAULTS + * If value is set to a non zero value, then the user specified + * SSL_CTX passed in using MOSQ_OPT_SSL_CTX will have the default + * options applied to it. This means that you only need to change + * the values that are relevant to you. If you use this option then + * you must configure the TLS options as normal, i.e. you should + * use <mosquitto_tls_set> to configure the cafile/capath as a + * minimum. + * This option is only available for openssl 1.1.0 and higher. + * MOSQ_OPT_TLS_OCSP_REQUIRED + * Set whether OCSP checking on TLS connections is required. Set to + * 1 to enable checking, or 0 (the default) for no checking. + */ +libmosq_EXPORT int mosquitto_int_option(struct mosquitto *mosq, enum mosq_opt_t option, int value); + + +/* + * Function: mosquitto_void_option + * + * Used to set void* options for the client. + * + * Parameters: + * mosq - a valid mosquitto instance. + * option - the option to set. + * value - the option specific value. + * + * Options: + * MOSQ_OPT_SSL_CTX + * Pass an openssl SSL_CTX to be used when creating TLS connections + * rather than libmosquitto creating its own. This must be called + * before connecting to have any effect. If you use this option, the + * onus is on you to ensure that you are using secure settings. + * Setting to NULL means that libmosquitto will use its own SSL_CTX + * if TLS is to be used. + * This option is only available for openssl 1.1.0 and higher. + */ +libmosq_EXPORT int mosquitto_void_option(struct mosquitto *mosq, enum mosq_opt_t option, void *value); + + +/* + * Function: mosquitto_reconnect_delay_set + * + * Control the behaviour of the client when it has unexpectedly disconnected in + * <mosquitto_loop_forever> or after <mosquitto_loop_start>. The default + * behaviour if this function is not used is to repeatedly attempt to reconnect + * with a delay of 1 second until the connection succeeds. + * + * Use reconnect_delay parameter to change the delay between successive + * reconnection attempts. You may also enable exponential backoff of the time + * between reconnections by setting reconnect_exponential_backoff to true and + * set an upper bound on the delay with reconnect_delay_max. + * + * Example 1: + * delay=2, delay_max=10, exponential_backoff=False + * Delays would be: 2, 4, 6, 8, 10, 10, ... + * + * Example 2: + * delay=3, delay_max=30, exponential_backoff=True + * Delays would be: 3, 6, 12, 24, 30, 30, ... + * + * Parameters: + * mosq - a valid mosquitto instance. + * reconnect_delay - the number of seconds to wait between + * reconnects. + * reconnect_delay_max - the maximum number of seconds to wait + * between reconnects. + * reconnect_exponential_backoff - use exponential backoff between + * reconnect attempts. Set to true to enable + * exponential backoff. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + */ +libmosq_EXPORT int mosquitto_reconnect_delay_set(struct mosquitto *mosq, unsigned int reconnect_delay, unsigned int reconnect_delay_max, bool reconnect_exponential_backoff); + +/* + * Function: mosquitto_max_inflight_messages_set + * + * This function is deprected. Use the <mosquitto_int_option> function with the + * MOSQ_OPT_SEND_MAXIMUM option instead. + * + * Set the number of QoS 1 and 2 messages that can be "in flight" at one time. + * An in flight message is part way through its delivery flow. Attempts to send + * further messages with <mosquitto_publish> will result in the messages being + * queued until the number of in flight messages reduces. + * + * A higher number here results in greater message throughput, but if set + * higher than the maximum in flight messages on the broker may lead to + * delays in the messages being acknowledged. + * + * Set to 0 for no maximum. + * + * Parameters: + * mosq - a valid mosquitto instance. + * max_inflight_messages - the maximum number of inflight messages. Defaults + * to 20. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + */ +libmosq_EXPORT int mosquitto_max_inflight_messages_set(struct mosquitto *mosq, unsigned int max_inflight_messages); + +/* + * Function: mosquitto_message_retry_set + * + * This function now has no effect. + */ +libmosq_EXPORT void mosquitto_message_retry_set(struct mosquitto *mosq, unsigned int message_retry); + +/* + * Function: mosquitto_user_data_set + * + * When <mosquitto_new> is called, the pointer given as the "obj" parameter + * will be passed to the callbacks as user data. The <mosquitto_user_data_set> + * function allows this obj parameter to be updated at any time. This function + * will not modify the memory pointed to by the current user data pointer. If + * it is dynamically allocated memory you must free it yourself. + * + * Parameters: + * mosq - a valid mosquitto instance. + * obj - A user pointer that will be passed as an argument to any callbacks + * that are specified. + */ +libmosq_EXPORT void mosquitto_user_data_set(struct mosquitto *mosq, void *obj); + +/* Function: mosquitto_userdata + * + * Retrieve the "userdata" variable for a mosquitto client. + * + * Parameters: + * mosq - a valid mosquitto instance. + * + * Returns: + * A pointer to the userdata member variable. + */ +libmosq_EXPORT void *mosquitto_userdata(struct mosquitto *mosq); + + +/* ====================================================================== + * + * Section: TLS support + * + * ====================================================================== */ +/* + * Function: mosquitto_tls_set + * + * Configure the client for certificate based SSL/TLS support. Must be called + * before <mosquitto_connect>. + * + * Cannot be used in conjunction with <mosquitto_tls_psk_set>. + * + * Define the Certificate Authority certificates to be trusted (ie. the server + * certificate must be signed with one of these certificates) using cafile. + * + * If the server you are connecting to requires clients to provide a + * certificate, define certfile and keyfile with your client certificate and + * private key. If your private key is encrypted, provide a password callback + * function or you will have to enter the password at the command line. + * + * Parameters: + * mosq - a valid mosquitto instance. + * cafile - path to a file containing the PEM encoded trusted CA + * certificate files. Either cafile or capath must not be NULL. + * capath - path to a directory containing the PEM encoded trusted CA + * certificate files. See mosquitto.conf for more details on + * configuring this directory. Either cafile or capath must not + * be NULL. + * certfile - path to a file containing the PEM encoded certificate file + * for this client. If NULL, keyfile must also be NULL and no + * client certificate will be used. + * keyfile - path to a file containing the PEM encoded private key for + * this client. If NULL, certfile must also be NULL and no + * client certificate will be used. + * pw_callback - if keyfile is encrypted, set pw_callback to allow your client + * to pass the correct password for decryption. If set to NULL, + * the password must be entered on the command line. + * Your callback must write the password into "buf", which is + * "size" bytes long. The return value must be the length of the + * password. "userdata" will be set to the calling mosquitto + * instance. The mosquitto userdata member variable can be + * retrieved using <mosquitto_userdata>. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * + * See Also: + * <mosquitto_tls_opts_set>, <mosquitto_tls_psk_set>, + * <mosquitto_tls_insecure_set>, <mosquitto_userdata> + */ +libmosq_EXPORT 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)); + +/* + * Function: mosquitto_tls_insecure_set + * + * Configure verification of the server hostname in the server certificate. If + * value is set to true, it is impossible to guarantee that the host you are + * connecting to is not impersonating your server. This can be useful in + * initial server testing, but makes it possible for a malicious third party to + * impersonate your server through DNS spoofing, for example. + * Do not use this function in a real system. Setting value to true makes the + * connection encryption pointless. + * Must be called before <mosquitto_connect>. + * + * Parameters: + * mosq - a valid mosquitto instance. + * value - if set to false, the default, certificate hostname checking is + * performed. If set to true, no hostname checking is performed and + * the connection is insecure. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * + * See Also: + * <mosquitto_tls_set> + */ +libmosq_EXPORT int mosquitto_tls_insecure_set(struct mosquitto *mosq, bool value); + +/* + * Function: mosquitto_tls_opts_set + * + * Set advanced SSL/TLS options. Must be called before <mosquitto_connect>. + * + * Parameters: + * mosq - a valid mosquitto instance. + * cert_reqs - an integer defining the verification requirements the client + * will impose on the server. This can be one of: + * * SSL_VERIFY_NONE (0): the server will not be verified in any way. + * * SSL_VERIFY_PEER (1): the server certificate will be verified + * and the connection aborted if the verification fails. + * The default and recommended value is SSL_VERIFY_PEER. Using + * SSL_VERIFY_NONE provides no security. + * tls_version - the version of the SSL/TLS protocol to use as a string. If NULL, + * the default value is used. The default value and the + * available values depend on the version of openssl that the + * library was compiled against. For openssl >= 1.0.1, the + * available options are tlsv1.2, tlsv1.1 and tlsv1, with tlv1.2 + * as the default. For openssl < 1.0.1, only tlsv1 is available. + * ciphers - a string describing the ciphers available for use. See the + * "openssl ciphers" tool for more information. If NULL, the + * default ciphers will be used. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * + * See Also: + * <mosquitto_tls_set> + */ +libmosq_EXPORT int mosquitto_tls_opts_set(struct mosquitto *mosq, int cert_reqs, const char *tls_version, const char *ciphers); + +/* + * Function: mosquitto_tls_psk_set + * + * Configure the client for pre-shared-key based TLS support. Must be called + * before <mosquitto_connect>. + * + * Cannot be used in conjunction with <mosquitto_tls_set>. + * + * Parameters: + * mosq - a valid mosquitto instance. + * psk - the pre-shared-key in hex format with no leading "0x". + * identity - the identity of this client. May be used as the username + * depending on the server settings. + * ciphers - a string describing the PSK ciphers available for use. See the + * "openssl ciphers" tool for more information. If NULL, the + * default ciphers will be used. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * + * See Also: + * <mosquitto_tls_set> + */ +libmosq_EXPORT int mosquitto_tls_psk_set(struct mosquitto *mosq, const char *psk, const char *identity, const char *ciphers); + + +/* ====================================================================== + * + * Section: Callbacks + * + * ====================================================================== */ +/* + * Function: mosquitto_connect_callback_set + * + * Set the connect callback. This is called when the broker sends a CONNACK + * message in response to a connection. + * + * Parameters: + * mosq - a valid mosquitto instance. + * on_connect - a callback function in the following form: + * void callback(struct mosquitto *mosq, void *obj, int rc) + * + * Callback Parameters: + * mosq - the mosquitto instance making the callback. + * obj - the user data provided in <mosquitto_new> + * rc - the return code of the connection response, one of: + * + * * 0 - success + * * 1 - connection refused (unacceptable protocol version) + * * 2 - connection refused (identifier rejected) + * * 3 - connection refused (broker unavailable) + * * 4-255 - reserved for future use + */ +libmosq_EXPORT void mosquitto_connect_callback_set(struct mosquitto *mosq, void (*on_connect)(struct mosquitto *, void *, int)); + +/* + * Function: mosquitto_connect_with_flags_callback_set + * + * Set the connect callback. This is called when the broker sends a CONNACK + * message in response to a connection. + * + * Parameters: + * mosq - a valid mosquitto instance. + * on_connect - a callback function in the following form: + * void callback(struct mosquitto *mosq, void *obj, int rc) + * + * Callback Parameters: + * mosq - the mosquitto instance making the callback. + * obj - the user data provided in <mosquitto_new> + * rc - the return code of the connection response, one of: + * flags - the connect flags. + * + * * 0 - success + * * 1 - connection refused (unacceptable protocol version) + * * 2 - connection refused (identifier rejected) + * * 3 - connection refused (broker unavailable) + * * 4-255 - reserved for future use + */ +libmosq_EXPORT void mosquitto_connect_with_flags_callback_set(struct mosquitto *mosq, void (*on_connect)(struct mosquitto *, void *, int, int)); + +/* + * Function: mosquitto_connect_v5_callback_set + * + * Set the connect callback. This is called when the broker sends a CONNACK + * message in response to a connection. + * + * Parameters: + * mosq - a valid mosquitto instance. + * on_connect - a callback function in the following form: + * void callback(struct mosquitto *mosq, void *obj, int rc) + * + * Callback Parameters: + * mosq - the mosquitto instance making the callback. + * obj - the user data provided in <mosquitto_new> + * rc - the return code of the connection response, one of: + * * 0 - success + * * 1 - connection refused (unacceptable protocol version) + * * 2 - connection refused (identifier rejected) + * * 3 - connection refused (broker unavailable) + * * 4-255 - reserved for future use + * flags - the connect flags. + * props - list of MQTT 5 properties, or NULL + * + */ +libmosq_EXPORT void mosquitto_connect_v5_callback_set(struct mosquitto *mosq, void (*on_connect)(struct mosquitto *, void *, int, int, const mosquitto_property *props)); + +/* + * Function: mosquitto_disconnect_callback_set + * + * Set the disconnect callback. This is called when the broker has received the + * DISCONNECT command and has disconnected the client. + * + * Parameters: + * mosq - a valid mosquitto instance. + * on_disconnect - a callback function in the following form: + * void callback(struct mosquitto *mosq, void *obj) + * + * Callback Parameters: + * mosq - the mosquitto instance making the callback. + * obj - the user data provided in <mosquitto_new> + * rc - integer value indicating the reason for the disconnect. A value of 0 + * means the client has called <mosquitto_disconnect>. Any other value + * indicates that the disconnect is unexpected. + */ +libmosq_EXPORT void mosquitto_disconnect_callback_set(struct mosquitto *mosq, void (*on_disconnect)(struct mosquitto *, void *, int)); + +/* + * Function: mosquitto_disconnect_v5_callback_set + * + * Set the disconnect callback. This is called when the broker has received the + * DISCONNECT command and has disconnected the client. + * + * Parameters: + * mosq - a valid mosquitto instance. + * on_disconnect - a callback function in the following form: + * void callback(struct mosquitto *mosq, void *obj) + * + * Callback Parameters: + * mosq - the mosquitto instance making the callback. + * obj - the user data provided in <mosquitto_new> + * rc - integer value indicating the reason for the disconnect. A value of 0 + * means the client has called <mosquitto_disconnect>. Any other value + * indicates that the disconnect is unexpected. + * props - list of MQTT 5 properties, or NULL + */ +libmosq_EXPORT void mosquitto_disconnect_v5_callback_set(struct mosquitto *mosq, void (*on_disconnect)(struct mosquitto *, void *, int, const mosquitto_property *)); + +/* + * Function: mosquitto_publish_callback_set + * + * Set the publish callback. This is called when a message initiated with + * <mosquitto_publish> has been sent to the broker successfully. + * + * Parameters: + * mosq - a valid mosquitto instance. + * on_publish - a callback function in the following form: + * void callback(struct mosquitto *mosq, void *obj, int mid) + * + * Callback Parameters: + * mosq - the mosquitto instance making the callback. + * obj - the user data provided in <mosquitto_new> + * mid - the message id of the sent message. + */ +libmosq_EXPORT void mosquitto_publish_callback_set(struct mosquitto *mosq, void (*on_publish)(struct mosquitto *, void *, int)); + +/* + * Function: mosquitto_publish_v5_callback_set + * + * Set the publish callback. This is called when a message initiated with + * <mosquitto_publish> has been sent to the broker. This callback will be + * called both if the message is sent successfully, or if the broker responded + * with an error, which will be reflected in the reason_code parameter. + * + * Parameters: + * mosq - a valid mosquitto instance. + * on_publish - a callback function in the following form: + * void callback(struct mosquitto *mosq, void *obj, int mid) + * + * Callback Parameters: + * mosq - the mosquitto instance making the callback. + * obj - the user data provided in <mosquitto_new> + * mid - the message id of the sent message. + * reason_code - the MQTT 5 reason code + * props - list of MQTT 5 properties, or NULL + */ +libmosq_EXPORT void mosquitto_publish_v5_callback_set(struct mosquitto *mosq, void (*on_publish)(struct mosquitto *, void *, int, int, const mosquitto_property *)); + +/* + * Function: mosquitto_message_callback_set + * + * Set the message callback. This is called when a message is received from the + * broker. + * + * Parameters: + * mosq - a valid mosquitto instance. + * on_message - a callback function in the following form: + * void callback(struct mosquitto *mosq, void *obj, const struct mosquitto_message *message) + * + * Callback Parameters: + * mosq - the mosquitto instance making the callback. + * obj - the user data provided in <mosquitto_new> + * message - the message data. This variable and associated memory will be + * freed by the library after the callback completes. The client + * should make copies of any of the data it requires. + * + * See Also: + * <mosquitto_message_copy> + */ +libmosq_EXPORT void mosquitto_message_callback_set(struct mosquitto *mosq, void (*on_message)(struct mosquitto *, void *, const struct mosquitto_message *)); + +/* + * Function: mosquitto_message_v5_callback_set + * + * Set the message callback. This is called when a message is received from the + * broker. + * + * Parameters: + * mosq - a valid mosquitto instance. + * on_message - a callback function in the following form: + * void callback(struct mosquitto *mosq, void *obj, const struct mosquitto_message *message) + * + * Callback Parameters: + * mosq - the mosquitto instance making the callback. + * obj - the user data provided in <mosquitto_new> + * message - the message data. This variable and associated memory will be + * freed by the library after the callback completes. The client + * should make copies of any of the data it requires. + * props - list of MQTT 5 properties, or NULL + * + * See Also: + * <mosquitto_message_copy> + */ +libmosq_EXPORT void mosquitto_message_v5_callback_set(struct mosquitto *mosq, void (*on_message)(struct mosquitto *, void *, const struct mosquitto_message *, const mosquitto_property *)); + +/* + * Function: mosquitto_subscribe_callback_set + * + * Set the subscribe callback. This is called when the broker responds to a + * subscription request. + * + * Parameters: + * mosq - a valid mosquitto instance. + * on_subscribe - a callback function in the following form: + * void callback(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos) + * + * Callback Parameters: + * mosq - the mosquitto instance making the callback. + * obj - the user data provided in <mosquitto_new> + * mid - the message id of the subscribe message. + * qos_count - the number of granted subscriptions (size of granted_qos). + * granted_qos - an array of integers indicating the granted QoS for each of + * the subscriptions. + */ +libmosq_EXPORT void mosquitto_subscribe_callback_set(struct mosquitto *mosq, void (*on_subscribe)(struct mosquitto *, void *, int, int, const int *)); + +/* + * Function: mosquitto_subscribe_v5_callback_set + * + * Set the subscribe callback. This is called when the broker responds to a + * subscription request. + * + * Parameters: + * mosq - a valid mosquitto instance. + * on_subscribe - a callback function in the following form: + * void callback(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos) + * + * Callback Parameters: + * mosq - the mosquitto instance making the callback. + * obj - the user data provided in <mosquitto_new> + * mid - the message id of the subscribe message. + * qos_count - the number of granted subscriptions (size of granted_qos). + * granted_qos - an array of integers indicating the granted QoS for each of + * the subscriptions. + * props - list of MQTT 5 properties, or NULL + */ +libmosq_EXPORT void mosquitto_subscribe_v5_callback_set(struct mosquitto *mosq, void (*on_subscribe)(struct mosquitto *, void *, int, int, const int *, const mosquitto_property *)); + +/* + * Function: mosquitto_unsubscribe_callback_set + * + * Set the unsubscribe callback. This is called when the broker responds to a + * unsubscription request. + * + * Parameters: + * mosq - a valid mosquitto instance. + * on_unsubscribe - a callback function in the following form: + * void callback(struct mosquitto *mosq, void *obj, int mid) + * + * Callback Parameters: + * mosq - the mosquitto instance making the callback. + * obj - the user data provided in <mosquitto_new> + * mid - the message id of the unsubscribe message. + */ +libmosq_EXPORT void mosquitto_unsubscribe_callback_set(struct mosquitto *mosq, void (*on_unsubscribe)(struct mosquitto *, void *, int)); + +/* + * Function: mosquitto_unsubscribe_v5_callback_set + * + * Set the unsubscribe callback. This is called when the broker responds to a + * unsubscription request. + * + * Parameters: + * mosq - a valid mosquitto instance. + * on_unsubscribe - a callback function in the following form: + * void callback(struct mosquitto *mosq, void *obj, int mid) + * + * Callback Parameters: + * mosq - the mosquitto instance making the callback. + * obj - the user data provided in <mosquitto_new> + * mid - the message id of the unsubscribe message. + * props - list of MQTT 5 properties, or NULL + */ +libmosq_EXPORT void mosquitto_unsubscribe_v5_callback_set(struct mosquitto *mosq, void (*on_unsubscribe)(struct mosquitto *, void *, int, const mosquitto_property *)); + +/* + * Function: mosquitto_log_callback_set + * + * Set the logging callback. This should be used if you want event logging + * information from the client library. + * + * mosq - a valid mosquitto instance. + * on_log - a callback function in the following form: + * void callback(struct mosquitto *mosq, void *obj, int level, const char *str) + * + * Callback Parameters: + * mosq - the mosquitto instance making the callback. + * obj - the user data provided in <mosquitto_new> + * level - the log message level from the values: + * MOSQ_LOG_INFO + * MOSQ_LOG_NOTICE + * MOSQ_LOG_WARNING + * MOSQ_LOG_ERR + * MOSQ_LOG_DEBUG + * str - the message string. + */ +libmosq_EXPORT void mosquitto_log_callback_set(struct mosquitto *mosq, void (*on_log)(struct mosquitto *, void *, int, const char *)); + +/* + * Function: mosquitto_string_option + * + * Used to set const char* options for the client. + * + * Parameters: + * mosq - a valid mosquitto instance. + * option - the option to set. + * value - the option specific value. + * + * Options: + * MOSQ_OPT_TLS_ENGINE + * Configure the client for TLS Engine support. Pass a TLS Engine ID + * to be used when creating TLS connections. + * Must be set before <mosquitto_connect>. + * MOSQ_OPT_TLS_KEYFORM + * Configure the client to treat the keyfile differently depending + * on its type. Must be set before <mosquitto_connect>. + * Set as either "pem" or "engine", to determine from where the + * private key for a TLS connection will be obtained. Defaults to + * "pem", a normal private key file. + * MOSQ_OPT_TLS_KPASS_SHA1 + * Where the TLS Engine requires the use of a password to be + * accessed, this option allows a hex encoded SHA1 hash of the + * private key password to be passed to the engine directly. + * Must be set before <mosquitto_connect>. + * MOSQ_OPT_TLS_ALPN + * If the broker being connected to has multiple services available + * on a single TLS port, such as both MQTT and WebSockets, use this + * option to configure the ALPN option for the connection. + */ +libmosq_EXPORT int mosquitto_string_option(struct mosquitto *mosq, enum mosq_opt_t option, const char *value); + + +/* + * Function: mosquitto_reconnect_delay_set + * + * Control the behaviour of the client when it has unexpectedly disconnected in + * <mosquitto_loop_forever> or after <mosquitto_loop_start>. The default + * behaviour if this function is not used is to repeatedly attempt to reconnect + * with a delay of 1 second until the connection succeeds. + * + * Use reconnect_delay parameter to change the delay between successive + * reconnection attempts. You may also enable exponential backoff of the time + * between reconnections by setting reconnect_exponential_backoff to true and + * set an upper bound on the delay with reconnect_delay_max. + * + * Example 1: + * delay=2, delay_max=10, exponential_backoff=False + * Delays would be: 2, 4, 6, 8, 10, 10, ... + * + * Example 2: + * delay=3, delay_max=30, exponential_backoff=True + * Delays would be: 3, 6, 12, 24, 30, 30, ... + * + * Parameters: + * mosq - a valid mosquitto instance. + * reconnect_delay - the number of seconds to wait between + * reconnects. + * reconnect_delay_max - the maximum number of seconds to wait + * between reconnects. + * reconnect_exponential_backoff - use exponential backoff between + * reconnect attempts. Set to true to enable + * exponential backoff. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + */ +libmosq_EXPORT int mosquitto_reconnect_delay_set(struct mosquitto *mosq, unsigned int reconnect_delay, unsigned int reconnect_delay_max, bool reconnect_exponential_backoff); + + +/* ============================================================================= + * + * Section: SOCKS5 proxy functions + * + * ============================================================================= + */ + +/* + * Function: mosquitto_socks5_set + * + * Configure the client to use a SOCKS5 proxy when connecting. Must be called + * before connecting. "None" and "username/password" authentication is + * supported. + * + * Parameters: + * mosq - a valid mosquitto instance. + * host - the SOCKS5 proxy host to connect to. + * port - the SOCKS5 proxy port to use. + * username - if not NULL, use this username when authenticating with the proxy. + * password - if not NULL and username is not NULL, use this password when + * authenticating with the proxy. + */ +libmosq_EXPORT int mosquitto_socks5_set(struct mosquitto *mosq, const char *host, int port, const char *username, const char *password); + + +/* ============================================================================= + * + * Section: Utility functions + * + * ============================================================================= + */ + +/* + * Function: mosquitto_strerror + * + * Call to obtain a const string description of a mosquitto error number. + * + * Parameters: + * mosq_errno - a mosquitto error number. + * + * Returns: + * A constant string describing the error. + */ +libmosq_EXPORT const char *mosquitto_strerror(int mosq_errno); + +/* + * Function: mosquitto_connack_string + * + * Call to obtain a const string description of an MQTT connection result. + * + * Parameters: + * connack_code - an MQTT connection result. + * + * Returns: + * A constant string describing the result. + */ +libmosq_EXPORT const char *mosquitto_connack_string(int connack_code); + +/* + * Function: mosquitto_reason_string + * + * Call to obtain a const string description of an MQTT reason code. + * + * Parameters: + * reason_code - an MQTT reason code. + * + * Returns: + * A constant string describing the reason. + */ +libmosq_EXPORT const char *mosquitto_reason_string(int reason_code); + +/* Function: mosquitto_string_to_command + * + * Take a string input representing an MQTT command and convert it to the + * libmosquitto integer representation. + * + * Parameters: + * str - the string to parse. + * cmd - pointer to an int, for the result. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - on an invalid input. + * + * Example: + * mosquitto_string_to_command("CONNECT", &cmd); + * // cmd == CMD_CONNECT + */ +libmosq_EXPORT int mosquitto_string_to_command(const char *str, int *cmd); + +/* + * Function: mosquitto_sub_topic_tokenise + * + * Tokenise a topic or subscription string into an array of strings + * representing the topic hierarchy. + * + * For example: + * + * subtopic: "a/deep/topic/hierarchy" + * + * Would result in: + * + * topics[0] = "a" + * topics[1] = "deep" + * topics[2] = "topic" + * topics[3] = "hierarchy" + * + * and: + * + * subtopic: "/a/deep/topic/hierarchy/" + * + * Would result in: + * + * topics[0] = NULL + * topics[1] = "a" + * topics[2] = "deep" + * topics[3] = "topic" + * topics[4] = "hierarchy" + * + * Parameters: + * subtopic - the subscription/topic to tokenise + * topics - a pointer to store the array of strings + * count - an int pointer to store the number of items in the topics array. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * MOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8 + * + * Example: + * + * > char **topics; + * > int topic_count; + * > int i; + * > + * > mosquitto_sub_topic_tokenise("$SYS/broker/uptime", &topics, &topic_count); + * > + * > for(i=0; i<token_count; i++){ + * > printf("%d: %s\n", i, topics[i]); + * > } + * + * See Also: + * <mosquitto_sub_topic_tokens_free> + */ +libmosq_EXPORT int mosquitto_sub_topic_tokenise(const char *subtopic, char ***topics, int *count); + +/* + * Function: mosquitto_sub_topic_tokens_free + * + * Free memory that was allocated in <mosquitto_sub_topic_tokenise>. + * + * Parameters: + * topics - pointer to string array. + * count - count of items in string array. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * + * See Also: + * <mosquitto_sub_topic_tokenise> + */ +libmosq_EXPORT int mosquitto_sub_topic_tokens_free(char ***topics, int count); + +/* + * Function: mosquitto_topic_matches_sub + * Function: mosquitto_topic_matches_sub2 + * + * Check whether a topic matches a subscription. + * + * For example: + * + * foo/bar would match the subscription foo/# or +/bar + * non/matching would not match the subscription non/+/+ + * + * Parameters: + * sub - subscription string to check topic against. + * sublen - length in bytes of sub string + * topic - topic to check. + * topiclen - length in bytes of topic string + * result - bool pointer to hold result. Will be set to true if the topic + * matches the subscription. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + */ +libmosq_EXPORT int mosquitto_topic_matches_sub(const char *sub, const char *topic, bool *result); +libmosq_EXPORT int mosquitto_topic_matches_sub2(const char *sub, size_t sublen, const char *topic, size_t topiclen, bool *result); + +/* + * Function: mosquitto_pub_topic_check + * + * Check whether a topic to be used for publishing is valid. + * + * This searches for + or # in a topic and checks its length. + * + * This check is already carried out in <mosquitto_publish> and + * <mosquitto_will_set>, there is no need to call it directly before them. It + * may be useful if you wish to check the validity of a topic in advance of + * making a connection for example. + * + * Parameters: + * topic - the topic to check + * topiclen - length of the topic in bytes + * + * Returns: + * MOSQ_ERR_SUCCESS - for a valid topic + * MOSQ_ERR_INVAL - if the topic contains a + or a #, or if it is too long. + * MOSQ_ERR_MALFORMED_UTF8 - if sub or topic is not valid UTF-8 + * + * See Also: + * <mosquitto_sub_topic_check> + */ +libmosq_EXPORT int mosquitto_pub_topic_check(const char *topic); +libmosq_EXPORT int mosquitto_pub_topic_check2(const char *topic, size_t topiclen); + +/* + * Function: mosquitto_sub_topic_check + * + * Check whether a topic to be used for subscribing is valid. + * + * This searches for + or # in a topic and checks that they aren't in invalid + * positions, such as with foo/#/bar, foo/+bar or foo/bar#, and checks its + * length. + * + * This check is already carried out in <mosquitto_subscribe> and + * <mosquitto_unsubscribe>, there is no need to call it directly before them. + * It may be useful if you wish to check the validity of a topic in advance of + * making a connection for example. + * + * Parameters: + * topic - the topic to check + * topiclen - the length in bytes of the topic + * + * Returns: + * MOSQ_ERR_SUCCESS - for a valid topic + * MOSQ_ERR_INVAL - if the topic contains a + or a # that is in an + * invalid position, or if it is too long. + * MOSQ_ERR_MALFORMED_UTF8 - if topic is not valid UTF-8 + * + * See Also: + * <mosquitto_sub_topic_check> + */ +libmosq_EXPORT int mosquitto_sub_topic_check(const char *topic); +libmosq_EXPORT int mosquitto_sub_topic_check2(const char *topic, size_t topiclen); + + +struct libmosquitto_will { + char *topic; + void *payload; + int payloadlen; + int qos; + bool retain; +}; + +struct libmosquitto_auth { + char *username; + char *password; +}; + +struct libmosquitto_tls { + char *cafile; + char *capath; + char *certfile; + char *keyfile; + char *ciphers; + char *tls_version; + int (*pw_callback)(char *buf, int size, int rwflag, void *userdata); + int cert_reqs; +}; + +/* + * Function: mosquitto_subscribe_simple + * + * Helper function to make subscribing to a topic and retrieving some messages + * very straightforward. + * + * This connects to a broker, subscribes to a topic, waits for msg_count + * messages to be received, then returns after disconnecting cleanly. + * + * Parameters: + * messages - pointer to a "struct mosquitto_message *". The received + * messages will be returned here. On error, this will be set to + * NULL. + * msg_count - the number of messages to retrieve. + * want_retained - if set to true, stale retained messages will be treated as + * normal messages with regards to msg_count. If set to + * false, they will be ignored. + * topic - the subscription topic to use (wildcards are allowed). + * qos - the qos to use for the subscription. + * host - the broker to connect to. + * port - the network port the broker is listening on. + * client_id - the client id to use, or NULL if a random client id should be + * generated. + * keepalive - the MQTT keepalive value. + * clean_session - the MQTT clean session flag. + * username - the username string, or NULL for no username authentication. + * password - the password string, or NULL for an empty password. + * will - a libmosquitto_will struct containing will information, or NULL for + * no will. + * tls - a libmosquitto_tls struct containing TLS related parameters, or NULL + * for no use of TLS. + * + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * >0 - on error. + */ +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); + + +/* + * Function: mosquitto_subscribe_callback + * + * Helper function to make subscribing to a topic and processing some messages + * very straightforward. + * + * This connects to a broker, subscribes to a topic, then passes received + * messages to a user provided callback. If the callback returns a 1, it then + * disconnects cleanly and returns. + * + * Parameters: + * callback - a callback function in the following form: + * int callback(struct mosquitto *mosq, void *obj, const struct mosquitto_message *message) + * Note that this is the same as the normal on_message callback, + * except that it returns an int. + * userdata - user provided pointer that will be passed to the callback. + * topic - the subscription topic to use (wildcards are allowed). + * qos - the qos to use for the subscription. + * host - the broker to connect to. + * port - the network port the broker is listening on. + * client_id - the client id to use, or NULL if a random client id should be + * generated. + * keepalive - the MQTT keepalive value. + * clean_session - the MQTT clean session flag. + * username - the username string, or NULL for no username authentication. + * password - the password string, or NULL for an empty password. + * will - a libmosquitto_will struct containing will information, or NULL for + * no will. + * tls - a libmosquitto_tls struct containing TLS related parameters, or NULL + * for no use of TLS. + * + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * >0 - on error. + */ +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); + + +/* + * Function: mosquitto_validate_utf8 + * + * Helper function to validate whether a UTF-8 string is valid, according to + * the UTF-8 spec and the MQTT additions. + * + * Parameters: + * str - a string to check + * len - the length of the string in bytes + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if str is NULL or len<0 or len>65536 + * MOSQ_ERR_MALFORMED_UTF8 - if str is not valid UTF-8 + */ +libmosq_EXPORT int mosquitto_validate_utf8(const char *str, int len); + + +/* ============================================================================= + * + * Section: Properties + * + * ============================================================================= + */ + + +/* + * Function: mosquitto_property_add_byte + * + * Add a new byte property to a property list. + * + * If *proplist == NULL, a new list will be created, otherwise the new property + * will be appended to the list. + * + * Parameters: + * proplist - pointer to mosquitto_property pointer, the list of properties + * identifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR) + * value - integer value for the new property + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if identifier is invalid, or if proplist is NULL + * MOSQ_ERR_NOMEM - on out of memory + * + * Example: + * mosquitto_property *proplist = NULL; + * mosquitto_property_add_byte(&proplist, MQTT_PROP_PAYLOAD_FORMAT_IDENTIFIER, 1); + */ +libmosq_EXPORT int mosquitto_property_add_byte(mosquitto_property **proplist, int identifier, uint8_t value); + +/* + * Function: mosquitto_property_add_int16 + * + * Add a new int16 property to a property list. + * + * If *proplist == NULL, a new list will be created, otherwise the new property + * will be appended to the list. + * + * Parameters: + * proplist - pointer to mosquitto_property pointer, the list of properties + * identifier - property identifier (e.g. MQTT_PROP_RECEIVE_MAXIMUM) + * value - integer value for the new property + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if identifier is invalid, or if proplist is NULL + * MOSQ_ERR_NOMEM - on out of memory + * + * Example: + * mosquitto_property *proplist = NULL; + * mosquitto_property_add_int16(&proplist, MQTT_PROP_RECEIVE_MAXIMUM, 1000); + */ +libmosq_EXPORT int mosquitto_property_add_int16(mosquitto_property **proplist, int identifier, uint16_t value); + +/* + * Function: mosquitto_property_add_int32 + * + * Add a new int32 property to a property list. + * + * If *proplist == NULL, a new list will be created, otherwise the new property + * will be appended to the list. + * + * Parameters: + * proplist - pointer to mosquitto_property pointer, the list of properties + * identifier - property identifier (e.g. MQTT_PROP_MESSAGE_EXPIRY_INTERVAL) + * value - integer value for the new property + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if identifier is invalid, or if proplist is NULL + * MOSQ_ERR_NOMEM - on out of memory + * + * Example: + * mosquitto_property *proplist = NULL; + * mosquitto_property_add_int32(&proplist, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, 86400); + */ +libmosq_EXPORT int mosquitto_property_add_int32(mosquitto_property **proplist, int identifier, uint32_t value); + +/* + * Function: mosquitto_property_add_varint + * + * Add a new varint property to a property list. + * + * If *proplist == NULL, a new list will be created, otherwise the new property + * will be appended to the list. + * + * Parameters: + * proplist - pointer to mosquitto_property pointer, the list of properties + * identifier - property identifier (e.g. MQTT_PROP_SUBSCRIPTION_IDENTIFIER) + * value - integer value for the new property + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if identifier is invalid, or if proplist is NULL + * MOSQ_ERR_NOMEM - on out of memory + * + * Example: + * mosquitto_property *proplist = NULL; + * mosquitto_property_add_varint(&proplist, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 1); + */ +libmosq_EXPORT int mosquitto_property_add_varint(mosquitto_property **proplist, int identifier, uint32_t value); + +/* + * Function: mosquitto_property_add_binary + * + * Add a new binary property to a property list. + * + * If *proplist == NULL, a new list will be created, otherwise the new property + * will be appended to the list. + * + * Parameters: + * proplist - pointer to mosquitto_property pointer, the list of properties + * identifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR) + * value - pointer to the property data + * len - length of property data in bytes + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if identifier is invalid, or if proplist is NULL + * MOSQ_ERR_NOMEM - on out of memory + * + * Example: + * mosquitto_property *proplist = NULL; + * mosquitto_property_add_binary(&proplist, MQTT_PROP_AUTHENTICATION_DATA, auth_data, auth_data_len); + */ +libmosq_EXPORT int mosquitto_property_add_binary(mosquitto_property **proplist, int identifier, const void *value, uint16_t len); + +/* + * Function: mosquitto_property_add_string + * + * Add a new string property to a property list. + * + * If *proplist == NULL, a new list will be created, otherwise the new property + * will be appended to the list. + * + * Parameters: + * proplist - pointer to mosquitto_property pointer, the list of properties + * identifier - property identifier (e.g. MQTT_PROP_CONTENT_TYPE) + * value - string value for the new property, must be UTF-8 and zero terminated + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if identifier is invalid, if value is NULL, or if proplist is NULL + * MOSQ_ERR_NOMEM - on out of memory + * MOSQ_ERR_MALFORMED_UTF8 - value is not valid UTF-8. + * + * Example: + * mosquitto_property *proplist = NULL; + * mosquitto_property_add_string(&proplist, MQTT_PROP_CONTENT_TYPE, "application/json"); + */ +libmosq_EXPORT int mosquitto_property_add_string(mosquitto_property **proplist, int identifier, const char *value); + +/* + * Function: mosquitto_property_add_string_pair + * + * Add a new string pair property to a property list. + * + * If *proplist == NULL, a new list will be created, otherwise the new property + * will be appended to the list. + * + * Parameters: + * proplist - pointer to mosquitto_property pointer, the list of properties + * identifier - property identifier (e.g. MQTT_PROP_USER_PROPERTY) + * name - string name for the new property, must be UTF-8 and zero terminated + * value - string value for the new property, must be UTF-8 and zero terminated + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if identifier is invalid, if name or value is NULL, or if proplist is NULL + * MOSQ_ERR_NOMEM - on out of memory + * MOSQ_ERR_MALFORMED_UTF8 - if name or value are not valid UTF-8. + * + * Example: + * mosquitto_property *proplist = NULL; + * mosquitto_property_add_string_pair(&proplist, MQTT_PROP_USER_PROPERTY, "client", "mosquitto_pub"); + */ +libmosq_EXPORT int mosquitto_property_add_string_pair(mosquitto_property **proplist, int identifier, const char *name, const char *value); + +/* + * Function: mosquitto_property_read_byte + * + * Attempt to read a byte property matching an identifier, from a property list + * or single property. This function can search for multiple entries of the + * same identifier by using the returned value and skip_first. Note however + * that it is forbidden for most properties to be duplicated. + * + * If the property is not found, *value will not be modified, so it is safe to + * pass a variable with a default value to be potentially overwritten: + * + * uint16_t keepalive = 60; // default value + * // Get value from property list, or keep default if not found. + * mosquitto_property_read_int16(proplist, MQTT_PROP_SERVER_KEEP_ALIVE, &keepalive, false); + * + * Parameters: + * proplist - mosquitto_property pointer, the list of properties or single property + * identifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR) + * value - pointer to store the value, or NULL if the value is not required. + * skip_first - boolean that indicates whether the first item in the list + * should be ignored or not. Should usually be set to false. + * + * Returns: + * A valid property pointer if the property is found + * NULL, if the property is not found, or proplist is NULL. + * + * Example: + * // proplist is obtained from a callback + * mosquitto_property *prop; + * prop = mosquitto_property_read_byte(proplist, identifier, &value, false); + * while(prop){ + * printf("value: %s\n", value); + * prop = mosquitto_property_read_byte(prop, identifier, &value); + * } + */ +libmosq_EXPORT const mosquitto_property *mosquitto_property_read_byte( + const mosquitto_property *proplist, + int identifier, + uint8_t *value, + bool skip_first); + +/* + * Function: mosquitto_property_read_int16 + * + * Read an int16 property value from a property. + * + * Parameters: + * property - property to read + * identifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR) + * value - pointer to store the value, or NULL if the value is not required. + * skip_first - boolean that indicates whether the first item in the list + * should be ignored or not. Should usually be set to false. + * + * Returns: + * A valid property pointer if the property is found + * NULL, if the property is not found, or proplist is NULL. + * + * Example: + * See <mosquitto_property_read_byte> + */ +libmosq_EXPORT const mosquitto_property *mosquitto_property_read_int16( + const mosquitto_property *proplist, + int identifier, + uint16_t *value, + bool skip_first); + +/* + * Function: mosquitto_property_read_int32 + * + * Read an int32 property value from a property. + * + * Parameters: + * property - pointer to mosquitto_property pointer, the list of properties + * identifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR) + * value - pointer to store the value, or NULL if the value is not required. + * skip_first - boolean that indicates whether the first item in the list + * should be ignored or not. Should usually be set to false. + * + * Returns: + * A valid property pointer if the property is found + * NULL, if the property is not found, or proplist is NULL. + * + * Example: + * See <mosquitto_property_read_byte> + */ +libmosq_EXPORT const mosquitto_property *mosquitto_property_read_int32( + const mosquitto_property *proplist, + int identifier, + uint32_t *value, + bool skip_first); + +/* + * Function: mosquitto_property_read_varint + * + * Read a varint property value from a property. + * + * Parameters: + * property - property to read + * identifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR) + * value - pointer to store the value, or NULL if the value is not required. + * skip_first - boolean that indicates whether the first item in the list + * should be ignored or not. Should usually be set to false. + * + * Returns: + * A valid property pointer if the property is found + * NULL, if the property is not found, or proplist is NULL. + * + * Example: + * See <mosquitto_property_read_byte> + */ +libmosq_EXPORT const mosquitto_property *mosquitto_property_read_varint( + const mosquitto_property *proplist, + int identifier, + uint32_t *value, + bool skip_first); + +/* + * Function: mosquitto_property_read_binary + * + * Read a binary property value from a property. + * + * On success, value must be free()'d by the application. + * + * Parameters: + * property - property to read + * identifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR) + * value - pointer to store the value, or NULL if the value is not required. + * skip_first - boolean that indicates whether the first item in the list + * should be ignored or not. Should usually be set to false. + * + * Returns: + * A valid property pointer if the property is found + * NULL, if the property is not found, or proplist is NULL, or if an out of memory condition occurred. + * + * Example: + * See <mosquitto_property_read_byte> + */ +libmosq_EXPORT const mosquitto_property *mosquitto_property_read_binary( + const mosquitto_property *proplist, + int identifier, + void **value, + uint16_t *len, + bool skip_first); + +/* + * Function: mosquitto_property_read_string + * + * Read a string property value from a property. + * + * On success, value must be free()'d by the application. + * + * Parameters: + * property - property to read + * identifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR) + * value - pointer to char*, for the property data to be stored in, or NULL if + * the value is not required. + * skip_first - boolean that indicates whether the first item in the list + * should be ignored or not. Should usually be set to false. + * + * Returns: + * A valid property pointer if the property is found + * NULL, if the property is not found, or proplist is NULL, or if an out of memory condition occurred. + * + * Example: + * See <mosquitto_property_read_byte> + */ +libmosq_EXPORT const mosquitto_property *mosquitto_property_read_string( + const mosquitto_property *proplist, + int identifier, + char **value, + bool skip_first); + +/* + * Function: mosquitto_property_read_string_pair + * + * Read a string pair property value pair from a property. + * + * On success, name and value must be free()'d by the application. + * + * Parameters: + * property - property to read + * identifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR) + * name - pointer to char* for the name property data to be stored in, or NULL + * if the name is not required. + * value - pointer to char*, for the property data to be stored in, or NULL if + * the value is not required. + * skip_first - boolean that indicates whether the first item in the list + * should be ignored or not. Should usually be set to false. + * + * Returns: + * A valid property pointer if the property is found + * NULL, if the property is not found, or proplist is NULL, or if an out of memory condition occurred. + * + * Example: + * See <mosquitto_property_read_byte> + */ +libmosq_EXPORT const mosquitto_property *mosquitto_property_read_string_pair( + const mosquitto_property *proplist, + int identifier, + char **name, + char **value, + bool skip_first); + +/* + * Function: mosquitto_property_free_all + * + * Free all properties from a list of properties. Frees the list and sets *properties to NULL. + * + * Parameters: + * properties - list of properties to free + * + * Example: + * mosquitto_properties *properties = NULL; + * // Add properties + * mosquitto_property_free_all(&properties); + */ +libmosq_EXPORT void mosquitto_property_free_all(mosquitto_property **properties); + +/* + * Function: mosquitto_property_copy_all + * + * Parameters: + * dest : pointer for new property list + * src : property list + * + * Returns: + * MOSQ_ERR_SUCCESS - on successful copy + * MOSQ_ERR_INVAL - if dest is NULL + * MOSQ_ERR_NOMEM - on out of memory (dest will be set to NULL) + */ +libmosq_EXPORT int mosquitto_property_copy_all(mosquitto_property **dest, const mosquitto_property *src); + +/* + * Function: mosquitto_property_check_command + * + * Check whether a property identifier is valid for the given command. + * + * Parameters: + * command - MQTT command (e.g. CMD_CONNECT) + * identifier - MQTT property (e.g. MQTT_PROP_USER_PROPERTY) + * + * Returns: + * MOSQ_ERR_SUCCESS - if the identifier is valid for command + * MOSQ_ERR_PROTOCOL - if the identifier is not valid for use with command. + */ +libmosq_EXPORT int mosquitto_property_check_command(int command, int identifier); + + +/* + * Function: mosquitto_property_check_all + * + * Check whether a list of properties are valid for a particular command, + * whether there are duplicates, and whether the values are valid where + * possible. + * + * Note that this function is used internally in the library whenever + * properties are passed to it, so in basic use this is not needed, but should + * be helpful to check property lists *before* the point of using them. + * + * Parameters: + * command - MQTT command (e.g. CMD_CONNECT) + * properties - list of MQTT properties to check. + * + * Returns: + * MOSQ_ERR_SUCCESS - if all properties are valid + * MOSQ_ERR_DUPLICATE_PROPERTY - if a property is duplicated where it is forbidden. + * MOSQ_ERR_PROTOCOL - if any property is invalid + */ +libmosq_EXPORT int mosquitto_property_check_all(int command, const mosquitto_property *properties); + +/* Function: mosquitto_string_to_property_info + * + * Parse a property name string and convert to a property identifier and data type. + * The property name is as defined in the MQTT specification, with - as a + * separator, for example: payload-format-indicator. + * + * Parameters: + * propname - the string to parse + * identifier - pointer to an int to receive the property identifier + * type - pointer to an int to receive the property type + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if the string does not match a property + * + * Example: + * mosquitto_string_to_property_info("response-topic", &id, &type); + * // id == MQTT_PROP_RESPONSE_TOPIC + * // type == MQTT_PROP_TYPE_STRING + */ +libmosq_EXPORT int mosquitto_string_to_property_info(const char *propname, int *identifier, int *type); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libs/libmosquitto/include/uthash.h b/libs/libmosquitto/include/uthash.h new file mode 100644 index 0000000000..915a8254ee --- /dev/null +++ b/libs/libmosquitto/include/uthash.h @@ -0,0 +1,948 @@ +/* +Copyright (c) 2003-2013, Troy D. Hanson http://troydhanson.github.com/uthash/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef UTHASH_H +#define UTHASH_H + +#include <string.h> /* memcmp,strlen */ +#include <stddef.h> /* ptrdiff_t */ +#include <stdlib.h> /* exit() */ + +/* These macros use decltype or the earlier __typeof GNU extension. + As decltype is only available in newer compilers (VS2010 or gcc 4.3+ + when compiling c++ source) this code uses whatever method is needed + or, for VS2008 where neither is available, uses casting workarounds. */ +#ifdef _MSC_VER /* MS compiler */ +#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ +#define DECLTYPE(x) (decltype(x)) +#else /* VS2008 or older (or VS2010 in C mode) */ +#define NO_DECLTYPE +#define DECLTYPE(x) +#endif +#else /* GNU, Sun and other compilers */ +#define DECLTYPE(x) (__typeof(x)) +#endif + +#ifdef NO_DECLTYPE +#define DECLTYPE_ASSIGN(dst,src) \ +do { \ + char **_da_dst = (char**)(&(dst)); \ + *_da_dst = (char*)(src); \ +} while(0) +#else +#define DECLTYPE_ASSIGN(dst,src) \ +do { \ + (dst) = DECLTYPE(dst)(src); \ +} while(0) +#endif + +/* a number of the hash function use uint32_t which isn't defined on win32 */ +#ifdef _MSC_VER +typedef unsigned int uint32_t; +typedef unsigned char uint8_t; +#else +#include <inttypes.h> /* uint32_t */ +#endif + +#define UTHASH_VERSION 1.9.8 + +#ifndef uthash_fatal +#define uthash_fatal(msg) exit(-1) /* fatal error (out of memory,etc) */ +#endif +#ifndef uthash_malloc +#define uthash_malloc(sz) malloc(sz) /* malloc fcn */ +#endif +#ifndef uthash_free +#define uthash_free(ptr,sz) free(ptr) /* free fcn */ +#endif + +#ifndef uthash_noexpand_fyi +#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */ +#endif +#ifndef uthash_expand_fyi +#define uthash_expand_fyi(tbl) /* can be defined to log expands */ +#endif + +/* initial number of buckets */ +#define HASH_INITIAL_NUM_BUCKETS 32 /* initial number of buckets */ +#define HASH_INITIAL_NUM_BUCKETS_LOG2 5 /* lg2 of initial number of buckets */ +#define HASH_BKT_CAPACITY_THRESH 10 /* expand when bucket count reaches */ + +/* calculate the element whose hash handle address is hhe */ +#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho))) + +#define HASH_FIND(hh,head,keyptr,keylen,out) \ +do { \ + unsigned _hf_bkt,_hf_hashv; \ + out=NULL; \ + if (head) { \ + HASH_FCN(keyptr,keylen, (head)->hh.tbl->num_buckets, _hf_hashv, _hf_bkt); \ + if (HASH_BLOOM_TEST((head)->hh.tbl, _hf_hashv)) { \ + HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], \ + keyptr,keylen,out); \ + } \ + } \ +} while (0) + +#ifdef HASH_BLOOM +#define HASH_BLOOM_BITLEN (1ULL << HASH_BLOOM) +#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8) + ((HASH_BLOOM_BITLEN%8) ? 1:0) +#define HASH_BLOOM_MAKE(tbl) \ +do { \ + (tbl)->bloom_nbits = HASH_BLOOM; \ + (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \ + if (!((tbl)->bloom_bv)) { uthash_fatal( "out of memory"); } \ + memset((tbl)->bloom_bv, 0, HASH_BLOOM_BYTELEN); \ + (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \ +} while (0) + +#define HASH_BLOOM_FREE(tbl) \ +do { \ + uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ +} while (0) + +#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8] |= (1U << ((idx)%8))) +#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8] & (1U << ((idx)%8))) + +#define HASH_BLOOM_ADD(tbl,hashv) \ + HASH_BLOOM_BITSET((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1))) + +#define HASH_BLOOM_TEST(tbl,hashv) \ + HASH_BLOOM_BITTEST((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1))) + +#else +#define HASH_BLOOM_MAKE(tbl) +#define HASH_BLOOM_FREE(tbl) +#define HASH_BLOOM_ADD(tbl,hashv) +#define HASH_BLOOM_TEST(tbl,hashv) (1) +#define HASH_BLOOM_BYTELEN 0 +#endif + +#define HASH_MAKE_TABLE(hh,head) \ +do { \ + (head)->hh.tbl = (UT_hash_table*)uthash_malloc( \ + sizeof(UT_hash_table)); \ + if (!((head)->hh.tbl)) { uthash_fatal( "out of memory"); } \ + memset((head)->hh.tbl, 0, sizeof(UT_hash_table)); \ + (head)->hh.tbl->tail = &((head)->hh); \ + (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \ + (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \ + (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \ + (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \ + HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ + if (! (head)->hh.tbl->buckets) { uthash_fatal( "out of memory"); } \ + memset((head)->hh.tbl->buckets, 0, \ + HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ + HASH_BLOOM_MAKE((head)->hh.tbl); \ + (head)->hh.tbl->signature = HASH_SIGNATURE; \ +} while(0) + +#define HASH_ADD(hh,head,fieldname,keylen_in,add) \ + HASH_ADD_KEYPTR(hh,head,&((add)->fieldname),keylen_in,add) + +#define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced) \ +do { \ + replaced=NULL; \ + HASH_FIND(hh,head,&((add)->fieldname),keylen_in,replaced); \ + if (replaced!=NULL) { \ + HASH_DELETE(hh,head,replaced); \ + }; \ + HASH_ADD(hh,head,fieldname,keylen_in,add); \ +} while(0) + +#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \ +do { \ + unsigned _ha_bkt; \ + (add)->hh.next = NULL; \ + (add)->hh.key = (char*)keyptr; \ + (add)->hh.keylen = (unsigned)keylen_in; \ + if (!(head)) { \ + head = (add); \ + (head)->hh.prev = NULL; \ + HASH_MAKE_TABLE(hh,head); \ + } else { \ + (head)->hh.tbl->tail->next = (add); \ + (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \ + (head)->hh.tbl->tail = &((add)->hh); \ + } \ + (head)->hh.tbl->num_items++; \ + (add)->hh.tbl = (head)->hh.tbl; \ + HASH_FCN(keyptr,keylen_in, (head)->hh.tbl->num_buckets, \ + (add)->hh.hashv, _ha_bkt); \ + HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt],&(add)->hh); \ + HASH_BLOOM_ADD((head)->hh.tbl,(add)->hh.hashv); \ + HASH_EMIT_KEY(hh,head,keyptr,keylen_in); \ + HASH_FSCK(hh,head); \ +} while(0) + +#define HASH_TO_BKT( hashv, num_bkts, bkt ) \ +do { \ + bkt = ((hashv) & ((num_bkts) - 1)); \ +} while(0) + +/* delete "delptr" from the hash table. + * "the usual" patch-up process for the app-order doubly-linked-list. + * The use of _hd_hh_del below deserves special explanation. + * These used to be expressed using (delptr) but that led to a bug + * if someone used the same symbol for the head and deletee, like + * HASH_DELETE(hh,users,users); + * We want that to work, but by changing the head (users) below + * we were forfeiting our ability to further refer to the deletee (users) + * in the patch-up process. Solution: use scratch space to + * copy the deletee pointer, then the latter references are via that + * scratch pointer rather than through the repointed (users) symbol. + */ +#define HASH_DELETE(hh,head,delptr) \ +do { \ + unsigned _hd_bkt; \ + struct UT_hash_handle *_hd_hh_del; \ + if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) ) { \ + uthash_free((head)->hh.tbl->buckets, \ + (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \ + HASH_BLOOM_FREE((head)->hh.tbl); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + head = NULL; \ + } else { \ + _hd_hh_del = &((delptr)->hh); \ + if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) { \ + (head)->hh.tbl->tail = \ + (UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \ + (head)->hh.tbl->hho); \ + } \ + if ((delptr)->hh.prev) { \ + ((UT_hash_handle*)((ptrdiff_t)((delptr)->hh.prev) + \ + (head)->hh.tbl->hho))->next = (delptr)->hh.next; \ + } else { \ + DECLTYPE_ASSIGN(head,(delptr)->hh.next); \ + } \ + if (_hd_hh_del->next) { \ + ((UT_hash_handle*)((ptrdiff_t)_hd_hh_del->next + \ + (head)->hh.tbl->hho))->prev = \ + _hd_hh_del->prev; \ + } \ + HASH_TO_BKT( _hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ + HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \ + (head)->hh.tbl->num_items--; \ + } \ + HASH_FSCK(hh,head); \ +} while (0) + + +/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */ +#define HASH_FIND_STR(head,findstr,out) \ + HASH_FIND(hh,head,findstr,strlen(findstr),out) +#define HASH_ADD_STR(head,strfield,add) \ + HASH_ADD(hh,head,strfield,strlen(add->strfield),add) +#define HASH_REPLACE_STR(head,strfield,add,replaced) \ + HASH_REPLACE(hh,head,strfield,strlen(add->strfield),add,replaced) +#define HASH_FIND_INT(head,findint,out) \ + HASH_FIND(hh,head,findint,sizeof(int),out) +#define HASH_ADD_INT(head,intfield,add) \ + HASH_ADD(hh,head,intfield,sizeof(int),add) +#define HASH_REPLACE_INT(head,intfield,add,replaced) \ + HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced) +#define HASH_FIND_PTR(head,findptr,out) \ + HASH_FIND(hh,head,findptr,sizeof(void *),out) +#define HASH_ADD_PTR(head,ptrfield,add) \ + HASH_ADD(hh,head,ptrfield,sizeof(void *),add) +#define HASH_REPLACE_PTR(head,ptrfield,add) \ + HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced) +#define HASH_DEL(head,delptr) \ + HASH_DELETE(hh,head,delptr) + +/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined. + * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined. + */ +#ifdef HASH_DEBUG +#define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0) +#define HASH_FSCK(hh,head) \ +do { \ + unsigned _bkt_i; \ + unsigned _count, _bkt_count; \ + char *_prev; \ + struct UT_hash_handle *_thh; \ + if (head) { \ + _count = 0; \ + for( _bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; _bkt_i++) { \ + _bkt_count = 0; \ + _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \ + _prev = NULL; \ + while (_thh) { \ + if (_prev != (char*)(_thh->hh_prev)) { \ + HASH_OOPS("invalid hh_prev %p, actual %p\n", \ + _thh->hh_prev, _prev ); \ + } \ + _bkt_count++; \ + _prev = (char*)(_thh); \ + _thh = _thh->hh_next; \ + } \ + _count += _bkt_count; \ + if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \ + HASH_OOPS("invalid bucket count %d, actual %d\n", \ + (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \ + } \ + } \ + if (_count != (head)->hh.tbl->num_items) { \ + HASH_OOPS("invalid hh item count %d, actual %d\n", \ + (head)->hh.tbl->num_items, _count ); \ + } \ + /* traverse hh in app order; check next/prev integrity, count */ \ + _count = 0; \ + _prev = NULL; \ + _thh = &(head)->hh; \ + while (_thh) { \ + _count++; \ + if (_prev !=(char*)(_thh->prev)) { \ + HASH_OOPS("invalid prev %p, actual %p\n", \ + _thh->prev, _prev ); \ + } \ + _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \ + _thh = ( _thh->next ? (UT_hash_handle*)((char*)(_thh->next) + \ + (head)->hh.tbl->hho) : NULL ); \ + } \ + if (_count != (head)->hh.tbl->num_items) { \ + HASH_OOPS("invalid app item count %d, actual %d\n", \ + (head)->hh.tbl->num_items, _count ); \ + } \ + } \ +} while (0) +#else +#define HASH_FSCK(hh,head) +#endif + +/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to + * the descriptor to which this macro is defined for tuning the hash function. + * The app can #include <unistd.h> to get the prototype for write(2). */ +#ifdef HASH_EMIT_KEYS +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \ +do { \ + unsigned _klen = fieldlen; \ + write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \ + write(HASH_EMIT_KEYS, keyptr, fieldlen); \ +} while (0) +#else +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) +#endif + +/* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */ +#ifdef HASH_FUNCTION +#define HASH_FCN HASH_FUNCTION +#else +#define HASH_FCN HASH_JEN +#endif + +/* The Bernstein hash function, used in Perl prior to v5.6 */ +#define HASH_BER(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _hb_keylen=keylen; \ + char *_hb_key=(char*)(key); \ + (hashv) = 0; \ + while (_hb_keylen--) { (hashv) = ((hashv) * 33) + *_hb_key++; } \ + bkt = (hashv) & (num_bkts-1); \ +} while (0) + + +/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at + * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */ +#define HASH_SAX(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _sx_i; \ + char *_hs_key=(char*)(key); \ + hashv = 0; \ + for(_sx_i=0; _sx_i < keylen; _sx_i++) \ + hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \ + bkt = hashv & (num_bkts-1); \ +} while (0) + +#define HASH_FNV(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _fn_i; \ + char *_hf_key=(char*)(key); \ + hashv = 2166136261UL; \ + for(_fn_i=0; _fn_i < keylen; _fn_i++) \ + hashv = (hashv * 16777619) ^ _hf_key[_fn_i]; \ + bkt = hashv & (num_bkts-1); \ +} while(0) + +#define HASH_OAT(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _ho_i; \ + char *_ho_key=(char*)(key); \ + hashv = 0; \ + for(_ho_i=0; _ho_i < keylen; _ho_i++) { \ + hashv += _ho_key[_ho_i]; \ + hashv += (hashv << 10); \ + hashv ^= (hashv >> 6); \ + } \ + hashv += (hashv << 3); \ + hashv ^= (hashv >> 11); \ + hashv += (hashv << 15); \ + bkt = hashv & (num_bkts-1); \ +} while(0) + +#define HASH_JEN_MIX(a,b,c) \ +do { \ + a -= b; a -= c; a ^= ( c >> 13 ); \ + b -= c; b -= a; b ^= ( a << 8 ); \ + c -= a; c -= b; c ^= ( b >> 13 ); \ + a -= b; a -= c; a ^= ( c >> 12 ); \ + b -= c; b -= a; b ^= ( a << 16 ); \ + c -= a; c -= b; c ^= ( b >> 5 ); \ + a -= b; a -= c; a ^= ( c >> 3 ); \ + b -= c; b -= a; b ^= ( a << 10 ); \ + c -= a; c -= b; c ^= ( b >> 15 ); \ +} while (0) + +#define HASH_JEN(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned _hj_i,_hj_j,_hj_k; \ + unsigned char *_hj_key=(unsigned char*)(key); \ + hashv = 0xfeedbeef; \ + _hj_i = _hj_j = 0x9e3779b9; \ + _hj_k = (unsigned)keylen; \ + while (_hj_k >= 12) { \ + _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \ + + ( (unsigned)_hj_key[2] << 16 ) \ + + ( (unsigned)_hj_key[3] << 24 ) ); \ + _hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \ + + ( (unsigned)_hj_key[6] << 16 ) \ + + ( (unsigned)_hj_key[7] << 24 ) ); \ + hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \ + + ( (unsigned)_hj_key[10] << 16 ) \ + + ( (unsigned)_hj_key[11] << 24 ) ); \ + \ + HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ + \ + _hj_key += 12; \ + _hj_k -= 12; \ + } \ + hashv += keylen; \ + switch ( _hj_k ) { \ + case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); \ + case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); \ + case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); \ + case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); \ + case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); \ + case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); \ + case 5: _hj_j += _hj_key[4]; \ + case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); \ + case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); \ + case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); \ + case 1: _hj_i += _hj_key[0]; \ + } \ + HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ + bkt = hashv & (num_bkts-1); \ +} while(0) + +/* The Paul Hsieh hash function */ +#undef get16bits +#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ + || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) +#define get16bits(d) (*((const uint16_t *) (d))) +#endif + +#if !defined (get16bits) +#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \ + +(uint32_t)(((const uint8_t *)(d))[0]) ) +#endif +#define HASH_SFH(key,keylen,num_bkts,hashv,bkt) \ +do { \ + unsigned char *_sfh_key=(unsigned char*)(key); \ + uint32_t _sfh_tmp, _sfh_len = keylen; \ + \ + int _sfh_rem = _sfh_len & 3; \ + _sfh_len >>= 2; \ + hashv = 0xcafebabe; \ + \ + /* Main loop */ \ + for (;_sfh_len > 0; _sfh_len--) { \ + hashv += get16bits (_sfh_key); \ + _sfh_tmp = (uint32_t)(get16bits (_sfh_key+2)) << 11 ^ hashv; \ + hashv = (hashv << 16) ^ _sfh_tmp; \ + _sfh_key += 2*sizeof (uint16_t); \ + hashv += hashv >> 11; \ + } \ + \ + /* Handle end cases */ \ + switch (_sfh_rem) { \ + case 3: hashv += get16bits (_sfh_key); \ + hashv ^= hashv << 16; \ + hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)] << 18); \ + hashv += hashv >> 11; \ + break; \ + case 2: hashv += get16bits (_sfh_key); \ + hashv ^= hashv << 11; \ + hashv += hashv >> 17; \ + break; \ + case 1: hashv += *_sfh_key; \ + hashv ^= hashv << 10; \ + hashv += hashv >> 1; \ + } \ + \ + /* Force "avalanching" of final 127 bits */ \ + hashv ^= hashv << 3; \ + hashv += hashv >> 5; \ + hashv ^= hashv << 4; \ + hashv += hashv >> 17; \ + hashv ^= hashv << 25; \ + hashv += hashv >> 6; \ + bkt = hashv & (num_bkts-1); \ +} while(0) + +#ifdef HASH_USING_NO_STRICT_ALIASING +/* The MurmurHash exploits some CPU's (x86,x86_64) tolerance for unaligned reads. + * For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error. + * MurmurHash uses the faster approach only on CPU's where we know it's safe. + * + * Note the preprocessor built-in defines can be emitted using: + * + * gcc -m64 -dM -E - < /dev/null (on gcc) + * cc -## a.c (where a.c is a simple test file) (Sun Studio) + */ +#if (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86)) +#define MUR_GETBLOCK(p,i) p[i] +#else /* non intel */ +#define MUR_PLUS0_ALIGNED(p) (((unsigned long)p & 0x3) == 0) +#define MUR_PLUS1_ALIGNED(p) (((unsigned long)p & 0x3) == 1) +#define MUR_PLUS2_ALIGNED(p) (((unsigned long)p & 0x3) == 2) +#define MUR_PLUS3_ALIGNED(p) (((unsigned long)p & 0x3) == 3) +#define WP(p) ((uint32_t*)((unsigned long)(p) & ~3UL)) +#if (defined(__BIG_ENDIAN__) || defined(SPARC) || defined(__ppc__) || defined(__ppc64__)) +#define MUR_THREE_ONE(p) ((((*WP(p))&0x00ffffff) << 8) | (((*(WP(p)+1))&0xff000000) >> 24)) +#define MUR_TWO_TWO(p) ((((*WP(p))&0x0000ffff) <<16) | (((*(WP(p)+1))&0xffff0000) >> 16)) +#define MUR_ONE_THREE(p) ((((*WP(p))&0x000000ff) <<24) | (((*(WP(p)+1))&0xffffff00) >> 8)) +#else /* assume little endian non-intel */ +#define MUR_THREE_ONE(p) ((((*WP(p))&0xffffff00) >> 8) | (((*(WP(p)+1))&0x000000ff) << 24)) +#define MUR_TWO_TWO(p) ((((*WP(p))&0xffff0000) >>16) | (((*(WP(p)+1))&0x0000ffff) << 16)) +#define MUR_ONE_THREE(p) ((((*WP(p))&0xff000000) >>24) | (((*(WP(p)+1))&0x00ffffff) << 8)) +#endif +#define MUR_GETBLOCK(p,i) (MUR_PLUS0_ALIGNED(p) ? ((p)[i]) : \ + (MUR_PLUS1_ALIGNED(p) ? MUR_THREE_ONE(p) : \ + (MUR_PLUS2_ALIGNED(p) ? MUR_TWO_TWO(p) : \ + MUR_ONE_THREE(p)))) +#endif +#define MUR_ROTL32(x,r) (((x) << (r)) | ((x) >> (32 - (r)))) +#define MUR_FMIX(_h) \ +do { \ + _h ^= _h >> 16; \ + _h *= 0x85ebca6b; \ + _h ^= _h >> 13; \ + _h *= 0xc2b2ae35l; \ + _h ^= _h >> 16; \ +} while(0) + +#define HASH_MUR(key,keylen,num_bkts,hashv,bkt) \ +do { \ + const uint8_t *_mur_data = (const uint8_t*)(key); \ + const int _mur_nblocks = (keylen) / 4; \ + uint32_t _mur_h1 = 0xf88D5353; \ + uint32_t _mur_c1 = 0xcc9e2d51; \ + uint32_t _mur_c2 = 0x1b873593; \ + uint32_t _mur_k1 = 0; \ + const uint8_t *_mur_tail; \ + const uint32_t *_mur_blocks = (const uint32_t*)(_mur_data+_mur_nblocks*4); \ + int _mur_i; \ + for(_mur_i = -_mur_nblocks; _mur_i; _mur_i++) { \ + _mur_k1 = MUR_GETBLOCK(_mur_blocks,_mur_i); \ + _mur_k1 *= _mur_c1; \ + _mur_k1 = MUR_ROTL32(_mur_k1,15); \ + _mur_k1 *= _mur_c2; \ + \ + _mur_h1 ^= _mur_k1; \ + _mur_h1 = MUR_ROTL32(_mur_h1,13); \ + _mur_h1 = _mur_h1*5+0xe6546b64; \ + } \ + _mur_tail = (const uint8_t*)(_mur_data + _mur_nblocks*4); \ + _mur_k1=0; \ + switch((keylen) & 3) { \ + case 3: _mur_k1 ^= _mur_tail[2] << 16; \ + case 2: _mur_k1 ^= _mur_tail[1] << 8; \ + case 1: _mur_k1 ^= _mur_tail[0]; \ + _mur_k1 *= _mur_c1; \ + _mur_k1 = MUR_ROTL32(_mur_k1,15); \ + _mur_k1 *= _mur_c2; \ + _mur_h1 ^= _mur_k1; \ + } \ + _mur_h1 ^= (keylen); \ + MUR_FMIX(_mur_h1); \ + hashv = _mur_h1; \ + bkt = hashv & (num_bkts-1); \ +} while(0) +#endif /* HASH_USING_NO_STRICT_ALIASING */ + +/* key comparison function; return 0 if keys equal */ +#define HASH_KEYCMP(a,b,len) memcmp(a,b,len) + +/* iterate over items in a known bucket to find desired item */ +#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,out) \ +do { \ + if (head.hh_head) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,head.hh_head)); \ + else out=NULL; \ + while (out) { \ + if ((out)->hh.keylen == keylen_in) { \ + if ((HASH_KEYCMP((out)->hh.key,keyptr,keylen_in)) == 0) break; \ + } \ + if ((out)->hh.hh_next) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,(out)->hh.hh_next)); \ + else out = NULL; \ + } \ +} while(0) + +/* add an item to a bucket */ +#define HASH_ADD_TO_BKT(head,addhh) \ +do { \ + head.count++; \ + (addhh)->hh_next = head.hh_head; \ + (addhh)->hh_prev = NULL; \ + if (head.hh_head) { (head).hh_head->hh_prev = (addhh); } \ + (head).hh_head=addhh; \ + if (head.count >= ((head.expand_mult+1) * HASH_BKT_CAPACITY_THRESH) \ + && (addhh)->tbl->noexpand != 1) { \ + HASH_EXPAND_BUCKETS((addhh)->tbl); \ + } \ +} while(0) + +/* remove an item from a given bucket */ +#define HASH_DEL_IN_BKT(hh,head,hh_del) \ + (head).count--; \ + if ((head).hh_head == hh_del) { \ + (head).hh_head = hh_del->hh_next; \ + } \ + if (hh_del->hh_prev) { \ + hh_del->hh_prev->hh_next = hh_del->hh_next; \ + } \ + if (hh_del->hh_next) { \ + hh_del->hh_next->hh_prev = hh_del->hh_prev; \ + } + +/* Bucket expansion has the effect of doubling the number of buckets + * and redistributing the items into the new buckets. Ideally the + * items will distribute more or less evenly into the new buckets + * (the extent to which this is true is a measure of the quality of + * the hash function as it applies to the key domain). + * + * With the items distributed into more buckets, the chain length + * (item count) in each bucket is reduced. Thus by expanding buckets + * the hash keeps a bound on the chain length. This bounded chain + * length is the essence of how a hash provides constant time lookup. + * + * The calculation of tbl->ideal_chain_maxlen below deserves some + * explanation. First, keep in mind that we're calculating the ideal + * maximum chain length based on the *new* (doubled) bucket count. + * In fractions this is just n/b (n=number of items,b=new num buckets). + * Since the ideal chain length is an integer, we want to calculate + * ceil(n/b). We don't depend on floating point arithmetic in this + * hash, so to calculate ceil(n/b) with integers we could write + * + * ceil(n/b) = (n/b) + ((n%b)?1:0) + * + * and in fact a previous version of this hash did just that. + * But now we have improved things a bit by recognizing that b is + * always a power of two. We keep its base 2 log handy (call it lb), + * so now we can write this with a bit shift and logical AND: + * + * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0) + * + */ +#define HASH_EXPAND_BUCKETS(tbl) \ +do { \ + unsigned _he_bkt; \ + unsigned _he_bkt_i; \ + struct UT_hash_handle *_he_thh, *_he_hh_nxt; \ + UT_hash_bucket *_he_new_buckets, *_he_newbkt; \ + _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \ + 2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ + if (!_he_new_buckets) { uthash_fatal( "out of memory"); } \ + memset(_he_new_buckets, 0, \ + 2 * tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ + tbl->ideal_chain_maxlen = \ + (tbl->num_items >> (tbl->log2_num_buckets+1)) + \ + ((tbl->num_items & ((tbl->num_buckets*2)-1)) ? 1 : 0); \ + tbl->nonideal_items = 0; \ + for(_he_bkt_i = 0; _he_bkt_i < tbl->num_buckets; _he_bkt_i++) \ + { \ + _he_thh = tbl->buckets[ _he_bkt_i ].hh_head; \ + while (_he_thh) { \ + _he_hh_nxt = _he_thh->hh_next; \ + HASH_TO_BKT( _he_thh->hashv, tbl->num_buckets*2, _he_bkt); \ + _he_newbkt = &(_he_new_buckets[ _he_bkt ]); \ + if (++(_he_newbkt->count) > tbl->ideal_chain_maxlen) { \ + tbl->nonideal_items++; \ + _he_newbkt->expand_mult = _he_newbkt->count / \ + tbl->ideal_chain_maxlen; \ + } \ + _he_thh->hh_prev = NULL; \ + _he_thh->hh_next = _he_newbkt->hh_head; \ + if (_he_newbkt->hh_head) _he_newbkt->hh_head->hh_prev = \ + _he_thh; \ + _he_newbkt->hh_head = _he_thh; \ + _he_thh = _he_hh_nxt; \ + } \ + } \ + uthash_free( tbl->buckets, tbl->num_buckets*sizeof(struct UT_hash_bucket) ); \ + tbl->num_buckets *= 2; \ + tbl->log2_num_buckets++; \ + tbl->buckets = _he_new_buckets; \ + tbl->ineff_expands = (tbl->nonideal_items > (tbl->num_items >> 1)) ? \ + (tbl->ineff_expands+1) : 0; \ + if (tbl->ineff_expands > 1) { \ + tbl->noexpand=1; \ + uthash_noexpand_fyi(tbl); \ + } \ + uthash_expand_fyi(tbl); \ +} while(0) + + +/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */ +/* Note that HASH_SORT assumes the hash handle name to be hh. + * HASH_SRT was added to allow the hash handle name to be passed in. */ +#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn) +#define HASH_SRT(hh,head,cmpfcn) \ +do { \ + unsigned _hs_i; \ + unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \ + struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \ + if (head) { \ + _hs_insize = 1; \ + _hs_looping = 1; \ + _hs_list = &((head)->hh); \ + while (_hs_looping) { \ + _hs_p = _hs_list; \ + _hs_list = NULL; \ + _hs_tail = NULL; \ + _hs_nmerges = 0; \ + while (_hs_p) { \ + _hs_nmerges++; \ + _hs_q = _hs_p; \ + _hs_psize = 0; \ + for ( _hs_i = 0; _hs_i < _hs_insize; _hs_i++ ) { \ + _hs_psize++; \ + _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ + ((void*)((char*)(_hs_q->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + if (! (_hs_q) ) break; \ + } \ + _hs_qsize = _hs_insize; \ + while ((_hs_psize > 0) || ((_hs_qsize > 0) && _hs_q )) { \ + if (_hs_psize == 0) { \ + _hs_e = _hs_q; \ + _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ + ((void*)((char*)(_hs_q->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + _hs_qsize--; \ + } else if ( (_hs_qsize == 0) || !(_hs_q) ) { \ + _hs_e = _hs_p; \ + if (_hs_p){ \ + _hs_p = (UT_hash_handle*)((_hs_p->next) ? \ + ((void*)((char*)(_hs_p->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + } \ + _hs_psize--; \ + } else if (( \ + cmpfcn(DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)), \ + DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q))) \ + ) <= 0) { \ + _hs_e = _hs_p; \ + if (_hs_p){ \ + _hs_p = (UT_hash_handle*)((_hs_p->next) ? \ + ((void*)((char*)(_hs_p->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + } \ + _hs_psize--; \ + } else { \ + _hs_e = _hs_q; \ + _hs_q = (UT_hash_handle*)((_hs_q->next) ? \ + ((void*)((char*)(_hs_q->next) + \ + (head)->hh.tbl->hho)) : NULL); \ + _hs_qsize--; \ + } \ + if ( _hs_tail ) { \ + _hs_tail->next = ((_hs_e) ? \ + ELMT_FROM_HH((head)->hh.tbl,_hs_e) : NULL); \ + } else { \ + _hs_list = _hs_e; \ + } \ + if (_hs_e) { \ + _hs_e->prev = ((_hs_tail) ? \ + ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL); \ + } \ + _hs_tail = _hs_e; \ + } \ + _hs_p = _hs_q; \ + } \ + if (_hs_tail){ \ + _hs_tail->next = NULL; \ + } \ + if ( _hs_nmerges <= 1 ) { \ + _hs_looping=0; \ + (head)->hh.tbl->tail = _hs_tail; \ + DECLTYPE_ASSIGN(head,ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \ + } \ + _hs_insize *= 2; \ + } \ + HASH_FSCK(hh,head); \ + } \ +} while (0) + +/* This function selects items from one hash into another hash. + * The end result is that the selected items have dual presence + * in both hashes. There is no copy of the items made; rather + * they are added into the new hash through a secondary hash + * hash handle that must be present in the structure. */ +#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \ +do { \ + unsigned _src_bkt, _dst_bkt; \ + void *_last_elt=NULL, *_elt; \ + UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \ + ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \ + if (src) { \ + for(_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \ + for(_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \ + _src_hh; \ + _src_hh = _src_hh->hh_next) { \ + _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \ + if (cond(_elt)) { \ + _dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho); \ + _dst_hh->key = _src_hh->key; \ + _dst_hh->keylen = _src_hh->keylen; \ + _dst_hh->hashv = _src_hh->hashv; \ + _dst_hh->prev = _last_elt; \ + _dst_hh->next = NULL; \ + if (_last_elt_hh) { _last_elt_hh->next = _elt; } \ + if (!dst) { \ + DECLTYPE_ASSIGN(dst,_elt); \ + HASH_MAKE_TABLE(hh_dst,dst); \ + } else { \ + _dst_hh->tbl = (dst)->hh_dst.tbl; \ + } \ + HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \ + HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt],_dst_hh); \ + (dst)->hh_dst.tbl->num_items++; \ + _last_elt = _elt; \ + _last_elt_hh = _dst_hh; \ + } \ + } \ + } \ + } \ + HASH_FSCK(hh_dst,dst); \ +} while (0) + +#define HASH_CLEAR(hh,head) \ +do { \ + if (head) { \ + uthash_free((head)->hh.tbl->buckets, \ + (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \ + HASH_BLOOM_FREE((head)->hh.tbl); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + (head)=NULL; \ + } \ +} while(0) + +#define HASH_OVERHEAD(hh,head) \ + (size_t)((((head)->hh.tbl->num_items * sizeof(UT_hash_handle)) + \ + ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket)) + \ + (sizeof(UT_hash_table)) + \ + (HASH_BLOOM_BYTELEN))) + +#ifdef NO_DECLTYPE +#define HASH_ITER(hh,head,el,tmp) \ +for((el)=(head), (*(char**)(&(tmp)))=(char*)((head)?(head)->hh.next:NULL); \ + el; (el)=(tmp),(*(char**)(&(tmp)))=(char*)((tmp)?(tmp)->hh.next:NULL)) +#else +#define HASH_ITER(hh,head,el,tmp) \ +for((el)=(head),(tmp)=DECLTYPE(el)((head)?(head)->hh.next:NULL); \ + el; (el)=(tmp),(tmp)=DECLTYPE(el)((tmp)?(tmp)->hh.next:NULL)) +#endif + +/* obtain a count of items in the hash */ +#define HASH_COUNT(head) HASH_CNT(hh,head) +#define HASH_CNT(hh,head) ((head)?((head)->hh.tbl->num_items):0) + +typedef struct UT_hash_bucket { + struct UT_hash_handle *hh_head; + unsigned count; + + /* expand_mult is normally set to 0. In this situation, the max chain length + * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If + * the bucket's chain exceeds this length, bucket expansion is triggered). + * However, setting expand_mult to a non-zero value delays bucket expansion + * (that would be triggered by additions to this particular bucket) + * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH. + * (The multiplier is simply expand_mult+1). The whole idea of this + * multiplier is to reduce bucket expansions, since they are expensive, in + * situations where we know that a particular bucket tends to be overused. + * It is better to let its chain length grow to a longer yet-still-bounded + * value, than to do an O(n) bucket expansion too often. + */ + unsigned expand_mult; + +} UT_hash_bucket; + +/* random signature used only to find hash tables in external analysis */ +#define HASH_SIGNATURE 0xa0111fe1 +#define HASH_BLOOM_SIGNATURE 0xb12220f2 + +typedef struct UT_hash_table { + UT_hash_bucket *buckets; + unsigned num_buckets, log2_num_buckets; + unsigned num_items; + struct UT_hash_handle *tail; /* tail hh in app order, for fast append */ + ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */ + + /* in an ideal situation (all buckets used equally), no bucket would have + * more than ceil(#items/#buckets) items. that's the ideal chain length. */ + unsigned ideal_chain_maxlen; + + /* nonideal_items is the number of items in the hash whose chain position + * exceeds the ideal chain maxlen. these items pay the penalty for an uneven + * hash distribution; reaching them in a chain traversal takes >ideal steps */ + unsigned nonideal_items; + + /* ineffective expands occur when a bucket doubling was performed, but + * afterward, more than half the items in the hash had nonideal chain + * positions. If this happens on two consecutive expansions we inhibit any + * further expansion, as it's not helping; this happens when the hash + * function isn't a good fit for the key domain. When expansion is inhibited + * the hash will still work, albeit no longer in constant time. */ + unsigned ineff_expands, noexpand; + + uint32_t signature; /* used only to find hash tables in external analysis */ +#ifdef HASH_BLOOM + uint32_t bloom_sig; /* used only to test bloom exists in external analysis */ + uint8_t *bloom_bv; + char bloom_nbits; +#endif + +} UT_hash_table; + +typedef struct UT_hash_handle { + struct UT_hash_table *tbl; + void *prev; /* prev element in app order */ + void *next; /* next element in app order */ + struct UT_hash_handle *hh_prev; /* previous hh in bucket order */ + struct UT_hash_handle *hh_next; /* next hh in bucket order */ + void *key; /* ptr to enclosing struct's key */ + unsigned keylen; /* enclosing struct's key len */ + unsigned hashv; /* result of hash-fcn(key) */ +} UT_hash_handle; + +#endif /* UTHASH_H */ diff --git a/libs/libmosquitto/include/utlist.h b/libs/libmosquitto/include/utlist.h new file mode 100644 index 0000000000..5bb1ac9b72 --- /dev/null +++ b/libs/libmosquitto/include/utlist.h @@ -0,0 +1,1073 @@ +/* +Copyright (c) 2007-2018, Troy D. Hanson http://troydhanson.github.com/uthash/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef UTLIST_H +#define UTLIST_H + +#define UTLIST_VERSION 2.1.0 + +#include <assert.h> + +/* + * This file contains macros to manipulate singly and doubly-linked lists. + * + * 1. LL_ macros: singly-linked lists. + * 2. DL_ macros: doubly-linked lists. + * 3. CDL_ macros: circular doubly-linked lists. + * + * To use singly-linked lists, your structure must have a "next" pointer. + * To use doubly-linked lists, your structure must "prev" and "next" pointers. + * Either way, the pointer to the head of the list must be initialized to NULL. + * + * ----------------.EXAMPLE ------------------------- + * struct item { + * int id; + * struct item *prev, *next; + * } + * + * struct item *list = NULL: + * + * int main() { + * struct item *item; + * ... allocate and populate item ... + * DL_APPEND(list, item); + * } + * -------------------------------------------------- + * + * For doubly-linked lists, the append and delete macros are O(1) + * For singly-linked lists, append and delete are O(n) but prepend is O(1) + * The sort macro is O(n log(n)) for all types of single/double/circular lists. + */ + +/* These macros use decltype or the earlier __typeof GNU extension. + As decltype is only available in newer compilers (VS2010 or gcc 4.3+ + when compiling c++ source) this code uses whatever method is needed + or, for VS2008 where neither is available, uses casting workarounds. */ +#if !defined(LDECLTYPE) && !defined(NO_DECLTYPE) +#if defined(_MSC_VER) /* MS compiler */ +#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ +#define LDECLTYPE(x) decltype(x) +#else /* VS2008 or older (or VS2010 in C mode) */ +#define NO_DECLTYPE +#endif +#elif defined(__BORLANDC__) || defined(__ICCARM__) || defined(__LCC__) || defined(__WATCOMC__) +#define NO_DECLTYPE +#else /* GNU, Sun and other compilers */ +#define LDECLTYPE(x) __typeof(x) +#endif +#endif + +/* for VS2008 we use some workarounds to get around the lack of decltype, + * namely, we always reassign our tmp variable to the list head if we need + * to dereference its prev/next pointers, and save/restore the real head.*/ +#ifdef NO_DECLTYPE +#define IF_NO_DECLTYPE(x) x +#define LDECLTYPE(x) char* +#define UTLIST_SV(elt,list) _tmp = (char*)(list); {char **_alias = (char**)&(list); *_alias = (elt); } +#define UTLIST_NEXT(elt,list,next) ((char*)((list)->next)) +#define UTLIST_NEXTASGN(elt,list,to,next) { char **_alias = (char**)&((list)->next); *_alias=(char*)(to); } +/* #define UTLIST_PREV(elt,list,prev) ((char*)((list)->prev)) */ +#define UTLIST_PREVASGN(elt,list,to,prev) { char **_alias = (char**)&((list)->prev); *_alias=(char*)(to); } +#define UTLIST_RS(list) { char **_alias = (char**)&(list); *_alias=_tmp; } +#define UTLIST_CASTASGN(a,b) { char **_alias = (char**)&(a); *_alias=(char*)(b); } +#else +#define IF_NO_DECLTYPE(x) +#define UTLIST_SV(elt,list) +#define UTLIST_NEXT(elt,list,next) ((elt)->next) +#define UTLIST_NEXTASGN(elt,list,to,next) ((elt)->next)=(to) +/* #define UTLIST_PREV(elt,list,prev) ((elt)->prev) */ +#define UTLIST_PREVASGN(elt,list,to,prev) ((elt)->prev)=(to) +#define UTLIST_RS(list) +#define UTLIST_CASTASGN(a,b) (a)=(b) +#endif + +/****************************************************************************** + * The sort macro is an adaptation of Simon Tatham's O(n log(n)) mergesort * + * Unwieldy variable names used here to avoid shadowing passed-in variables. * + *****************************************************************************/ +#define LL_SORT(list, cmp) \ + LL_SORT2(list, cmp, next) + +#define LL_SORT2(list, cmp, next) \ +do { \ + LDECLTYPE(list) _ls_p; \ + LDECLTYPE(list) _ls_q; \ + LDECLTYPE(list) _ls_e; \ + LDECLTYPE(list) _ls_tail; \ + IF_NO_DECLTYPE(LDECLTYPE(list) _tmp;) \ + int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ + if (list) { \ + _ls_insize = 1; \ + _ls_looping = 1; \ + while (_ls_looping) { \ + UTLIST_CASTASGN(_ls_p,list); \ + (list) = NULL; \ + _ls_tail = NULL; \ + _ls_nmerges = 0; \ + while (_ls_p) { \ + _ls_nmerges++; \ + _ls_q = _ls_p; \ + _ls_psize = 0; \ + for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ + _ls_psize++; \ + UTLIST_SV(_ls_q,list); _ls_q = UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); \ + if (!_ls_q) break; \ + } \ + _ls_qsize = _ls_insize; \ + while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ + if (_ls_psize == 0) { \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ + } else if (_ls_qsize == 0 || !_ls_q) { \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ + } else if (cmp(_ls_p,_ls_q) <= 0) { \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ + } else { \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ + } \ + if (_ls_tail) { \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_ls_e,next); UTLIST_RS(list); \ + } else { \ + UTLIST_CASTASGN(list,_ls_e); \ + } \ + _ls_tail = _ls_e; \ + } \ + _ls_p = _ls_q; \ + } \ + if (_ls_tail) { \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,NULL,next); UTLIST_RS(list); \ + } \ + if (_ls_nmerges <= 1) { \ + _ls_looping=0; \ + } \ + _ls_insize *= 2; \ + } \ + } \ +} while (0) + + +#define DL_SORT(list, cmp) \ + DL_SORT2(list, cmp, prev, next) + +#define DL_SORT2(list, cmp, prev, next) \ +do { \ + LDECLTYPE(list) _ls_p; \ + LDECLTYPE(list) _ls_q; \ + LDECLTYPE(list) _ls_e; \ + LDECLTYPE(list) _ls_tail; \ + IF_NO_DECLTYPE(LDECLTYPE(list) _tmp;) \ + int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ + if (list) { \ + _ls_insize = 1; \ + _ls_looping = 1; \ + while (_ls_looping) { \ + UTLIST_CASTASGN(_ls_p,list); \ + (list) = NULL; \ + _ls_tail = NULL; \ + _ls_nmerges = 0; \ + while (_ls_p) { \ + _ls_nmerges++; \ + _ls_q = _ls_p; \ + _ls_psize = 0; \ + for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ + _ls_psize++; \ + UTLIST_SV(_ls_q,list); _ls_q = UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); \ + if (!_ls_q) break; \ + } \ + _ls_qsize = _ls_insize; \ + while ((_ls_psize > 0) || ((_ls_qsize > 0) && _ls_q)) { \ + if (_ls_psize == 0) { \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ + } else if ((_ls_qsize == 0) || (!_ls_q)) { \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ + } else if (cmp(_ls_p,_ls_q) <= 0) { \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ + } else { \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ + } \ + if (_ls_tail) { \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_ls_e,next); UTLIST_RS(list); \ + } else { \ + UTLIST_CASTASGN(list,_ls_e); \ + } \ + UTLIST_SV(_ls_e,list); UTLIST_PREVASGN(_ls_e,list,_ls_tail,prev); UTLIST_RS(list); \ + _ls_tail = _ls_e; \ + } \ + _ls_p = _ls_q; \ + } \ + UTLIST_CASTASGN((list)->prev, _ls_tail); \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,NULL,next); UTLIST_RS(list); \ + if (_ls_nmerges <= 1) { \ + _ls_looping=0; \ + } \ + _ls_insize *= 2; \ + } \ + } \ +} while (0) + +#define CDL_SORT(list, cmp) \ + CDL_SORT2(list, cmp, prev, next) + +#define CDL_SORT2(list, cmp, prev, next) \ +do { \ + LDECLTYPE(list) _ls_p; \ + LDECLTYPE(list) _ls_q; \ + LDECLTYPE(list) _ls_e; \ + LDECLTYPE(list) _ls_tail; \ + LDECLTYPE(list) _ls_oldhead; \ + LDECLTYPE(list) _tmp; \ + int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ + if (list) { \ + _ls_insize = 1; \ + _ls_looping = 1; \ + while (_ls_looping) { \ + UTLIST_CASTASGN(_ls_p,list); \ + UTLIST_CASTASGN(_ls_oldhead,list); \ + (list) = NULL; \ + _ls_tail = NULL; \ + _ls_nmerges = 0; \ + while (_ls_p) { \ + _ls_nmerges++; \ + _ls_q = _ls_p; \ + _ls_psize = 0; \ + for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ + _ls_psize++; \ + UTLIST_SV(_ls_q,list); \ + if (UTLIST_NEXT(_ls_q,list,next) == _ls_oldhead) { \ + _ls_q = NULL; \ + } else { \ + _ls_q = UTLIST_NEXT(_ls_q,list,next); \ + } \ + UTLIST_RS(list); \ + if (!_ls_q) break; \ + } \ + _ls_qsize = _ls_insize; \ + while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ + if (_ls_psize == 0) { \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ + if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ + } else if (_ls_qsize == 0 || !_ls_q) { \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ + if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ + } else if (cmp(_ls_p,_ls_q) <= 0) { \ + _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ + UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ + if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ + } else { \ + _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ + UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ + if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ + } \ + if (_ls_tail) { \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_ls_e,next); UTLIST_RS(list); \ + } else { \ + UTLIST_CASTASGN(list,_ls_e); \ + } \ + UTLIST_SV(_ls_e,list); UTLIST_PREVASGN(_ls_e,list,_ls_tail,prev); UTLIST_RS(list); \ + _ls_tail = _ls_e; \ + } \ + _ls_p = _ls_q; \ + } \ + UTLIST_CASTASGN((list)->prev,_ls_tail); \ + UTLIST_CASTASGN(_tmp,list); \ + UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_tmp,next); UTLIST_RS(list); \ + if (_ls_nmerges <= 1) { \ + _ls_looping=0; \ + } \ + _ls_insize *= 2; \ + } \ + } \ +} while (0) + +/****************************************************************************** + * singly linked list macros (non-circular) * + *****************************************************************************/ +#define LL_PREPEND(head,add) \ + LL_PREPEND2(head,add,next) + +#define LL_PREPEND2(head,add,next) \ +do { \ + (add)->next = (head); \ + (head) = (add); \ +} while (0) + +#define LL_CONCAT(head1,head2) \ + LL_CONCAT2(head1,head2,next) + +#define LL_CONCAT2(head1,head2,next) \ +do { \ + LDECLTYPE(head1) _tmp; \ + if (head1) { \ + _tmp = (head1); \ + while (_tmp->next) { _tmp = _tmp->next; } \ + _tmp->next=(head2); \ + } else { \ + (head1)=(head2); \ + } \ +} while (0) + +#define LL_APPEND(head,add) \ + LL_APPEND2(head,add,next) + +#define LL_APPEND2(head,add,next) \ +do { \ + LDECLTYPE(head) _tmp; \ + (add)->next=NULL; \ + if (head) { \ + _tmp = (head); \ + while (_tmp->next) { _tmp = _tmp->next; } \ + _tmp->next=(add); \ + } else { \ + (head)=(add); \ + } \ +} while (0) + +#define LL_INSERT_INORDER(head,add,cmp) \ + LL_INSERT_INORDER2(head,add,cmp,next) + +#define LL_INSERT_INORDER2(head,add,cmp,next) \ +do { \ + LDECLTYPE(head) _tmp; \ + if (head) { \ + LL_LOWER_BOUND2(head, _tmp, add, cmp, next); \ + LL_APPEND_ELEM2(head, _tmp, add, next); \ + } else { \ + (head) = (add); \ + (head)->next = NULL; \ + } \ +} while (0) + +#define LL_LOWER_BOUND(head,elt,like,cmp) \ + LL_LOWER_BOUND2(head,elt,like,cmp,next) + +#define LL_LOWER_BOUND2(head,elt,like,cmp,next) \ + do { \ + if ((head) == NULL || (cmp(head, like)) >= 0) { \ + (elt) = NULL; \ + } else { \ + for ((elt) = (head); (elt)->next != NULL; (elt) = (elt)->next) { \ + if (cmp((elt)->next, like) >= 0) { \ + break; \ + } \ + } \ + } \ + } while (0) + +#define LL_DELETE(head,del) \ + LL_DELETE2(head,del,next) + +#define LL_DELETE2(head,del,next) \ +do { \ + LDECLTYPE(head) _tmp; \ + if ((head) == (del)) { \ + (head)=(head)->next; \ + } else { \ + _tmp = (head); \ + while (_tmp->next && (_tmp->next != (del))) { \ + _tmp = _tmp->next; \ + } \ + if (_tmp->next) { \ + _tmp->next = (del)->next; \ + } \ + } \ +} while (0) + +#define LL_COUNT(head,el,counter) \ + LL_COUNT2(head,el,counter,next) \ + +#define LL_COUNT2(head,el,counter,next) \ +do { \ + (counter) = 0; \ + LL_FOREACH2(head,el,next) { ++(counter); } \ +} while (0) + +#define LL_FOREACH(head,el) \ + LL_FOREACH2(head,el,next) + +#define LL_FOREACH2(head,el,next) \ + for ((el) = (head); el; (el) = (el)->next) + +#define LL_FOREACH_SAFE(head,el,tmp) \ + LL_FOREACH_SAFE2(head,el,tmp,next) + +#define LL_FOREACH_SAFE2(head,el,tmp,next) \ + for ((el) = (head); (el) && ((tmp) = (el)->next, 1); (el) = (tmp)) + +#define LL_SEARCH_SCALAR(head,out,field,val) \ + LL_SEARCH_SCALAR2(head,out,field,val,next) + +#define LL_SEARCH_SCALAR2(head,out,field,val,next) \ +do { \ + LL_FOREACH2(head,out,next) { \ + if ((out)->field == (val)) break; \ + } \ +} while (0) + +#define LL_SEARCH(head,out,elt,cmp) \ + LL_SEARCH2(head,out,elt,cmp,next) + +#define LL_SEARCH2(head,out,elt,cmp,next) \ +do { \ + LL_FOREACH2(head,out,next) { \ + if ((cmp(out,elt))==0) break; \ + } \ +} while (0) + +#define LL_REPLACE_ELEM2(head, el, add, next) \ +do { \ + LDECLTYPE(head) _tmp; \ + assert((head) != NULL); \ + assert((el) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el)->next; \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + _tmp = (head); \ + while (_tmp->next && (_tmp->next != (el))) { \ + _tmp = _tmp->next; \ + } \ + if (_tmp->next) { \ + _tmp->next = (add); \ + } \ + } \ +} while (0) + +#define LL_REPLACE_ELEM(head, el, add) \ + LL_REPLACE_ELEM2(head, el, add, next) + +#define LL_PREPEND_ELEM2(head, el, add, next) \ +do { \ + if (el) { \ + LDECLTYPE(head) _tmp; \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el); \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + _tmp = (head); \ + while (_tmp->next && (_tmp->next != (el))) { \ + _tmp = _tmp->next; \ + } \ + if (_tmp->next) { \ + _tmp->next = (add); \ + } \ + } \ + } else { \ + LL_APPEND2(head, add, next); \ + } \ +} while (0) \ + +#define LL_PREPEND_ELEM(head, el, add) \ + LL_PREPEND_ELEM2(head, el, add, next) + +#define LL_APPEND_ELEM2(head, el, add, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el)->next; \ + (el)->next = (add); \ + } else { \ + LL_PREPEND2(head, add, next); \ + } \ +} while (0) \ + +#define LL_APPEND_ELEM(head, el, add) \ + LL_APPEND_ELEM2(head, el, add, next) + +#ifdef NO_DECLTYPE +/* Here are VS2008 / NO_DECLTYPE replacements for a few functions */ + +#undef LL_CONCAT2 +#define LL_CONCAT2(head1,head2,next) \ +do { \ + char *_tmp; \ + if (head1) { \ + _tmp = (char*)(head1); \ + while ((head1)->next) { (head1) = (head1)->next; } \ + (head1)->next = (head2); \ + UTLIST_RS(head1); \ + } else { \ + (head1)=(head2); \ + } \ +} while (0) + +#undef LL_APPEND2 +#define LL_APPEND2(head,add,next) \ +do { \ + if (head) { \ + (add)->next = head; /* use add->next as a temp variable */ \ + while ((add)->next->next) { (add)->next = (add)->next->next; } \ + (add)->next->next=(add); \ + } else { \ + (head)=(add); \ + } \ + (add)->next=NULL; \ +} while (0) + +#undef LL_INSERT_INORDER2 +#define LL_INSERT_INORDER2(head,add,cmp,next) \ +do { \ + if ((head) == NULL || (cmp(head, add)) >= 0) { \ + (add)->next = (head); \ + (head) = (add); \ + } else { \ + char *_tmp = (char*)(head); \ + while ((head)->next != NULL && (cmp((head)->next, add)) < 0) { \ + (head) = (head)->next; \ + } \ + (add)->next = (head)->next; \ + (head)->next = (add); \ + UTLIST_RS(head); \ + } \ +} while (0) + +#undef LL_DELETE2 +#define LL_DELETE2(head,del,next) \ +do { \ + if ((head) == (del)) { \ + (head)=(head)->next; \ + } else { \ + char *_tmp = (char*)(head); \ + while ((head)->next && ((head)->next != (del))) { \ + (head) = (head)->next; \ + } \ + if ((head)->next) { \ + (head)->next = ((del)->next); \ + } \ + UTLIST_RS(head); \ + } \ +} while (0) + +#undef LL_REPLACE_ELEM2 +#define LL_REPLACE_ELEM2(head, el, add, next) \ +do { \ + assert((head) != NULL); \ + assert((el) != NULL); \ + assert((add) != NULL); \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + (add)->next = head; \ + while ((add)->next->next && ((add)->next->next != (el))) { \ + (add)->next = (add)->next->next; \ + } \ + if ((add)->next->next) { \ + (add)->next->next = (add); \ + } \ + } \ + (add)->next = (el)->next; \ +} while (0) + +#undef LL_PREPEND_ELEM2 +#define LL_PREPEND_ELEM2(head, el, add, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + (add)->next = (head); \ + while ((add)->next->next && ((add)->next->next != (el))) { \ + (add)->next = (add)->next->next; \ + } \ + if ((add)->next->next) { \ + (add)->next->next = (add); \ + } \ + } \ + (add)->next = (el); \ + } else { \ + LL_APPEND2(head, add, next); \ + } \ +} while (0) \ + +#endif /* NO_DECLTYPE */ + +/****************************************************************************** + * doubly linked list macros (non-circular) * + *****************************************************************************/ +#define DL_PREPEND(head,add) \ + DL_PREPEND2(head,add,prev,next) + +#define DL_PREPEND2(head,add,prev,next) \ +do { \ + (add)->next = (head); \ + if (head) { \ + (add)->prev = (head)->prev; \ + (head)->prev = (add); \ + } else { \ + (add)->prev = (add); \ + } \ + (head) = (add); \ +} while (0) + +#define DL_APPEND(head,add) \ + DL_APPEND2(head,add,prev,next) + +#define DL_APPEND2(head,add,prev,next) \ +do { \ + if (head) { \ + (add)->prev = (head)->prev; \ + (head)->prev->next = (add); \ + (head)->prev = (add); \ + (add)->next = NULL; \ + } else { \ + (head)=(add); \ + (head)->prev = (head); \ + (head)->next = NULL; \ + } \ +} while (0) + +#define DL_INSERT_INORDER(head,add,cmp) \ + DL_INSERT_INORDER2(head,add,cmp,prev,next) + +#define DL_INSERT_INORDER2(head,add,cmp,prev,next) \ +do { \ + LDECLTYPE(head) _tmp; \ + if (head) { \ + DL_LOWER_BOUND2(head, _tmp, add, cmp, next); \ + DL_APPEND_ELEM2(head, _tmp, add, prev, next); \ + } else { \ + (head) = (add); \ + (head)->prev = (head); \ + (head)->next = NULL; \ + } \ +} while (0) + +#define DL_LOWER_BOUND(head,elt,like,cmp) \ + DL_LOWER_BOUND2(head,elt,like,cmp,next) + +#define DL_LOWER_BOUND2(head,elt,like,cmp,next) \ +do { \ + if ((head) == NULL || (cmp(head, like)) >= 0) { \ + (elt) = NULL; \ + } else { \ + for ((elt) = (head); (elt)->next != NULL; (elt) = (elt)->next) { \ + if ((cmp((elt)->next, like)) >= 0) { \ + break; \ + } \ + } \ + } \ +} while (0) + +#define DL_CONCAT(head1,head2) \ + DL_CONCAT2(head1,head2,prev,next) + +#define DL_CONCAT2(head1,head2,prev,next) \ +do { \ + LDECLTYPE(head1) _tmp; \ + if (head2) { \ + if (head1) { \ + UTLIST_CASTASGN(_tmp, (head2)->prev); \ + (head2)->prev = (head1)->prev; \ + (head1)->prev->next = (head2); \ + UTLIST_CASTASGN((head1)->prev, _tmp); \ + } else { \ + (head1)=(head2); \ + } \ + } \ +} while (0) + +#define DL_DELETE(head,del) \ + DL_DELETE2(head,del,prev,next) + +#define DL_DELETE2(head,del,prev,next) \ +do { \ + assert((head) != NULL); \ + assert((del)->prev != NULL); \ + if ((del)->prev == (del)) { \ + (head)=NULL; \ + } else if ((del)==(head)) { \ + (del)->next->prev = (del)->prev; \ + (head) = (del)->next; \ + } else { \ + (del)->prev->next = (del)->next; \ + if ((del)->next) { \ + (del)->next->prev = (del)->prev; \ + } else { \ + (head)->prev = (del)->prev; \ + } \ + } \ +} while (0) + +#define DL_COUNT(head,el,counter) \ + DL_COUNT2(head,el,counter,next) \ + +#define DL_COUNT2(head,el,counter,next) \ +do { \ + (counter) = 0; \ + DL_FOREACH2(head,el,next) { ++(counter); } \ +} while (0) + +#define DL_FOREACH(head,el) \ + DL_FOREACH2(head,el,next) + +#define DL_FOREACH2(head,el,next) \ + for ((el) = (head); el; (el) = (el)->next) + +/* this version is safe for deleting the elements during iteration */ +#define DL_FOREACH_SAFE(head,el,tmp) \ + DL_FOREACH_SAFE2(head,el,tmp,next) + +#define DL_FOREACH_SAFE2(head,el,tmp,next) \ + for ((el) = (head); (el) && ((tmp) = (el)->next, 1); (el) = (tmp)) + +/* these are identical to their singly-linked list counterparts */ +#define DL_SEARCH_SCALAR LL_SEARCH_SCALAR +#define DL_SEARCH LL_SEARCH +#define DL_SEARCH_SCALAR2 LL_SEARCH_SCALAR2 +#define DL_SEARCH2 LL_SEARCH2 + +#define DL_REPLACE_ELEM2(head, el, add, prev, next) \ +do { \ + assert((head) != NULL); \ + assert((el) != NULL); \ + assert((add) != NULL); \ + if ((head) == (el)) { \ + (head) = (add); \ + (add)->next = (el)->next; \ + if ((el)->next == NULL) { \ + (add)->prev = (add); \ + } else { \ + (add)->prev = (el)->prev; \ + (add)->next->prev = (add); \ + } \ + } else { \ + (add)->next = (el)->next; \ + (add)->prev = (el)->prev; \ + (add)->prev->next = (add); \ + if ((el)->next == NULL) { \ + (head)->prev = (add); \ + } else { \ + (add)->next->prev = (add); \ + } \ + } \ +} while (0) + +#define DL_REPLACE_ELEM(head, el, add) \ + DL_REPLACE_ELEM2(head, el, add, prev, next) + +#define DL_PREPEND_ELEM2(head, el, add, prev, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el); \ + (add)->prev = (el)->prev; \ + (el)->prev = (add); \ + if ((head) == (el)) { \ + (head) = (add); \ + } else { \ + (add)->prev->next = (add); \ + } \ + } else { \ + DL_APPEND2(head, add, prev, next); \ + } \ +} while (0) \ + +#define DL_PREPEND_ELEM(head, el, add) \ + DL_PREPEND_ELEM2(head, el, add, prev, next) + +#define DL_APPEND_ELEM2(head, el, add, prev, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el)->next; \ + (add)->prev = (el); \ + (el)->next = (add); \ + if ((add)->next) { \ + (add)->next->prev = (add); \ + } else { \ + (head)->prev = (add); \ + } \ + } else { \ + DL_PREPEND2(head, add, prev, next); \ + } \ +} while (0) \ + +#define DL_APPEND_ELEM(head, el, add) \ + DL_APPEND_ELEM2(head, el, add, prev, next) + +#ifdef NO_DECLTYPE +/* Here are VS2008 / NO_DECLTYPE replacements for a few functions */ + +#undef DL_INSERT_INORDER2 +#define DL_INSERT_INORDER2(head,add,cmp,prev,next) \ +do { \ + if ((head) == NULL) { \ + (add)->prev = (add); \ + (add)->next = NULL; \ + (head) = (add); \ + } else if ((cmp(head, add)) >= 0) { \ + (add)->prev = (head)->prev; \ + (add)->next = (head); \ + (head)->prev = (add); \ + (head) = (add); \ + } else { \ + char *_tmp = (char*)(head); \ + while ((head)->next && (cmp((head)->next, add)) < 0) { \ + (head) = (head)->next; \ + } \ + (add)->prev = (head); \ + (add)->next = (head)->next; \ + (head)->next = (add); \ + UTLIST_RS(head); \ + if ((add)->next) { \ + (add)->next->prev = (add); \ + } else { \ + (head)->prev = (add); \ + } \ + } \ +} while (0) +#endif /* NO_DECLTYPE */ + +/****************************************************************************** + * circular doubly linked list macros * + *****************************************************************************/ +#define CDL_APPEND(head,add) \ + CDL_APPEND2(head,add,prev,next) + +#define CDL_APPEND2(head,add,prev,next) \ +do { \ + if (head) { \ + (add)->prev = (head)->prev; \ + (add)->next = (head); \ + (head)->prev = (add); \ + (add)->prev->next = (add); \ + } else { \ + (add)->prev = (add); \ + (add)->next = (add); \ + (head) = (add); \ + } \ +} while (0) + +#define CDL_PREPEND(head,add) \ + CDL_PREPEND2(head,add,prev,next) + +#define CDL_PREPEND2(head,add,prev,next) \ +do { \ + if (head) { \ + (add)->prev = (head)->prev; \ + (add)->next = (head); \ + (head)->prev = (add); \ + (add)->prev->next = (add); \ + } else { \ + (add)->prev = (add); \ + (add)->next = (add); \ + } \ + (head) = (add); \ +} while (0) + +#define CDL_INSERT_INORDER(head,add,cmp) \ + CDL_INSERT_INORDER2(head,add,cmp,prev,next) + +#define CDL_INSERT_INORDER2(head,add,cmp,prev,next) \ +do { \ + LDECLTYPE(head) _tmp; \ + if (head) { \ + CDL_LOWER_BOUND2(head, _tmp, add, cmp, next); \ + CDL_APPEND_ELEM2(head, _tmp, add, prev, next); \ + } else { \ + (head) = (add); \ + (head)->next = (head); \ + (head)->prev = (head); \ + } \ +} while (0) + +#define CDL_LOWER_BOUND(head,elt,like,cmp) \ + CDL_LOWER_BOUND2(head,elt,like,cmp,next) + +#define CDL_LOWER_BOUND2(head,elt,like,cmp,next) \ +do { \ + if ((head) == NULL || (cmp(head, like)) >= 0) { \ + (elt) = NULL; \ + } else { \ + for ((elt) = (head); (elt)->next != (head); (elt) = (elt)->next) { \ + if ((cmp((elt)->next, like)) >= 0) { \ + break; \ + } \ + } \ + } \ +} while (0) + +#define CDL_DELETE(head,del) \ + CDL_DELETE2(head,del,prev,next) + +#define CDL_DELETE2(head,del,prev,next) \ +do { \ + if (((head)==(del)) && ((head)->next == (head))) { \ + (head) = NULL; \ + } else { \ + (del)->next->prev = (del)->prev; \ + (del)->prev->next = (del)->next; \ + if ((del) == (head)) (head)=(del)->next; \ + } \ +} while (0) + +#define CDL_COUNT(head,el,counter) \ + CDL_COUNT2(head,el,counter,next) \ + +#define CDL_COUNT2(head, el, counter,next) \ +do { \ + (counter) = 0; \ + CDL_FOREACH2(head,el,next) { ++(counter); } \ +} while (0) + +#define CDL_FOREACH(head,el) \ + CDL_FOREACH2(head,el,next) + +#define CDL_FOREACH2(head,el,next) \ + for ((el)=(head);el;(el)=(((el)->next==(head)) ? NULL : (el)->next)) + +#define CDL_FOREACH_SAFE(head,el,tmp1,tmp2) \ + CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next) + +#define CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next) \ + for ((el) = (head), (tmp1) = (head) ? (head)->prev : NULL; \ + (el) && ((tmp2) = (el)->next, 1); \ + (el) = ((el) == (tmp1) ? NULL : (tmp2))) + +#define CDL_SEARCH_SCALAR(head,out,field,val) \ + CDL_SEARCH_SCALAR2(head,out,field,val,next) + +#define CDL_SEARCH_SCALAR2(head,out,field,val,next) \ +do { \ + CDL_FOREACH2(head,out,next) { \ + if ((out)->field == (val)) break; \ + } \ +} while (0) + +#define CDL_SEARCH(head,out,elt,cmp) \ + CDL_SEARCH2(head,out,elt,cmp,next) + +#define CDL_SEARCH2(head,out,elt,cmp,next) \ +do { \ + CDL_FOREACH2(head,out,next) { \ + if ((cmp(out,elt))==0) break; \ + } \ +} while (0) + +#define CDL_REPLACE_ELEM2(head, el, add, prev, next) \ +do { \ + assert((head) != NULL); \ + assert((el) != NULL); \ + assert((add) != NULL); \ + if ((el)->next == (el)) { \ + (add)->next = (add); \ + (add)->prev = (add); \ + (head) = (add); \ + } else { \ + (add)->next = (el)->next; \ + (add)->prev = (el)->prev; \ + (add)->next->prev = (add); \ + (add)->prev->next = (add); \ + if ((head) == (el)) { \ + (head) = (add); \ + } \ + } \ +} while (0) + +#define CDL_REPLACE_ELEM(head, el, add) \ + CDL_REPLACE_ELEM2(head, el, add, prev, next) + +#define CDL_PREPEND_ELEM2(head, el, add, prev, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el); \ + (add)->prev = (el)->prev; \ + (el)->prev = (add); \ + (add)->prev->next = (add); \ + if ((head) == (el)) { \ + (head) = (add); \ + } \ + } else { \ + CDL_APPEND2(head, add, prev, next); \ + } \ +} while (0) + +#define CDL_PREPEND_ELEM(head, el, add) \ + CDL_PREPEND_ELEM2(head, el, add, prev, next) + +#define CDL_APPEND_ELEM2(head, el, add, prev, next) \ +do { \ + if (el) { \ + assert((head) != NULL); \ + assert((add) != NULL); \ + (add)->next = (el)->next; \ + (add)->prev = (el); \ + (el)->next = (add); \ + (add)->next->prev = (add); \ + } else { \ + CDL_PREPEND2(head, add, prev, next); \ + } \ +} while (0) + +#define CDL_APPEND_ELEM(head, el, add) \ + CDL_APPEND_ELEM2(head, el, add, prev, next) + +#ifdef NO_DECLTYPE +/* Here are VS2008 / NO_DECLTYPE replacements for a few functions */ + +#undef CDL_INSERT_INORDER2 +#define CDL_INSERT_INORDER2(head,add,cmp,prev,next) \ +do { \ + if ((head) == NULL) { \ + (add)->prev = (add); \ + (add)->next = (add); \ + (head) = (add); \ + } else if ((cmp(head, add)) >= 0) { \ + (add)->prev = (head)->prev; \ + (add)->next = (head); \ + (add)->prev->next = (add); \ + (head)->prev = (add); \ + (head) = (add); \ + } else { \ + char *_tmp = (char*)(head); \ + while ((char*)(head)->next != _tmp && (cmp((head)->next, add)) < 0) { \ + (head) = (head)->next; \ + } \ + (add)->prev = (head); \ + (add)->next = (head)->next; \ + (add)->next->prev = (add); \ + (head)->next = (add); \ + UTLIST_RS(head); \ + } \ +} while (0) +#endif /* NO_DECLTYPE */ + +#endif /* UTLIST_H */ diff --git a/libs/libmosquitto/libmosquitto.vcxproj b/libs/libmosquitto/libmosquitto.vcxproj new file mode 100644 index 0000000000..a9154fd38c --- /dev/null +++ b/libs/libmosquitto/libmosquitto.vcxproj @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{6D19209B-ECE7-4B9C-941C-1AA2B484F199}</ProjectGuid> + <ProjectName>libmosquitto</ProjectName> + <TargetName>libmosquitto</TargetName> + </PropertyGroup> + <Import Project="$(ProjectDir)..\..\build\vc.common\lib.props" /> + <ItemDefinitionGroup> + <ClCompile> + <AdditionalIncludeDirectories>.\include\;.\src\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> + <PreprocessorDefinitions>libmosquitto_EXPORTS;WIN32;WITH_TLS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ClCompile> + <Link> + <AdditionalDependencies>libeay32.lib;ssleay32.lib;%(AdditionalDependencies)</AdditionalDependencies> + </Link> + </ItemDefinitionGroup> +</Project>
\ No newline at end of file diff --git a/libs/libmosquitto/libmosquitto.vcxproj.filters b/libs/libmosquitto/libmosquitto.vcxproj.filters new file mode 100644 index 0000000000..8f90aeb3d5 --- /dev/null +++ b/libs/libmosquitto/libmosquitto.vcxproj.filters @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$(ProjectDir)..\..\build\vc.common\common.filters" /> +</Project> 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 |