diff options
Diffstat (limited to 'mqtt_websockets/src/include')
-rw-r--r-- | mqtt_websockets/src/include/common_internal.h | 37 | ||||
-rw-r--r-- | mqtt_websockets/src/include/common_public.h | 33 | ||||
-rw-r--r-- | mqtt_websockets/src/include/endian_compat.h | 41 | ||||
-rw-r--r-- | mqtt_websockets/src/include/mqtt_constants.h | 101 | ||||
-rw-r--r-- | mqtt_websockets/src/include/mqtt_ng.h | 97 | ||||
-rw-r--r-- | mqtt_websockets/src/include/mqtt_wss_client.h | 172 | ||||
-rw-r--r-- | mqtt_websockets/src/include/mqtt_wss_log.h | 37 | ||||
-rw-r--r-- | mqtt_websockets/src/include/ws_client.h | 130 |
8 files changed, 648 insertions, 0 deletions
diff --git a/mqtt_websockets/src/include/common_internal.h b/mqtt_websockets/src/include/common_internal.h new file mode 100644 index 00000000..2e656355 --- /dev/null +++ b/mqtt_websockets/src/include/common_internal.h @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-3.0-only +// +// This program is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along with this program. +// If not, see <https://www.gnu.org/licenses/>. + +#ifndef COMMON_INTERNAL_H +#define COMMON_INTERNAL_H + +#include "endian_compat.h" + +#ifdef MQTT_WSS_CUSTOM_ALLOC +#include "mqtt_wss_pal.h" +#else +#define mw_malloc(...) malloc(__VA_ARGS__) +#define mw_calloc(...) calloc(__VA_ARGS__) +#define mw_free(...) free(__VA_ARGS__) +#define mw_strdup(...) strdup(__VA_ARGS__) +#define mw_realloc(...) realloc(__VA_ARGS__) +#endif + +#ifndef MQTT_WSS_FRAG_MEMALIGN +#define MQTT_WSS_FRAG_MEMALIGN (8) +#endif + +#define OPENSSL_VERSION_095 0x00905100L +#define OPENSSL_VERSION_097 0x00907000L +#define OPENSSL_VERSION_110 0x10100000L +#define OPENSSL_VERSION_111 0x10101000L + +#endif /* COMMON_INTERNAL_H */ diff --git a/mqtt_websockets/src/include/common_public.h b/mqtt_websockets/src/include/common_public.h new file mode 100644 index 00000000..a855737f --- /dev/null +++ b/mqtt_websockets/src/include/common_public.h @@ -0,0 +1,33 @@ +#ifndef MQTT_WEBSOCKETS_COMMON_PUBLIC_H +#define MQTT_WEBSOCKETS_COMMON_PUBLIC_H + +#include <stddef.h> + +/* free_fnc_t in general (in whatever function or struct it is used) + * decides how the related data will be handled. + * - If NULL the data are copied internally (causing malloc and later free) + * - If pointer provided the free function pointed will be called when data are no longer needed + * to free associated memory. This is effectively transfering ownership of that pointer to the library. + * This also allows caller to provide custom free function other than system one. + * - If == CALLER_RESPONSIBILITY the library will not copy the data pointed to and will not call free + * at the end. This is usefull to avoid copying memory (and associated malloc/free) when data are for + * example static. In this case caller has to guarantee the memory pointed to will be valid for entire duration + * it is needed. For example by freeing the data after PUBACK is received or by data being static. + */ +typedef void (*free_fnc_t)(void *ptr); +void _caller_responsibility(void *ptr); +#define CALLER_RESPONSIBILITY ((free_fnc_t)&_caller_responsibility) + +struct mqtt_ng_stats { + size_t tx_bytes_queued; + int tx_messages_queued; + int tx_messages_sent; + int rx_messages_rcvd; + size_t tx_buffer_used; + size_t tx_buffer_free; + size_t tx_buffer_size; + // part of transaction buffer that containes mesages we can free alredy during the garbage colleciton step + size_t tx_buffer_reclaimable; +}; + +#endif /* MQTT_WEBSOCKETS_COMMON_PUBLIC_H */ diff --git a/mqtt_websockets/src/include/endian_compat.h b/mqtt_websockets/src/include/endian_compat.h new file mode 100644 index 00000000..076ccbe8 --- /dev/null +++ b/mqtt_websockets/src/include/endian_compat.h @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-3.0-only +// +// This program is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along with this program. +// If not, see <https://www.gnu.org/licenses/>. + +#ifndef MQTT_WSS_ENDIAN_COMPAT_H +#define MQTT_WSS_ENDIAN_COMPAT_H + +#ifdef __APPLE__ + #include <libkern/OSByteOrder.h> + + #define htobe16(x) OSSwapHostToBigInt16(x) + #define htole16(x) OSSwapHostToLittleInt16(x) + #define be16toh(x) OSSwapBigToHostInt16(x) + #define le16toh(x) OSSwapLittleToHostInt16(x) + + #define htobe32(x) OSSwapHostToBigInt32(x) + #define htole32(x) OSSwapHostToLittleInt32(x) + #define be32toh(x) OSSwapBigToHostInt32(x) + #define le32toh(x) OSSwapLittleToHostInt32(x) + + #define htobe64(x) OSSwapHostToBigInt64(x) + #define htole64(x) OSSwapHostToLittleInt64(x) + #define be64toh(x) OSSwapBigToHostInt64(x) + #define le64toh(x) OSSwapLittleToHostInt64(x) +#else +#ifdef __FreeBSD__ + #include <sys/endian.h> +#else + #include <endian.h> +#endif +#endif + +#endif /* MQTT_WSS_ENDIAN_COMPAT_H */ diff --git a/mqtt_websockets/src/include/mqtt_constants.h b/mqtt_websockets/src/include/mqtt_constants.h new file mode 100644 index 00000000..1db49897 --- /dev/null +++ b/mqtt_websockets/src/include/mqtt_constants.h @@ -0,0 +1,101 @@ +#ifndef MQTT_CONSTANTS_H +#define MQTT_CONSTANTS_H + +#define MQTT_MAX_QOS 0x02 + +#define MQTT_VERSION_5_0 0x5 + +/* [MQTT-1.5.5] most significant bit + of MQTT Variable Byte Integer signifies + there are more bytes following */ +#define MQTT_VBI_CONTINUATION_FLAG 0x80 +#define MQTT_VBI_DATA_MASK 0x7F +#define MQTT_VBI_MAXBYTES 4 + +/* MQTT control packet types as defined in + 2.1.2 MQTT Control Packet type */ +#define MQTT_CPT_CONNECT 0x1 +#define MQTT_CPT_CONNACK 0x2 +#define MQTT_CPT_PUBLISH 0x3 +#define MQTT_CPT_PUBACK 0x4 +#define MQTT_CPT_PUBREC 0x5 +#define MQTT_CPT_PUBREL 0x6 +#define MQTT_CPT_PUBCOMP 0x7 +#define MQTT_CPT_SUBSCRIBE 0x8 +#define MQTT_CPT_SUBACK 0x9 +#define MQTT_CPT_UNSUBSCRIBE 0xA +#define MQTT_CPT_UNSUBACK 0xB +#define MQTT_CPT_PINGREQ 0xC +#define MQTT_CPT_PINGRESP 0xD +#define MQTT_CPT_DISCONNECT 0xE +#define MQTT_CPT_AUTH 0xF + +// MQTT CONNECT FLAGS (spec:3.1.2.3) +#define MQTT_CONNECT_FLAG_USERNAME 0x80 +#define MQTT_CONNECT_FLAG_PASSWORD 0x40 +#define MQTT_CONNECT_FLAG_LWT_RETAIN 0x20 +#define MQTT_CONNECT_FLAG_LWT 0x04 +#define MQTT_CONNECT_FLAG_CLEAN_START 0x02 + +#define MQTT_CONNECT_FLAG_QOS_MASK 0x18 +#define MQTT_CONNECT_FLAG_QOS_BITSHIFT 3 + +#define MQTT_MAX_CLIENT_ID 23 /* [MQTT-3.1.3-5] */ + +// MQTT Property identifiers [MQTT-2.2.2.2] +#define MQTT_PROP_PAYLOAD_FMT_INDICATOR 0x01 +#define MQTT_PROP_PAYLOAD_FMT_INDICATOR_NAME "Payload Format Indicator" +#define MQTT_PROP_MSG_EXPIRY_INTERVAL 0x02 +#define MQTT_PROP_MSG_EXPIRY_INTERVAL_NAME "Message Expiry Interval" +#define MQTT_PROP_CONTENT_TYPE 0x03 +#define MQTT_PROP_CONTENT_TYPE_NAME "Content Type" +#define MQTT_PROP_RESPONSE_TOPIC 0x08 +#define MQTT_PROP_RESPONSE_TOPIC_NAME "Response Topic" +#define MQTT_PROP_CORRELATION_DATA 0x09 +#define MQTT_PROP_CORRELATION_DATA_NAME "Correlation Data" +#define MQTT_PROP_SUB_IDENTIFIER 0x0B +#define MQTT_PROP_SUB_IDENTIFIER_NAME "Subscription Identifier" +#define MQTT_PROP_SESSION_EXPIRY_INTERVAL 0x11 +#define MQTT_PROP_SESSION_EXPIRY_INTERVAL_NAME "Session Expiry Interval" +#define MQTT_PROP_ASSIGNED_CLIENT_ID 0x12 +#define MQTT_PROP_ASSIGNED_CLIENT_ID_NAME "Assigned Client Identifier" +#define MQTT_PROP_SERVER_KEEP_ALIVE 0x13 +#define MQTT_PROP_SERVER_KEEP_ALIVE_NAME "Server Keep Alive" +#define MQTT_PROP_AUTH_METHOD 0x15 +#define MQTT_PROP_AUTH_METHOD_NAME "Authentication Method" +#define MQTT_PROP_AUTH_DATA 0x16 +#define MQTT_PROP_AUTH_DATA_NAME "Authentication Data" +#define MQTT_PROP_REQ_PROBLEM_INFO 0x17 +#define MQTT_PROP_REQ_PROBLEM_INFO_NAME "Request Problem Information" +#define MQTT_PROP_WILL_DELAY_INTERVAL 0x18 +#define MQTT_PROP_WIIL_DELAY_INTERVAL_NAME "Will Delay Interval" +#define MQTT_PROP_REQ_RESP_INFORMATION 0x19 +#define MQTT_PROP_REQ_RESP_INFORMATION_NAME "Request Response Information" +#define MQTT_PROP_RESP_INFORMATION 0x1A +#define MQTT_PROP_RESP_INFORMATION_NAME "Response Information" +#define MQTT_PROP_SERVER_REF 0x1C +#define MQTT_PROP_SERVER_REF_NAME "Server Reference" +#define MQTT_PROP_REASON_STR 0x1F +#define MQTT_PROP_REASON_STR_NAME "Reason String" +#define MQTT_PROP_RECEIVE_MAX 0x21 +#define MQTT_PROP_RECEIVE_MAX_NAME "Receive Maximum" +#define MQTT_PROP_TOPIC_ALIAS_MAX 0x22 +#define MQTT_PROP_TOPIC_ALIAS_MAX_NAME "Topic Alias Maximum" +#define MQTT_PROP_TOPIC_ALIAS 0x23 +#define MQTT_PROP_TOPIC_ALIAS_NAME "Topic Alias" +#define MQTT_PROP_MAX_QOS 0x24 +#define MQTT_PROP_MAX_QOS_NAME "Maximum QoS" +#define MQTT_PROP_RETAIN_AVAIL 0x25 +#define MQTT_PROP_RETAIN_AVAIL_NAME "Retain Available" +#define MQTT_PROP_USR 0x26 +#define MQTT_PROP_USR_NAME "User Property" +#define MQTT_PROP_MAX_PKT_SIZE 0x27 +#define MQTT_PROP_MAX_PKT_SIZE_NAME "Maximum Packet Size" +#define MQTT_PROP_WILDCARD_SUB_AVAIL 0x28 +#define MQTT_PROP_WILDCARD_SUB_AVAIL_NAME "Wildcard Subscription Available" +#define MQTT_PROP_SUB_ID_AVAIL 0x29 +#define MQTT_PROP_SUB_ID_AVAIL_NAME "Subscription Identifier Available" +#define MQTT_PROP_SHARED_SUB_AVAIL 0x2A +#define MQTT_PROP_SHARED_SUB_AVAIL_NAME "Shared Subscription Available" + +#endif /* MQTT_CONSTANTS_H */ diff --git a/mqtt_websockets/src/include/mqtt_ng.h b/mqtt_websockets/src/include/mqtt_ng.h new file mode 100644 index 00000000..09668d09 --- /dev/null +++ b/mqtt_websockets/src/include/mqtt_ng.h @@ -0,0 +1,97 @@ +#include <stdint.h> +#include <sys/types.h> +#include <time.h> + +#include "ringbuffer.h" +#include "common_public.h" + +#define MQTT_NG_MSGGEN_OK 0 +// MQTT_NG_MSGGEN_USER_ERROR means parameters given to this function +// do not make sense or are out of MQTT specs +#define MQTT_NG_MSGGEN_USER_ERROR 1 +#define MQTT_NG_MSGGEN_BUFFER_OOM 2 +#define MQTT_NG_MSGGEN_MSG_TOO_BIG 3 + +struct mqtt_ng_client; + +/* Converts integer to MQTT Variable Byte Integer as per 1.5.5 of MQTT 5 specs + * @param input value to be converted + * @param output pointer to memory where output will be written to. Must allow up to 4 bytes to be written. + * @return number of bytes written to output or <= 0 if error in which case contents of output are undefined + */ +int uint32_to_mqtt_vbi(uint32_t input, char *output); + +struct mqtt_lwt_properties { + char *will_topic; + free_fnc_t will_topic_free; + + void *will_message; + free_fnc_t will_message_free; + size_t will_message_size; + + int will_qos; + int will_retain; +}; + +struct mqtt_auth_properties { + char *client_id; + free_fnc_t client_id_free; + char *username; + free_fnc_t username_free; + char *password; + free_fnc_t password_free; +}; + +int mqtt_ng_connect(struct mqtt_ng_client *client, + struct mqtt_auth_properties *auth, + struct mqtt_lwt_properties *lwt, + uint8_t clean_start, + uint16_t keep_alive); + +int mqtt_ng_publish(struct mqtt_ng_client *client, + char *topic, + free_fnc_t topic_free, + void *msg, + free_fnc_t msg_free, + size_t msg_len, + uint8_t publish_flags, + uint16_t *packet_id); + +struct mqtt_sub { + char *topic; + free_fnc_t topic_free; + uint8_t options; +}; + +int mqtt_ng_subscribe(struct mqtt_ng_client *client, struct mqtt_sub *subscriptions, size_t subscription_count); + +int mqtt_ng_ping(struct mqtt_ng_client *client); + +typedef ssize_t (*mqtt_ng_send_fnc_t)(void *user_ctx, const void* buf, size_t len); + +struct mqtt_ng_init { + mqtt_wss_log_ctx_t log; + rbuf_t data_in; + mqtt_ng_send_fnc_t data_out_fnc; + void *user_ctx; + + void (*puback_callback)(uint16_t packet_id); + void (*connack_callback)(void* user_ctx, int connack_reply); + void (*msg_callback)(const char *topic, const void *msg, size_t msglen, int qos); +}; + +struct mqtt_ng_client *mqtt_ng_init(struct mqtt_ng_init *settings); + +void mqtt_ng_destroy(struct mqtt_ng_client *client); + +int mqtt_ng_disconnect(struct mqtt_ng_client *client, uint8_t reason_code); + +int mqtt_ng_sync(struct mqtt_ng_client *client); + +time_t mqtt_ng_last_send_time(struct mqtt_ng_client *client); + +void mqtt_ng_set_max_mem(struct mqtt_ng_client *client, size_t bytes); + +void mqtt_ng_get_stats(struct mqtt_ng_client *client, struct mqtt_ng_stats *stats); + +int mqtt_ng_set_topic_alias(struct mqtt_ng_client *client, const char *topic); diff --git a/mqtt_websockets/src/include/mqtt_wss_client.h b/mqtt_websockets/src/include/mqtt_wss_client.h new file mode 100644 index 00000000..e325961b --- /dev/null +++ b/mqtt_websockets/src/include/mqtt_wss_client.h @@ -0,0 +1,172 @@ +// Copyright (C) 2020 Timotej Šiškovič +// SPDX-License-Identifier: GPL-3.0-only +// +// This program is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along with this program. +// If not, see <https://www.gnu.org/licenses/>. + +#ifndef MQTT_WSS_CLIENT_H +#define MQTT_WSS_CLIENT_H + +#include <stdint.h> +#include <stddef.h> //size_t + +#include "mqtt_wss_log.h" +#include "common_public.h" + +// All OK call me at your earliest convinience +#define MQTT_WSS_OK 0 +/* All OK, poll timeout you requested when calling mqtt_wss_service expired - you might want to know if timeout + * happened or we got some data or handle same as MQTT_WSS_OK + */ +#define MQTT_WSS_OK_TO 1 +// Connection was closed by remote +#define MQTT_WSS_ERR_CONN_DROP -1 +// Error in MQTT protocol (e.g. malformed packet) +#define MQTT_WSS_ERR_PROTO_MQTT -2 +// Error in WebSocket protocol (e.g. malformed packet) +#define MQTT_WSS_ERR_PROTO_WS -3 + +#define MQTT_WSS_ERR_TX_BUF_TOO_SMALL -4 +#define MQTT_WSS_ERR_RX_BUF_TOO_SMALL -5 + +#define MQTT_WSS_ERR_TOO_BIG_FOR_SERVER -6 +// if client was initialized with MQTT 3 but MQTT 5 feature +// was requested by user of library +#define MQTT_WSS_ERR_CANT_DO -8 + +typedef struct mqtt_wss_client_struct *mqtt_wss_client; + +typedef void (*msg_callback_fnc_t)(const char *topic, const void *msg, size_t msglen, int qos); +/* Creates new instance of MQTT over WSS. Doesn't start connection. + * @param log_prefix this is prefix to be used when logging to discern between multiple + * mqtt_wss instances. Can be NULL. + * @param log_callback is function pointer to fnc to be called when mqtt_wss wants + * to log. This allows plugging this library into your own logging system/solution. + * If NULL STDOUT/STDERR will be used. + * @param msg_callback is function pointer to function which will be called + * when application level message arrives from broker (for subscribed topics). + * Can be NULL if you are not interested about incoming messages. + * @param puback_callback is function pointer to function to be called when QOS1 Publish + * is acknowledged by server + */ +mqtt_wss_client mqtt_wss_new(const char *log_prefix, + mqtt_wss_log_callback_t log_callback, + msg_callback_fnc_t msg_callback, + void (*puback_callback)(uint16_t packet_id)); + +void mqtt_wss_set_max_buf_size(mqtt_wss_client client, size_t size); + +void mqtt_wss_destroy(mqtt_wss_client client); + +struct mqtt_connect_params; +struct mqtt_wss_proxy; + +#define MQTT_WSS_SSL_CERT_CHECK_FULL 0x00 +#define MQTT_WSS_SSL_ALLOW_SELF_SIGNED 0x01 +#define MQTT_WSS_SSL_DONT_CHECK_CERTS 0x08 + +/* Will block until the MQTT over WSS connection is established or return error + * @param client mqtt_wss_client which should connect + * @param host to connect to (where MQTT over WSS server is listening) + * @param port to connect to (where MQTT over WSS server is listening) + * @param mqtt_params pointer to mqtt_connect_params structure which contains MQTT credentials and settings + * @param ssl_flags parameters for OpenSSL, 0=MQTT_WSS_SSL_CERT_CHECK_FULL + */ +int mqtt_wss_connect(mqtt_wss_client client, char *host, int port, struct mqtt_connect_params *mqtt_params, int ssl_flags, struct mqtt_wss_proxy *proxy); +int mqtt_wss_service(mqtt_wss_client client, int timeout_ms); +void mqtt_wss_disconnect(mqtt_wss_client client, int timeout_ms); + +// we redefine this instead of using MQTT-C flags as in future +// we want to support different MQTT implementations if needed +enum mqtt_wss_publish_flags { + MQTT_WSS_PUB_QOS0 = 0x0, + MQTT_WSS_PUB_QOS1 = 0x1, + MQTT_WSS_PUB_QOS2 = 0x2, + MQTT_WSS_PUB_QOSMASK = 0x3, + MQTT_WSS_PUB_RETAIN = 0x4 +}; + +struct mqtt_connect_params { + const char *clientid; + const char *username; + const char *password; + const char *will_topic; + const void *will_msg; + enum mqtt_wss_publish_flags will_flags; + size_t will_msg_len; + int keep_alive; + int drop_on_publish_fail; +}; + +enum mqtt_wss_proxy_type { + MQTT_WSS_DIRECT = 0, + MQTT_WSS_PROXY_HTTP +}; + +struct mqtt_wss_proxy { + enum mqtt_wss_proxy_type type; + const char *host; + int port; + const char *username; + const char *password; +}; + +/* TODO!!! update the description + * Publishes MQTT message and gets message id + * @param client mqtt_wss_client which should transfer the message + * @param topic MQTT topic to publish message to (0 terminated C string) + * @param msg Message to be published (no need for 0 termination) + * @param msg_len Length of the message to be published + * @param publish_flags see enum mqtt_wss_publish_flags e.g. (MQTT_WSS_PUB_QOS1 | MQTT_WSS_PUB_RETAIN) + * @param packet_id is 16 bit unsigned int representing ID that can be used to pair with PUBACK callback + * for usages where application layer wants to know which messages are delivered when + * @return Returns 0 on success + */ +int mqtt_wss_publish5(mqtt_wss_client client, + char *topic, + free_fnc_t topic_free, + void *msg, + free_fnc_t msg_free, + size_t msg_len, + uint8_t publish_flags, + uint16_t *packet_id); + +int mqtt_wss_set_topic_alias(mqtt_wss_client client, const char *topic); + +/* Subscribes to MQTT topic + * @param client mqtt_wss_client which should do the subscription + * @param topic MQTT topic to subscribe to + * @param max_qos_level maximum QOS level that broker can send to us on this subscription + * @return Returns 0 on success + */ +int mqtt_wss_subscribe(mqtt_wss_client client, char *topic, int max_qos_level); + + +struct mqtt_wss_stats { + uint64_t bytes_tx; + uint64_t bytes_rx; +#ifdef MQTT_WSS_CPUSTATS + uint64_t time_keepalive; + uint64_t time_read_socket; + uint64_t time_write_socket; + uint64_t time_process_websocket; + uint64_t time_process_mqtt; +#endif + struct mqtt_ng_stats mqtt; +}; + +struct mqtt_wss_stats mqtt_wss_get_stats(mqtt_wss_client client); + +#ifdef MQTT_WSS_DEBUG +#include <openssl/ssl.h> +void mqtt_wss_set_SSL_CTX_keylog_cb(mqtt_wss_client client, void (*ssl_ctx_keylog_cb)(const SSL *ssl, const char *line)); +#endif + +#endif /* MQTT_WSS_CLIENT_H */ diff --git a/mqtt_websockets/src/include/mqtt_wss_log.h b/mqtt_websockets/src/include/mqtt_wss_log.h new file mode 100644 index 00000000..a33c460c --- /dev/null +++ b/mqtt_websockets/src/include/mqtt_wss_log.h @@ -0,0 +1,37 @@ +#ifndef MQTT_WSS_LOG_H +#define MQTT_WSS_LOG_H + +typedef enum mqtt_wss_log_type { + MQTT_WSS_LOG_DEBUG = 0x01, + MQTT_WSS_LOG_INFO = 0x02, + MQTT_WSS_LOG_WARN = 0x03, + MQTT_WSS_LOG_ERROR = 0x81, + MQTT_WSS_LOG_FATAL = 0x88 +} mqtt_wss_log_type_t; + +typedef void (*mqtt_wss_log_callback_t)(mqtt_wss_log_type_t, const char*); + +typedef struct mqtt_wss_log_ctx *mqtt_wss_log_ctx_t; + +/** Creates logging context with optional prefix and optional callback + * @param ctx_prefix String to be prefixed to every log message. + * This is useful if multiple clients are instantiated to be able to + * know which one this message belongs to. Can be `NULL` for no prefix. + * @param log_callback Callback to be called instead of logging to + * `STDOUT` or `STDERR` (if debug enabled otherwise silent). Callback has to be + * pointer to function of `void function(mqtt_wss_log_type_t, const char*)` type. + * If `NULL` default will be used (silent or STDERR/STDOUT). + * @return mqtt_wss_log_ctx_t or `NULL` on error */ +mqtt_wss_log_ctx_t mqtt_wss_log_ctx_create(const char *ctx_prefix, mqtt_wss_log_callback_t log_callback); + +/** Destroys logging context and cleans up the memory + * @param ctx Context to destroy */ +void mqtt_wss_log_ctx_destroy(mqtt_wss_log_ctx_t ctx); + +void mws_fatal(mqtt_wss_log_ctx_t ctx, const char *fmt, ...); +void mws_error(mqtt_wss_log_ctx_t ctx, const char *fmt, ...); +void mws_warn (mqtt_wss_log_ctx_t ctx, const char *fmt, ...); +void mws_info (mqtt_wss_log_ctx_t ctx, const char *fmt, ...); +void mws_debug(mqtt_wss_log_ctx_t ctx, const char *fmt, ...); + +#endif /* MQTT_WSS_LOG_H */ diff --git a/mqtt_websockets/src/include/ws_client.h b/mqtt_websockets/src/include/ws_client.h new file mode 100644 index 00000000..de4fac40 --- /dev/null +++ b/mqtt_websockets/src/include/ws_client.h @@ -0,0 +1,130 @@ +// Copyright (C) 2020 Timotej Šiškovič +// SPDX-License-Identifier: GPL-3.0-only +// +// This program is free software: you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free Software Foundation, version 3. +// +// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +// See the GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License along with this program. +// If not, see <https://www.gnu.org/licenses/>. + +#ifndef WS_CLIENT_H +#define WS_CLIENT_H + +#include "ringbuffer.h" +#include "mqtt_wss_log.h" + +#include <stdint.h> + +#define WS_CLIENT_NEED_MORE_BYTES 0x10 +#define WS_CLIENT_PARSING_DONE 0x11 +#define WS_CLIENT_CONNECTION_CLOSED 0x12 +#define WS_CLIENT_PROTOCOL_ERROR -0x10 +#define WS_CLIENT_BUFFER_FULL -0x11 +#define WS_CLIENT_INTERNAL_ERROR -0x12 + +enum websocket_client_conn_state { + WS_RAW = 0, + WS_HANDSHAKE, + WS_ESTABLISHED, + WS_ERROR, // connection has to be restarted if this is reached + WS_CONN_CLOSED_GRACEFUL +}; + +enum websocket_client_hdr_parse_state { + WS_HDR_HTTP = 0, // need to check HTTP/1.1 + WS_HDR_RC, // need to read HTTP code + WS_HDR_ENDLINE, // need to read rest of the first line + WS_HDR_PARSE_HEADERS, // rest of the header until CRLF CRLF + WS_HDR_PARSE_DONE, + WS_HDR_ALL_DONE +}; + +enum websocket_client_rx_ws_parse_state { + WS_FIRST_2BYTES = 0, + WS_PAYLOAD_EXTENDED_16, + WS_PAYLOAD_EXTENDED_64, + WS_PAYLOAD_DATA, // BINARY payload to be passed to MQTT + WS_PAYLOAD_CONNECTION_CLOSE, + WS_PAYLOAD_CONNECTION_CLOSE_EC, + WS_PAYLOAD_CONNECTION_CLOSE_MSG, + WS_PAYLOAD_SKIP_UNKNOWN_PAYLOAD, + WS_PAYLOAD_PING_REQ_PAYLOAD, // PING payload to be sent back as PONG + WS_PACKET_DONE +}; + +enum websocket_opcode { + WS_OP_CONTINUATION_FRAME = 0x0, + WS_OP_TEXT_FRAME = 0x1, + WS_OP_BINARY_FRAME = 0x2, + WS_OP_CONNECTION_CLOSE = 0x8, + WS_OP_PING = 0x9, + WS_OP_PONG = 0xA +}; + +struct ws_op_close_payload { + uint16_t ec; + char *reason; +}; + +struct http_header { + char *key; + char *value; + struct http_header *next; +}; + +typedef struct websocket_client { + enum websocket_client_conn_state state; + + struct ws_handshake { + enum websocket_client_hdr_parse_state hdr_state; + char *nonce_reply; + int nonce_matched; + int http_code; + char *http_reply_msg; + struct http_header *headers; + struct http_header *headers_tail; + int hdr_count; + } hs; + + struct ws_rx { + enum websocket_client_rx_ws_parse_state parse_state; + enum websocket_opcode opcode; + uint64_t payload_length; + uint64_t payload_processed; + union { + struct ws_op_close_payload op_close; + char *ping_msg; + } specific_data; + } rx; + + rbuf_t buf_read; // from SSL + rbuf_t buf_write; // to SSL and then to socket + // TODO if ringbuffer gets multiple tail support + // we can work without buf_to_mqtt and thus reduce + // memory usage and remove one more memcpy buf_read->buf_to_mqtt + rbuf_t buf_to_mqtt; // RAW data for MQTT lib + + int entropy_fd; + + // careful host is borrowed, don't free + char **host; + mqtt_wss_log_ctx_t log; +} ws_client; + +ws_client *ws_client_new(size_t buf_size, char **host, mqtt_wss_log_ctx_t log); +void ws_client_destroy(ws_client *client); +void ws_client_reset(ws_client *client); + +int ws_client_start_handshake(ws_client *client); + +int ws_client_want_write(ws_client *client); + +int ws_client_process(ws_client *client); + +int ws_client_send(ws_client *client, enum websocket_opcode frame_type, const char *data, size_t size); + +#endif /* WS_CLIENT_H */ |