From 73e82730cec3a83ff05d3df03e0e42c47664000a Mon Sep 17 00:00:00 2001 From: Eric Blankenhorn Date: Thu, 20 Jul 2023 16:47:36 -0500 Subject: [PATCH 01/62] Adding publish and subscribe atomic client examples --- .gitignore | 2 + CMakeLists.txt | 2 + README.md | 7 + examples/include.am | 28 +- examples/mqttclient/mqttclient.c | 3 - examples/mqttexample.c | 10 +- examples/mqttexample.h | 1 + examples/mqttnet.c | 19 +- examples/pub-sub/mqtt-pub-sub.h | 43 ++ examples/pub-sub/mqtt-pub.c | 530 +++++++++++++++++++++++++ examples/pub-sub/mqtt-sub.c | 655 +++++++++++++++++++++++++++++++ 11 files changed, 1286 insertions(+), 14 deletions(-) create mode 100644 examples/pub-sub/mqtt-pub-sub.h create mode 100644 examples/pub-sub/mqtt-pub.c create mode 100644 examples/pub-sub/mqtt-sub.c diff --git a/.gitignore b/.gitignore index ca4c36036..96e4168cb 100644 --- a/.gitignore +++ b/.gitignore @@ -78,6 +78,8 @@ examples/sn-client/sn-client_qos-1 examples/sn-client/sn-multithread examples/multithread/multithread examples/wiot/wiot +examples/pub-sub/mqtt-pub +examples/pub-sub/mqtt-sub # eclipse .cproject diff --git a/CMakeLists.txt b/CMakeLists.txt index d81318f9c..d8c6b7354 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -147,6 +147,8 @@ if (WOLFMQTT_EXAMPLES) add_mqtt_example(azureiothub azure/azureiothub.c) add_mqtt_example(fwpush firmware/fwpush.c) add_mqtt_example(fwclient firmware/fwclient.c) + add_mqtt_example(mqtt-pub pub-sub/mqtt-pub.c) + add_mqtt_example(mqtt-pub pub-sub/mqtt-sub.c) endif() #################################################### diff --git a/README.md b/README.md index 961e7de7a..956299cc5 100644 --- a/README.md +++ b/README.md @@ -213,6 +213,13 @@ WolfGatewayQoS-1,wolfMQTT/example/testTopic, 1 ### Multithread Example This example exercises the multithreading capabilities of the client library. The client implements two tasks: one that publishes to the broker; and another that waits for messages from the broker. The publish thread is created `NUM_PUB_TASKS` times (10 by default) and sends unique messages to the broker. This feature is enabled using the `--enable-mt` configuration option. The example is located in `/examples/multithread/`. +### Atomic publish and subscribe examples +In the `examples/pub-sub` folder, there are two simple client examples: +* mqtt-pub - publishes to a topic +* mqtt-sub - subscribes to a topic and waits for messages + +These examples are useful for quickly testing or scripting. + ## Example Options The command line examples can be executed with optional parameters. To see a list of the available parameters, add the `-?` diff --git a/examples/include.am b/examples/include.am index bc64ffee7..5043d709f 100644 --- a/examples/include.am +++ b/examples/include.am @@ -13,7 +13,9 @@ noinst_PROGRAMS += examples/mqttclient/mqttclient \ examples/multithread/multithread \ examples/sn-client/sn-client \ examples/sn-client/sn-client_qos-1 \ - examples/sn-client/sn-multithread + examples/sn-client/sn-multithread \ + examples/pub-sub/mqtt-pub \ + examples/pub-sub/mqtt-sub noinst_HEADERS += examples/mqttclient/mqttclient.h \ examples/mqttsimple/mqttsimple.h \ @@ -28,7 +30,8 @@ noinst_HEADERS += examples/mqttclient/mqttclient.h \ examples/mqttport.h \ examples/nbclient/nbclient.h \ examples/multithread/multithread.h \ - examples/sn-client/sn-client.h + examples/sn-client/sn-client.h \ + examples/pub-sub/mqtt-pub-sub.h # MQTT Client Example examples_mqttclient_mqttclient_SOURCES = examples/mqttclient/mqttclient.c \ @@ -127,6 +130,21 @@ examples_sn_client_sn_multithread_SOURCES = examples/sn-client/sn-multithr examples_sn_client_sn_multithread_LDADD = src/libwolfmqtt.la examples_sn_client_sn_multithread_DEPENDENCIES = src/libwolfmqtt.la examples_sn_client_sn_multithread_CPPFLAGS = -I$(top_srcdir)/examples $(AM_CPPFLAGS) + +# MQTT pub and sub clients +examples_pub_sub_mqtt_pub_SOURCES = examples/pub-sub/mqtt-pub.c \ + examples/mqttnet.c \ + examples/mqttexample.c +examples_pub_sub_mqtt_pub_LDADD = src/libwolfmqtt.la +examples_pub_sub_mqtt_pub_DEPENDENCIES = src/libwolfmqtt.la +examples_pub_sub_mqtt_pub_CPPFLAGS = -I$(top_srcdir)/examples $(AM_CPPFLAGS) + +examples_pub_sub_mqtt_sub_SOURCES = examples/pub-sub/mqtt-sub.c \ + examples/mqttnet.c \ + examples/mqttexample.c +examples_pub_sub_mqtt_sub_LDADD = src/libwolfmqtt.la +examples_pub_sub_mqtt_sub_DEPENDENCIES = src/libwolfmqtt.la +examples_pub_sub_mqtt_sub_CPPFLAGS = -I$(top_srcdir)/examples $(AM_CPPFLAGS) endif @@ -145,6 +163,8 @@ dist_example_DATA+= examples/multithread/multithread.c dist_example_DATA+= examples/sn-client/sn-client.c dist_example_DATA+= examples/sn-client/sn-client_qos-1.c dist_example_DATA+= examples/sn-client/sn-multithread.c +dist_example_DATA+= examples/pub-sub/mqtt-pub.c +dist_example_DATA+= examples/pub-sub/mqtt-sub.c DISTCLEANFILES+= examples/mqttclient/.libs/mqttclient \ examples/firmware/.libs/fwpush \ @@ -156,7 +176,9 @@ DISTCLEANFILES+= examples/mqttclient/.libs/mqttclient \ examples/multithread/.libs/multithread \ examples/sn-client/.libs/sn-client \ examples/sn-client/.libs/sn-client_qos-1 \ - examples/sn-client/.libs/sn-multithread + examples/sn-client/.libs/sn-multithread \ + examples/pub-sub/mqtt-pub \ + examples/pub-sub/mqtt-sub EXTRA_DIST+= examples/mqttuart.c \ examples/publish.dat \ diff --git a/examples/mqttclient/mqttclient.c b/examples/mqttclient/mqttclient.c index ef4721ca3..e8dee1912 100644 --- a/examples/mqttclient/mqttclient.c +++ b/examples/mqttclient/mqttclient.c @@ -648,9 +648,6 @@ int mqttclient_test(MQTTCtx *mqttCtx) PRINTF("MQTT Disconnect: %s (%d)", MqttClient_ReturnCodeToString(rc), rc); - if (rc != MQTT_CODE_SUCCESS) { - goto disconn; - } rc = MqttClient_NetDisconnect(&mqttCtx->client); diff --git a/examples/mqttexample.c b/examples/mqttexample.c index 9a3a495bb..8a8b08bef 100644 --- a/examples/mqttexample.c +++ b/examples/mqttexample.c @@ -247,6 +247,9 @@ void mqtt_show_usage(MQTTCtx* mqttCtx) #endif PRINTF("-T Test mode"); PRINTF("-f Use file contents for publish"); + if (!mqttCtx->debug_on) { + PRINTF("-d Enable example debug messages"); + } } void mqtt_init_ctx(MQTTCtx* mqttCtx) @@ -259,6 +262,7 @@ void mqtt_init_ctx(MQTTCtx* mqttCtx) mqttCtx->client_id = kDefClientId; mqttCtx->topic_name = kDefTopicName; mqttCtx->cmd_timeout_ms = DEFAULT_CMD_TIMEOUT_MS; + mqttCtx->debug_on = 1; #ifdef WOLFMQTT_V5 mqttCtx->max_packet_size = DEFAULT_MAX_PKT_SZ; mqttCtx->topic_alias = 1; @@ -286,7 +290,7 @@ int mqtt_parse_args(MQTTCtx* mqttCtx, int argc, char** argv) #define MQTT_V5_ARGS "" #endif - while ((rc = mygetopt(argc, argv, "?h:p:q:sk:i:lu:w:m:n:C:Tf:rt" \ + while ((rc = mygetopt(argc, argv, "?h:p:q:sk:i:lu:w:m:n:C:Tf:rtd" \ MQTT_TLS_ARGS MQTT_V5_ARGS)) != -1) { switch ((char)rc) { case '?' : @@ -364,6 +368,10 @@ int mqtt_parse_args(MQTTCtx* mqttCtx, int argc, char** argv) mqttCtx->use_tls = 1; break; + case 'd': + mqttCtx->debug_on = 1; + break; + #ifdef ENABLE_MQTT_TLS case 'A': mTlsCaFile = myoptarg; diff --git a/examples/mqttexample.h b/examples/mqttexample.h index 8e4b09e6a..00249f87b 100644 --- a/examples/mqttexample.h +++ b/examples/mqttexample.h @@ -168,6 +168,7 @@ typedef struct _MQTTCtx { #endif byte clean_session; byte test_mode; + byte debug_on:1; /* enable debug messages in example */ #ifdef WOLFMQTT_V5 byte subId_not_avail; /* Server property */ byte enable_eauth; /* Enhanced authentication */ diff --git a/examples/mqttnet.c b/examples/mqttnet.c index 13be7a525..3828bf3e4 100644 --- a/examples/mqttnet.c +++ b/examples/mqttnet.c @@ -76,8 +76,10 @@ static int NetConnect(void *context, const char* host, word16 port, switch (sock->stat) { case SOCK_BEGIN: - PRINTF("NetConnect: Host %s, Port %u, Timeout %d ms, Use TLS %d", - host, port, timeout_ms, mqttCtx->use_tls); + if (mqttCtx->debug_on) { + PRINTF("NetConnect: Host %s, Port %u, Timeout %d ms, Use TLS %d", + host, port, timeout_ms, mqttCtx->use_tls); + } hostIp = FreeRTOS_gethostbyname_a(host, NULL, 0, 0); if (hostIp == 0) @@ -255,9 +257,10 @@ static int NetConnect(void *context, const char* host, word16 port, switch(sock->stat) { case SOCK_BEGIN: { - PRINTF("NetConnect: Host %s, Port %u, Timeout %d ms, Use TLS %d", - host, port, timeout_ms, mqttCtx->use_tls); - + if (mqttCtx->debug_on) { + PRINTF("NetConnect: Host %s, Port %u, Timeout %d ms, " + "Use TLS %d", host, port, timeout_ms, mqttCtx->use_tls); + } XMEMSET(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; @@ -446,8 +449,10 @@ static int NetConnect(void *context, const char* host, word16 port, switch(sock->stat) { case SOCK_BEGIN: { - PRINTF("NetConnect: Host %s, Port %u, Timeout %d ms, Use TLS %d", - host, port, timeout_ms, mqttCtx->use_tls); + if (mqttCtx->debug_on) { + PRINTF("NetConnect: Host %s, Port %u, Timeout %d ms, " + "Use TLS %d", host, port, timeout_ms, mqttCtx->use_tls); + } XMEMSET(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; diff --git a/examples/pub-sub/mqtt-pub-sub.h b/examples/pub-sub/mqtt-pub-sub.h new file mode 100644 index 000000000..60d0ccbbb --- /dev/null +++ b/examples/pub-sub/mqtt-pub-sub.h @@ -0,0 +1,43 @@ +/* mqtt-pub-sub + * + * Copyright (C) 2006-2023 wolfSSL Inc. + * + * This file is part of wolfMQTT. + * + * wolfMQTT 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; either version 2 of the License, or + * (at your option) any later version. + * + * wolfMQTT 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#ifndef WOLFMQTT_PUB_SUB_H +#define WOLFMQTT_PUB_SUB_H + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Exposed functions */ +int pub_client(MQTTCtx *mqttCtx); +int sub_client(MQTTCtx *mqttCtx); + +#if defined(NO_MAIN_DRIVER) +int mqttPub_main(int argc, char** argv); +int mqttSub_main(int argc, char** argv); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* WOLFMQTT_PUB_SUB_H */ diff --git a/examples/pub-sub/mqtt-pub.c b/examples/pub-sub/mqtt-pub.c new file mode 100644 index 000000000..d14029e92 --- /dev/null +++ b/examples/pub-sub/mqtt-pub.c @@ -0,0 +1,530 @@ +/* mqtt-pub.c + * + * Copyright (C) 2006-2023 wolfSSL Inc. + * + * This file is part of wolfMQTT. + * + * wolfMQTT 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; either version 2 of the License, or + * (at your option) any later version. + * + * wolfMQTT 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Include the autoconf generated config.h */ +#ifdef HAVE_CONFIG_H + #include +#endif + +#include "examples/mqttnet.h" +#include "examples/pub-sub/mqtt-pub-sub.h" + +/* Configuration */ + +/* Maximum size for network read/write callbacks. There is also a v5 define that + describes the max MQTT control packet size, DEFAULT_MAX_PKT_SZ. */ +#define MAX_BUFFER_SIZE 1024 + +#ifdef WOLFMQTT_PROPERTY_CB +#define MAX_CLIENT_ID_LEN 64 +char gClientId[MAX_CLIENT_ID_LEN] = {0}; +#endif + +#ifdef WOLFMQTT_DISCONNECT_CB +/* callback indicates a network error occurred */ +static int mqtt_disconnect_cb(MqttClient* client, int error_code, void* ctx) +{ + (void)client; + (void)ctx; + PRINTF("Network Error Callback: %s (error %d)", + MqttClient_ReturnCodeToString(error_code), error_code); + return 0; +} +#endif + + +#ifdef WOLFMQTT_PROPERTY_CB +/* The property callback is called after decoding a packet that contains at + least one property. The property list is deallocated after returning from + the callback. */ +static int mqtt_property_cb(MqttClient *client, MqttProp *head, void *ctx) +{ + MqttProp *prop = head; + int rc = 0; + MQTTCtx* mqttCtx; + + if ((client == NULL) || (client->ctx == NULL)) { + return MQTT_CODE_ERROR_BAD_ARG; + } + mqttCtx = (MQTTCtx*)client->ctx; + + while (prop != NULL) { + PRINTF("Property CB: Type %d", prop->type); + switch (prop->type) { + case MQTT_PROP_ASSIGNED_CLIENT_ID: + /* Store client ID in global */ + mqttCtx->client_id = &gClientId[0]; + + /* Store assigned client ID from CONNACK*/ + XSTRNCPY((char*)mqttCtx->client_id, prop->data_str.str, + MAX_CLIENT_ID_LEN - 1); + /* should use strlcpy() semantics, but non-portable */ + ((char*)mqttCtx->client_id)[MAX_CLIENT_ID_LEN - 1] = '\0'; + break; + + case MQTT_PROP_SUBSCRIPTION_ID_AVAIL: + mqttCtx->subId_not_avail = + prop->data_byte == 0; + break; + + case MQTT_PROP_TOPIC_ALIAS_MAX: + mqttCtx->topic_alias_max = + (mqttCtx->topic_alias_max < prop->data_short) ? + mqttCtx->topic_alias_max : prop->data_short; + break; + + case MQTT_PROP_MAX_PACKET_SZ: + if ((prop->data_int > 0) && + (prop->data_int <= MQTT_PACKET_SZ_MAX)) + { + client->packet_sz_max = + (client->packet_sz_max < prop->data_int) ? + client->packet_sz_max : prop->data_int; + } + else { + /* Protocol error */ + rc = MQTT_CODE_ERROR_PROPERTY; + } + break; + + case MQTT_PROP_SERVER_KEEP_ALIVE: + mqttCtx->keep_alive_sec = prop->data_short; + break; + + case MQTT_PROP_MAX_QOS: + client->max_qos = prop->data_byte; + break; + + case MQTT_PROP_RETAIN_AVAIL: + client->retain_avail = prop->data_byte; + break; + + case MQTT_PROP_REASON_STR: + PRINTF("Reason String: %.*s", + prop->data_str.len, prop->data_str.str); + break; + + case MQTT_PROP_USER_PROP: + PRINTF("User property: key=\"%.*s\", value=\"%.*s\"", + prop->data_str.len, prop->data_str.str, + prop->data_str2.len, prop->data_str2.str); + break; + + case MQTT_PROP_PAYLOAD_FORMAT_IND: + case MQTT_PROP_MSG_EXPIRY_INTERVAL: + case MQTT_PROP_CONTENT_TYPE: + case MQTT_PROP_RESP_TOPIC: + case MQTT_PROP_CORRELATION_DATA: + case MQTT_PROP_SUBSCRIPTION_ID: + case MQTT_PROP_SESSION_EXPIRY_INTERVAL: + case MQTT_PROP_TOPIC_ALIAS: + case MQTT_PROP_TYPE_MAX: + case MQTT_PROP_RECEIVE_MAX: + case MQTT_PROP_WILDCARD_SUB_AVAIL: + case MQTT_PROP_SHARED_SUBSCRIPTION_AVAIL: + case MQTT_PROP_RESP_INFO: + case MQTT_PROP_SERVER_REF: + case MQTT_PROP_AUTH_METHOD: + case MQTT_PROP_AUTH_DATA: + case MQTT_PROP_NONE: + break; + case MQTT_PROP_REQ_PROB_INFO: + case MQTT_PROP_WILL_DELAY_INTERVAL: + case MQTT_PROP_REQ_RESP_INFO: + default: + /* Invalid */ + rc = MQTT_CODE_ERROR_PROPERTY; + break; + } + prop = prop->next; + } + + (void)ctx; + + return rc; +} +#endif /* WOLFMQTT_PROPERTY_CB */ + +int pub_client(MQTTCtx *mqttCtx) +{ + int rc = MQTT_CODE_SUCCESS; + + /* Initialize Network */ + rc = MqttClientNet_Init(&mqttCtx->net, mqttCtx); + if (mqttCtx->debug_on) { + PRINTF("MQTT Net Init: %s (%d)", + MqttClient_ReturnCodeToString(rc), rc); + } + if (rc != MQTT_CODE_SUCCESS) { + goto exit; + } + + /* setup tx/rx buffers */ + mqttCtx->tx_buf = (byte*)WOLFMQTT_MALLOC(MAX_BUFFER_SIZE); + mqttCtx->rx_buf = (byte*)WOLFMQTT_MALLOC(MAX_BUFFER_SIZE); + + /* Initialize MqttClient structure */ + rc = MqttClient_Init(&mqttCtx->client, &mqttCtx->net, + NULL, + mqttCtx->tx_buf, MAX_BUFFER_SIZE, + mqttCtx->rx_buf, MAX_BUFFER_SIZE, + mqttCtx->cmd_timeout_ms); + + if (mqttCtx->debug_on) { + PRINTF("MQTT Init: %s (%d)", + MqttClient_ReturnCodeToString(rc), rc); + } + if (rc != MQTT_CODE_SUCCESS) { + goto exit; + } + /* The client.ctx will be stored in the cert callback ctx during + MqttSocket_Connect for use by mqtt_tls_verify_cb */ + mqttCtx->client.ctx = mqttCtx; + +#ifdef WOLFMQTT_DISCONNECT_CB + /* setup disconnect callback */ + rc = MqttClient_SetDisconnectCallback(&mqttCtx->client, + mqtt_disconnect_cb, NULL); + if (rc != MQTT_CODE_SUCCESS) { + goto exit; + } +#endif +#ifdef WOLFMQTT_PROPERTY_CB + rc = MqttClient_SetPropertyCallback(&mqttCtx->client, + mqtt_property_cb, NULL); + if (rc != MQTT_CODE_SUCCESS) { + goto exit; + } +#endif + + /* Connect to broker */ + rc = MqttClient_NetConnect(&mqttCtx->client, mqttCtx->host, + mqttCtx->port, + DEFAULT_CON_TIMEOUT_MS, mqttCtx->use_tls, mqtt_tls_cb); + + if (mqttCtx->debug_on) { + PRINTF("MQTT Socket Connect: %s (%d)", + MqttClient_ReturnCodeToString(rc), rc); + } + + if (rc != MQTT_CODE_SUCCESS) { + goto exit; + } + + /* Build connect packet */ + XMEMSET(&mqttCtx->connect, 0, sizeof(MqttConnect)); + mqttCtx->connect.keep_alive_sec = mqttCtx->keep_alive_sec; + mqttCtx->connect.clean_session = mqttCtx->clean_session; + mqttCtx->connect.client_id = mqttCtx->client_id; + + /* Last will and testament sent by broker to subscribers + of topic when broker connection is lost */ + XMEMSET(&mqttCtx->lwt_msg, 0, sizeof(mqttCtx->lwt_msg)); + mqttCtx->connect.lwt_msg = &mqttCtx->lwt_msg; + mqttCtx->connect.enable_lwt = mqttCtx->enable_lwt; + if (mqttCtx->enable_lwt) { + /* Send client id in LWT payload */ + mqttCtx->lwt_msg.qos = mqttCtx->qos; + mqttCtx->lwt_msg.retain = 0; + mqttCtx->lwt_msg.topic_name = WOLFMQTT_TOPIC_NAME"lwttopic"; + mqttCtx->lwt_msg.buffer = (byte*)mqttCtx->client_id; + mqttCtx->lwt_msg.total_len = (word16)XSTRLEN(mqttCtx->client_id); + +#ifdef WOLFMQTT_V5 + { + /* Add a 5 second delay to sending the LWT */ + MqttProp* prop = MqttClient_PropsAdd(&mqttCtx->lwt_msg.props); + prop->type = MQTT_PROP_WILL_DELAY_INTERVAL; + prop->data_int = 5; + } +#endif + } + /* Optional authentication */ + mqttCtx->connect.username = mqttCtx->username; + mqttCtx->connect.password = mqttCtx->password; +#ifdef WOLFMQTT_V5 + mqttCtx->client.packet_sz_max = mqttCtx->max_packet_size; + mqttCtx->client.enable_eauth = mqttCtx->enable_eauth; + + if (mqttCtx->client.enable_eauth == 1) { + /* Enhanced authentication */ + /* Add property: Authentication Method */ + MqttProp* prop = MqttClient_PropsAdd(&mqttCtx->connect.props); + prop->type = MQTT_PROP_AUTH_METHOD; + prop->data_str.str = (char*)DEFAULT_AUTH_METHOD; + prop->data_str.len = (word16)XSTRLEN(prop->data_str.str); + } + { + /* Request Response Information */ + MqttProp* prop = MqttClient_PropsAdd(&mqttCtx->connect.props); + prop->type = MQTT_PROP_REQ_RESP_INFO; + prop->data_byte = 1; + } + { + /* Request Problem Information */ + MqttProp* prop = MqttClient_PropsAdd(&mqttCtx->connect.props); + prop->type = MQTT_PROP_REQ_PROB_INFO; + prop->data_byte = 1; + } + { + /* Maximum Packet Size */ + MqttProp* prop = MqttClient_PropsAdd(&mqttCtx->connect.props); + prop->type = MQTT_PROP_MAX_PACKET_SZ; + prop->data_int = (word32)mqttCtx->max_packet_size; + } + { + /* Topic Alias Maximum */ + MqttProp* prop = MqttClient_PropsAdd(&mqttCtx->connect.props); + prop->type = MQTT_PROP_TOPIC_ALIAS_MAX; + prop->data_short = mqttCtx->topic_alias_max; + } + if (mqttCtx->clean_session == 0) { + /* Session expiry interval */ + MqttProp* prop = MqttClient_PropsAdd(&mqttCtx->connect.props); + prop->type = MQTT_PROP_SESSION_EXPIRY_INTERVAL; + prop->data_int = DEFAULT_SESS_EXP_INT; /* Session does not expire */ + } +#endif + + /* Send Connect and wait for Connect Ack */ + rc = MqttClient_Connect(&mqttCtx->client, &mqttCtx->connect); + if (mqttCtx->debug_on) { + PRINTF("MQTT Connect: Proto (%s), %s (%d)", + MqttClient_GetProtocolVersionString(&mqttCtx->client), + MqttClient_ReturnCodeToString(rc), rc); + } + if (rc != MQTT_CODE_SUCCESS) { + goto disconn; + } + +#ifdef WOLFMQTT_V5 + if (mqttCtx->connect.props != NULL) { + /* Release the allocated properties */ + MqttClient_PropsFree(mqttCtx->connect.props); + } + if (mqttCtx->lwt_msg.props != NULL) { + /* Release the allocated properties */ + MqttClient_PropsFree(mqttCtx->lwt_msg.props); + } +#endif + + /* Publish Topic */ + XMEMSET(&mqttCtx->publish, 0, sizeof(MqttPublish)); + mqttCtx->publish.retain = 0; + mqttCtx->publish.qos = mqttCtx->qos; + mqttCtx->publish.duplicate = 0; + mqttCtx->publish.topic_name = mqttCtx->topic_name; + mqttCtx->publish.packet_id = mqtt_get_packetid(); + + if (mqttCtx->pub_file) { + /* If a file is specified, then read into the allocated buffer */ + rc = mqtt_file_load(mqttCtx->pub_file, &mqttCtx->publish.buffer, + (int*)&mqttCtx->publish.total_len); + if (rc != MQTT_CODE_SUCCESS) { + /* There was an error loading the file */ + PRINTF("MQTT Publish file error: %d", rc); + } + } + else { + mqttCtx->publish.buffer = (byte*)mqttCtx->message; + mqttCtx->publish.total_len = (word16)XSTRLEN(mqttCtx->message); + } + + if (rc == MQTT_CODE_SUCCESS) { + #ifdef WOLFMQTT_V5 + { + /* Payload Format Indicator */ + MqttProp* prop = MqttClient_PropsAdd(&mqttCtx->publish.props); + prop->type = MQTT_PROP_PAYLOAD_FORMAT_IND; + prop->data_byte = 1; + } + { + /* Content Type */ + MqttProp* prop = MqttClient_PropsAdd(&mqttCtx->publish.props); + prop->type = MQTT_PROP_CONTENT_TYPE; + prop->data_str.str = (char*)"wolf_type"; + prop->data_str.len = (word16)XSTRLEN(prop->data_str.str); + } + if ((mqttCtx->topic_alias_max > 0) && + (mqttCtx->topic_alias > 0) && + (mqttCtx->topic_alias < mqttCtx->topic_alias_max)) { + /* Topic Alias */ + MqttProp* prop = MqttClient_PropsAdd(&mqttCtx->publish.props); + prop->type = MQTT_PROP_TOPIC_ALIAS; + prop->data_short = mqttCtx->topic_alias; + } + #endif + + /* This loop allows payloads larger than the buffer to be sent by + repeatedly calling publish. + */ + do { + rc = MqttClient_Publish(&mqttCtx->client, &mqttCtx->publish); + } while(rc == MQTT_CODE_PUB_CONTINUE); + + if ((mqttCtx->pub_file) && (mqttCtx->publish.buffer)) { + WOLFMQTT_FREE(mqttCtx->publish.buffer); + } + if (mqttCtx->debug_on) { + PRINTF("MQTT Publish: Topic %s, %s (%d)", + mqttCtx->publish.topic_name, + MqttClient_ReturnCodeToString(rc), rc); + } + if (rc != MQTT_CODE_SUCCESS) { + #ifdef WOLFMQTT_V5 + if (mqttCtx->qos > 0) { + PRINTF("\tResponse Reason Code %d", mqttCtx->publish.resp.reason_code); + } + #endif + goto disconn; + } + #ifdef WOLFMQTT_V5 + if (mqttCtx->publish.props != NULL) { + /* Release the allocated properties */ + MqttClient_PropsFree(mqttCtx->publish.props); + } + #endif + } + + +disconn: + /* Disconnect */ + XMEMSET(&mqttCtx->disconnect, 0, sizeof(mqttCtx->disconnect)); +#ifdef WOLFMQTT_V5 + { + /* Session expiry interval */ + MqttProp* prop = MqttClient_PropsAdd(&mqttCtx->disconnect.props); + prop->type = MQTT_PROP_SESSION_EXPIRY_INTERVAL; + prop->data_int = 0; + } + #if 0 /* enable to test sending a disconnect reason code */ + if (mqttCtx->enable_lwt) { + /* Disconnect with Will Message */ + mqttCtx->disconnect.reason_code = MQTT_REASON_DISCONNECT_W_WILL_MSG; + } + #endif +#endif + rc = MqttClient_Disconnect_ex(&mqttCtx->client, &mqttCtx->disconnect); +#ifdef WOLFMQTT_V5 + if (mqttCtx->disconnect.props != NULL) { + /* Release the allocated properties */ + MqttClient_PropsFree(mqttCtx->disconnect.props); + } +#endif + + if (mqttCtx->debug_on) { + PRINTF("MQTT Disconnect: %s (%d)", + MqttClient_ReturnCodeToString(rc), rc); + } + + rc = MqttClient_NetDisconnect(&mqttCtx->client); + if (mqttCtx->debug_on) { + PRINTF("MQTT Socket Disconnect: %s (%d)", + MqttClient_ReturnCodeToString(rc), rc); + } +exit: + + /* Free resources */ + if (mqttCtx->tx_buf) WOLFMQTT_FREE(mqttCtx->tx_buf); + if (mqttCtx->rx_buf) WOLFMQTT_FREE(mqttCtx->rx_buf); + + /* Cleanup network */ + MqttClientNet_DeInit(&mqttCtx->net); + + MqttClient_DeInit(&mqttCtx->client); + + return rc; +} + + +/* so overall tests can pull in test function */ + #ifdef USE_WINDOWS_API + #include /* for ctrl handler */ + + static BOOL CtrlHandler(DWORD fdwCtrlType) + { + if (fdwCtrlType == CTRL_C_EVENT) { + mStopRead = 1; + PRINTF("Received Ctrl+c"); + return TRUE; + } + return FALSE; + } + #elif HAVE_SIGNAL + #include + static void sig_handler(int signo) + { + if (signo == SIGINT) { + PRINTF("Received SIGINT"); + } + } + #endif + +#if defined(NO_MAIN_DRIVER) +int mqttPub_main(int argc, char** argv) +#else +int main(int argc, char** argv) +#endif +{ + int rc; + MQTTCtx mqttCtx; + + /* init defaults */ + mqtt_init_ctx(&mqttCtx); + + /* Set default host to localhost */ + mqttCtx.host = "localhost"; + + /* Set default client ID */ + mqttCtx.client_id = "wolfMQTT_pub"; + + /* Set example debug messages off (turn on with '-d') */ + mqttCtx.debug_on = 0; + + /* parse arguments */ + rc = mqtt_parse_args(&mqttCtx, argc, argv); + if (rc != 0) { + if (rc == MY_EX_USAGE) { + /* return success, so make check passes with TLS disabled */ + return 0; + } + return rc; + } + +#ifdef USE_WINDOWS_API + if (SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, + TRUE) == FALSE) + { + PRINTF("Error setting Ctrl Handler! Error %d", (int)GetLastError()); + } +#elif HAVE_SIGNAL + if (signal(SIGINT, sig_handler) == SIG_ERR) { + PRINTF("Can't catch SIGINT"); + } +#endif + + rc = pub_client(&mqttCtx); + + mqtt_free_ctx(&mqttCtx); + + return (rc == 0) ? 0 : EXIT_FAILURE; +} + diff --git a/examples/pub-sub/mqtt-sub.c b/examples/pub-sub/mqtt-sub.c new file mode 100644 index 000000000..cbace383c --- /dev/null +++ b/examples/pub-sub/mqtt-sub.c @@ -0,0 +1,655 @@ +/* mqtt-sub.c + * + * Copyright (C) 2006-2023 wolfSSL Inc. + * + * This file is part of wolfMQTT. + * + * wolfMQTT 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; either version 2 of the License, or + * (at your option) any later version. + * + * wolfMQTT 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Include the autoconf generated config.h */ +#ifdef HAVE_CONFIG_H + #include +#endif + +#include "examples/mqttnet.h" +#include "examples/pub-sub/mqtt-pub-sub.h" + +/* Locals */ +static int mStopRead = 0; + +/* Configuration */ + +/* Maximum size for network read/write callbacks. There is also a v5 define that + describes the max MQTT control packet size, DEFAULT_MAX_PKT_SZ. */ +#define MAX_BUFFER_SIZE 1024 + +#ifdef WOLFMQTT_PROPERTY_CB +#define MAX_CLIENT_ID_LEN 64 +char gClientId[MAX_CLIENT_ID_LEN] = {0}; +#endif + +#ifdef WOLFMQTT_DISCONNECT_CB +/* callback indicates a network error occurred */ +static int mqtt_disconnect_cb(MqttClient* client, int error_code, void* ctx) +{ + MQTTCtx* mqttCtx = (MQTTCtx*)client->ctx; + (void)ctx; + + if (mqttCtx->debug_on) { + PRINTF("Network Error Callback: %s (error %d)", + MqttClient_ReturnCodeToString(error_code), error_code); + } + return 0; +} +#endif + +static int mqtt_message_cb(MqttClient *client, MqttMessage *msg, + byte msg_new, byte msg_done) +{ + byte buf[PRINT_BUFFER_SIZE+1]; + word32 len; + MQTTCtx* mqttCtx = (MQTTCtx*)client->ctx; + + (void)mqttCtx; + + if (msg_new) { + /* Determine min size to dump */ + len = msg->topic_name_len; + if (len > PRINT_BUFFER_SIZE) { + len = PRINT_BUFFER_SIZE; + } + XMEMCPY(buf, msg->topic_name, len); + buf[len] = '\0'; /* Make sure its null terminated */ + + if (mqttCtx->debug_on) { + /* Print incoming message */ + PRINTF("MQTT Message: Topic %s, Qos %d, Len %u", + buf, msg->qos, msg->total_len); + } + } + + /* Print message payload */ + len = msg->buffer_len; + if (len > PRINT_BUFFER_SIZE) { + len = PRINT_BUFFER_SIZE; + } + XMEMCPY(buf, msg->buffer, len); + buf[len] = '\0'; /* Make sure its null terminated */ + if (mqttCtx->debug_on) { + PRINTF("Payload (%d - %d) printing %d bytes:" LINE_END "%s", + msg->buffer_pos, msg->buffer_pos + msg->buffer_len, len, buf); + } + else { + PRINTF("%s", buf); + } + + #ifdef WOLFMQTT_V5 + { + /* Properties can be checked in the message callback */ + MqttProp *prop = msg->props; + while (prop != NULL) + { + if (prop->type == MQTT_PROP_CONTENT_TYPE) { + PRINTF("Content type: %.*s", prop->data_str.len, + prop->data_str.str); + } + prop = prop->next; + } + } + #endif + + if (msg_done && mqttCtx->debug_on) { + PRINTF("MQTT Message: Done"); + } + + /* Return negative to terminate publish processing */ + return MQTT_CODE_SUCCESS; +} + +#ifdef WOLFMQTT_PROPERTY_CB +/* The property callback is called after decoding a packet that contains at + least one property. The property list is deallocated after returning from + the callback. */ +static int mqtt_property_cb(MqttClient *client, MqttProp *head, void *ctx) +{ + MqttProp *prop = head; + int rc = 0; + MQTTCtx* mqttCtx; + + if ((client == NULL) || (client->ctx == NULL)) { + return MQTT_CODE_ERROR_BAD_ARG; + } + mqttCtx = (MQTTCtx*)client->ctx; + + while (prop != NULL) { + PRINTF("Property CB: Type %d", prop->type); + switch (prop->type) { + case MQTT_PROP_ASSIGNED_CLIENT_ID: + /* Store client ID in global */ + mqttCtx->client_id = &gClientId[0]; + + /* Store assigned client ID from CONNACK*/ + XSTRNCPY((char*)mqttCtx->client_id, prop->data_str.str, + MAX_CLIENT_ID_LEN - 1); + /* should use strlcpy() semantics, but non-portable */ + ((char*)mqttCtx->client_id)[MAX_CLIENT_ID_LEN - 1] = '\0'; + break; + + case MQTT_PROP_SUBSCRIPTION_ID_AVAIL: + mqttCtx->subId_not_avail = + prop->data_byte == 0; + break; + + case MQTT_PROP_TOPIC_ALIAS_MAX: + mqttCtx->topic_alias_max = + (mqttCtx->topic_alias_max < prop->data_short) ? + mqttCtx->topic_alias_max : prop->data_short; + break; + + case MQTT_PROP_MAX_PACKET_SZ: + if ((prop->data_int > 0) && + (prop->data_int <= MQTT_PACKET_SZ_MAX)) + { + client->packet_sz_max = + (client->packet_sz_max < prop->data_int) ? + client->packet_sz_max : prop->data_int; + } + else { + /* Protocol error */ + rc = MQTT_CODE_ERROR_PROPERTY; + } + break; + + case MQTT_PROP_SERVER_KEEP_ALIVE: + mqttCtx->keep_alive_sec = prop->data_short; + break; + + case MQTT_PROP_MAX_QOS: + client->max_qos = prop->data_byte; + break; + + case MQTT_PROP_RETAIN_AVAIL: + client->retain_avail = prop->data_byte; + break; + + case MQTT_PROP_REASON_STR: + PRINTF("Reason String: %.*s", + prop->data_str.len, prop->data_str.str); + break; + + case MQTT_PROP_USER_PROP: + PRINTF("User property: key=\"%.*s\", value=\"%.*s\"", + prop->data_str.len, prop->data_str.str, + prop->data_str2.len, prop->data_str2.str); + break; + + case MQTT_PROP_PAYLOAD_FORMAT_IND: + case MQTT_PROP_MSG_EXPIRY_INTERVAL: + case MQTT_PROP_CONTENT_TYPE: + case MQTT_PROP_RESP_TOPIC: + case MQTT_PROP_CORRELATION_DATA: + case MQTT_PROP_SUBSCRIPTION_ID: + case MQTT_PROP_SESSION_EXPIRY_INTERVAL: + case MQTT_PROP_TOPIC_ALIAS: + case MQTT_PROP_TYPE_MAX: + case MQTT_PROP_RECEIVE_MAX: + case MQTT_PROP_WILDCARD_SUB_AVAIL: + case MQTT_PROP_SHARED_SUBSCRIPTION_AVAIL: + case MQTT_PROP_RESP_INFO: + case MQTT_PROP_SERVER_REF: + case MQTT_PROP_AUTH_METHOD: + case MQTT_PROP_AUTH_DATA: + case MQTT_PROP_NONE: + break; + case MQTT_PROP_REQ_PROB_INFO: + case MQTT_PROP_WILL_DELAY_INTERVAL: + case MQTT_PROP_REQ_RESP_INFO: + default: + /* Invalid */ + rc = MQTT_CODE_ERROR_PROPERTY; + break; + } + prop = prop->next; + } + + (void)ctx; + + return rc; +} +#endif /* WOLFMQTT_PROPERTY_CB */ + +int sub_client(MQTTCtx *mqttCtx) +{ + int rc = MQTT_CODE_SUCCESS, i; + + if (mqttCtx->debug_on) { + PRINTF("Subscribe Client: QoS %d, Use TLS %d", mqttCtx->qos, + mqttCtx->use_tls); + } + /* Initialize Network */ + rc = MqttClientNet_Init(&mqttCtx->net, mqttCtx); + if (mqttCtx->debug_on) { + PRINTF("MQTT Net Init: %s (%d)", + MqttClient_ReturnCodeToString(rc), rc); + } + if (rc != MQTT_CODE_SUCCESS) { + goto exit; + } + + /* setup tx/rx buffers */ + mqttCtx->tx_buf = (byte*)WOLFMQTT_MALLOC(MAX_BUFFER_SIZE); + mqttCtx->rx_buf = (byte*)WOLFMQTT_MALLOC(MAX_BUFFER_SIZE); + + /* Initialize MqttClient structure */ + rc = MqttClient_Init(&mqttCtx->client, &mqttCtx->net, + mqtt_message_cb, + mqttCtx->tx_buf, MAX_BUFFER_SIZE, + mqttCtx->rx_buf, MAX_BUFFER_SIZE, + mqttCtx->cmd_timeout_ms); + + if (mqttCtx->debug_on) { + PRINTF("MQTT Init: %s (%d)", + MqttClient_ReturnCodeToString(rc), rc); + } + if (rc != MQTT_CODE_SUCCESS) { + goto exit; + } + /* The client.ctx will be stored in the cert callback ctx during + MqttSocket_Connect for use by mqtt_tls_verify_cb */ + mqttCtx->client.ctx = mqttCtx; + +#ifdef WOLFMQTT_DISCONNECT_CB + /* setup disconnect callback */ + rc = MqttClient_SetDisconnectCallback(&mqttCtx->client, + mqtt_disconnect_cb, NULL); + if (rc != MQTT_CODE_SUCCESS) { + goto exit; + } +#endif +#ifdef WOLFMQTT_PROPERTY_CB + rc = MqttClient_SetPropertyCallback(&mqttCtx->client, + mqtt_property_cb, NULL); + if (rc != MQTT_CODE_SUCCESS) { + goto exit; + } +#endif + + /* Connect to broker */ + rc = MqttClient_NetConnect(&mqttCtx->client, mqttCtx->host, + mqttCtx->port, + DEFAULT_CON_TIMEOUT_MS, mqttCtx->use_tls, mqtt_tls_cb); + + if (mqttCtx->debug_on) { + PRINTF("MQTT Socket Connect: %s (%d)", + MqttClient_ReturnCodeToString(rc), rc); + } + if (rc != MQTT_CODE_SUCCESS) { + goto exit; + } + + /* Build connect packet */ + XMEMSET(&mqttCtx->connect, 0, sizeof(MqttConnect)); + mqttCtx->connect.keep_alive_sec = mqttCtx->keep_alive_sec; + mqttCtx->connect.clean_session = mqttCtx->clean_session; + mqttCtx->connect.client_id = mqttCtx->client_id; + + /* Last will and testament sent by broker to subscribers + of topic when broker connection is lost */ + XMEMSET(&mqttCtx->lwt_msg, 0, sizeof(mqttCtx->lwt_msg)); + mqttCtx->connect.lwt_msg = &mqttCtx->lwt_msg; + mqttCtx->connect.enable_lwt = mqttCtx->enable_lwt; + if (mqttCtx->enable_lwt) { + /* Send client id in LWT payload */ + mqttCtx->lwt_msg.qos = mqttCtx->qos; + mqttCtx->lwt_msg.retain = 0; + mqttCtx->lwt_msg.topic_name = WOLFMQTT_TOPIC_NAME"lwttopic"; + mqttCtx->lwt_msg.buffer = (byte*)mqttCtx->client_id; + mqttCtx->lwt_msg.total_len = (word16)XSTRLEN(mqttCtx->client_id); + +#ifdef WOLFMQTT_V5 + { + /* Add a 5 second delay to sending the LWT */ + MqttProp* prop = MqttClient_PropsAdd(&mqttCtx->lwt_msg.props); + prop->type = MQTT_PROP_WILL_DELAY_INTERVAL; + prop->data_int = 5; + } +#endif + } + /* Optional authentication */ + mqttCtx->connect.username = mqttCtx->username; + mqttCtx->connect.password = mqttCtx->password; +#ifdef WOLFMQTT_V5 + mqttCtx->client.packet_sz_max = mqttCtx->max_packet_size; + + { + /* Request Response Information */ + MqttProp* prop = MqttClient_PropsAdd(&mqttCtx->connect.props); + prop->type = MQTT_PROP_REQ_RESP_INFO; + prop->data_byte = 1; + } + { + /* Request Problem Information */ + MqttProp* prop = MqttClient_PropsAdd(&mqttCtx->connect.props); + prop->type = MQTT_PROP_REQ_PROB_INFO; + prop->data_byte = 1; + } + { + /* Maximum Packet Size */ + MqttProp* prop = MqttClient_PropsAdd(&mqttCtx->connect.props); + prop->type = MQTT_PROP_MAX_PACKET_SZ; + prop->data_int = (word32)mqttCtx->max_packet_size; + } + { + /* Topic Alias Maximum */ + MqttProp* prop = MqttClient_PropsAdd(&mqttCtx->connect.props); + prop->type = MQTT_PROP_TOPIC_ALIAS_MAX; + prop->data_short = mqttCtx->topic_alias_max; + } + if (mqttCtx->clean_session == 0) { + /* Session expiry interval */ + MqttProp* prop = MqttClient_PropsAdd(&mqttCtx->connect.props); + prop->type = MQTT_PROP_SESSION_EXPIRY_INTERVAL; + prop->data_int = DEFAULT_SESS_EXP_INT; /* Session does not expire */ + } +#endif + + /* Send Connect and wait for Connect Ack */ + rc = MqttClient_Connect(&mqttCtx->client, &mqttCtx->connect); + + if (mqttCtx->debug_on) { + PRINTF("MQTT Connect: Proto (%s), %s (%d)", + MqttClient_GetProtocolVersionString(&mqttCtx->client), + MqttClient_ReturnCodeToString(rc), rc); + } + if (rc != MQTT_CODE_SUCCESS) { + goto disconn; + } + +#ifdef WOLFMQTT_V5 + if (mqttCtx->connect.props != NULL) { + /* Release the allocated properties */ + MqttClient_PropsFree(mqttCtx->connect.props); + } + if (mqttCtx->lwt_msg.props != NULL) { + /* Release the allocated properties */ + MqttClient_PropsFree(mqttCtx->lwt_msg.props); + } +#endif + + if (mqttCtx->debug_on) { + /* Validate Connect Ack info */ + PRINTF("MQTT Connect Ack: Return Code %u, Session Present %d", + mqttCtx->connect.ack.return_code, + (mqttCtx->connect.ack.flags & + MQTT_CONNECT_ACK_FLAG_SESSION_PRESENT) ? + 1 : 0 + ); +#ifdef WOLFMQTT_PROPERTY_CB + /* Print the acquired client ID */ + PRINTF("MQTT Connect Ack: Assigned Client ID: %s", + mqttCtx->client_id); +#endif + } + + /* Build list of topics */ + XMEMSET(&mqttCtx->subscribe, 0, sizeof(MqttSubscribe)); + + i = 0; + mqttCtx->topics[i].topic_filter = mqttCtx->topic_name; + mqttCtx->topics[i].qos = mqttCtx->qos; + +#ifdef WOLFMQTT_V5 + if (mqttCtx->subId_not_avail != 1) { + /* Subscription Identifier */ + MqttProp* prop; + prop = MqttClient_PropsAdd(&mqttCtx->subscribe.props); + prop->type = MQTT_PROP_SUBSCRIPTION_ID; + prop->data_int = DEFAULT_SUB_ID; + } +#endif + + /* Subscribe Topic */ + mqttCtx->subscribe.packet_id = mqtt_get_packetid(); + mqttCtx->subscribe.topic_count = + sizeof(mqttCtx->topics) / sizeof(MqttTopic); + mqttCtx->subscribe.topics = mqttCtx->topics; + + rc = MqttClient_Subscribe(&mqttCtx->client, &mqttCtx->subscribe); + +#ifdef WOLFMQTT_V5 + if (mqttCtx->subscribe.props != NULL) { + /* Release the allocated properties */ + MqttClient_PropsFree(mqttCtx->subscribe.props); + } +#endif + + if (mqttCtx->debug_on) { + PRINTF("MQTT Subscribe: %s (%d)", + MqttClient_ReturnCodeToString(rc), rc); + } + if (rc != MQTT_CODE_SUCCESS) { + goto disconn; + } + + if (mqttCtx->debug_on) { + /* show subscribe results */ + for (i = 0; i < mqttCtx->subscribe.topic_count; i++) { + MqttTopic *topic = &mqttCtx->subscribe.topics[i]; + PRINTF(" Topic %s, Qos %u, Return Code %u", + topic->topic_filter, + topic->qos, topic->return_code); + } + } + /* Read Loop */ + if (mqttCtx->debug_on) { + PRINTF("MQTT Waiting for message..."); + } + + do { + /* Try and read packet */ + rc = MqttClient_WaitMessage(&mqttCtx->client, + mqttCtx->cmd_timeout_ms); + + #ifdef WOLFMQTT_NONBLOCK + /* Track elapsed time with no activity and trigger timeout */ + rc = mqtt_check_timeout(rc, &mqttCtx->start_sec, + mqttCtx->cmd_timeout_ms/1000); + #endif + + if (mStopRead) { + rc = MQTT_CODE_SUCCESS; + if (mqttCtx->debug_on) { + PRINTF("MQTT Exiting..."); + } + break; + } + + /* check return code */ + #ifdef WOLFMQTT_ENABLE_STDIN_CAP + else if (rc == MQTT_CODE_STDIN_WAKE) { + XMEMSET(mqttCtx->rx_buf, 0, MAX_BUFFER_SIZE); + if (XFGETS((char*)mqttCtx->rx_buf, MAX_BUFFER_SIZE - 1, + stdin) != NULL) { + } + } + #endif + else if (rc == MQTT_CODE_ERROR_TIMEOUT) { + /* Keep Alive */ + if (mqttCtx->debug_on) { + PRINTF("Keep-alive timeout, sending ping"); + } + rc = MqttClient_Ping_ex(&mqttCtx->client, &mqttCtx->ping); + if (rc != MQTT_CODE_SUCCESS) { + PRINTF("MQTT Ping Keep Alive Error: %s (%d)", + MqttClient_ReturnCodeToString(rc), rc); + break; + } + } + else if (rc != MQTT_CODE_SUCCESS) { + /* There was an error */ + PRINTF("MQTT Message Wait: %s (%d)", + MqttClient_ReturnCodeToString(rc), rc); + break; + } + } while (!mStopRead); + + /* Check for error */ + if (rc != MQTT_CODE_SUCCESS) { + goto disconn; + } + + /* Unsubscribe Topics */ + XMEMSET(&mqttCtx->unsubscribe, 0, sizeof(MqttUnsubscribe)); + mqttCtx->unsubscribe.packet_id = mqtt_get_packetid(); + mqttCtx->unsubscribe.topic_count = + sizeof(mqttCtx->topics) / sizeof(MqttTopic); + mqttCtx->unsubscribe.topics = mqttCtx->topics; + + /* Unsubscribe Topics */ + rc = MqttClient_Unsubscribe(&mqttCtx->client, + &mqttCtx->unsubscribe); + + if (mqttCtx->debug_on) { + PRINTF("MQTT Unsubscribe: %s (%d)", + MqttClient_ReturnCodeToString(rc), rc); + } + if (rc != MQTT_CODE_SUCCESS) { + goto disconn; + } + mqttCtx->return_code = rc; + +disconn: + /* Disconnect */ + XMEMSET(&mqttCtx->disconnect, 0, sizeof(mqttCtx->disconnect)); +#ifdef WOLFMQTT_V5 + { + /* Session expiry interval */ + MqttProp* prop = MqttClient_PropsAdd(&mqttCtx->disconnect.props); + prop->type = MQTT_PROP_SESSION_EXPIRY_INTERVAL; + prop->data_int = 0; + } + #if 0 /* enable to test sending a disconnect reason code */ + if (mqttCtx->enable_lwt) { + /* Disconnect with Will Message */ + mqttCtx->disconnect.reason_code = MQTT_REASON_DISCONNECT_W_WILL_MSG; + } + #endif +#endif + rc = MqttClient_Disconnect_ex(&mqttCtx->client, &mqttCtx->disconnect); +#ifdef WOLFMQTT_V5 + if (mqttCtx->disconnect.props != NULL) { + /* Release the allocated properties */ + MqttClient_PropsFree(mqttCtx->disconnect.props); + } +#endif + + if (mqttCtx->debug_on) { + PRINTF("MQTT Disconnect: %s (%d)", + MqttClient_ReturnCodeToString(rc), rc); + } + rc = MqttClient_NetDisconnect(&mqttCtx->client); + + if (mqttCtx->debug_on) { + PRINTF("MQTT Socket Disconnect: %s (%d)", + MqttClient_ReturnCodeToString(rc), rc); + } +exit: + + /* Free resources */ + if (mqttCtx->tx_buf) WOLFMQTT_FREE(mqttCtx->tx_buf); + if (mqttCtx->rx_buf) WOLFMQTT_FREE(mqttCtx->rx_buf); + + /* Cleanup network */ + MqttClientNet_DeInit(&mqttCtx->net); + + MqttClient_DeInit(&mqttCtx->client); + + return rc; +} + + +/* so overall tests can pull in test function */ + #ifdef USE_WINDOWS_API + #include /* for ctrl handler */ + + static BOOL CtrlHandler(DWORD fdwCtrlType) + { + if (fdwCtrlType == CTRL_C_EVENT) { + mStopRead = 1; + return TRUE; + } + return FALSE; + } + #elif HAVE_SIGNAL + #include + static void sig_handler(int signo) + { + if (signo == SIGINT) { + mStopRead = 1; + } + } + #endif + +#if defined(NO_MAIN_DRIVER) +int mqttSub_main(int argc, char** argv) +#else +int main(int argc, char** argv) +#endif +{ + int rc; + MQTTCtx mqttCtx; + + /* init defaults */ + mqtt_init_ctx(&mqttCtx); + + /* Set default client ID */ + mqttCtx.client_id = "wolfMQTT_sub"; + + /* Set default host to localhost */ + mqttCtx.host = "localhost"; + + /* Set example debug messages off (turn on with '-d') */ + mqttCtx.debug_on = 0; + + /* parse arguments */ + rc = mqtt_parse_args(&mqttCtx, argc, argv); + if (rc != 0) { + if (rc == MY_EX_USAGE) { + /* return success, so make check passes with TLS disabled */ + return 0; + } + return rc; + } + +#ifdef USE_WINDOWS_API + if (SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, + TRUE) == FALSE) + { + PRINTF("Error setting Ctrl Handler! Error %d", (int)GetLastError()); + } +#elif HAVE_SIGNAL + if (signal(SIGINT, sig_handler) == SIG_ERR) { + PRINTF("Can't catch SIGINT"); + } +#endif + + rc = sub_client(&mqttCtx); + + mqtt_free_ctx(&mqttCtx); + + return (rc == 0) ? 0 : EXIT_FAILURE; +} + From 531fc56d6267178b398f82c1349e27931150adfc Mon Sep 17 00:00:00 2001 From: Eric Blankenhorn Date: Fri, 15 Sep 2023 13:50:35 -0500 Subject: [PATCH 02/62] Fix publish with topic ID >=127 --- examples/sn-client/sn-client.c | 49 +++++++++++++++++++++------------- src/mqtt_packet.c | 4 +-- 2 files changed, 32 insertions(+), 21 deletions(-) diff --git a/examples/sn-client/sn-client.c b/examples/sn-client/sn-client.c index 9d7ab5ad7..e60c1f5af 100644 --- a/examples/sn-client/sn-client.c +++ b/examples/sn-client/sn-client.c @@ -49,13 +49,22 @@ static int sn_message_cb(MqttClient *client, MqttMessage *msg, MQTTCtx* mqttCtx = (MQTTCtx*)client->ctx; if (msg_new) { - /* Topic ID or short topic name */ - topicId = (word16)(msg->topic_name[0] << 8 | msg->topic_name[1]); - - /* Print incoming message */ - PRINTF("MQTT-SN Message: Topic ID %d, Qos %d, Id %d, Len %u", - topicId, msg->qos, msg->packet_id, msg->total_len); - + if (!(msg->topic_type & SN_TOPIC_ID_TYPE_SHORT)) { + /* Topic ID name */ + topicId = (word16)((byte)msg->topic_name[0] << 8 | + (byte)msg->topic_name[1]); + + /* Print incoming message */ + PRINTF("MQTT-SN Message: Topic ID %hu, Qos %d, Id %d, Len %u", + topicId, msg->qos, msg->packet_id, msg->total_len); + } + else { + /* Topic short name */ + /* Print incoming message */ + PRINTF("MQTT-SN Message: Topic ID %c%c, Qos %d, Id %d, Len %u", + msg->topic_name[0], msg->topic_name[1], + msg->qos, msg->packet_id, msg->total_len); + } /* for test mode: check if TEST_MESSAGE was received */ if (mqttCtx != NULL && mqttCtx->test_mode) { if (XSTRLEN(TEST_MESSAGE) == msg->buffer_len && @@ -89,7 +98,7 @@ static int sn_message_cb(MqttClient *client, MqttMessage *msg, assigns a new topic ID to a topic name. */ static int sn_reg_callback(word16 topicId, const char* topicName, void *ctx) { - PRINTF("MQTT-SN Register CB: New topic ID: %d : \"%s\"", topicId, topicName); + PRINTF("MQTT-SN Register CB: New topic ID: %hu : \"%s\"", topicId, topicName); (void)ctx; return(MQTT_CODE_SUCCESS); @@ -112,7 +121,9 @@ int sn_test(MQTTCtx *mqttCtx) int rc = MQTT_CODE_SUCCESS; word16 topicID; - PRINTF("MQTT-SN Client: QoS %d", mqttCtx->qos); + PRINTF("MQTT-SN Client: Client ID %s, QoS %d", + mqttCtx->client_id, + mqttCtx->qos); /* Initialize Network */ rc = SN_ClientNet_Init(&mqttCtx->net, mqttCtx); @@ -232,7 +243,7 @@ int sn_test(MQTTCtx *mqttCtx) /* Topic ID is returned in RegAck */ topicID = regist->regack.topicId; } - PRINTF("....MQTT-SN Register Ack: rc = %d, topic id = %d", + PRINTF("....MQTT-SN Register Ack: rc = %d, topic id = %hu", regist->regack.return_code, regist->regack.topicId); } @@ -251,7 +262,7 @@ int sn_test(MQTTCtx *mqttCtx) PRINTF("MQTT-SN Subscribe: topic name = %s", subscribe.topicNameId); rc = SN_Client_Subscribe(&mqttCtx->client, &subscribe); - PRINTF("....MQTT-SN Subscribe Ack: topic id = %d, rc = %d", + PRINTF("....MQTT-SN Subscribe Ack: topic id = %hu, rc = %d", subscribe.subAck.topicId, subscribe.subAck.return_code); if ((rc == 0) && (subscribe.subAck.return_code == SN_RC_ACCEPTED)) { @@ -278,7 +289,7 @@ int sn_test(MQTTCtx *mqttCtx) PRINTF("MQTT-SN Subscribe: topic name = %s", subscribe.topicNameId); rc = SN_Client_Subscribe(&mqttCtx->client, &subscribe); - PRINTF("....MQTT-SN Subscribe Ack: topic id = %d, rc = %d", + PRINTF("....MQTT-SN Subscribe Ack: topic id = %hu, rc = %d", subscribe.subAck.topicId, (rc == 0) ? subscribe.subAck.return_code : rc); } @@ -305,8 +316,8 @@ int sn_test(MQTTCtx *mqttCtx) rc = SN_Client_Publish(&mqttCtx->client, &mqttCtx->publishSN); - PRINTF("MQTT-SN Publish: topic id = %d, rc = %d\r\nPayload = %s", - (word16)*mqttCtx->publishSN.topic_name, + PRINTF("MQTT-SN Publish: topic id = %hu, rc = %d\r\nPayload = %s", + *(word16*)mqttCtx->publishSN.topic_name, mqttCtx->publishSN.return_code, mqttCtx->publishSN.buffer); if (rc != MQTT_CODE_SUCCESS) { @@ -343,12 +354,12 @@ int sn_test(MQTTCtx *mqttCtx) subscribe.topicNameId = pd_topic_id; subscribe.packet_id = mqtt_get_packetid(); - PRINTF("MQTT-SN Predefined Subscribe: topic id = %d", + PRINTF("MQTT-SN Predefined Subscribe: topic id = %hu", subscribe.topicNameId[1]); rc = SN_Client_Subscribe(&mqttCtx->client, &subscribe); if (rc == MQTT_CODE_SUCCESS) { - PRINTF("....MQTT-SN Predefined Subscribe Ack: topic id = %d, rc = %d", + PRINTF("....MQTT-SN Predefined Subscribe Ack: topic id = %hu, rc = %d", subscribe.subAck.topicId, subscribe.subAck.return_code); } if ((rc == MQTT_CODE_SUCCESS) && (subscribe.subAck.return_code != 0)) { @@ -376,7 +387,7 @@ int sn_test(MQTTCtx *mqttCtx) rc = SN_Client_Publish(&mqttCtx->client, &publish); - PRINTF("MQTT-SN Predefined Publish: topic id = %d, rc = %d\r\nPayload = %s", + PRINTF("MQTT-SN Predefined Publish: topic id = %hu, rc = %d\r\nPayload = %s", publish.topic_name[1], publish.return_code, publish.buffer); @@ -392,7 +403,7 @@ int sn_test(MQTTCtx *mqttCtx) unsub.topicNameId = pd_topic_id; unsub.packet_id = mqtt_get_packetid(); - PRINTF("MQTT-SN Unsubscribe Predefined Topic: topic ID = %d", + PRINTF("MQTT-SN Unsubscribe Predefined Topic: topic id = %hu", unsub.topicNameId[1]); rc = SN_Client_Unsubscribe(&mqttCtx->client, &unsub); PRINTF("....MQTT-SN Unsubscribe Predefined Topic Ack: rc = %d", rc); @@ -534,7 +545,7 @@ int sn_test(MQTTCtx *mqttCtx) mqttCtx->publishSN.total_len = (word16)rc; rc = SN_Client_Publish(&mqttCtx->client, &mqttCtx->publishSN); - PRINTF("MQTT-SN Publish: topic id = %d, rc = %d\r\nPayload = %s", + PRINTF("MQTT-SN Publish: topic id = %hu, rc = %d\r\nPayload = %s", (word16)*mqttCtx->publishSN.topic_name, mqttCtx->publishSN.return_code, mqttCtx->publishSN.buffer); diff --git a/src/mqtt_packet.c b/src/mqtt_packet.c index 895449ad5..e6d3a7131 100644 --- a/src/mqtt_packet.c +++ b/src/mqtt_packet.c @@ -3138,7 +3138,7 @@ int SN_Encode_Publish(byte *tx_buf, int tx_buf_len, SN_Publish *publish) } else { /* Topic ID */ - tx_payload += MqttEncode_Num(tx_payload, (word16)*publish->topic_name); + tx_payload += MqttEncode_Num(tx_payload, *(word16*)publish->topic_name); } tx_payload += MqttEncode_Num(tx_payload, publish->packet_id); @@ -3201,7 +3201,7 @@ int SN_Decode_Publish(byte *rx_buf, int rx_buf_len, SN_Publish *publish) publish->retain = flags & SN_PACKET_FLAG_RETAIN; - publish->type = flags & SN_PACKET_FLAG_TOPICIDTYPE_MASK; + publish->topic_type = flags & SN_PACKET_FLAG_TOPICIDTYPE_MASK; /* Decode payload */ From 42b45094a28ba55d89eb8a150139d995b7d7b7cf Mon Sep 17 00:00:00 2001 From: David Garske Date: Fri, 29 Sep 2023 09:35:11 -0700 Subject: [PATCH 03/62] Possible patch for POSIX conditional wait issue. ZD 16769 --- src/mqtt_client.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/mqtt_client.c b/src/mqtt_client.c index b2ab4747e..d9fd0a57a 100644 --- a/src/mqtt_client.c +++ b/src/mqtt_client.c @@ -81,18 +81,18 @@ static int MqttClient_Publish_ReadPayload(MqttClient* client, } #elif defined(WOLFMQTT_POSIX_SEMAPHORES) /* Posix style semaphore */ - int wm_SemInit(wm_Sem *s){ + int wm_SemInit(wm_Sem *s) { s->lockCount = 0; pthread_mutex_init(&s->mutex, NULL); pthread_cond_init(&s->cond, NULL); return 0; } - int wm_SemFree(wm_Sem *s){ + int wm_SemFree(wm_Sem *s) { pthread_mutex_destroy(&s->mutex); pthread_cond_destroy(&s->cond); return 0; } - int wm_SemLock(wm_Sem *s){ + int wm_SemLock(wm_Sem *s) { pthread_mutex_lock(&s->mutex); while (s->lockCount > 0) pthread_cond_wait(&s->cond, &s->mutex); @@ -100,10 +100,12 @@ static int MqttClient_Publish_ReadPayload(MqttClient* client, pthread_mutex_unlock(&s->mutex); return 0; } - int wm_SemUnlock(wm_Sem *s){ + int wm_SemUnlock(wm_Sem *s) { pthread_mutex_lock(&s->mutex); - s->lockCount--; - pthread_cond_signal(&s->cond); + if (s->lockCount > 0) { + s->lockCount--; + pthread_cond_signal(&s->cond); + } pthread_mutex_unlock(&s->mutex); return 0; } From 4c0adce224aa20669be04aa22271520e04e4616c Mon Sep 17 00:00:00 2001 From: David Garske Date: Wed, 11 Oct 2023 17:33:10 -0700 Subject: [PATCH 04/62] Add support for using a simple posix mutex by disabling the conditional signal using `WOLFMQTT_NO_COND_SIGNAL`. --- README.md | 4 ++++ src/mqtt_client.c | 10 +++++++++- wolfmqtt/mqtt_types.h | 4 +++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6d177d81c..7398f0cad 100644 --- a/README.md +++ b/README.md @@ -195,6 +195,10 @@ More about MQTT-SN examples in [examples/sn-client/README.md](examples/sn-client ### Multithread Example This example exercises the multithreading capabilities of the client library. The client implements two tasks: one that publishes to the broker; and another that waits for messages from the broker. The publish thread is created `NUM_PUB_TASKS` times (10 by default) and sends unique messages to the broker. This feature is enabled using the `--enable-mt` configuration option. The example is located in `/examples/multithread/`. +The multi-threading feature can also be used with the non-blocking socket (--enable-nb). + +If you are having issues with thread synchronization on Linux consider using not the conditional signal (`WOLFMQTT_NO_COND_SIGNAL`). + ### Atomic publish and subscribe examples In the `examples/pub-sub` folder, there are two simple client examples: * mqtt-pub - publishes to a topic diff --git a/src/mqtt_client.c b/src/mqtt_client.c index d9fd0a57a..0a5091bb8 100644 --- a/src/mqtt_client.c +++ b/src/mqtt_client.c @@ -82,30 +82,38 @@ static int MqttClient_Publish_ReadPayload(MqttClient* client, #elif defined(WOLFMQTT_POSIX_SEMAPHORES) /* Posix style semaphore */ int wm_SemInit(wm_Sem *s) { + #ifndef WOLFMQTT_NO_COND_SIGNAL s->lockCount = 0; - pthread_mutex_init(&s->mutex, NULL); pthread_cond_init(&s->cond, NULL); + #endif + pthread_mutex_init(&s->mutex, NULL); return 0; } int wm_SemFree(wm_Sem *s) { pthread_mutex_destroy(&s->mutex); + #ifndef WOLFMQTT_NO_COND_SIGNAL pthread_cond_destroy(&s->cond); + #endif return 0; } int wm_SemLock(wm_Sem *s) { pthread_mutex_lock(&s->mutex); + #ifndef WOLFMQTT_NO_COND_SIGNAL while (s->lockCount > 0) pthread_cond_wait(&s->cond, &s->mutex); s->lockCount++; pthread_mutex_unlock(&s->mutex); + #endif return 0; } int wm_SemUnlock(wm_Sem *s) { + #ifndef WOLFMQTT_NO_COND_SIGNAL pthread_mutex_lock(&s->mutex); if (s->lockCount > 0) { s->lockCount--; pthread_cond_signal(&s->cond); } + #endif pthread_mutex_unlock(&s->mutex); return 0; } diff --git a/wolfmqtt/mqtt_types.h b/wolfmqtt/mqtt_types.h index f18655ec6..cc47234d1 100644 --- a/wolfmqtt/mqtt_types.h +++ b/wolfmqtt/mqtt_types.h @@ -106,9 +106,11 @@ #define WOLFMQTT_POSIX_SEMAPHORES #include typedef struct { + #ifndef WOLFMQTT_NO_COND_SIGNAL volatile int lockCount; - pthread_mutex_t mutex; pthread_cond_t cond; + #endif + pthread_mutex_t mutex; } wm_Sem; #elif defined(FREERTOS) From 06465df3dda8543e89b3002ffda578581a69e6c9 Mon Sep 17 00:00:00 2001 From: Eric Blankenhorn Date: Mon, 16 Oct 2023 16:53:40 -0500 Subject: [PATCH 05/62] Exclude CI tests with external brokers --- .github/workflows/fsanitize-check.yml | 2 ++ .github/workflows/macos-check.yml | 2 ++ .github/workflows/ubuntu-check.yml | 6 ++++-- CMakeLists.txt | 2 +- scripts/awsiot.test | 22 +++++++++++++--------- scripts/azureiothub.test | 22 +++++++++++++--------- 6 files changed, 35 insertions(+), 21 deletions(-) diff --git a/.github/workflows/fsanitize-check.yml b/.github/workflows/fsanitize-check.yml index 7ec5483bf..ee87798ef 100644 --- a/.github/workflows/fsanitize-check.yml +++ b/.github/workflows/fsanitize-check.yml @@ -11,6 +11,8 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 5 + env: + WOLFMQTT_NO_EXTERNAL_BROKER_TESTS: 1 steps: - uses: actions/checkout@master diff --git a/.github/workflows/macos-check.yml b/.github/workflows/macos-check.yml index eea2f48a8..1474e1f26 100644 --- a/.github/workflows/macos-check.yml +++ b/.github/workflows/macos-check.yml @@ -11,6 +11,8 @@ jobs: runs-on: macos-latest timeout-minutes: 10 + env: + WOLFMQTT_NO_EXTERNAL_BROKER_TESTS: 1 steps: - uses: actions/checkout@master with: diff --git a/.github/workflows/ubuntu-check.yml b/.github/workflows/ubuntu-check.yml index c0a65c04a..7baf97b18 100644 --- a/.github/workflows/ubuntu-check.yml +++ b/.github/workflows/ubuntu-check.yml @@ -18,10 +18,10 @@ jobs: repository: wolfssl/wolfssl path: wolfssl - name: wolfssl autogen - working-directory: ./wolfssl + working-directory: ./wolfssl run: ./autogen.sh - name: wolfssl configure - working-directory: ./wolfssl + working-directory: ./wolfssl run: ./configure --enable-enckeys - name: wolfssl make working-directory: ./wolfssl @@ -43,6 +43,8 @@ jobs: if: ${{ failure() && steps.make-check.outcome == 'failure' }} run: | more test-suite.log + env: + WOLFMQTT_NO_EXTERNAL_BROKER_TESTS: 1 - name: configure with SN Enabled run: ./configure --enable-sn - name: make diff --git a/CMakeLists.txt b/CMakeLists.txt index d8c6b7354..f38bffdc9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -148,7 +148,7 @@ if (WOLFMQTT_EXAMPLES) add_mqtt_example(fwpush firmware/fwpush.c) add_mqtt_example(fwclient firmware/fwclient.c) add_mqtt_example(mqtt-pub pub-sub/mqtt-pub.c) - add_mqtt_example(mqtt-pub pub-sub/mqtt-sub.c) + add_mqtt_example(mqtt-sub pub-sub/mqtt-sub.c) endif() #################################################### diff --git a/scripts/awsiot.test b/scripts/awsiot.test index 9bff5f945..a153dcf41 100755 --- a/scripts/awsiot.test +++ b/scripts/awsiot.test @@ -5,18 +5,22 @@ # Check for application [ ! -x ./examples/aws/awsiot ] && echo -e "\n\nAWS IoT MQTT Client doesn't exist" && exit 1 -def_args="-T -C 2000" +if test -n "$WOLFMQTT_NO_EXTERNAL_BROKER_TESTS"; then + echo "WOLFMQTT_NO_EXTERNAL_BROKER_TESTS set, won't run" +else + def_args="-T -C 2000" -# Run with TLS and QoS 0-1 + # Run with TLS and QoS 0-1 -./examples/aws/awsiot $def_args -t -q 0 $1 -RESULT=$? -[ $RESULT -ne 0 ] && echo -e "\n\nAWS IoT MQTT Client failed! TLS=On, QoS=0" && exit 1 + ./examples/aws/awsiot $def_args -t -q 0 $1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "\n\nAWS IoT MQTT Client failed! TLS=On, QoS=0" && exit 1 -./examples/aws/awsiot $def_args -t -q 1 $1 -RESULT=$? -[ $RESULT -ne 0 ] && echo -e "\n\nAWS IoT MQTT Client failed! TLS=On, QoS=1" && exit 1 + ./examples/aws/awsiot $def_args -t -q 1 $1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "\n\nAWS IoT MQTT Client failed! TLS=On, QoS=1" && exit 1 -echo -e "\n\nAWS IoT MQTT Client Tests Passed" + echo -e "\n\nAWS IoT MQTT Client Tests Passed" +fi exit 0 diff --git a/scripts/azureiothub.test b/scripts/azureiothub.test index e180e45fe..37c94d193 100755 --- a/scripts/azureiothub.test +++ b/scripts/azureiothub.test @@ -5,18 +5,22 @@ # Check for application [ ! -x ./examples/azure/azureiothub ] && echo -e "\n\nAzureIotHub MQTT Client doesn't exist" && exit 1 -def_args="-T -C 2000" +if test -n "$WOLFMQTT_NO_EXTERNAL_BROKER_TESTS"; then + echo "WOLFMQTT_NO_EXTERNAL_BROKER_TESTS set, won't run" +else + def_args="-T -C 2000" -# Run with TLS and QoS 0-1 + # Run with TLS and QoS 0-1 -./examples/azure/azureiothub $def_args -t -q 0 $1 -RESULT=$? -[ $RESULT -ne 0 ] && echo -e "\n\nAzureIotHub MQTT Client failed! TLS=On, QoS=0" && exit 1 + ./examples/azure/azureiothub $def_args -t -q 0 $1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "\n\nAzureIotHub MQTT Client failed! TLS=On, QoS=0" && exit 1 -./examples/azure/azureiothub $def_args -t -q 1 $1 -RESULT=$? -[ $RESULT -ne 0 ] && echo -e "\n\nAzureIotHub MQTT Client failed! TLS=On, QoS=1" && exit 1 + ./examples/azure/azureiothub $def_args -t -q 1 $1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "\n\nAzureIotHub MQTT Client failed! TLS=On, QoS=1" && exit 1 -echo -e "\n\nAzureIotHub MQTT Client Tests Passed" + echo -e "\n\nAzureIotHub MQTT Client Tests Passed" +fi exit 0 From 0afb4e3070cb778919c637c322602937db1aafde Mon Sep 17 00:00:00 2001 From: David Garske Date: Thu, 12 Oct 2023 13:39:51 -0700 Subject: [PATCH 06/62] Improvements for client property stack. Don't use lock for `WOLFMQTT_DYN_PROP`. If using multi-threading make sure `clientPropStack_lock` is not de-initialized, since it could be shared by multiple client instances. Add CI test. ZD 16814 --- .github/workflows/ubuntu-check.yml | 11 ++++++++ src/mqtt_packet.c | 41 +++++++++++++++++++----------- 2 files changed, 37 insertions(+), 15 deletions(-) diff --git a/.github/workflows/ubuntu-check.yml b/.github/workflows/ubuntu-check.yml index 7baf97b18..d8bd3107f 100644 --- a/.github/workflows/ubuntu-check.yml +++ b/.github/workflows/ubuntu-check.yml @@ -78,3 +78,14 @@ jobs: if: ${{ failure() && steps.make-check-nonblock-mt.outcome == 'failure' }} run: | more test-suite.log + - name: configure with Multi-threading and WOLFMQTT_DYN_PROP + run: ./configure --enable-mt CFLAGS="-DWOLFMQTT_DYN_PROP" + - name: make + run: make + - name: make check + id: make-check-mt-dynprop + run: make check + - name: Show logs on failure + if: ${{ failure() && steps.make-check-mt-dynprop.outcome == 'failure' }} + run: | + more test-suite.log diff --git a/src/mqtt_packet.c b/src/mqtt_packet.c index e6d3a7131..c84c66664 100644 --- a/src/mqtt_packet.c +++ b/src/mqtt_packet.c @@ -128,6 +128,8 @@ static const struct MqttPropMatrix gPropMatrix[] = { /* WOLFMQTT_DYN_PROP allows property allocation using malloc */ #ifndef WOLFMQTT_DYN_PROP + +/* Maximum number of active static properties - overridable */ #ifndef MQTT_MAX_PROPS #define MQTT_MAX_PROPS 30 #endif @@ -135,11 +137,12 @@ static const struct MqttPropMatrix gPropMatrix[] = { /* Property structure allocation array. Property type equal to zero indicates unused element. */ static MqttProp clientPropStack[MQTT_MAX_PROPS]; -#endif /* WOLFMQTT_DYN_PROP */ #ifdef WOLFMQTT_MULTITHREAD +static volatile int clientPropStack_lockInit = 0; static wm_Sem clientPropStack_lock; #endif +#endif /* WOLFMQTT_DYN_PROP */ #endif /* WOLFMQTT_V5 */ /* Positive return value is header length, zero or negative indicates error */ @@ -1793,20 +1796,28 @@ int MqttDecode_Auth(byte *rx_buf, int rx_buf_len, MqttAuth *auth) return header_len + remain_len; } -int MqttProps_Init(void) { -#ifdef WOLFMQTT_MULTITHREAD - return wm_SemInit(&clientPropStack_lock); -#else - return 0; +int MqttProps_Init(void) +{ + int ret = MQTT_CODE_SUCCESS; +#if !defined(WOLFMQTT_DYN_PROP) && defined(WOLFMQTT_MULTITHREAD) + if (clientPropStack_lockInit == 0) { + clientPropStack_lockInit++; + ret = wm_SemInit(&clientPropStack_lock); + } #endif + return ret; } -int MqttProps_ShutDown(void) { -#ifdef WOLFMQTT_MULTITHREAD - return wm_SemFree(&clientPropStack_lock); -#else - return 0; +int MqttProps_ShutDown(void) +{ + int ret = MQTT_CODE_SUCCESS; +#if !defined(WOLFMQTT_DYN_PROP) && defined(WOLFMQTT_MULTITHREAD) + clientPropStack_lockInit--; + if (clientPropStack_lockInit == 0) { + ret = wm_SemFree(&clientPropStack_lock); + } #endif + return ret; } /* Add property */ @@ -1821,7 +1832,7 @@ MqttProp* MqttProps_Add(MqttProp **head) return NULL; } -#ifdef WOLFMQTT_MULTITHREAD +#if !defined(WOLFMQTT_DYN_PROP) && defined(WOLFMQTT_MULTITHREAD) if (wm_SemLock(&clientPropStack_lock) != 0) { return NULL; } @@ -1870,7 +1881,7 @@ MqttProp* MqttProps_Add(MqttProp **head) (void)MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PROPERTY); } -#ifdef WOLFMQTT_MULTITHREAD +#if !defined(WOLFMQTT_DYN_PROP) && defined(WOLFMQTT_MULTITHREAD) (void)wm_SemUnlock(&clientPropStack_lock); #endif @@ -1881,7 +1892,7 @@ MqttProp* MqttProps_Add(MqttProp **head) int MqttProps_Free(MqttProp *head) { int ret = MQTT_CODE_SUCCESS; -#ifdef WOLFMQTT_MULTITHREAD +#if !defined(WOLFMQTT_DYN_PROP) && defined(WOLFMQTT_MULTITHREAD) if ((ret = wm_SemLock(&clientPropStack_lock)) != 0) { return ret; } @@ -1898,7 +1909,7 @@ int MqttProps_Free(MqttProp *head) head = tmp; #endif } -#ifdef WOLFMQTT_MULTITHREAD +#if !defined(WOLFMQTT_DYN_PROP) && defined(WOLFMQTT_MULTITHREAD) (void)wm_SemUnlock(&clientPropStack_lock); #endif return ret; From eb057eabf6bd4616a1f9e3ba613ccf752cd40f30 Mon Sep 17 00:00:00 2001 From: Eric Blankenhorn Date: Fri, 20 Oct 2023 16:40:38 -0500 Subject: [PATCH 07/62] Add mosquitto to CI tests --- .github/workflows/fsanitize-check.yml | 35 +++++++++++- .github/workflows/macos-check.yml | 18 +++++- .github/workflows/ubuntu-check.yml | 79 +++++++++++++++------------ .github/workflows/zephyr.yml | 5 +- scripts/broker_test/mosquitto.conf | 4 +- scripts/client.test | 30 ++++++---- scripts/firmware.test | 30 +++++++--- scripts/multithread.test | 22 +++++--- scripts/nbclient.test | 20 ++++--- zephyr/samples/client.c | 7 +++ 10 files changed, 171 insertions(+), 79 deletions(-) diff --git a/.github/workflows/fsanitize-check.yml b/.github/workflows/fsanitize-check.yml index ee87798ef..543e55420 100644 --- a/.github/workflows/fsanitize-check.yml +++ b/.github/workflows/fsanitize-check.yml @@ -15,15 +15,36 @@ jobs: WOLFMQTT_NO_EXTERNAL_BROKER_TESTS: 1 steps: + - name: Install dependencies + run: | + # Don't prompt for anything + export DEBIAN_FRONTEND=noninteractive + sudo apt-get update + # Install mosquitto + sudo apt-get install -y mosquitto bubblewrap + + - name: Setup mosquitto broker + run: | + # Disable default broker daemon + sudo service mosquitto stop + sleep 1 + + # This is some debug info useful if something goes wrong + - name: Show network status + run: | + sudo ifconfig + sudo route + sudo netstat -tulpan + - uses: actions/checkout@master with: repository: wolfssl/wolfssl path: wolfssl - name: wolfssl autogen - working-directory: ./wolfssl + working-directory: ./wolfssl run: ./autogen.sh - name: wolfssl configure - working-directory: ./wolfssl + working-directory: ./wolfssl run: ./configure --enable-enckeys - name: wolfssl make working-directory: ./wolfssl @@ -34,27 +55,37 @@ jobs: - uses: actions/checkout@master - name: autogen run: ./autogen.sh + - name: configure run: ./configure CC="gcc -fsanitize=address" - name: make run: make - name: make check run: make check + - name: configure with SN Enabled run: ./configure --enable-sn CC="gcc -fsanitize=address" - name: make run: make - name: make check run: make check + - name: configure with Non-Block run: ./configure --enable-nonblock CFLAGS="-DWOLFMQTT_TEST_NONBLOCK" CC="gcc -fsanitize=address" - name: make run: make - name: make check run: make check + - name: configure with Non-Block and Multi-threading run: ./configure --enable-mt --enable-nonblock CFLAGS="-DWOLFMQTT_TEST_NONBLOCK" CC="gcc -fsanitize=address" - name: make run: make - name: make check run: make check + + # capture logs on failure + - name: Show logs on failure + if: failure() || cancelled() + run: | + more test-suite.log diff --git a/.github/workflows/macos-check.yml b/.github/workflows/macos-check.yml index 1474e1f26..c748881f8 100644 --- a/.github/workflows/macos-check.yml +++ b/.github/workflows/macos-check.yml @@ -19,12 +19,13 @@ jobs: repository: wolfssl/wolfssl path: wolfssl - name: brew - run: brew install automake libtool + run: brew install automake libtool md5sha1sum + - name: wolfssl autogen - working-directory: ./wolfssl + working-directory: ./wolfssl run: ./autogen.sh - name: wolfssl configure - working-directory: ./wolfssl + working-directory: ./wolfssl run: ./configure --enable-enckeys - name: wolfssl make working-directory: ./wolfssl @@ -32,30 +33,41 @@ jobs: - name: wolfssl make install working-directory: ./wolfssl run: make install + - uses: actions/checkout@master - name: autogen run: ./autogen.sh + - name: configure run: ./configure - name: make run: make - name: make check run: make check + - name: configure with SN Enabled run: ./configure --enable-sn - name: make run: make - name: make check run: make check + - name: configure with Non-Block run: ./configure --enable-nonblock CFLAGS="-DWOLFMQTT_TEST_NONBLOCK" - name: make run: make - name: make check run: make check + - name: configure with Non-Block and Multi-threading run: ./configure --enable-mt --enable-nonblock CFLAGS="-DWOLFMQTT_TEST_NONBLOCK" - name: make run: make - name: make check run: make check + + # capture logs on failure + - name: Show logs on failure + if: failure() || cancelled() + run: | + cat test-suite.log diff --git a/.github/workflows/ubuntu-check.yml b/.github/workflows/ubuntu-check.yml index d8bd3107f..811233988 100644 --- a/.github/workflows/ubuntu-check.yml +++ b/.github/workflows/ubuntu-check.yml @@ -13,6 +13,27 @@ jobs: timeout-minutes: 5 steps: + - name: Install dependencies + run: | + # Don't prompt for anything + export DEBIAN_FRONTEND=noninteractive + sudo apt-get update + # Install mosquitto + sudo apt-get install -y mosquitto bubblewrap + + - name: Setup mosquitto broker + run: | + # Disable default broker daemon + sudo service mosquitto stop + sleep 1 + + # This is some debug info useful if something goes wrong + - name: Show network status + run: | + sudo ifconfig + sudo route + sudo netstat -tulpan + - uses: actions/checkout@master with: repository: wolfssl/wolfssl @@ -29,63 +50,51 @@ jobs: - name: wolfssl make install working-directory: ./wolfssl run: sudo make install + - uses: actions/checkout@master - - name: autogen + - name: wolfmqtt autogen run: ./autogen.sh - - name: configure + + - name: wolfmqtt configure run: ./configure - - name: make + - name: wolfmqtt make run: make - - name: make check - id: make-check + - name: wolfmqtt make check run: make check - - name: Show logs on failure - if: ${{ failure() && steps.make-check.outcome == 'failure' }} - run: | - more test-suite.log + env: WOLFMQTT_NO_EXTERNAL_BROKER_TESTS: 1 - - name: configure with SN Enabled + + - name: wolfmqtt configure with SN Enabled run: ./configure --enable-sn - - name: make + - name: wolfmqtt make run: make - - name: make check - id: make-check-sn + - name: wolfmqtt make check run: make check - - name: Show logs on failure - if: ${{ failure() && steps.make-check-sn.outcome == 'failure' }} - run: | - more test-suite.log - - name: configure with Non-Block + + - name: wolfmqtt configure with Non-Block run: ./configure --enable-nonblock CFLAGS="-DWOLFMQTT_TEST_NONBLOCK" - - name: make + - name: wolfmqtt make run: make - - name: make check - id: make-check-nonblock + - name: wolfmqtt make check run: make check - - name: Show logs on failure - if: ${{ failure() && steps.make-check-nonblock.outcome == 'failure' }} - run: | - more test-suite.log - - name: configure with Non-Block and Multi-threading + + - name: wolfmqtt configure with Non-Block and Multi-threading run: ./configure --enable-mt --enable-nonblock CFLAGS="-DWOLFMQTT_TEST_NONBLOCK" - - name: make + - name: wolfmqtt make run: make - - name: make check - id: make-check-nonblock-mt + - name: wolfmqtt make check run: make check - - name: Show logs on failure - if: ${{ failure() && steps.make-check-nonblock-mt.outcome == 'failure' }} - run: | - more test-suite.log + - name: configure with Multi-threading and WOLFMQTT_DYN_PROP run: ./configure --enable-mt CFLAGS="-DWOLFMQTT_DYN_PROP" - name: make run: make - name: make check - id: make-check-mt-dynprop run: make check + + # capture logs on failure - name: Show logs on failure - if: ${{ failure() && steps.make-check-mt-dynprop.outcome == 'failure' }} + if: failure() || cancelled() run: | more test-suite.log diff --git a/.github/workflows/zephyr.yml b/.github/workflows/zephyr.yml index eb81835c0..8501ea4f2 100644 --- a/.github/workflows/zephyr.yml +++ b/.github/workflows/zephyr.yml @@ -1,4 +1,6 @@ name: Zephyr tests +env: + WOLFMQTT_NO_EXTERNAL_BROKER_TESTS: 1 on: push: @@ -17,6 +19,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 20 steps: + - name: Install dependencies run: | # Don't prompt for anything @@ -86,7 +89,7 @@ jobs: sudo service mosquitto stop mosquitto -c scripts/broker_test/mosquitto.conf &> broker.log & sleep 1 # let the broker set everything up - mosquitto_sub -t sensors &> sub.log & + mosquitto_sub -p 11883 -t sensors &> sub.log & # This is some debug info useful if something goes wrong - name: Show network status diff --git a/scripts/broker_test/mosquitto.conf b/scripts/broker_test/mosquitto.conf index 23e3be18e..7e27dc735 100644 --- a/scripts/broker_test/mosquitto.conf +++ b/scripts/broker_test/mosquitto.conf @@ -7,7 +7,7 @@ # ================================================================= # Port to use for the default listener. -listener 1883 +listener 11883 allow_anonymous true # ----------------------------------------------------------------- @@ -19,7 +19,7 @@ allow_anonymous true # # See also the mosquitto-tls man page. -listener 8883 +listener 18883 allow_anonymous true # At least one of cafile or capath must be defined. They both diff --git a/scripts/client.test b/scripts/client.test index fc241e1e2..482427790 100755 --- a/scripts/client.test +++ b/scripts/client.test @@ -8,9 +8,11 @@ broker_pid=$no_pid do_cleanup() { if [ $broker_pid != $no_pid ] then - kill -6 $broker_pid + kill -9 $broker_pid + echo "Killed broker PID $broker_pid" + broker_pid=$no_pid fi - + if [ $1 -ne 0 ] then exit 1 @@ -29,6 +31,7 @@ then if [ "${AM_BWRAPPED-}" != "yes" ]; then bwrap_path="$(command -v bwrap)" if [ -n "$bwrap_path" ]; then + echo "Client test using bwrap" export AM_BWRAPPED=yes exec "$bwrap_path" --unshare-net --dev-bind / / "$0" "$@" fi @@ -36,42 +39,45 @@ then # Run mosquitto broker mosquitto -c scripts/broker_test/mosquitto.conf & broker_pid=$! + echo "Broker PID is $broker_pid" def_args="${def_args} -h localhost" - mutual_auth_args="${mutual_auth_args} -c certs/client-cert.pem -K certs/client-key.pem" - ecc_mutual_auth_args="${mutual_auth_args} -c certs/client-ecc-cert.pem -K certs/ecc-client-key.pem" + tls_port_args="-p 18883" + port_args="-p 11883" + mutual_auth_args="-c certs/client-cert.pem -K certs/client-key.pem" + ecc_mutual_auth_args="-c certs/client-ecc-cert.pem -K certs/ecc-client-key.pem" fi # Run with and without TLS and QoS 0-2 -./examples/mqttclient/mqttclient $def_args -q 0 $1 +./examples/mqttclient/mqttclient $def_args $port_args -q 0 $1 RESULT=$? [ $RESULT -ne 0 ] && echo -e "\n\nMQTT Client failed! TLS=Off, QoS=0" && do_cleanup "-1" -./examples/mqttclient/mqttclient $def_args -q 1 $1 +./examples/mqttclient/mqttclient $def_args $port_args -q 1 $1 RESULT=$? [ $RESULT -ne 0 ] && echo -e "\n\nMQTT Client failed! TLS=Off, QoS=1" && do_cleanup "-1" -./examples/mqttclient/mqttclient $def_args -q 2 $1 +./examples/mqttclient/mqttclient $def_args $port_args -q 2 $1 RESULT=$? [ $RESULT -ne 0 ] && echo -e "\n\nMQTT Client failed! TLS=Off, QoS=2" && do_cleanup "-1" -./examples/mqttclient/mqttclient $def_args -t -q 0 $1 +./examples/mqttclient/mqttclient $def_args $tls_port_args -t -q 0 $1 RESULT=$? [ $RESULT -ne 0 ] && echo -e "\n\nMQTT Client failed! TLS=On, QoS=0" && do_cleanup "-1" -./examples/mqttclient/mqttclient $def_args -t -q 1 $1 +./examples/mqttclient/mqttclient $def_args $tls_port_args -t -q 1 $1 RESULT=$? [ $RESULT -ne 0 ] && echo -e "\n\nMQTT Client failed! TLS=On, QoS=1" && do_cleanup "-1" -./examples/mqttclient/mqttclient $def_args -t -q 2 $1 +./examples/mqttclient/mqttclient $def_args $tls_port_args -t -q 2 $1 RESULT=$? [ $RESULT -ne 0 ] && echo -e "\n\nMQTT Client failed! TLS=On, QoS=2" && do_cleanup "-1" -./examples/mqttclient/mqttclient $def_args $mutual_auth_args -t -q 0 $1 +./examples/mqttclient/mqttclient $def_args $mutual_auth_args $tls_port_args -t -q 0 $1 RESULT=$? [ $RESULT -ne 0 ] && echo -e "\n\nMQTT Client failed! TLS=On, QoS=0, RSA mutual auth" && do_cleanup "-1" -./examples/mqttclient/mqttclient $def_args $ecc_mutual_auth_args -t -q 0 $1 +./examples/mqttclient/mqttclient $def_args $ecc_mutual_auth_args $tls_port_args -t -q 0 $1 RESULT=$? [ $RESULT -ne 0 ] && echo -e "\n\nMQTT Client failed! TLS=On, QoS=0, ECC mutual auth" && do_cleanup "-1" diff --git a/scripts/firmware.test b/scripts/firmware.test index a8c3cfb1e..6f2ce615f 100755 --- a/scripts/firmware.test +++ b/scripts/firmware.test @@ -6,9 +6,16 @@ no_pid=-1 broker_pid=$no_pid do_cleanup() { + if [ $ENABLE_MQTT_TLS -ne 1 ]; then + # Delete file + rm $fileout + fi + if [ $broker_pid != $no_pid ] then - kill -6 $broker_pid + kill -9 $broker_pid + echo "Killed broker PID $broker_pid" + broker_pid=$no_pid fi if [ $1 -ne 0 ] @@ -32,6 +39,7 @@ then if [ "${AM_BWRAPPED-}" != "yes" ]; then bwrap_path="$(command -v bwrap)" if [ -n "$bwrap_path" ]; then + echo "Firmware test using bwrap" export AM_BWRAPPED=yes exec "$bwrap_path" --unshare-net --dev-bind / / "$0" "$@" fi @@ -39,9 +47,13 @@ then # Run mosquitto broker mosquitto -c scripts/broker_test/mosquitto.conf & broker_pid=$! - def_args="${def_args} -h localhost" + echo "Broker PID is $broker_pid" + def_args="${def_args} -h localhost -p 18883" fi +grep -F -e 'ENABLE_MQTT_TLS' ./wolfmqtt/options.h +ENABLE_MQTT_TLS=$? + # Start firmware push ./examples/firmware/fwpush $def_args -r -f $filein $1 server_result=$? @@ -52,13 +64,13 @@ server_result=$? client_result=$? [ $client_result -ne 0 ] && echo -e "\n\nMQTT Example fwclient failed!" && do_cleanup "-1" -# Compare files -md5sum -b $filein $fileout -compare_result=$? -[ $client_result -ne 0 ] && echo -e "\n\nMQTT Example firmware compare failed!" && do_cleanup "-1" - -# Delete file -rm $fileout +if [ $ENABLE_MQTT_TLS -ne 1 ]; then + # Compare files + echo "Comparing files" + md5sum -b $filein $fileout + compare_result=$? + [ $compare_result -ne 0 ] && echo -e "\n\nMQTT Example firmware compare failed!" && do_cleanup "-1" +fi # End broker do_cleanup "0" diff --git a/scripts/multithread.test b/scripts/multithread.test index 0cf80148d..72f671df1 100755 --- a/scripts/multithread.test +++ b/scripts/multithread.test @@ -1,6 +1,6 @@ #!/bin/bash -# MQTT Client test +# MQTT Multithread Client test no_pid=-1 broker_pid=$no_pid @@ -8,7 +8,9 @@ broker_pid=$no_pid do_cleanup() { if [ $broker_pid != $no_pid ] then - kill -6 $broker_pid + kill -9 $broker_pid + echo "Killed broker PID $broker_pid" + broker_pid=$no_pid fi if [ $1 -ne 0 ] @@ -29,6 +31,7 @@ then if [ "${AM_BWRAPPED-}" != "yes" ]; then bwrap_path="$(command -v bwrap)" if [ -n "$bwrap_path" ]; then + echo "multithread test using bwrap" export AM_BWRAPPED=yes exec "$bwrap_path" --unshare-net --dev-bind / / "$0" "$@" fi @@ -36,32 +39,35 @@ then # Run mosquitto broker mosquitto -c scripts/broker_test/mosquitto.conf & broker_pid=$! + echo "Broker PID is $broker_pid" def_args="${def_args} -h localhost" + tls_port_args="-p 18883" + port_args="-p 11883" fi # Run with and without TLS and QoS 0-2 -./examples/multithread/multithread $def_args -q 0 $1 +./examples/multithread/multithread $def_args $port_args -q 0 $1 RESULT=$? [ $RESULT -ne 0 ] && echo -e "\n\nMultithread Client failed! TLS=Off, QoS=0" && do_cleanup "-1" -./examples/multithread/multithread $def_args -q 1 $1 +./examples/multithread/multithread $def_args $port_args -q 1 $1 RESULT=$? [ $RESULT -ne 0 ] && echo -e "\n\nMultithread Client failed! TLS=Off, QoS=1" && do_cleanup "-1" -./examples/multithread/multithread $def_args -q 2 $1 +./examples/multithread/multithread $def_args $port_args -q 2 $1 RESULT=$? [ $RESULT -ne 0 ] && echo -e "\n\nMultithread Client failed! TLS=Off, QoS=2" && do_cleanup "-1" -./examples/multithread/multithread $def_args -t -q 0 $1 +./examples/multithread/multithread $def_args $tls_port_args -t -q 0 $1 RESULT=$? [ $RESULT -ne 0 ] && echo -e "\n\nMultithread Client failed! TLS=On, QoS=0" && do_cleanup "-1" -./examples/multithread/multithread $def_args -t -q 1 $1 +./examples/multithread/multithread $def_args $tls_port_args -t -q 1 $1 RESULT=$? [ $RESULT -ne 0 ] && echo -e "\n\nMultithread Client failed! TLS=On, QoS=1" && do_cleanup "-1" -./examples/multithread/multithread $def_args -t -q 2 $1 +./examples/multithread/multithread $def_args $tls_port_args -t -q 2 $1 RESULT=$? [ $RESULT -ne 0 ] && echo -e "\n\nMultithread Client failed! TLS=On, QoS=2" && do_cleanup "-1" diff --git a/scripts/nbclient.test b/scripts/nbclient.test index 940dab8a9..8d760d083 100755 --- a/scripts/nbclient.test +++ b/scripts/nbclient.test @@ -8,7 +8,9 @@ broker_pid=$no_pid do_cleanup() { if [ $broker_pid != $no_pid ] then - kill -6 $broker_pid + kill -9 $broker_pid + echo "Killed broker PID $broker_pid" + broker_pid=$no_pid fi if [ $1 -ne 0 ] @@ -31,6 +33,7 @@ then if [ "${AM_BWRAPPED-}" != "yes" ]; then bwrap_path="$(command -v bwrap)" if [ -n "$bwrap_path" ]; then + echo "nbclient test using bwrap" export AM_BWRAPPED=yes exec "$bwrap_path" --unshare-net --dev-bind / / "$0" "$@" fi @@ -38,32 +41,35 @@ then # Run mosquitto broker mosquitto -c scripts/broker_test/mosquitto.conf & broker_pid=$! + echo "Broker PID is $broker_pid" def_args="${def_args} -h localhost" + tls_port_args="-p 18883" + port_args="-p 11883" fi # Run with and without TLS and QoS 0-2 -./examples/nbclient/nbclient $def_args -q 0 $1 +./examples/nbclient/nbclient $def_args $port_args -q 0 $1 RESULT=$? [ $RESULT -ne 0 ] && echo -e "\n\nNon-blocking Client failed! TLS=Off, QoS=0" && do_cleanup "-1" -./examples/nbclient/nbclient $def_args -q 1 $1 +./examples/nbclient/nbclient $def_args $port_args -q 1 $1 RESULT=$? [ $RESULT -ne 0 ] && echo -e "\n\nNon-blocking Client failed! TLS=Off, QoS=1" && do_cleanup "-1" -./examples/nbclient/nbclient $def_args -q 2 $1 +./examples/nbclient/nbclient $def_args $port_args -q 2 $1 RESULT=$? [ $RESULT -ne 0 ] && echo -e "\n\nNon-blocking Client failed! TLS=Off, QoS=2" && do_cleanup "-1" -./examples/nbclient/nbclient $def_args -t -q 0 $1 +./examples/nbclient/nbclient $def_args $tls_port_args -t -q 0 $1 RESULT=$? [ $RESULT -ne 0 ] && echo -e "\n\nNon-blocking Client failed! TLS=On, QoS=0" && do_cleanup "-1" -./examples/nbclient/nbclient $def_args -t -q 1 $1 +./examples/nbclient/nbclient $def_args $tls_port_args -t -q 1 $1 RESULT=$? [ $RESULT -ne 0 ] && echo -e "\n\nNon-blocking Client failed! TLS=On, QoS=1" && do_cleanup "-1" -./examples/nbclient/nbclient $def_args -t -q 2 $1 +./examples/nbclient/nbclient $def_args $tls_port_args -t -q 2 $1 RESULT=$? [ $RESULT -ne 0 ] && echo -e "\n\nNon-blocking Client failed! TLS=On, QoS=2" && do_cleanup "-1" diff --git a/zephyr/samples/client.c b/zephyr/samples/client.c index c85f1fde3..07f71e76e 100644 --- a/zephyr/samples/client.c +++ b/zephyr/samples/client.c @@ -32,6 +32,13 @@ int main(void) mqttCtx.test_mode = 1; + /* Set port as configured in scripts/broker_test/mosquitto.conf */ +#if defined(WOLFMQTT_DEFAULT_TLS) && (WOLFMQTT_DEFAULT_TLS == 1) + mqttCtx.port = 18883; +#else + mqttCtx.port = 11883; +#endif + rc = mqttclient_test(&mqttCtx); mqtt_free_ctx(&mqttCtx); From c7a4c7bf2fbf18a6a2a1c01b7ff5b6f6ea8c171c Mon Sep 17 00:00:00 2001 From: David Garske Date: Fri, 27 Oct 2023 11:17:37 -0700 Subject: [PATCH 08/62] * Fixes for non-blocking publish with payload larger than maximum TX buffer. ZD 16769 * Fixes for write non-blocking (would block) edge cases. * Fix for write position on cancel of message in progress. * Added check for zero return on read, which is a compatibility layer indicator for disconnect. --- src/mqtt_client.c | 49 +++++++++++++++++++++++++++++++++++++++-------- src/mqtt_socket.c | 7 +++++-- 2 files changed, 46 insertions(+), 10 deletions(-) diff --git a/src/mqtt_client.c b/src/mqtt_client.c index 0a5091bb8..fe3c05f76 100644 --- a/src/mqtt_client.c +++ b/src/mqtt_client.c @@ -1233,8 +1233,10 @@ static int MqttClient_WaitType(MqttClient *client, void *packet_obj, rc = MqttPacket_Write(client, client->tx_buf, client->write.len); #ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE) - break; + if (rc == MQTT_CODE_CONTINUE) { + /* keep send mutex locked and return to caller */ + return rc; + } #endif if (rc == client->write.len) { rc = 0; /* success */ @@ -1312,7 +1314,6 @@ static int MqttClient_WaitType(MqttClient *client, void *packet_obj, if (!waitMatchFound) { /* if we get here, then the we are still waiting for a packet */ mms_stat->read = MQTT_MSG_BEGIN; - MQTT_TRACE_MSG("Wait Again"); #ifdef WOLFMQTT_NONBLOCK /* for non-blocking return with code continue instead of waiting again * if called with packet type and id of 'any' */ @@ -1320,6 +1321,7 @@ static int MqttClient_WaitType(MqttClient *client, void *packet_obj, return MQTT_CODE_CONTINUE; } #endif + MQTT_TRACE_MSG("Wait Again"); goto wait_again; } @@ -1679,13 +1681,20 @@ static int MqttClient_Publish_WritePayload(MqttClient *client, if (client == NULL || publish == NULL) return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - if (pubCb) { + if (pubCb) { /* use publish callback to get data */ word32 tmp_len = publish->buffer_len; do { - /* Use the callback to get payload */ - if ((client->write.len = pubCb(publish)) < 0) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_CALLBACK); + /* use the client->write.len to handle non-blocking re-entry when + * new publish callback data is needed */ + if (client->write.len == 0) { + /* Use the callback to get payload */ + if ((client->write.len = pubCb(publish)) < 0) { + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("Publish callback error %d", client->write.len); + #endif + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_CALLBACK); + } } if ((word32)client->write.len < publish->buffer_len) { @@ -1715,6 +1724,7 @@ static int MqttClient_Publish_WritePayload(MqttClient *client, publish->buffer_pos += publish->intBuf_pos; publish->intBuf_pos = 0; + client->write.len = 0; /* reset current write len */ } while (publish->buffer_pos < publish->total_len); } @@ -1746,8 +1756,17 @@ static int MqttClient_Publish_WritePayload(MqttClient *client, /* Check if we are done sending publish message */ if (publish->buffer_pos < publish->buffer_len) { + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("Publish Write: not done (%d remain)", + publish->buffer_len - publish->buffer_pos); + #endif return MQTT_CODE_PUB_CONTINUE; } + #ifdef WOLFMQTT_DEBUG_CLIENT + else { + PRINTF("Publish Write: done"); + } + #endif #else do { rc = MqttPacket_Write(client, client->tx_buf, client->write.len); @@ -1779,6 +1798,11 @@ static int MqttClient_Publish_WritePayload(MqttClient *client, /* If transferring more chunks */ publish->buffer_pos += publish->intBuf_pos; if (publish->buffer_pos < publish->total_len) { + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("Publish Write: chunk (%d remain)", + publish->total_len - publish->buffer_pos); + #endif + /* Build next payload to send */ client->write.len = (publish->total_len - publish->buffer_pos); if (client->write.len > client->tx_buf_len) { @@ -1786,6 +1810,11 @@ static int MqttClient_Publish_WritePayload(MqttClient *client, } rc = MQTT_CODE_PUB_CONTINUE; } + #ifdef WOLFMQTT_DEBUG_CLIENT + else { + PRINTF("Publish Write: chunked done"); + } + #endif } } return rc; @@ -1897,6 +1926,9 @@ static int MqttPublishMsg(MqttClient *client, MqttPublish *publish, return rc; } + /* reset client->write.len */ + client->write.len = 0; + /* advance state */ publish->stat.write = MQTT_MSG_PAYLOAD; } @@ -1906,7 +1938,7 @@ static int MqttPublishMsg(MqttClient *client, MqttPublish *publish, { rc = MqttClient_Publish_WritePayload(client, publish, pubCb); #ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE) + if (rc == MQTT_CODE_CONTINUE || rc == MQTT_CODE_PUB_CONTINUE) return rc; #endif #ifdef WOLFMQTT_MULTITHREAD @@ -2562,6 +2594,7 @@ int MqttClient_CancelMessage(MqttClient *client, MqttObject* msg) #ifdef WOLFMQTT_DEBUG_CLIENT PRINTF("Cancel Write Lock"); #endif + client->write.pos = 0; /* reset current write position */ mms_stat->isWriteLocked = 0; wm_SemUnlock(&client->lockSend); } diff --git a/src/mqtt_socket.c b/src/mqtt_socket.c index ce6b2cefb..25bb0950a 100644 --- a/src/mqtt_socket.c +++ b/src/mqtt_socket.c @@ -235,9 +235,7 @@ static int MqttSocket_ReadDo(MqttClient *client, byte* buf, int buf_len, client->tls.sockRcRead = 0; /* init value */ rc = wolfSSL_read(client->tls.ssl, (char*)buf, buf_len); if (rc < 0) { - #if defined(WOLFMQTT_DEBUG_SOCKET) || defined(WOLFSSL_ASYNC_CRYPT) int error = wolfSSL_get_error(client->tls.ssl, 0); - #endif #ifdef WOLFMQTT_DEBUG_SOCKET if (error != WOLFSSL_ERROR_WANT_READ && error != WC_PENDING_E) { @@ -252,7 +250,12 @@ static int MqttSocket_ReadDo(MqttClient *client, byte* buf, int buf_len, if (error == WC_PENDING_E) { rc = MQTT_CODE_CONTINUE; } + else #endif + /* used with compatibility layer to communicate peer close */ + if (error == WOLFSSL_ERROR_ZERO_RETURN) { + rc = MQTT_CODE_ERROR_NETWORK; + } } } else From c973f0f966855cf4f55a2df2aba5a49f10549635 Mon Sep 17 00:00:00 2001 From: David Garske Date: Fri, 27 Oct 2023 11:19:47 -0700 Subject: [PATCH 09/62] * Added write WOLFMQTT_TEST_NONBLOCK support. * Fix to make sure ctrl+c is honored during a want read/write case. * Fix the firmware update example to properly synchronize publish and use a unique topic name. * Improve multi-thread test message to use larger size (> tx but) and in test mode check for actual message. * Improve remote test done logic. * Only run GitHub CI for PR's, not local push. * Properly set `WOLFMQTT_NO_EXTERNAL_BROKER_TESTS` for each test. * Use cat for logs, not more. --- .github/workflows/fsanitize-check.yml | 4 +- .github/workflows/macos-check.yml | 2 +- .github/workflows/ubuntu-check.yml | 16 +++-- .github/workflows/windows-check.yml | 2 +- examples/aws/awsiot.c | 30 +++++--- examples/azure/azureiothub.c | 70 ++++++++++++------- examples/firmware/fwclient.c | 37 ++++++---- examples/firmware/fwpush.c | 20 +++--- examples/firmware/fwpush.h | 2 +- examples/mqttclient/mqttclient.c | 5 +- examples/mqttexample.c | 67 +++++++++++------- examples/mqttexample.h | 3 + examples/multithread/multithread.c | 98 ++++++++++++++++----------- examples/nbclient/nbclient.c | 26 ++++--- examples/pub-sub/mqtt-pub.c | 5 +- examples/wiot/wiot.c | 24 +++++-- scripts/awsiot.test | 1 - scripts/azureiothub.test | 3 +- scripts/firmware.test | 17 +++-- src/mqtt_socket.c | 53 ++++++++++++--- 20 files changed, 308 insertions(+), 177 deletions(-) diff --git a/.github/workflows/fsanitize-check.yml b/.github/workflows/fsanitize-check.yml index 543e55420..073496907 100644 --- a/.github/workflows/fsanitize-check.yml +++ b/.github/workflows/fsanitize-check.yml @@ -2,7 +2,7 @@ name: fsanitize check Test on: push: - branches: [ '*' ] + branches: [ 'master', 'main', 'release/**' ] pull_request: branches: [ '*' ] @@ -88,4 +88,4 @@ jobs: - name: Show logs on failure if: failure() || cancelled() run: | - more test-suite.log + cat test-suite.log diff --git a/.github/workflows/macos-check.yml b/.github/workflows/macos-check.yml index c748881f8..b79b5af59 100644 --- a/.github/workflows/macos-check.yml +++ b/.github/workflows/macos-check.yml @@ -2,7 +2,7 @@ name: macOS Build Test on: push: - branches: [ '*' ] + branches: [ 'master', 'main', 'release/**' ] pull_request: branches: [ '*' ] diff --git a/.github/workflows/ubuntu-check.yml b/.github/workflows/ubuntu-check.yml index 811233988..cd1ecf65b 100644 --- a/.github/workflows/ubuntu-check.yml +++ b/.github/workflows/ubuntu-check.yml @@ -2,7 +2,7 @@ name: Ubuntu Build Test on: push: - branches: [ '*' ] + branches: [ 'master', 'main', 'release/**' ] pull_request: branches: [ '*' ] @@ -59,13 +59,13 @@ jobs: run: ./configure - name: wolfmqtt make run: make + # Note: this will run the external tests for this CI only - name: wolfmqtt make check run: make check - env: - WOLFMQTT_NO_EXTERNAL_BROKER_TESTS: 1 - - name: wolfmqtt configure with SN Enabled + env: + WOLFMQTT_NO_EXTERNAL_BROKER_TESTS: 1 run: ./configure --enable-sn - name: wolfmqtt make run: make @@ -73,6 +73,8 @@ jobs: run: make check - name: wolfmqtt configure with Non-Block + env: + WOLFMQTT_NO_EXTERNAL_BROKER_TESTS: 1 run: ./configure --enable-nonblock CFLAGS="-DWOLFMQTT_TEST_NONBLOCK" - name: wolfmqtt make run: make @@ -80,6 +82,8 @@ jobs: run: make check - name: wolfmqtt configure with Non-Block and Multi-threading + env: + WOLFMQTT_NO_EXTERNAL_BROKER_TESTS: 1 run: ./configure --enable-mt --enable-nonblock CFLAGS="-DWOLFMQTT_TEST_NONBLOCK" - name: wolfmqtt make run: make @@ -87,6 +91,8 @@ jobs: run: make check - name: configure with Multi-threading and WOLFMQTT_DYN_PROP + env: + WOLFMQTT_NO_EXTERNAL_BROKER_TESTS: 1 run: ./configure --enable-mt CFLAGS="-DWOLFMQTT_DYN_PROP" - name: make run: make @@ -97,4 +103,4 @@ jobs: - name: Show logs on failure if: failure() || cancelled() run: | - more test-suite.log + cat test-suite.log diff --git a/.github/workflows/windows-check.yml b/.github/workflows/windows-check.yml index 287d7c8f8..5b9bed0e5 100644 --- a/.github/workflows/windows-check.yml +++ b/.github/workflows/windows-check.yml @@ -2,7 +2,7 @@ name: Windows Build Test on: push: - branches: [ '*' ] + branches: [ 'master', 'main', 'release/**' ] pull_request: branches: [ '*' ] diff --git a/examples/aws/awsiot.c b/examples/aws/awsiot.c index 5f8d701de..cd77c3807 100644 --- a/examples/aws/awsiot.c +++ b/examples/aws/awsiot.c @@ -54,6 +54,7 @@ /* Locals */ static int mStopRead = 0; +static int mTestDone = 0; /* Configuration */ #define APP_HARDWARE "wolf_aws_iot_demo" @@ -66,8 +67,9 @@ static int mStopRead = 0; #define AWSIOT_KEEP_ALIVE_SEC DEFAULT_KEEP_ALIVE_SEC #define AWSIOT_CMD_TIMEOUT_MS DEFAULT_CMD_TIMEOUT_MS -#define AWSIOT_SUBSCRIBE_TOPIC "$aws/things/" AWSIOT_DEVICE_ID "/shadow/update/delta" #define AWSIOT_PUBLISH_TOPIC "$aws/things/" AWSIOT_DEVICE_ID "/shadow/update" +#define AWSIOT_SUBSCRIBE_TOPIC AWSIOT_PUBLISH_TOPIC + #define AWSIOT_PUBLISH_MSG_SZ 400 @@ -293,6 +295,9 @@ static int mqtt_message_cb(MqttClient *client, MqttMessage *msg, if (msg_done) { PRINTF("MQTT Message: Done"); + if (mqttCtx->test_mode) { + mTestDone = 1; + } } /* Return negative to terminate publish processing */ @@ -626,13 +631,6 @@ int awsiot_test(MQTTCtx *mqttCtx) mqttCtx->stat = WMQ_WAIT_MSG; do { - /* check for test mode or stop */ - if (mStopRead || mqttCtx->test_mode) { - rc = MQTT_CODE_SUCCESS; - PRINTF("MQTT Exiting..."); - break; - } - /* Try and read packet */ rc = MqttClient_WaitMessage(&mqttCtx->client, mqttCtx->cmd_timeout_ms); @@ -646,8 +644,17 @@ int awsiot_test(MQTTCtx *mqttCtx) if (rc == MQTT_CODE_CONTINUE) { return rc; } + + /* check for test mode or stop */ + if (mStopRead || mTestDone) { + rc = MQTT_CODE_SUCCESS; + mqttCtx->stat = WMQ_DISCONNECT; + PRINTF("MQTT Exiting..."); + break; + } + #ifdef WOLFMQTT_ENABLE_STDIN_CAP - else if (rc == MQTT_CODE_STDIN_WAKE) { + if (rc == MQTT_CODE_STDIN_WAKE) { /* Get data from STDIO */ XMEMSET(mqttCtx->rx_buf, 0, MAX_BUFFER_SIZE); if (XFGETS((char*)mqttCtx->rx_buf, MAX_BUFFER_SIZE - 1, stdin) != NULL) { @@ -672,8 +679,9 @@ int awsiot_test(MQTTCtx *mqttCtx) MqttClient_ReturnCodeToString(rc), rc); } } + else #endif - else if (rc == MQTT_CODE_ERROR_TIMEOUT) { + if (rc == MQTT_CODE_ERROR_TIMEOUT) { /* Keep Alive */ PRINTF("Keep-alive timeout, sending ping"); @@ -838,7 +846,7 @@ int awsiot_test(MQTTCtx *mqttCtx) #ifdef ENABLE_AWSIOT_EXAMPLE do { rc = awsiot_test(&mqttCtx); - } while (rc == MQTT_CODE_CONTINUE); + } while (!mStopRead && rc == MQTT_CODE_CONTINUE); mqtt_free_ctx(&mqttCtx); #else diff --git a/examples/azure/azureiothub.c b/examples/azure/azureiothub.c index 5b6fbbbef..463e67256 100644 --- a/examples/azure/azureiothub.c +++ b/examples/azure/azureiothub.c @@ -67,6 +67,7 @@ /* Locals */ static int mStopRead = 0; +static int mTestDone = 0; /* Configuration */ /* Reference: @@ -160,6 +161,9 @@ static int mqtt_message_cb(MqttClient *client, MqttMessage *msg, if (msg_done) { PRINTF("MQTT Message: Done"); + if (mqttCtx->test_mode) { + mTestDone = 1; + } } /* Return negative to terminate publish processing */ @@ -449,13 +453,6 @@ int azureiothub_test(MQTTCtx *mqttCtx) mqttCtx->stat = WMQ_WAIT_MSG; do { - /* check for test mode or stop */ - if (mStopRead || mqttCtx->test_mode) { - rc = MQTT_CODE_SUCCESS; - PRINTF("MQTT Exiting..."); - break; - } - /* Try and read packet */ rc = MqttClient_WaitMessage(&mqttCtx->client, mqttCtx->cmd_timeout_ms); @@ -469,8 +466,17 @@ int azureiothub_test(MQTTCtx *mqttCtx) if (rc == MQTT_CODE_CONTINUE) { return rc; } + + /* check for test mode or stop */ + if (mStopRead || mTestDone) { + rc = MQTT_CODE_SUCCESS; + mqttCtx->stat = WMQ_DISCONNECT; + PRINTF("MQTT Exiting..."); + goto disconn; + } + #ifdef WOLFMQTT_ENABLE_STDIN_CAP - else if (rc == MQTT_CODE_STDIN_WAKE) { + if (rc == MQTT_CODE_STDIN_WAKE) { /* Get data from STDIO */ XMEMSET(mqttCtx->rx_buf, 0, MAX_BUFFER_SIZE); if (XFGETS((char*)mqttCtx->rx_buf, MAX_BUFFER_SIZE - 1, stdin) != NULL) { @@ -493,38 +499,53 @@ int azureiothub_test(MQTTCtx *mqttCtx) MqttClient_ReturnCodeToString(rc), rc); } } + else #endif - else if (rc == MQTT_CODE_ERROR_TIMEOUT) { + if (rc == MQTT_CODE_ERROR_TIMEOUT) { /* Keep Alive */ PRINTF("Keep-alive timeout, sending ping"); - - rc = MqttClient_Ping_ex(&mqttCtx->client, &mqttCtx->ping); - if (rc == MQTT_CODE_CONTINUE) { - return rc; - } - else if (rc != MQTT_CODE_SUCCESS) { - PRINTF("MQTT Ping Keep Alive Error: %s (%d)", - MqttClient_ReturnCodeToString(rc), rc); - break; - } + mqttCtx->stat = WMQ_PING; + break; } else if (rc != MQTT_CODE_SUCCESS) { /* There was an error */ PRINTF("MQTT Message Wait: %s (%d)", MqttClient_ReturnCodeToString(rc), rc); - break; + goto disconn; } } while (1); + } + FALL_THROUGH; - /* Check for error */ - if (rc != MQTT_CODE_SUCCESS) { - goto disconn; + case WMQ_PING: + { + mqttCtx->stat = WMQ_PING; + + rc = MqttClient_Ping_ex(&mqttCtx->client, &mqttCtx->ping); + if (rc == MQTT_CODE_CONTINUE) { + return rc; + } + else if (rc != MQTT_CODE_SUCCESS) { + PRINTF("MQTT Ping Keep Alive Error: %s (%d)", + MqttClient_ReturnCodeToString(rc), rc); + break; + } + + if (mqttCtx->test_mode) { + PRINTF("MQTT Ping Done, exiting for test mode"); + mTestDone = 1; + } + else { + mqttCtx->stat = WMQ_WAIT_MSG; + break; } } FALL_THROUGH; case WMQ_DISCONNECT: { + mqttCtx->stat = WMQ_DISCONNECT; + /* Disconnect */ rc = MqttClient_Disconnect(&mqttCtx->client); if (rc == MQTT_CODE_CONTINUE) { @@ -559,7 +580,6 @@ int azureiothub_test(MQTTCtx *mqttCtx) } case WMQ_UNSUB: /* not used */ - case WMQ_PING: default: rc = MQTT_CODE_ERROR_STAT; goto exit; @@ -659,7 +679,7 @@ int azureiothub_test(MQTTCtx *mqttCtx) #ifdef ENABLE_AZUREIOTHUB_EXAMPLE do { rc = azureiothub_test(&mqttCtx); - } while (rc == MQTT_CODE_CONTINUE); + } while (!mStopRead && rc == MQTT_CODE_CONTINUE); mqtt_free_ctx(&mqttCtx); #else diff --git a/examples/firmware/fwclient.c b/examples/firmware/fwclient.c index 126105e93..b28914ef7 100644 --- a/examples/firmware/fwclient.c +++ b/examples/firmware/fwclient.c @@ -59,6 +59,7 @@ /* Locals */ static int mStopRead = 0; +static int mTestDone = 0; static byte* mFwBuf; @@ -162,13 +163,13 @@ static int mqtt_message_cb(MqttClient *client, MqttMessage *msg, /* Verify this message is for the firmware topic */ if (msg_new && - XMEMCMP(msg->topic_name, FIRMWARE_TOPIC_NAME, + XSTRNCMP(msg->topic_name, mqttCtx->topic_name, msg->topic_name_len) == 0 && !mFwBuf) { /* Allocate buffer for entire message */ /* Note: On an embedded system this could just be a write to flash. - If writting to flash change FIRMWARE_MAX_BUFFER to match + If writing to flash change FIRMWARE_MAX_BUFFER to match block size */ mFwBuf = (byte*)WOLFMQTT_MALLOC(msg->total_len); if (mFwBuf == NULL) { @@ -193,7 +194,7 @@ static int mqtt_message_cb(MqttClient *client, MqttMessage *msg, /* for test mode stop client */ if (mqttCtx->test_mode) { - mStopRead = 1; + mTestDone = 1; } } } @@ -325,8 +326,6 @@ int fwclient_test(MQTTCtx *mqttCtx) mqttCtx->subscribe.packet_id = mqtt_get_packetid(); mqttCtx->subscribe.topic_count = 1; mqttCtx->subscribe.topics = mqttCtx->topics; - mqttCtx->topics[0].topic_filter = FIRMWARE_TOPIC_NAME; - mqttCtx->topics[0].qos = mqttCtx->qos; } FALL_THROUGH; @@ -365,13 +364,6 @@ int fwclient_test(MQTTCtx *mqttCtx) rc = MqttClient_WaitMessage(&mqttCtx->client, mqttCtx->cmd_timeout_ms); - /* check for test mode */ - if (mStopRead) { - rc = MQTT_CODE_SUCCESS; - PRINTF("MQTT Exiting..."); - break; - } - #ifdef WOLFMQTT_NONBLOCK /* Track elapsed time with no activity and trigger timeout */ rc = mqtt_check_timeout(rc, &mqttCtx->start_sec, @@ -382,7 +374,20 @@ int fwclient_test(MQTTCtx *mqttCtx) if (rc == MQTT_CODE_CONTINUE) { return rc; } - else if (rc == MQTT_CODE_ERROR_TIMEOUT) { + + /* check for test mode */ + if (mStopRead || mTestDone) { + rc = MQTT_CODE_SUCCESS; + mqttCtx->stat = WMQ_DISCONNECT; + PRINTF("MQTT Exiting..."); + break; + } + + if (rc == MQTT_CODE_ERROR_TIMEOUT) { + if (mqttCtx->test_mode) { + PRINTF("Timeout in test mode, exit early!"); + mTestDone = 1; + } /* Keep Alive */ PRINTF("Keep-alive timeout, sending ping"); @@ -523,7 +528,9 @@ int fwclient_test(MQTTCtx *mqttCtx) /* init defaults */ mqtt_init_ctx(&mqttCtx); mqttCtx.app_name = "fwclient"; - mqttCtx.client_id = FIRMWARE_CLIIENT_ID; + mqttCtx.client_id = mqtt_append_random(FIRMWARE_CLIIENT_ID, + (word32)XSTRLEN(FIRMWARE_CLIIENT_ID)); + mqttCtx.dynamicClientId = 1; mqttCtx.topic_name = FIRMWARE_TOPIC_NAME; mqttCtx.qos = FIRMWARE_MQTT_QOS; mqttCtx.pub_file = FIRMWARE_DEF_SAVE_AS; @@ -548,7 +555,7 @@ int fwclient_test(MQTTCtx *mqttCtx) #ifdef ENABLE_FIRMWARE_EXAMPLE do { rc = fwclient_test(&mqttCtx); - } while (rc == MQTT_CODE_CONTINUE); + } while (!mStopRead && rc == MQTT_CODE_CONTINUE); mqtt_free_ctx(&mqttCtx); #else diff --git a/examples/firmware/fwpush.c b/examples/firmware/fwpush.c index ae1672d6f..c16bbb77e 100644 --- a/examples/firmware/fwpush.c +++ b/examples/firmware/fwpush.c @@ -121,12 +121,11 @@ static int mqtt_publish_cb(MqttPublish *publish) { /* read a buffer of data from the file */ bytes_read = fread(publish->buffer, 1, publish->buffer_len, cbData->fp); - if (bytes_read != 0) { - ret = (int)bytes_read; - } + ret = (int)bytes_read; } if (cbData->fp && feof(cbData->fp)) { fclose(cbData->fp); + cbData->fp = NULL; } } } @@ -404,8 +403,8 @@ int fwpush_test(MQTTCtx *mqttCtx) } /* Calculate the total payload length and store the FirmwareHeader, - signature, and key in fwpushCBdata structure to be used by the - callback. */ + * signature, and key in FwpushCBdata structure to be used by the + * callback. */ cbData = (FwpushCBdata*)WOLFMQTT_MALLOC(sizeof(FwpushCBdata)); if (cbData == NULL) { rc = MQTT_CODE_ERROR_OUT_OF_BUFFER; @@ -418,7 +417,7 @@ int fwpush_test(MQTTCtx *mqttCtx) (int*)&mqttCtx->publish.total_len); /* The publish->ctx is available for use by the application to pass - data to the callback routine. */ + * data to the callback routine. */ mqttCtx->publish.ctx = cbData; if (rc != 0) { @@ -454,6 +453,8 @@ int fwpush_test(MQTTCtx *mqttCtx) case WMQ_DISCONNECT: { + mqttCtx->stat = WMQ_DISCONNECT; + /* Disconnect */ rc = MqttClient_Disconnect(&mqttCtx->client); if (rc == MQTT_CODE_CONTINUE) { @@ -505,6 +506,7 @@ int fwpush_test(MQTTCtx *mqttCtx) if (rc != MQTT_CODE_CONTINUE) { if (cbData) { + if (cbData->fp) fclose(cbData->fp); if (cbData->data) WOLFMQTT_FREE(cbData->data); WOLFMQTT_FREE(cbData); } @@ -561,7 +563,9 @@ int fwpush_test(MQTTCtx *mqttCtx) /* init defaults */ mqtt_init_ctx(&mqttCtx); mqttCtx.app_name = "fwpush"; - mqttCtx.client_id = FIRMWARE_PUSH_CLIENT_ID; + mqttCtx.client_id = mqtt_append_random(FIRMWARE_PUSH_CLIENT_ID, + (word32)XSTRLEN(FIRMWARE_PUSH_CLIENT_ID)); + mqttCtx.dynamicClientId = 1; mqttCtx.topic_name = FIRMWARE_TOPIC_NAME; mqttCtx.qos = FIRMWARE_MQTT_QOS; mqttCtx.pub_file = FIRMWARE_PUSH_DEF_FILE; @@ -586,7 +590,7 @@ int fwpush_test(MQTTCtx *mqttCtx) #ifdef ENABLE_FIRMWARE_EXAMPLE do { rc = fwpush_test(&mqttCtx); - } while (rc == MQTT_CODE_CONTINUE); + } while (!mStopRead && rc == MQTT_CODE_CONTINUE); mqtt_free_ctx(&mqttCtx); #else diff --git a/examples/firmware/fwpush.h b/examples/firmware/fwpush.h index 1f590a67c..270b593bc 100644 --- a/examples/firmware/fwpush.h +++ b/examples/firmware/fwpush.h @@ -33,7 +33,7 @@ typedef struct FwpushCBdata_s { const char *filename; byte *data; FILE *fp; -}FwpushCBdata; +} FwpushCBdata; /* Exposed functions */ int fwpush_test(MQTTCtx *mqttCtx); diff --git a/examples/mqttclient/mqttclient.c b/examples/mqttclient/mqttclient.c index e8dee1912..8c65813a2 100644 --- a/examples/mqttclient/mqttclient.c +++ b/examples/mqttclient/mqttclient.c @@ -497,11 +497,10 @@ int mqttclient_test(MQTTCtx *mqttCtx) #endif /* This loop allows payloads larger than the buffer to be sent by - repeatedly calling publish. - */ + * repeatedly calling publish. */ do { rc = MqttClient_Publish(&mqttCtx->client, &mqttCtx->publish); - } while(rc == MQTT_CODE_PUB_CONTINUE); + } while (rc == MQTT_CODE_PUB_CONTINUE || rc == MQTT_CODE_CONTINUE); if ((mqttCtx->pub_file) && (mqttCtx->publish.buffer)) { WOLFMQTT_FREE(mqttCtx->publish.buffer); diff --git a/examples/mqttexample.c b/examples/mqttexample.c index b94380eff..fdfa604a6 100644 --- a/examples/mqttexample.c +++ b/examples/mqttexample.c @@ -154,40 +154,56 @@ static int mqtt_get_rand(byte* data, word32 len) return ret; } -#ifndef TEST_RAND_SZ -#define TEST_RAND_SZ 4 -#endif -static char* mqtt_append_random(const char* inStr, word32 inLen) +int mqtt_fill_random_hexstr(char* buf, word32 bufLen) { - int rc; + int rc = 0; + word32 pos = 0, sz, i; const char kHexChar[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; - byte rndBytes[TEST_RAND_SZ], rndHexStr[TEST_RAND_SZ*2]; - char *tmp = NULL; - - rc = mqtt_get_rand(rndBytes, (word32)sizeof(rndBytes)); - if (rc == 0) { - /* Convert random to hex string */ - int i; - for (i=0; i<(int)sizeof(rndBytes); i++) { - byte in = rndBytes[i]; - rndHexStr[(i*2)] = kHexChar[in >> 4]; - rndHexStr[(i*2)+1] = kHexChar[in & 0xf]; + byte rndBytes[32]; /* fill up to x bytes at a time */ + + while (rc == 0 && pos < bufLen) { + sz = bufLen - pos; + if (sz > (int)sizeof(rndBytes)) + sz = (int)sizeof(rndBytes); + sz /= 2; /* 1 byte expands to 2 bytes */ + + rc = mqtt_get_rand(rndBytes, sz); + if (rc == 0) { + /* Convert random to hex string */ + for (i=0; i> 4]; + buf[pos + (i*2)+1] = kHexChar[in & 0xf]; + } + pos += sz*2; } } - if (rc == 0) { - /* Allocate topic name and client id */ - tmp = (char*)WOLFMQTT_MALLOC(inLen + 1 + sizeof(rndHexStr) + 1); - if (tmp == NULL) { - rc = MQTT_CODE_ERROR_MEMORY; - } + return rc; +} + +#ifndef TEST_RAND_SZ +#define TEST_RAND_SZ 4 +#endif +char* mqtt_append_random(const char* inStr, word32 inLen) +{ + int rc = 0; + char *tmp; + + tmp = (char*)WOLFMQTT_MALLOC(inLen + 1 + (TEST_RAND_SZ*2) + 1); + if (tmp == NULL) { + rc = MQTT_CODE_ERROR_MEMORY; } if (rc == 0) { /* Format: inStr + `_` randhex + null term */ XMEMCPY(tmp, inStr, inLen); tmp[inLen] = '_'; - XMEMCPY(tmp + inLen + 1, rndHexStr, sizeof(rndHexStr)); - tmp[inLen + 1 + sizeof(rndHexStr)] = '\0'; + rc = mqtt_fill_random_hexstr(tmp + inLen + 1, (TEST_RAND_SZ*2)); + tmp[inLen + 1 + (TEST_RAND_SZ*2)] = '\0'; /* null term */ + } + if (rc != 0) { + WOLFMQTT_FREE(tmp); + tmp = NULL; } return tmp; } @@ -347,7 +363,6 @@ int mqtt_parse_args(MQTTCtx* mqttCtx, int argc, char** argv) break; case 'T': - mqttCtx->test_mode = 1; break; @@ -407,7 +422,7 @@ int mqtt_parse_args(MQTTCtx* mqttCtx, int argc, char** argv) /* Remove SNI functionality for sn-client */ if(!XSTRNCMP(mqttCtx->app_name, "sn-client", 10)){ - #ifdef HAVE_SNI + #ifdef HAVE_SNI useSNI=0; #endif } diff --git a/examples/mqttexample.h b/examples/mqttexample.h index 9372a04fa..315f7e186 100644 --- a/examples/mqttexample.h +++ b/examples/mqttexample.h @@ -200,6 +200,9 @@ word16 mqtt_get_packetid(void); int mqtt_check_timeout(int rc, word32* start_sec, word32 timeout_sec); #endif +int mqtt_fill_random_hexstr(char* buf, word32 bufLen); +char* mqtt_append_random(const char* inStr, word32 inLen); + int mqtt_file_load(const char* filePath, byte** fileBuf, int *fileLen); #ifdef WOLFSSL_ENCRYPTED_KEYS diff --git a/examples/multithread/multithread.c b/examples/multithread/multithread.c index 92ce00692..b00365b95 100755 --- a/examples/multithread/multithread.c +++ b/examples/multithread/multithread.c @@ -33,19 +33,22 @@ #include +#ifdef WOLFMQTT_MULTITHREAD + /* Configuration */ -/* Maximum size for network read/write callbacks. There is also a v5 define that - describes the max MQTT control packet size, DEFAULT_MAX_PKT_SZ. */ -#define MAX_BUFFER_SIZE 1024 -#define TEST_MESSAGE "test00" /* Number of publish tasks. Each will send a unique message to the broker. */ #define NUM_PUB_TASKS 10 +/* Maximum size for network read/write callbacks. There is also a v5 define that + describes the max MQTT control packet size, DEFAULT_MAX_PKT_SZ. */ +#define MAX_BUFFER_SIZE 1024 -#ifdef WOLFMQTT_MULTITHREAD +/* Total size of test message to build */ +#define TEST_MESSAGE_SIZE 1048 /* span more than one max packet */ /* Locals */ +static char mTestMessage[TEST_MESSAGE_SIZE]; static int mStopRead = 0; static int mNumMsgsRecvd; static int mNumMsgsDone; @@ -100,7 +103,8 @@ static int mqtt_stop_get(void) return rc; } -static int check_response(MQTTCtx* mqttCtx, int rc, word32* startSec, int packet_type) +static int check_response(MQTTCtx* mqttCtx, int rc, word32* startSec, + int packet_type, word32 timeoutMs) { /* check for test mode */ if (mqtt_stop_get()) { @@ -114,12 +118,9 @@ static int check_response(MQTTCtx* mqttCtx, int rc, word32* startSec, int packet PRINTF("Test cancel by setting early timeout"); return MQTT_CODE_ERROR_TIMEOUT; } - else -#else - (void)packet_type; #endif /* Track elapsed time with no activity and trigger timeout */ - rc = mqtt_check_timeout(rc, startSec, mqttCtx->cmd_timeout_ms/1000); + rc = mqtt_check_timeout(rc, startSec, timeoutMs/1000); /* check return code */ if (rc == MQTT_CODE_CONTINUE) { @@ -128,11 +129,13 @@ static int check_response(MQTTCtx* mqttCtx, int rc, word32* startSec, int packet usleep(100*1000); #endif } -#else - (void)packet_type; - (void)startSec; +#endif /* WOLFMQTT_NONBLOCK */ + (void)mqttCtx; -#endif + (void)startSec; + (void)packet_type; + (void)timeoutMs; + return rc; } @@ -170,17 +173,6 @@ static int mqtt_message_cb(MqttClient *client, MqttMessage *msg, /* Print incoming message */ PRINTF("MQTT Message: Topic %s, Qos %d, Id %d, Len %u, %u, %u", buf, msg->qos, msg->packet_id, msg->total_len, msg->buffer_len, msg->buffer_pos); - - /* for test mode: count the number of TEST_MESSAGE matches received */ - if (mqttCtx->test_mode) { - if (XSTRLEN(TEST_MESSAGE) == msg->buffer_len && - /* Only compare the "test" part */ - XSTRNCMP(TEST_MESSAGE, (char*)msg->buffer, - msg->buffer_len-2) == 0) - { - mNumMsgsRecvd++; - } - } } /* Print message payload */ @@ -194,6 +186,17 @@ static int mqtt_message_cb(MqttClient *client, MqttMessage *msg, msg->buffer_pos, msg->buffer_pos + msg->buffer_len, len, buf); if (msg_done) { + /* for test mode: count the number of messages received */ + if (mqttCtx->test_mode) { + if (msg->buffer_pos + msg->buffer_len == + (word32)sizeof(mTestMessage) && + XMEMCMP(&mTestMessage[msg->buffer_pos], msg->buffer, + msg->buffer_len) == 0) + { + mNumMsgsRecvd++; + } + } + PRINTF("MQTT Message: Done"); } wm_SemUnlock(&mtLock); @@ -311,7 +314,8 @@ static int multithread_test_init(MQTTCtx *mqttCtx) do { rc = MqttClient_NetConnect(&mqttCtx->client, mqttCtx->host, mqttCtx->port, DEFAULT_CON_TIMEOUT_MS, mqttCtx->use_tls, mqtt_tls_cb); - rc = check_response(mqttCtx, rc, &startSec, MQTT_PACKET_TYPE_CONNECT); + rc = check_response(mqttCtx, rc, &startSec, MQTT_PACKET_TYPE_CONNECT, + DEFAULT_CON_TIMEOUT_MS); } while (rc == MQTT_CODE_CONTINUE); PRINTF("MQTT Socket Connect: %s (%d)", @@ -348,7 +352,8 @@ static int multithread_test_init(MQTTCtx *mqttCtx) startSec = 0; do { rc = MqttClient_Connect(&mqttCtx->client, &mqttCtx->connect); - rc = check_response(mqttCtx, rc, &startSec, MQTT_PACKET_TYPE_CONNECT); + rc = check_response(mqttCtx, rc, &startSec, MQTT_PACKET_TYPE_CONNECT, + DEFAULT_CON_TIMEOUT_MS); } while (rc == MQTT_CODE_CONTINUE); if (rc != MQTT_CODE_SUCCESS) { MqttClient_CancelMessage(&mqttCtx->client, @@ -421,7 +426,8 @@ static void *subscribe_task(void *param) do { rc = MqttClient_Subscribe(&mqttCtx->client, &mqttCtx->subscribe); - rc = check_response(mqttCtx, rc, &startSec, MQTT_PACKET_TYPE_SUBSCRIBE); + rc = check_response(mqttCtx, rc, &startSec, MQTT_PACKET_TYPE_SUBSCRIBE, + mqttCtx->cmd_timeout_ms); } while (rc == MQTT_CODE_CONTINUE); if (rc != MQTT_CODE_SUCCESS) { MqttClient_CancelMessage(&mqttCtx->client, @@ -499,12 +505,14 @@ static void *waitMessage_task(void *param) ){ cmd_timeout_ms = 1000; /* short timeout */ } + /* Try and read packet */ rc = MqttClient_WaitMessage(&mqttCtx->client, cmd_timeout_ms); if (mqttCtx->test_mode && rc == MQTT_CODE_ERROR_TIMEOUT) { rc = 0; } - rc = check_response(mqttCtx, rc, &startSec, MQTT_PACKET_TYPE_ANY); + rc = check_response(mqttCtx, rc, &startSec, MQTT_PACKET_TYPE_ANY, + cmd_timeout_ms); /* check return code */ if (rc == MQTT_CODE_CONTINUE) { @@ -528,8 +536,10 @@ static void *waitMessage_task(void *param) mqttCtx->publish.packet_id = mqtt_get_packetid_threadsafe(); mqttCtx->publish.buffer = mqttCtx->rx_buf; mqttCtx->publish.total_len = (word16)rc; - rc = MqttClient_Publish(&mqttCtx->client, - &mqttCtx->publish); + do { + rc = MqttClient_Publish(&mqttCtx->client, + &mqttCtx->publish); + } while (rc == MQTT_CODE_CONTINUE); PRINTF("MQTT Publish: Topic %s, %s (%d)", mqttCtx->publish.topic_name, MqttClient_ReturnCodeToString(rc), rc); @@ -538,6 +548,7 @@ static void *waitMessage_task(void *param) #endif else if (rc == MQTT_CODE_ERROR_TIMEOUT) { if (mqttCtx->test_mode) { + mqtt_stop_set(); /* timeout in test mode should exit */ PRINTF("MQTT Exiting timeout..."); break; @@ -571,7 +582,6 @@ static void *publish_task(void *param) #endif { int rc; - char buf[7]; MQTTCtx *mqttCtx = (MQTTCtx*)param; MqttPublish publish; word32 startSec = 0; @@ -583,15 +593,13 @@ static void *publish_task(void *param) publish.duplicate = 0; publish.topic_name = mqttCtx->topic_name; publish.packet_id = mqtt_get_packetid_threadsafe(); - XSTRNCPY(buf, TEST_MESSAGE, sizeof(buf)); - buf[4] = '0' + ((publish.packet_id / 10) % 10); - buf[5] = '0' + (publish.packet_id % 10); - publish.buffer = (byte*)buf; - publish.total_len = (word16)XSTRLEN(buf); + publish.buffer = (byte*)mTestMessage; + publish.total_len = sizeof(mTestMessage); do { rc = MqttClient_Publish_WriteOnly(&mqttCtx->client, &publish, NULL); - rc = check_response(mqttCtx, rc, &startSec, MQTT_PACKET_TYPE_PUBLISH); + rc = check_response(mqttCtx, rc, &startSec, MQTT_PACKET_TYPE_PUBLISH, + mqttCtx->cmd_timeout_ms); } while (rc == MQTT_CODE_CONTINUE); if (rc != MQTT_CODE_SUCCESS) { MqttClient_CancelMessage(&mqttCtx->client, (MqttObject*)&publish); @@ -630,9 +638,11 @@ static void *ping_task(void *param) startSec = 0; XMEMSET(&ping, 0, sizeof(ping)); + do { rc = MqttClient_Ping_ex(&mqttCtx->client, &ping); - rc = check_response(mqttCtx, rc, &startSec, MQTT_PACKET_TYPE_PING_REQ); + rc = check_response(mqttCtx, rc, &startSec, MQTT_PACKET_TYPE_PING_REQ, + mqttCtx->cmd_timeout_ms); } while (rc == MQTT_CODE_CONTINUE); if (rc != MQTT_CODE_SUCCESS) { MqttClient_CancelMessage(&mqttCtx->client, (MqttObject*)&ping); @@ -663,7 +673,8 @@ static int unsubscribe_do(MQTTCtx *mqttCtx) /* Unsubscribe Topics */ do { rc = MqttClient_Unsubscribe(&mqttCtx->client, &mqttCtx->unsubscribe); - rc = check_response(mqttCtx, rc, &startSec, MQTT_PACKET_TYPE_UNSUBSCRIBE); + rc = check_response(mqttCtx, rc, &startSec, MQTT_PACKET_TYPE_UNSUBSCRIBE, + mqttCtx->cmd_timeout_ms); } while (rc == MQTT_CODE_CONTINUE); if (rc != MQTT_CODE_SUCCESS) { MqttClient_CancelMessage(&mqttCtx->client, @@ -681,6 +692,12 @@ int multithread_test(MQTTCtx *mqttCtx) int rc = 0, i, threadCount = 0; THREAD_T threadList[NUM_PUB_TASKS+3]; + /* Build test message */ + rc = mqtt_fill_random_hexstr(mTestMessage, (word32)sizeof(mTestMessage)); + if (rc != 0) { + return rc; + } + rc = multithread_test_init(mqttCtx); if (rc == 0) { if (THREAD_CREATE(&threadList[threadCount++], subscribe_task, mqttCtx)) { @@ -705,6 +722,7 @@ int multithread_test(MQTTCtx *mqttCtx) PRINTF("THREAD_CREATE failed: %d", errno); return -1; } + /* Create threads that publish unique messages */ for (i = 0; i < NUM_PUB_TASKS; i++) { if (THREAD_CREATE(&threadList[threadCount++], publish_task, mqttCtx)) { diff --git a/examples/nbclient/nbclient.c b/examples/nbclient/nbclient.c index 487cad893..cf337310d 100644 --- a/examples/nbclient/nbclient.c +++ b/examples/nbclient/nbclient.c @@ -33,6 +33,7 @@ /* Locals */ static int mStopRead = 0; +static int mTestDone = 0; /* Configuration */ @@ -84,7 +85,7 @@ static int mqtt_message_cb(MqttClient *client, MqttMessage *msg, XSTRNCMP(DEFAULT_MESSAGE, (char*)msg->buffer, msg->buffer_len) == 0) { - mStopRead = 1; + mTestDone = 1; } } } @@ -467,13 +468,6 @@ int mqttclient_test(MQTTCtx *mqttCtx) rc = MqttClient_WaitMessage(&mqttCtx->client, mqttCtx->cmd_timeout_ms); - /* check for test mode */ - if (mStopRead) { - rc = MQTT_CODE_SUCCESS; - PRINTF("MQTT Exiting..."); - break; - } - /* Track elapsed time with no activity and trigger timeout */ rc = mqtt_check_timeout(rc, &mqttCtx->start_sec, mqttCtx->cmd_timeout_ms/1000); @@ -482,8 +476,17 @@ int mqttclient_test(MQTTCtx *mqttCtx) if (rc == MQTT_CODE_CONTINUE) { return rc; } + + /* check for test mode */ + if (mStopRead || mTestDone) { + rc = MQTT_CODE_SUCCESS; + mqttCtx->stat = WMQ_DISCONNECT; + PRINTF("MQTT Exiting..."); + break; + } + #ifdef WOLFMQTT_ENABLE_STDIN_CAP - else if (rc == MQTT_CODE_STDIN_WAKE) { + if (rc == MQTT_CODE_STDIN_WAKE) { XMEMSET(mqttCtx->rx_buf, 0, MAX_BUFFER_SIZE); if (XFGETS((char*)mqttCtx->rx_buf, MAX_BUFFER_SIZE - 1, stdin) != NULL) @@ -504,8 +507,9 @@ int mqttclient_test(MQTTCtx *mqttCtx) return rc; } } + else #endif - else if (rc == MQTT_CODE_ERROR_TIMEOUT) { + if (rc == MQTT_CODE_ERROR_TIMEOUT) { /* Need to send keep-alive ping */ PRINTF("Keep-alive timeout, sending ping"); rc = MQTT_CODE_CONTINUE; @@ -715,7 +719,7 @@ int mqttclient_test(MQTTCtx *mqttCtx) #ifdef WOLFSSL_ASYNC_CRYPT wolfSSL_AsyncPoll(mqttCtx.client.tls.ssl, WOLF_POLL_FLAG_CHECK_HW); #endif - } while (rc == MQTT_CODE_CONTINUE); + } while (!mStopRead && rc == MQTT_CODE_CONTINUE); mqtt_free_ctx(&mqttCtx); #else diff --git a/examples/pub-sub/mqtt-pub.c b/examples/pub-sub/mqtt-pub.c index d14029e92..b994cb68c 100644 --- a/examples/pub-sub/mqtt-pub.c +++ b/examples/pub-sub/mqtt-pub.c @@ -374,11 +374,10 @@ int pub_client(MQTTCtx *mqttCtx) #endif /* This loop allows payloads larger than the buffer to be sent by - repeatedly calling publish. - */ + * repeatedly calling publish. */ do { rc = MqttClient_Publish(&mqttCtx->client, &mqttCtx->publish); - } while(rc == MQTT_CODE_PUB_CONTINUE); + } while (rc == MQTT_CODE_PUB_CONTINUE); if ((mqttCtx->pub_file) && (mqttCtx->publish.buffer)) { WOLFMQTT_FREE(mqttCtx->publish.buffer); diff --git a/examples/wiot/wiot.c b/examples/wiot/wiot.c index a68cc69b4..9c4e59da8 100644 --- a/examples/wiot/wiot.c +++ b/examples/wiot/wiot.c @@ -38,6 +38,7 @@ /* Locals */ static int mStopRead = 0; +static int mTestDone = 0; /* Configuration */ #define MAX_BUFFER_SIZE 1024 /* Maximum size for network read/write callbacks */ @@ -101,7 +102,7 @@ static int mqtt_message_cb(MqttClient *client, MqttMessage *msg, if (mqttCtx->test_mode) { if (XSTRLEN(TEST_MESSAGE) == msg->buffer_len && XSTRNCMP(TEST_MESSAGE, (char*)msg->buffer, msg->buffer_len) == 0) { - mStopRead = 1; + mTestDone = 1; } } } @@ -276,16 +277,28 @@ int wiot_test(MQTTCtx *mqttCtx) rc = MqttClient_WaitMessage(&mqttCtx->client, mqttCtx->cmd_timeout_ms); + #ifdef WOLFMQTT_NONBLOCK + /* Track elapsed time with no activity and trigger timeout */ + rc = mqtt_check_timeout(rc, &mqttCtx->start_sec, + mqttCtx->cmd_timeout_ms/1000); + #endif + + /* check return code */ + if (rc == MQTT_CODE_CONTINUE) { + return rc; + } + /* check for test mode */ - if (mStopRead) { + if (mStopRead || mTestDone) { rc = MQTT_CODE_SUCCESS; + mqttCtx->stat = WMQ_DISCONNECT; PRINTF("MQTT Exiting..."); break; } /* check return code */ #ifdef WOLFMQTT_ENABLE_STDIN_CAP - else if (rc == MQTT_CODE_STDIN_WAKE) { + if (rc == MQTT_CODE_STDIN_WAKE) { XMEMSET(mqttCtx->rx_buf, 0, MAX_BUFFER_SIZE); if (XFGETS((char*)mqttCtx->rx_buf, MAX_BUFFER_SIZE - 1, stdin) != NULL) { rc = (int)XSTRLEN((char*)mqttCtx->rx_buf); @@ -306,8 +319,9 @@ int wiot_test(MQTTCtx *mqttCtx) MqttClient_ReturnCodeToString(rc), rc); } } + else #endif - else if (rc == MQTT_CODE_ERROR_TIMEOUT) { + if (rc == MQTT_CODE_ERROR_TIMEOUT) { /* Keep Alive */ PRINTF("Keep-alive timeout, sending ping"); @@ -437,7 +451,7 @@ int main(int argc, char** argv) do { rc = wiot_test(&mqttCtx); - } while (rc == MQTT_CODE_CONTINUE); + } while (!mStopRead && rc == MQTT_CODE_CONTINUE); mqtt_free_ctx(&mqttCtx); diff --git a/scripts/awsiot.test b/scripts/awsiot.test index a153dcf41..ce7e90472 100755 --- a/scripts/awsiot.test +++ b/scripts/awsiot.test @@ -11,7 +11,6 @@ else def_args="-T -C 2000" # Run with TLS and QoS 0-1 - ./examples/aws/awsiot $def_args -t -q 0 $1 RESULT=$? [ $RESULT -ne 0 ] && echo -e "\n\nAWS IoT MQTT Client failed! TLS=On, QoS=0" && exit 1 diff --git a/scripts/azureiothub.test b/scripts/azureiothub.test index 37c94d193..9616946a5 100755 --- a/scripts/azureiothub.test +++ b/scripts/azureiothub.test @@ -8,10 +8,11 @@ if test -n "$WOLFMQTT_NO_EXTERNAL_BROKER_TESTS"; then echo "WOLFMQTT_NO_EXTERNAL_BROKER_TESTS set, won't run" else + # Use short timeout here, since we can't get a publish response to complete test + # So use the timeout and ping response to complete test def_args="-T -C 2000" # Run with TLS and QoS 0-1 - ./examples/azure/azureiothub $def_args -t -q 0 $1 RESULT=$? [ $RESULT -ne 0 ] && echo -e "\n\nAzureIotHub MQTT Client failed! TLS=On, QoS=0" && exit 1 diff --git a/scripts/firmware.test b/scripts/firmware.test index 6f2ce615f..52926617b 100755 --- a/scripts/firmware.test +++ b/scripts/firmware.test @@ -28,7 +28,7 @@ do_cleanup() { [ ! -x ./examples/firmware/fwpush ] && echo -e "\n\nMQTT Example fwpush doesn't exist" && exit 1 [ ! -x ./examples/firmware/fwclient ] && echo -e "\n\nMQTT Example fwclient doesn't exist" && exit 1 -def_args="-t -T -C 2000" +def_args="-t -T -C 5000 -n wolfMQTT/example/firmware_$((RANDOM))" filein=./examples/publish.dat fileout=./examples/publish.dat.trs @@ -54,16 +54,19 @@ fi grep -F -e 'ENABLE_MQTT_TLS' ./wolfmqtt/options.h ENABLE_MQTT_TLS=$? +# Start firmware client +./examples/firmware/fwclient $def_args -f $fileout $1 & +client_result=$? +[ $client_result -ne 0 ] && echo -e "\n\nMQTT Example fwclient failed!" && do_cleanup "-1" + +# give some time for the client to connect and wait (it starts a new session) +sleep 0.5 + # Start firmware push ./examples/firmware/fwpush $def_args -r -f $filein $1 server_result=$? [ $server_result -ne 0 ] && echo -e "\n\nMQTT Example fwpush failed!" && do_cleanup "-1" -# Start firmware client -./examples/firmware/fwclient $def_args -f $fileout $1 -client_result=$? -[ $client_result -ne 0 ] && echo -e "\n\nMQTT Example fwclient failed!" && do_cleanup "-1" - if [ $ENABLE_MQTT_TLS -ne 1 ]; then # Compare files echo "Comparing files" @@ -74,7 +77,7 @@ fi # End broker do_cleanup "0" - + echo -e "\n\nFirmware Example MQTT Client Tests Passed" exit 0 diff --git a/src/mqtt_socket.c b/src/mqtt_socket.c index 25bb0950a..f8888d254 100644 --- a/src/mqtt_socket.c +++ b/src/mqtt_socket.c @@ -123,6 +123,15 @@ static int MqttSocket_WriteDo(MqttClient *client, const byte* buf, int buf_len, { int rc; +#if defined(WOLFMQTT_NONBLOCK) && defined(WOLFMQTT_TEST_NONBLOCK) + static int testNbWriteAlt = 0; + if (!testNbWriteAlt) { + testNbWriteAlt = 1; + return MQTT_CODE_CONTINUE; + } + testNbWriteAlt = 0; +#endif + #ifdef ENABLE_MQTT_TLS if (MqttClient_Flags(client,0,0) & MQTT_CLIENT_FLAG_IS_TLS) { client->tls.timeout_ms = timeout_ms; @@ -133,8 +142,11 @@ static int MqttSocket_WriteDo(MqttClient *client, const byte* buf, int buf_len, int error = wolfSSL_get_error(client->tls.ssl, 0); #endif #ifdef WOLFMQTT_DEBUG_SOCKET - if (error != WOLFSSL_ERROR_WANT_WRITE && - error != WC_PENDING_E) { + if (error != WOLFSSL_ERROR_WANT_WRITE + #ifdef WOLFSSL_ASYNC_CRYPT + && error != WC_PENDING_E + #endif + ) { PRINTF("MqttSocket_WriteDo: SSL Error=%d (rc %d, sockrc %d)", error, rc, client->tls.sockRcWrite); } @@ -152,6 +164,19 @@ static int MqttSocket_WriteDo(MqttClient *client, const byte* buf, int buf_len, else #endif /* ENABLE_MQTT_TLS */ { + #if defined(WOLFMQTT_NONBLOCK) && defined(WOLFMQTT_TEST_NONBLOCK) + static int testSmallerWrite = 0; + if (!testSmallerWrite) { + if (buf_len > 100) { + buf_len /= 2; + } + testSmallerWrite = 1; + } + else { + testSmallerWrite = 0; + } + #endif + rc = client->net->write(client->net->context, buf, buf_len, timeout_ms); } @@ -221,12 +246,12 @@ static int MqttSocket_ReadDo(MqttClient *client, byte* buf, int buf_len, int rc; #if defined(WOLFMQTT_NONBLOCK) && defined(WOLFMQTT_TEST_NONBLOCK) - static int testNbAlt = 0; - if (!testNbAlt) { - testNbAlt = 1; + static int testNbReadAlt = 0; + if (!testNbReadAlt) { + testNbReadAlt = 1; return MQTT_CODE_CONTINUE; } - testNbAlt = 0; + testNbReadAlt = 0; #endif #ifdef ENABLE_MQTT_TLS @@ -237,8 +262,11 @@ static int MqttSocket_ReadDo(MqttClient *client, byte* buf, int buf_len, if (rc < 0) { int error = wolfSSL_get_error(client->tls.ssl, 0); #ifdef WOLFMQTT_DEBUG_SOCKET - if (error != WOLFSSL_ERROR_WANT_READ && - error != WC_PENDING_E) { + if (error != WOLFSSL_ERROR_WANT_READ + #ifdef WOLFSSL_ASYNC_CRYPT + && error != WC_PENDING_E + #endif + ) { PRINTF("MqttSocket_ReadDo: SSL Error=%d (rc %d, sockrc %d)", error, rc, client->tls.sockRcRead); } @@ -488,9 +516,12 @@ int MqttSocket_Connect(MqttClient *client, const char* host, word16 port, int errnum = 0; if (client->tls.ssl) { errnum = wolfSSL_get_error(client->tls.ssl, 0); - if (errnum == WOLFSSL_ERROR_WANT_READ || - errnum == WOLFSSL_ERROR_WANT_WRITE || - errnum == WC_PENDING_E) { + if ( errnum == WOLFSSL_ERROR_WANT_READ + || errnum == WOLFSSL_ERROR_WANT_WRITE + #ifdef WOLFSSL_ASYNC_CRYPT + || errnum == WC_PENDING_E + #endif + ) { return MQTT_CODE_CONTINUE; } #ifdef WOLFMQTT_DEBUG_SOCKET From 44ca39c645e8996a1305e6b592dd769f38770262 Mon Sep 17 00:00:00 2001 From: Eric Blankenhorn Date: Fri, 27 Oct 2023 13:51:59 -0500 Subject: [PATCH 10/62] Refactor MQTT-SN code --- examples/include.am | 25 +- src/include.am | 5 + src/mqtt_client.c | 1851 +----------------------------------- src/mqtt_packet.c | 1550 +----------------------------- src/mqtt_sn_client.c | 1872 +++++++++++++++++++++++++++++++++++++ src/mqtt_sn_packet.c | 1576 +++++++++++++++++++++++++++++++ wolfmqtt.vcxproj | 4 + wolfmqtt/include.am | 5 + wolfmqtt/mqtt_client.h | 205 +--- wolfmqtt/mqtt_packet.h | 457 --------- wolfmqtt/mqtt_sn_client.h | 236 +++++ wolfmqtt/mqtt_sn_packet.h | 497 ++++++++++ 12 files changed, 4237 insertions(+), 4046 deletions(-) create mode 100644 src/mqtt_sn_client.c create mode 100644 src/mqtt_sn_packet.c create mode 100644 wolfmqtt/mqtt_sn_client.h create mode 100644 wolfmqtt/mqtt_sn_packet.h diff --git a/examples/include.am b/examples/include.am index c3963187f..dd0548eae 100644 --- a/examples/include.am +++ b/examples/include.am @@ -11,11 +11,13 @@ noinst_PROGRAMS += examples/mqttclient/mqttclient \ examples/wiot/wiot \ examples/nbclient/nbclient \ examples/multithread/multithread \ - examples/sn-client/sn-client \ - examples/sn-client/sn-client_qos-1 \ - examples/sn-client/sn-multithread \ examples/pub-sub/mqtt-pub \ examples/pub-sub/mqtt-sub +if BUILD_SN +noinst_PROGRAMS += examples/sn-client/sn-client \ + examples/sn-client/sn-client_qos-1 \ + examples/sn-client/sn-multithread +endif noinst_HEADERS += examples/mqttclient/mqttclient.h \ examples/mqttsimple/mqttsimple.h \ @@ -30,8 +32,10 @@ noinst_HEADERS += examples/mqttclient/mqttclient.h \ examples/mqttport.h \ examples/nbclient/nbclient.h \ examples/multithread/multithread.h \ - examples/sn-client/sn-client.h \ examples/pub-sub/mqtt-pub-sub.h +if BUILD_SN +noinst_HEADERS += examples/sn-client/sn-client.h +endif # MQTT Client Example examples_mqttclient_mqttclient_SOURCES = examples/mqttclient/mqttclient.c \ @@ -108,7 +112,7 @@ examples_wiot_wiot_LDADD = src/libwolfmqtt.la examples_wiot_wiot_DEPENDENCIES = src/libwolfmqtt.la examples_wiot_wiot_CPPFLAGS = -I$(top_srcdir)/examples $(AM_CPPFLAGS) - +if BUILD_SN # MQTT-SN Examples examples_sn_client_sn_client_SOURCES = examples/sn-client/sn-client.c \ examples/mqttnet.c \ @@ -130,6 +134,7 @@ examples_sn_client_sn_multithread_SOURCES = examples/sn-client/sn-multithr examples_sn_client_sn_multithread_LDADD = src/libwolfmqtt.la examples_sn_client_sn_multithread_DEPENDENCIES = src/libwolfmqtt.la examples_sn_client_sn_multithread_CPPFLAGS = -I$(top_srcdir)/examples $(AM_CPPFLAGS) +endif # MQTT pub and sub clients examples_pub_sub_mqtt_pub_SOURCES = examples/pub-sub/mqtt-pub.c \ @@ -160,9 +165,11 @@ dist_example_DATA+= examples/mqttnet.c \ examples/wiot/wiot.c dist_example_DATA+= examples/nbclient/nbclient.c dist_example_DATA+= examples/multithread/multithread.c +if BUILD_SN dist_example_DATA+= examples/sn-client/sn-client.c dist_example_DATA+= examples/sn-client/sn-client_qos-1.c dist_example_DATA+= examples/sn-client/sn-multithread.c +endif dist_example_DATA+= examples/pub-sub/mqtt-pub.c dist_example_DATA+= examples/pub-sub/mqtt-sub.c @@ -174,11 +181,13 @@ DISTCLEANFILES+= examples/mqttclient/.libs/mqttclient \ examples/wiot/.libs/wiot \ examples/nbclient/.libs/nbclient \ examples/multithread/.libs/multithread \ - examples/sn-client/.libs/sn-client \ - examples/sn-client/.libs/sn-client_qos-1 \ - examples/sn-client/.libs/sn-multithread \ examples/pub-sub/mqtt-pub \ examples/pub-sub/mqtt-sub +if BUILD_SN +DISTCLEANFILES+= examples/sn-client/.libs/sn-client \ + examples/sn-client/.libs/sn-client_qos-1 \ + examples/sn-client/.libs/sn-multithread +endif EXTRA_DIST+= examples/mqttuart.c \ examples/publish.dat \ diff --git a/src/include.am b/src/include.am index c3b7bab37..08f9cd162 100644 --- a/src/include.am +++ b/src/include.am @@ -8,6 +8,11 @@ src_libwolfmqtt_la_SOURCES = src/mqtt_client.c \ src/mqtt_packet.c \ src/mqtt_socket.c +if BUILD_SN +src_libwolfmqtt_la_SOURCES += src/mqtt_sn_client.c \ + src/mqtt_sn_packet.c +endif + src_libwolfmqtt_la_CFLAGS = -DBUILDING_WOLFMQTT $(AM_CFLAGS) src_libwolfmqtt_la_CPPFLAGS = -DBUILDING_WOLFMQTT $(AM_CPPFLAGS) src_libwolfmqtt_la_LDFLAGS = ${AM_LDFLAGS} -no-undefined -version-info ${WOLFMQTT_LIBRARY_VERSION} diff --git a/src/mqtt_client.c b/src/mqtt_client.c index fe3c05f76..de2bc6c57 100644 --- a/src/mqtt_client.c +++ b/src/mqtt_client.c @@ -160,7 +160,7 @@ static int MqttClient_Publish_ReadPayload(MqttClient* client, #endif /* These RespList functions assume caller has locked client->lockClient mutex */ -static int MqttClient_RespList_Add(MqttClient *client, +int MqttClient_RespList_Add(MqttClient *client, MqttPacketType packet_type, word16 packet_id, MqttPendResp *newResp, void *packet_obj) { @@ -208,7 +208,7 @@ static int MqttClient_RespList_Add(MqttClient *client, return 0; } -static void MqttClient_RespList_Remove(MqttClient *client, MqttPendResp *rmResp) +void MqttClient_RespList_Remove(MqttClient *client, MqttPendResp *rmResp) { MqttPendResp *tmpResp; @@ -252,7 +252,7 @@ static void MqttClient_RespList_Remove(MqttClient *client, MqttPendResp *rmResp) #endif } -static int MqttClient_RespList_Find(MqttClient *client, +int MqttClient_RespList_Find(MqttClient *client, MqttPacketType packet_type, word16 packet_id, MqttPendResp **retResp) { int rc = 0; @@ -2840,1848 +2840,3 @@ word32 MqttClient_Flags(MqttClient *client, word32 mask, word32 flags) } return ret; } - -#ifdef WOLFMQTT_SN - -/* Private functions */ -static int SN_Client_HandlePacket(MqttClient* client, SN_MsgType packet_type, - void* packet_obj, int timeout) -{ - int rc = MQTT_CODE_SUCCESS; - word16 packet_id = 0; - - (void)timeout; - - switch ((int)packet_type) - { - case SN_MSG_TYPE_GWINFO: - { - SN_GwInfo info, *p_info = &info; - if (packet_obj) { - p_info = (SN_GwInfo*)packet_obj; - } - else { - XMEMSET(p_info, 0, sizeof(SN_GwInfo)); - } - - rc = SN_Decode_GWInfo(client->rx_buf, client->packet.buf_len, - p_info); - if (rc <= 0) { - return rc; - } - break; - } - case SN_MSG_TYPE_CONNACK: - { - /* Decode connect ack */ - SN_ConnectAck connect_ack, *p_connect_ack = &connect_ack; - if (packet_obj) { - p_connect_ack = (SN_ConnectAck*)packet_obj; - } - else { - XMEMSET(p_connect_ack, 0, sizeof(SN_ConnectAck)); - } - p_connect_ack->return_code = - client->rx_buf[client->packet.buf_len-1]; - - break; - } - case SN_MSG_TYPE_WILLTOPICREQ: - { - rc = SN_Decode_WillTopicReq(client->rx_buf, client->packet.buf_len); - break; - } - case SN_MSG_TYPE_WILLMSGREQ: - { - rc = SN_Decode_WillMsgReq(client->rx_buf, client->packet.buf_len); - break; - } - case SN_MSG_TYPE_REGISTER: - { - /* Decode register */ - SN_Register reg_s; - - XMEMSET(®_s, 0, sizeof(SN_Register)); - - rc = SN_Decode_Register(client->rx_buf, client->packet.buf_len, - ®_s); - - if (rc > 0) { - /* Initialize the regack */ - reg_s.regack.packet_id = reg_s.packet_id; - reg_s.regack.topicId = reg_s.topicId; - reg_s.regack.return_code = SN_RC_NOTSUPPORTED; - - /* Call the register callback to allow app to - handle new topic ID assignment. */ - if (client->reg_cb != NULL) { - rc = client->reg_cb(reg_s.topicId, - reg_s.topicName, client->reg_ctx); - /* Set the regack return code */ - reg_s.regack.return_code = (rc >= 0) ? SN_RC_ACCEPTED : - SN_RC_INVTOPICNAME; - } - - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } - #endif - - /* Encode the register acknowledgment */ - rc = SN_Encode_RegAck(client->tx_buf, client->tx_buf_len, - ®_s.regack); - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d), ID %d", - rc, SN_Packet_TypeDesc(SN_MSG_TYPE_REGACK), - SN_MSG_TYPE_REGACK, reg_s.packet_id); - #endif - if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - client->write.len = rc; - - /* Send regack packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc != client->write.len) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - } - - break; - } - case SN_MSG_TYPE_REGACK: - { - /* Decode register ack */ - SN_RegAck regack_s, *p_regack = ®ack_s; - if (packet_obj) { - p_regack = (SN_RegAck*)packet_obj; - } - else { - XMEMSET(p_regack, 0, sizeof(SN_RegAck)); - } - - rc = SN_Decode_RegAck(client->rx_buf, client->packet.buf_len, - p_regack); - if (rc > 0) { - packet_id = p_regack->packet_id; - } - - break; - } - case SN_MSG_TYPE_PUBLISH: - { - SN_Publish pub, *p_pub = &pub; - if (packet_obj) { - p_pub = (SN_Publish*)packet_obj; - } - else { - XMEMSET(p_pub, 0, sizeof(SN_Publish)); - } - - /* Decode publish message */ - rc = SN_Decode_Publish(client->rx_buf, client->packet.buf_len, - p_pub); - if (rc <= 0) { - return rc; - } - - /* Issue callback for new message */ - if (client->msg_cb) { - /* if using the temp publish message buffer, - then populate message context with client context */ - if (&client->msgSN.publish == p_pub) - p_pub->ctx = client->ctx; - rc = client->msg_cb(client, (MqttMessage*)p_pub, 1, 1); - if (rc != MQTT_CODE_SUCCESS) { - return rc; - }; - } - - /* Handle Qos */ - if (p_pub->qos > MQTT_QOS_0) { - SN_MsgType type; - - packet_id = p_pub->packet_id; - - /* Determine packet type to write */ - type = (p_pub->qos == MQTT_QOS_1) ? - SN_MSG_TYPE_PUBACK : - SN_MSG_TYPE_PUBREC; - p_pub->resp.packet_id = packet_id; - - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } - #endif - - /* Encode publish response */ - rc = SN_Encode_PublishResp(client->tx_buf, - client->tx_buf_len, type, &p_pub->resp); - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d), ID %d," - " QoS %d", - rc, SN_Packet_TypeDesc(type), type, packet_id, - p_pub->qos); - #endif - if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - client->write.len = rc; - - /* Send packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - } - break; - } - case SN_MSG_TYPE_PUBACK: - case SN_MSG_TYPE_PUBCOMP: - case SN_MSG_TYPE_PUBREC: - case SN_MSG_TYPE_PUBREL: - { - SN_PublishResp publish_resp, *p_publish_resp = &publish_resp; - if (packet_obj) { - p_publish_resp = (SN_PublishResp*)packet_obj; - } - else { - XMEMSET(p_publish_resp, 0, sizeof(SN_PublishResp)); - } - - /* Decode publish response message */ - rc = SN_Decode_PublishResp(client->rx_buf, client->packet.buf_len, - packet_type, p_publish_resp); - if (rc <= 0) { - return rc; - } - packet_id = p_publish_resp->packet_id; - - /* If Qos then send response */ - if (packet_type == SN_MSG_TYPE_PUBREC || - packet_type == SN_MSG_TYPE_PUBREL) { - - byte resp_type = (packet_type == SN_MSG_TYPE_PUBREC) ? - SN_MSG_TYPE_PUBREL : SN_MSG_TYPE_PUBCOMP; - - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } - #endif - - /* Encode publish response */ - p_publish_resp->packet_id = packet_id; - rc = SN_Encode_PublishResp(client->tx_buf, - client->tx_buf_len, resp_type, p_publish_resp); - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d), ID %d", - rc, MqttPacket_TypeDesc(resp_type), resp_type, packet_id); - #endif - if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - client->write.len = rc; - - /* Send packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - } - break; - } - case SN_MSG_TYPE_SUBACK: - { - /* Decode subscribe ack */ - SN_SubAck subscribe_ack, *p_subscribe_ack = &subscribe_ack; - if (packet_obj) { - p_subscribe_ack = (SN_SubAck*)packet_obj; - } - else { - XMEMSET(p_subscribe_ack, 0, sizeof(SN_SubAck)); - } - - rc = SN_Decode_SubscribeAck(client->rx_buf, client->packet.buf_len, - p_subscribe_ack); - if (rc <= 0) { - return rc; - } - packet_id = p_subscribe_ack->packet_id; - - break; - } - case SN_MSG_TYPE_UNSUBACK: - { - /* Decode unsubscribe ack */ - SN_UnsubscribeAck unsubscribe_ack, - *p_unsubscribe_ack = &unsubscribe_ack; - if (packet_obj) { - p_unsubscribe_ack = (SN_UnsubscribeAck*)packet_obj; - } - else { - XMEMSET(p_unsubscribe_ack, 0, sizeof(SN_UnsubscribeAck)); - } - rc = SN_Decode_UnsubscribeAck(client->rx_buf, - client->packet.buf_len, p_unsubscribe_ack); - if (rc <= 0) { - return rc; - } - packet_id = p_unsubscribe_ack->packet_id; - - break; - } - case SN_MSG_TYPE_PING_RESP: - { - /* Decode ping */ - rc = SN_Decode_Ping(client->rx_buf, client->packet.buf_len); - break; - } - case SN_MSG_TYPE_PING_REQ: - { - /* Decode ping */ - rc = SN_Decode_Ping(client->rx_buf, client->packet.buf_len); - if (rc <= 0) { return rc; } - - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } - #endif - - /* Encode the ping packet as a response */ - rc = SN_Encode_Ping(client->tx_buf, client->tx_buf_len, NULL, - SN_MSG_TYPE_PING_RESP); - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d)", - rc, SN_Packet_TypeDesc(SN_MSG_TYPE_PING_RESP), - SN_MSG_TYPE_PING_RESP); - #endif - if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - client->write.len = rc; - - /* Send ping resp packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc != client->write.len) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - - break; - } - case SN_MSG_TYPE_WILLTOPICRESP: - { - /* Decode Will Topic Response */ - SN_WillTopicResp resp_s, *resp = &resp_s; - if (packet_obj) { - resp = (SN_WillTopicResp*)packet_obj; - } - else { - XMEMSET(resp, 0, sizeof(SN_WillTopicResp)); - } - rc = SN_Decode_WillTopicResponse(client->rx_buf, - client->packet.buf_len, &resp->return_code); - break; - } - case SN_MSG_TYPE_WILLMSGRESP: - { - /* Decode Will Message Response */ - SN_WillMsgResp resp_s, *resp = &resp_s; - if (packet_obj) { - resp = (SN_WillMsgResp*)packet_obj; - } - else { - XMEMSET(resp, 0, sizeof(SN_WillMsgResp)); - } - rc = SN_Decode_WillMsgResponse(client->rx_buf, - client->packet.buf_len, &resp->return_code); - break; - } - case SN_MSG_TYPE_DISCONNECT: - { - SN_Disconnect disc_s, *disc = &disc_s; - if (packet_obj) { - disc = (SN_Disconnect*)packet_obj; - } - else { - XMEMSET(disc, 0, sizeof(SN_Disconnect)); - } - /* Decode Disconnect */ - rc = SN_Decode_Disconnect(client->rx_buf, client->packet.buf_len); - -#ifdef WOLFMQTT_DISCONNECT_CB - /* Call disconnect callback to allow handling broker disconnect */ - if ((client->disconnect_cb != NULL) && (disc->sleepTmr == 0)) { - client->disconnect_cb(client, rc, client->disconnect_ctx); - } -#endif - break; - } - - default: - { - /* Other types are server side only, ignore */ - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("SN_Client_HandlePacket: Invalid client packet type %u!", - packet_type); - #endif - break; - } - } /* switch (packet_type) */ - - (void)packet_id; - - return rc; -} - -static int SN_Client_WaitType(MqttClient *client, void* packet_obj, - byte wait_type, word16 wait_packet_id, int timeout_ms) -{ - int rc; - word16 packet_id; - SN_MsgType packet_type; -#ifdef WOLFMQTT_MULTITHREAD - MqttPendResp *pendResp; -#endif - MqttMsgStat* mms_stat; - int waitMatchFound; - void* use_packet_obj = NULL; - - if (client == NULL || packet_obj == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* all packet type structures must have MqttMsgStat at top */ - mms_stat = (MqttMsgStat*)packet_obj; - -wait_again: - - /* initialize variables */ - packet_id = 0; - packet_type = SN_MSG_TYPE_RESERVED; -#ifdef WOLFMQTT_MULTITHREAD - pendResp = NULL; -#endif - waitMatchFound = 0; - -#ifdef WOLFMQTT_DEBUG_CLIENT - #ifdef WOLFMQTT_NONBLOCK - if (client->lastRc != MQTT_CODE_CONTINUE) - #endif - { - PRINTF("SN_Client_WaitType: Type %s (%d), ID %d", - SN_Packet_TypeDesc((SN_MsgType)wait_type), - wait_type, wait_packet_id); - } -#endif - - switch (mms_stat->read) - { - case MQTT_MSG_BEGIN: - { - #ifdef WOLFMQTT_MULTITHREAD - /* Lock recv socket mutex */ - rc = wm_SemLock(&client->lockRecv); - if (rc != 0) { - PRINTF("SN_Client_WaitType recv lock error"); - return rc; - } - mms_stat->isReadLocked = 1; - MQTT_TRACE_MSG("SN lockRecv"); - #endif - - /* reset the packet state used by SN_Packet_Read */ - client->packet.stat = MQTT_PK_BEGIN; - } - FALL_THROUGH; - - case MQTT_MSG_WAIT: - { - #ifdef WOLFMQTT_MULTITHREAD - /* Check to see if packet type and id have already completed */ - pendResp = NULL; - rc = wm_SemLock(&client->lockClient); - if (rc == 0) { - if (MqttClient_RespList_Find(client, (MqttPacketType)wait_type, - wait_packet_id, &pendResp)) { - if (pendResp->packetDone) { - /* pending response is already done, so return */ - rc = pendResp->packet_ret; - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("PendResp already Done %p: Rc %d", pendResp, rc); - #endif - MqttClient_RespList_Remove(client, pendResp); - wm_SemUnlock(&client->lockClient); - MQTT_TRACE_MSG("SN unlockRecv"); - wm_SemUnlock(&client->lockRecv); - return rc; - } - } - wm_SemUnlock(&client->lockClient); - } - else { - break; /* error */ - } - #endif /* WOLFMQTT_MULTITHREAD */ - - mms_stat->read = MQTT_MSG_WAIT; - - /* Wait for packet */ - rc = SN_Packet_Read(client, client->rx_buf, client->rx_buf_len, - timeout_ms); - if (rc <= 0) { - break; - } - - client->packet.buf_len = rc; - - /* Decode header */ - rc = SN_Decode_Header(client->rx_buf, client->packet.buf_len, - &packet_type, &packet_id); - if (rc < 0) { - break; - } - - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("Read Packet: Len %d, Type %d, ID %d", - client->packet.buf_len, packet_type, packet_id); - #endif - - mms_stat->read = MQTT_MSG_HEADER; - } - FALL_THROUGH; - - case MQTT_MSG_HEADER: - case MQTT_MSG_PAYLOAD: - case MQTT_MSG_PAYLOAD2: - { - SN_MsgType use_packet_type; - - /* Determine if we received data for this request */ - if ((wait_type == SN_MSG_TYPE_ANY || wait_type == packet_type) && - (wait_packet_id == 0 || wait_packet_id == packet_id)) - { - use_packet_obj = packet_obj; - waitMatchFound = 1; - } - else { - /* use generic packet object */ - use_packet_obj = &client->msgSN; - } - use_packet_type = packet_type; - - #ifdef WOLFMQTT_MULTITHREAD - /* Check to see if we have a pending response for this packet */ - pendResp = NULL; - rc = wm_SemLock(&client->lockClient); - if (rc == 0) { - if (MqttClient_RespList_Find(client, - (MqttPacketType)packet_type, packet_id, &pendResp)) { - /* we found packet match this incoming read packet */ - pendResp->packetProcessing = 1; - if (pendResp->packet_obj != packet_obj) { - use_packet_obj = pendResp->packet_obj; - use_packet_type = (SN_MsgType)pendResp->packet_type; - /* req from another thread... not a match */ - waitMatchFound = 0; - } - } - wm_SemUnlock(&client->lockClient); - } - else { - break; /* error */ - } - #endif /* WOLFMQTT_MULTITHREAD */ - - rc = SN_Client_HandlePacket(client, use_packet_type, use_packet_obj, - timeout_ms); - - #ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE) { - break; - } - #endif - - /* handle success case */ - if (rc >= 0) { - rc = MQTT_CODE_SUCCESS; - } - - #ifdef WOLFMQTT_MULTITHREAD - if (pendResp) { - /* Mark pending response entry done */ - if (wm_SemLock(&client->lockClient) == 0) { - pendResp->packetDone = 1; - pendResp->packet_ret = rc; - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("PendResp Marked Done %p", pendResp); - #endif - pendResp = NULL; - wm_SemUnlock(&client->lockClient); - } - } - #endif /* WOLFMQTT_MULTITHREAD */ - break; - } - - case MQTT_MSG_ACK: /* ack handled in SN_Client_HandlePacket */ - case MQTT_MSG_AUTH: - default: - { - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("SN_Client_WaitType: Invalid state %d!", mms_stat->read); - #endif - rc = MQTT_TRACE_ERROR(MQTT_CODE_ERROR_STAT); - break; - } - } /* switch (msg->stat) */ - -#ifdef WOLFMQTT_DEBUG_CLIENT - if (rc != MQTT_CODE_CONTINUE) { - PRINTF("SN_Client_WaitType: rc %d, state %d", rc, mms_stat->read); - } -#endif - - if (mms_stat->read == MQTT_MSG_WAIT || rc != MQTT_CODE_CONTINUE) { - /* reset state */ - mms_stat->read = MQTT_MSG_BEGIN; - - #ifdef WOLFMQTT_MULTITHREAD - if (mms_stat->isReadLocked) { - mms_stat->isReadLocked = 0; - wm_SemUnlock(&client->lockRecv); - } - #endif - } - -#ifdef WOLFMQTT_NONBLOCK - #ifdef WOLFMQTT_DEBUG_CLIENT - client->lastRc = rc; - #endif - if (rc == MQTT_CODE_CONTINUE) { - return rc; - } -#endif - - /* Clear shared union for next call */ - if ((MqttObject*)use_packet_obj == &client->msg) { - /* reset the members, but not the stat */ - XMEMSET(((byte*)&client->msg.stat) + sizeof(client->msg.stat), 0, - sizeof(client->msg)-sizeof(client->msg.stat)); - } - - if (rc < 0) { - #ifdef WOLFMQTT_DEBUG_CLIENT - if (rc != MQTT_CODE_CONTINUE) { - PRINTF("SN_Client_WaitType: Failure: %s (%d)", - MqttClient_ReturnCodeToString(rc), rc); - } - #endif - return rc; - } - - if (!waitMatchFound) { - /* if we get here, then the we are still waiting for a packet */ - goto wait_again; - } - - return rc; -} - -/* Public Functions */ - -int SN_Client_SetRegisterCallback(MqttClient *client, - SN_ClientRegisterCb regCb, - void* ctx) -{ - int rc = MQTT_CODE_SUCCESS; - - if (client == NULL) - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - -#ifdef WOLFMQTT_MULTITHREAD - rc = wm_SemLock(&client->lockClient); - if (rc == 0) { -#endif - - client->reg_cb = regCb; - client->reg_ctx = ctx; - -#ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockClient); - } -#endif - - return rc; -} - -int SN_Client_SearchGW(MqttClient *client, SN_SearchGw *search) -{ - int rc; - - /* Validate required arguments */ - if (client == NULL || search == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - if (search->stat.write == MQTT_MSG_BEGIN) { - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } - #endif - - /* Encode the search packet */ - rc = SN_Encode_SearchGW(client->tx_buf, client->tx_buf_len, - search->radius); - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d)", - rc, SN_Packet_TypeDesc(SN_MSG_TYPE_SEARCHGW), - SN_MSG_TYPE_SEARCHGW); - #endif - if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - client->write.len = rc; - - #ifdef WOLFMQTT_MULTITHREAD - rc = wm_SemLock(&client->lockClient); - if (rc == 0) { - /* inform other threads of expected response */ - rc = MqttClient_RespList_Add(client, - (MqttPacketType)SN_MSG_TYPE_GWINFO, 0, - &search->pendResp, &search->gwInfo); - wm_SemUnlock(&client->lockClient); - } - if (rc != 0) { - wm_SemUnlock(&client->lockSend); - return rc; /* Error locking client */ - } - #endif - - /* Send search for gateway packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc != client->write.len) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &search->pendResp); - wm_SemUnlock(&client->lockClient); - } - #endif - return rc; - } - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - - search->stat.write = MQTT_MSG_WAIT; - } - - /* Wait for gateway info packet */ - rc = SN_Client_WaitType(client, &search->gwInfo, SN_MSG_TYPE_GWINFO, 0, - client->cmd_timeout_ms); -#ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE) - return rc; -#endif -#ifdef WOLFMQTT_MULTITHREAD - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &search->pendResp); - wm_SemUnlock(&client->lockClient); - } -#endif - - /* reset state */ - search->stat.write = MQTT_MSG_BEGIN; - - return rc; -} - -static int SN_WillTopic(MqttClient *client, SN_Will *will) -{ - int rc; - - /* Validate required arguments */ - if (client == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - -#ifdef WOLFMQTT_MULTITHREAD - rc = wm_SemLock(&client->lockClient); - if (rc == 0) { - /* inform other threads of expected response */ - rc = MqttClient_RespList_Add(client, - (MqttPacketType)SN_MSG_TYPE_WILLTOPICREQ, 0, - &will->pendResp, &will->resp.topicResp); - wm_SemUnlock(&client->lockClient); - } - if (rc != 0) { - return rc; /* Error locking client */ - } -#endif - - /* Wait for Will Topic Request packet */ - rc = SN_Client_WaitType(client, will, - SN_MSG_TYPE_WILLTOPICREQ, 0, client->cmd_timeout_ms); -#ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE) - return rc; -#endif - -#ifdef WOLFMQTT_MULTITHREAD - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &will->pendResp); - wm_SemUnlock(&client->lockClient); - } -#endif - if (rc == 0) { - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } - #endif - - /* Encode Will Topic */ - rc = SN_Encode_WillTopic(client->tx_buf, client->tx_buf_len, - will); - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("EncodePacket: Len %d, Type %s (%d)", - rc, SN_Packet_TypeDesc(SN_MSG_TYPE_WILLTOPIC), - SN_MSG_TYPE_WILLTOPIC); - #endif - if (rc > 0) { - /* Send Will Topic packet */ - client->write.len = rc; - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc == client->write.len) { - rc = 0; - } - } - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - } - - return rc; -} - -static int SN_WillMessage(MqttClient *client, SN_Will *will) -{ - int rc; - - /* Validate required arguments */ - if (client == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - -#ifdef WOLFMQTT_MULTITHREAD - rc = wm_SemLock(&client->lockClient); - if (rc == 0) { - /* inform other threads of expected response */ - rc = MqttClient_RespList_Add(client, - (MqttPacketType)SN_MSG_TYPE_WILLMSGREQ, 0, - &will->pendResp, &will->resp.msgResp); - wm_SemUnlock(&client->lockClient); - } - if (rc != 0) { - return rc; /* Error locking client */ - } -#endif - - /* Wait for Will Message Request */ - rc = SN_Client_WaitType(client, &will->resp.msgResp, - SN_MSG_TYPE_WILLMSGREQ, 0, client->cmd_timeout_ms); - -#ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE) - return rc; -#endif - -#ifdef WOLFMQTT_MULTITHREAD - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &will->pendResp); - wm_SemUnlock(&client->lockClient); - } -#endif - - if (rc == 0) { - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } - #endif - /* Encode Will Message */ - rc = SN_Encode_WillMsg(client->tx_buf, - client->tx_buf_len, will); - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("EncodePacket: Len %d, Type %s (%d)", - rc, SN_Packet_TypeDesc(SN_MSG_TYPE_WILLMSG), - SN_MSG_TYPE_WILLMSG); - #endif - if (rc > 0) { - /* Send Will Topic packet */ - client->write.len = rc; - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc == client->write.len) { - rc = 0; - } - } - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - } - - return rc; -} - -int SN_Client_Connect(MqttClient *client, SN_Connect *mc_connect) -{ - int rc = 0; - static byte will_done; - - /* Validate required arguments */ - if ((client == NULL) || (mc_connect == NULL)) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - if (mc_connect->stat.write == MQTT_MSG_BEGIN) { - - will_done = 0; - - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } - #endif - - /* Encode the connect packet */ - rc = SN_Encode_Connect(client->tx_buf, client->tx_buf_len, mc_connect); -#ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d), ID %d, QoS %d", - rc, SN_Packet_TypeDesc(SN_MSG_TYPE_CONNECT), - SN_MSG_TYPE_CONNECT, 0, 0); -#endif - if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - client->write.len = rc; - - #ifdef WOLFMQTT_MULTITHREAD - rc = wm_SemLock(&client->lockClient); - if (rc == 0) { - /* inform other threads of expected response */ - rc = MqttClient_RespList_Add(client, - (MqttPacketType)SN_MSG_TYPE_CONNACK, 0, - &mc_connect->pendResp, &mc_connect->ack); - wm_SemUnlock(&client->lockClient); - } - if (rc != 0) { - wm_SemUnlock(&client->lockSend); - return rc; /* Error locking client */ - } - #endif - - /* Send connect packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc != client->write.len) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - - mc_connect->stat.write = MQTT_MSG_WAIT; - } - - if ((mc_connect->enable_lwt == 1) && (will_done != 1)) { - /* If the will is enabled, then the gateway requests the topic and - message in separate packets. */ - rc = SN_WillTopic(client, &mc_connect->will); - if (rc != 0) { - return rc; - } - - rc = SN_WillMessage(client, &mc_connect->will); - if (rc != 0) { - return rc; - } - will_done = 1; - } - - /* Wait for connect ack packet */ - rc = SN_Client_WaitType(client, &mc_connect->ack, - SN_MSG_TYPE_CONNACK, 0, client->cmd_timeout_ms); -#ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE) - return rc; -#endif - -#ifdef WOLFMQTT_MULTITHREAD - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &mc_connect->pendResp); - wm_SemUnlock(&client->lockClient); - } -#endif - - /* reset state */ - mc_connect->stat.write = MQTT_MSG_BEGIN; - - return rc; -} - -int SN_Client_WillTopicUpdate(MqttClient *client, SN_Will *will) -{ - int rc = 0; - - /* Validate required arguments */ - if (client == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - if (will->stat.write == MQTT_MSG_BEGIN) { - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } - #endif - - /* Encode Will Topic Update */ - rc = SN_Encode_WillTopicUpdate(client->tx_buf, - client->tx_buf_len, will); - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d)", - rc, SN_Packet_TypeDesc(SN_MSG_TYPE_WILLTOPICUPD), - SN_MSG_TYPE_WILLTOPICUPD); - #endif - if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - client->write.len = rc; - - #ifdef WOLFMQTT_MULTITHREAD - rc = wm_SemLock(&client->lockClient); - if (rc == 0) { - /* inform other threads of expected response */ - rc = MqttClient_RespList_Add(client, - (MqttPacketType)SN_MSG_TYPE_WILLTOPICRESP, - 0, &will->pendResp, &will->resp.topicResp); - wm_SemUnlock(&client->lockClient); - } - if (rc != 0) { - wm_SemUnlock(&client->lockSend); - return rc; /* Error locking client */ - } - #endif - - /* Send Will Topic Update packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc != client->write.len) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &will->pendResp); - wm_SemUnlock(&client->lockClient); - } - #endif - } - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - - will->stat.write = MQTT_MSG_WAIT; - } - - /* Wait for Will Topic Update Response packet */ - rc = SN_Client_WaitType(client, &will->resp.topicResp, - SN_MSG_TYPE_WILLTOPICRESP, 0, client->cmd_timeout_ms); -#ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE) - return rc; -#endif -#ifdef WOLFMQTT_MULTITHREAD - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &will->pendResp); - wm_SemUnlock(&client->lockClient); - } -#endif - - /* reset state */ - will->stat.write = MQTT_MSG_BEGIN; - - return rc; -} - -int SN_Client_WillMsgUpdate(MqttClient *client, SN_Will *will) -{ - int rc = 0; - - /* Validate required arguments */ - if ((client == NULL) || (will == NULL)) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - if (will->stat.write == MQTT_MSG_BEGIN) { - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } - #endif - /* Encode Will Message Update */ - rc = SN_Encode_WillMsgUpdate(client->tx_buf, - client->tx_buf_len, will); - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d)", - rc, SN_Packet_TypeDesc(SN_MSG_TYPE_WILLTOPICUPD), - SN_MSG_TYPE_WILLTOPICUPD); - #endif - if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - client->write.len = rc; - - #ifdef WOLFMQTT_MULTITHREAD - rc = wm_SemLock(&client->lockClient); - if (rc == 0) { - /* inform other threads of expected response */ - rc = MqttClient_RespList_Add(client, - (MqttPacketType)SN_MSG_TYPE_WILLMSGRESP, - 0, &will->pendResp, &will->resp.msgResp); - wm_SemUnlock(&client->lockClient); - } - if (rc != 0) { - wm_SemUnlock(&client->lockSend); - return rc; /* Error locking client */ - } - #endif - - /* Send Will Message Update packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc != client->write.len) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &will->pendResp); - wm_SemUnlock(&client->lockClient); - } - #endif - } - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - - will->stat.write = MQTT_MSG_WAIT; - } - - /* Wait for Will Message Update Response packet */ - rc = SN_Client_WaitType(client, &will->resp.msgResp, - SN_MSG_TYPE_WILLMSGRESP, 0, client->cmd_timeout_ms); -#ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE) - return rc; -#endif -#ifdef WOLFMQTT_MULTITHREAD - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &will->pendResp); - wm_SemUnlock(&client->lockClient); - } -#endif - - /* reset state */ - will->stat.write = MQTT_MSG_BEGIN; - - return rc; - -} - -int SN_Client_Subscribe(MqttClient *client, SN_Subscribe *subscribe) -{ - int rc = -1; - - /* Validate required arguments */ - if (client == NULL || subscribe == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - if (subscribe->stat.write == MQTT_MSG_BEGIN) { - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } - #endif - - /* Encode the subscribe packet */ - rc = SN_Encode_Subscribe(client->tx_buf, client->tx_buf_len, - subscribe); - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d), QoS %d", - rc, SN_Packet_TypeDesc(SN_MSG_TYPE_SUBSCRIBE), - SN_MSG_TYPE_SUBSCRIBE, subscribe->qos); - #endif - if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - client->write.len = rc; - - #ifdef WOLFMQTT_MULTITHREAD - rc = wm_SemLock(&client->lockClient); - if (rc == 0) { - /* inform other threads of expected response */ - rc = MqttClient_RespList_Add(client, - (MqttPacketType)SN_MSG_TYPE_SUBACK, subscribe->packet_id, - &subscribe->pendResp, &subscribe->subAck); - wm_SemUnlock(&client->lockClient); - } - if (rc != 0) { - wm_SemUnlock(&client->lockSend); - return rc; /* Error locking client */ - } - #endif - - /* Send subscribe packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc != client->write.len) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &subscribe->pendResp); - wm_SemUnlock(&client->lockClient); - } - #endif - return rc; - } - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - - subscribe->stat.write = MQTT_MSG_WAIT; - } - - /* Wait for subscribe ack packet */ - rc = SN_Client_WaitType(client, &subscribe->subAck, - SN_MSG_TYPE_SUBACK, subscribe->packet_id, client->cmd_timeout_ms); - -#ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE) - return rc; -#endif -#ifdef WOLFMQTT_MULTITHREAD - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &subscribe->pendResp); - wm_SemUnlock(&client->lockClient); - } -#endif - - /* reset state */ - subscribe->stat.write = MQTT_MSG_BEGIN; - - return rc; -} - -int SN_Client_Publish(MqttClient *client, SN_Publish *publish) -{ - int rc = MQTT_CODE_SUCCESS; - SN_MsgType resp_type; - - /* Validate required arguments */ - if (client == NULL || publish == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - switch (publish->stat.write) - { - case MQTT_MSG_BEGIN: - { - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } - #endif - - /* Encode the publish packet */ - rc = SN_Encode_Publish(client->tx_buf, client->tx_buf_len, - publish); - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d), ID %d," - " QoS %d", - rc, SN_Packet_TypeDesc(SN_MSG_TYPE_PUBLISH), - SN_MSG_TYPE_PUBLISH, publish->packet_id, - publish->qos); - #endif - if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - - client->write.len = rc; - publish->buffer_pos = 0; - - #ifdef WOLFMQTT_MULTITHREAD - if ((publish->qos == MQTT_QOS_1) || - (publish->qos == MQTT_QOS_2)) { - resp_type = (publish->qos == MQTT_QOS_1) ? - SN_MSG_TYPE_PUBACK : - SN_MSG_TYPE_PUBCOMP; - - rc = wm_SemLock(&client->lockClient); - if (rc == 0) { - /* inform other threads of expected response */ - rc = MqttClient_RespList_Add(client, - (MqttPacketType)resp_type, publish->packet_id, - &publish->pendResp, &publish->resp); - wm_SemUnlock(&client->lockClient); - } - if (rc != 0) { - wm_SemUnlock(&client->lockSend); - return rc; /* Error locking client */ - } - } - #endif - - publish->stat.write = MQTT_MSG_HEADER; - } - FALL_THROUGH; - - case MQTT_MSG_HEADER: - case MQTT_MSG_PAYLOAD: - case MQTT_MSG_PAYLOAD2: - { - /* Send packet and payload */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - #ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE) - return rc; - #endif - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - - if (rc < 0) { - #ifdef WOLFMQTT_MULTITHREAD - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &publish->pendResp); - wm_SemUnlock(&client->lockClient); - } - #endif - return rc; - } - - if (rc == client->write.len) { - rc = MQTT_CODE_SUCCESS; - } - else { - rc = -1; - } - - /* if not expecting a reply, the reset state and exit */ - if ((publish->qos == MQTT_QOS_0) || - (publish->qos == MQTT_QOS_3)) { - break; - } - - publish->stat.write = MQTT_MSG_WAIT; - } - FALL_THROUGH; - - case MQTT_MSG_WAIT: - { - /* Handle QoS */ - if ((publish->qos == MQTT_QOS_1) || - (publish->qos == MQTT_QOS_2)) { - - /* Determine packet type to wait for */ - resp_type = (publish->qos == MQTT_QOS_1) ? - SN_MSG_TYPE_PUBACK : - SN_MSG_TYPE_PUBCOMP; - - /* Wait for publish response packet */ - rc = SN_Client_WaitType(client, &publish->resp, - resp_type, publish->packet_id, client->cmd_timeout_ms); - #ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE) - break; - #endif - #ifdef WOLFMQTT_MULTITHREAD - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &publish->pendResp); - wm_SemUnlock(&client->lockClient); - } - #endif - - publish->return_code = publish->resp.return_code; - } - - break; - } - - case MQTT_MSG_ACK: - case MQTT_MSG_AUTH: - default: - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("SN_Client_Publish: Invalid state %d!", - publish->stat.write); - #endif - rc = MQTT_TRACE_ERROR(MQTT_CODE_ERROR_STAT); - break; - } /* switch (publish->stat) */ - - /* reset state */ -#ifdef WOLFMQTT_NONBLOCK - if (rc != MQTT_CODE_CONTINUE) -#endif - { - publish->stat.write = MQTT_MSG_BEGIN; - } - if (rc > 0) { - rc = MQTT_CODE_SUCCESS; - } - - return rc; -} - -int SN_Client_Unsubscribe(MqttClient *client, SN_Unsubscribe *unsubscribe) -{ - int rc; - - /* Validate required arguments */ - if (client == NULL || unsubscribe == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - if (unsubscribe->stat.write == MQTT_MSG_BEGIN) { - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } - #endif - - /* Encode the subscribe packet */ - rc = SN_Encode_Unsubscribe(client->tx_buf, client->tx_buf_len, - unsubscribe); - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d)", - rc, SN_Packet_TypeDesc(SN_MSG_TYPE_UNSUBSCRIBE), - SN_MSG_TYPE_UNSUBSCRIBE); - #endif - if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - client->write.len = rc; - - #ifdef WOLFMQTT_MULTITHREAD - rc = wm_SemLock(&client->lockClient); - if (rc == 0) { - /* inform other threads of expected response */ - rc = MqttClient_RespList_Add(client, - (MqttPacketType)SN_MSG_TYPE_UNSUBACK, - 0, &unsubscribe->pendResp, &unsubscribe->ack); - wm_SemUnlock(&client->lockClient); - } - if (rc != 0) { - wm_SemUnlock(&client->lockSend); - return rc; /* Error locking client */ - } - #endif - - /* Send unsubscribe packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc != client->write.len) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &unsubscribe->pendResp); - wm_SemUnlock(&client->lockClient); - } - #endif - } - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - - unsubscribe->stat.write = MQTT_MSG_WAIT; - } - - /* Wait for unsubscribe ack packet */ - rc = SN_Client_WaitType(client, &unsubscribe->ack, - SN_MSG_TYPE_UNSUBACK, unsubscribe->packet_id, - client->cmd_timeout_ms); - #ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE) - return rc; - #endif - #ifdef WOLFMQTT_MULTITHREAD - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &unsubscribe->pendResp); - wm_SemUnlock(&client->lockClient); - } - #endif - - /* reset state */ - unsubscribe->stat.write = MQTT_MSG_BEGIN; - - return rc; -} - -int SN_Client_Register(MqttClient *client, SN_Register *regist) -{ - int rc; - - /* Validate required arguments */ - if (client == NULL || regist == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - if (regist->stat.write == MQTT_MSG_BEGIN) { - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } - #endif - - /* Encode the register packet */ - rc = SN_Encode_Register(client->tx_buf, client->tx_buf_len, regist); - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d)", - rc, SN_Packet_TypeDesc(SN_MSG_TYPE_REGISTER), - SN_MSG_TYPE_REGISTER); - #endif - if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - client->write.len = rc; - - #ifdef WOLFMQTT_MULTITHREAD - rc = wm_SemLock(&client->lockClient); - if (rc == 0) { - /* inform other threads of expected response */ - rc = MqttClient_RespList_Add(client, - (MqttPacketType)SN_MSG_TYPE_REGACK, - regist->packet_id, ®ist->pendResp, ®ist->regack); - wm_SemUnlock(&client->lockClient); - } - if (rc != 0) { - wm_SemUnlock(&client->lockSend); - return rc; /* Error locking client */ - } - #endif - - /* Send register packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc != client->write.len) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, ®ist->pendResp); - wm_SemUnlock(&client->lockClient); - } - #endif - return rc; - } - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - - regist->stat.write = MQTT_MSG_WAIT; - } - - /* Wait for register acknowledge packet */ - rc = SN_Client_WaitType(client, ®ist->regack, - SN_MSG_TYPE_REGACK, regist->packet_id, client->cmd_timeout_ms); -#ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE) - return rc; -#endif -#ifdef WOLFMQTT_MULTITHREAD - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, ®ist->pendResp); - wm_SemUnlock(&client->lockClient); - } -#endif - - /* reset state */ - regist->stat.write = MQTT_MSG_BEGIN; - - return rc; -} - -int SN_Client_Ping(MqttClient *client, SN_PingReq *ping) -{ - int rc; - SN_PingReq loc_ping; - - /* Validate required arguments */ - if (client == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - if (ping == NULL) { - XMEMSET(&loc_ping, 0, sizeof(SN_PingReq)); - ping = &loc_ping; - } - - if (ping->stat.write == MQTT_MSG_BEGIN) { - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } - #endif - - /* Encode the ping packet as a request */ - rc = SN_Encode_Ping(client->tx_buf, client->tx_buf_len, ping, - SN_MSG_TYPE_PING_REQ); - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d)", - rc, SN_Packet_TypeDesc(SN_MSG_TYPE_PING_REQ), - SN_MSG_TYPE_PING_REQ); - #endif - if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - client->write.len = rc; - - #ifdef WOLFMQTT_MULTITHREAD - rc = wm_SemLock(&client->lockClient); - if (rc == 0) { - /* inform other threads of expected response */ - rc = MqttClient_RespList_Add(client, - (MqttPacketType)SN_MSG_TYPE_PING_RESP, 0, - &ping->pendResp, NULL); - wm_SemUnlock(&client->lockClient); - } - if (rc != 0) { - wm_SemUnlock(&client->lockSend); - return rc; /* Error locking client */ - } - #endif - - /* Send ping req packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc != client->write.len) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &ping->pendResp); - wm_SemUnlock(&client->lockClient); - } - #endif - return rc; - } - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - - ping->stat.write = MQTT_MSG_WAIT; - } - - /* Wait for ping resp packet */ - rc = SN_Client_WaitType(client, ping, - SN_MSG_TYPE_PING_RESP, 0, client->cmd_timeout_ms); -#ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE) - return rc; -#endif -#ifdef WOLFMQTT_MULTITHREAD - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &ping->pendResp); - wm_SemUnlock(&client->lockClient); - } -#endif - - /* reset state */ - ping->stat.write = MQTT_MSG_BEGIN; - - return rc; -} - -int SN_Client_Disconnect(MqttClient *client) -{ - return SN_Client_Disconnect_ex(client, NULL); -} - -int SN_Client_Disconnect_ex(MqttClient *client, SN_Disconnect *disconnect) -{ - int rc; - - /* Validate required arguments */ - if (client == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - -#ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } -#endif - - /* Encode the disconnect packet */ - rc = SN_Encode_Disconnect(client->tx_buf, client->tx_buf_len, disconnect); -#ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d)", - rc, SN_Packet_TypeDesc(SN_MSG_TYPE_DISCONNECT), - SN_MSG_TYPE_DISCONNECT); -#endif - if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - client->write.len = rc; - -#ifdef WOLFMQTT_MULTITHREAD - if ((disconnect != NULL) && (disconnect->sleepTmr != 0)) { - rc = wm_SemLock(&client->lockClient); - if (rc == 0) { - /* inform other threads of expected response */ - rc = MqttClient_RespList_Add(client, - (MqttPacketType)SN_MSG_TYPE_DISCONNECT, 0, - &disconnect->pendResp, NULL); - wm_SemUnlock(&client->lockClient); - } - if (rc != 0) { - wm_SemUnlock(&client->lockSend); - return rc; /* Error locking client */ - } - } -#endif - - /* Send disconnect packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc != client->write.len) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &disconnect->pendResp); - wm_SemUnlock(&client->lockClient); - } - #endif - return rc; - } -#ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); -#endif - - rc = MQTT_CODE_SUCCESS; - - /* If sleep was set, wait for response disconnect packet */ - if ((disconnect != NULL) && (disconnect->sleepTmr != 0)) { - rc = SN_Client_WaitType(client, disconnect, - SN_MSG_TYPE_DISCONNECT, 0, client->cmd_timeout_ms); - #ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE) - return rc; - #endif - #ifdef WOLFMQTT_MULTITHREAD - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &disconnect->pendResp); - wm_SemUnlock(&client->lockClient); - } - #endif - } - - return rc; -} - -int SN_Client_WaitMessage_ex(MqttClient *client, SN_Object* packet_obj, - int timeout_ms) -{ - return SN_Client_WaitType(client, packet_obj, - SN_MSG_TYPE_ANY, 0, timeout_ms); -} - -int SN_Client_WaitMessage(MqttClient *client, int timeout_ms) -{ - if (client == NULL) - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - return SN_Client_WaitMessage_ex(client, &client->msgSN, timeout_ms); -} - -#endif /* defined WOLFMQTT_SN */ diff --git a/src/mqtt_packet.c b/src/mqtt_packet.c index c84c66664..cafa8e842 100644 --- a/src/mqtt_packet.c +++ b/src/mqtt_packet.c @@ -1917,7 +1917,7 @@ int MqttProps_Free(MqttProp *head) #endif /* WOLFMQTT_V5 */ -static int MqttPacket_HandleNetError(MqttClient *client, int rc) +int MqttPacket_HandleNetError(MqttClient *client, int rc) { (void)client; #ifdef WOLFMQTT_DISCONNECT_CB @@ -2106,1551 +2106,3 @@ const char* MqttPacket_TypeDesc(MqttPacketType packet_type) } #endif - -#ifdef WOLFMQTT_SN -const char* SN_Packet_TypeDesc(SN_MsgType packet_type) -{ - switch (packet_type) { - case SN_MSG_TYPE_ADVERTISE: - return "Advertise"; - case SN_MSG_TYPE_SEARCHGW: - return "Search gateway"; - case SN_MSG_TYPE_GWINFO: - return "Gateway info"; - case SN_MSG_TYPE_CONNECT: - return "Connect"; - case SN_MSG_TYPE_CONNACK: - return "Connect Ack"; - case SN_MSG_TYPE_WILLTOPICREQ: - return "Will topic request"; - case SN_MSG_TYPE_WILLTOPIC: - return "Will topic set"; - case SN_MSG_TYPE_WILLMSGREQ: - return "Will message request"; - case SN_MSG_TYPE_WILLMSG: - return "Will message set"; - case SN_MSG_TYPE_REGISTER: - return "Register"; - case SN_MSG_TYPE_REGACK: - return "Register Ack"; - case SN_MSG_TYPE_PUBLISH: - return "Publish"; - case SN_MSG_TYPE_PUBACK: - return "Publish Ack"; - case SN_MSG_TYPE_PUBCOMP: - return "Publish complete"; - case SN_MSG_TYPE_PUBREC: - return "Publish Received"; - case SN_MSG_TYPE_PUBREL: - return "Publish Release"; - case SN_MSG_TYPE_SUBSCRIBE: - return "Subscribe"; - case SN_MSG_TYPE_SUBACK: - return "Subscribe Ack"; - case SN_MSG_TYPE_UNSUBSCRIBE: - return "Unsubscribe"; - case SN_MSG_TYPE_UNSUBACK: - return "Unsubscribe Ack"; - case SN_MSG_TYPE_PING_REQ: - return "Ping Req"; - case SN_MSG_TYPE_PING_RESP: - return "Ping Resp"; - case SN_MSG_TYPE_DISCONNECT: - return "Disconnect"; - case SN_MSG_TYPE_WILLTOPICUPD: - return "Will topic update"; - case SN_MSG_TYPE_WILLTOPICRESP: - return "WIll topic response"; - case SN_MSG_TYPE_WILLMSGUPD: - return "Will message update"; - case SN_MSG_TYPE_WILLMSGRESP: - return "Will message response"; - case SN_MSG_TYPE_ENCAPMSG: - return "Encapsulated message"; - case SN_MSG_TYPE_ANY: - return "Any"; - default: - break; - } - return "Unknown"; -} - -int SN_Decode_Header(byte *rx_buf, int rx_buf_len, - SN_MsgType* p_packet_type, word16* p_packet_id) -{ - SN_MsgType packet_type; - word16 total_len; - - if (rx_buf == NULL || rx_buf_len < MQTT_PACKET_HEADER_MIN_SIZE) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Decode fixed header */ - total_len = *rx_buf++; - if (total_len == SN_PACKET_LEN_IND) { - /* The length is stored in the next two bytes */ - rx_buf += MqttDecode_Num(rx_buf, &total_len); - } - - if (total_len > rx_buf_len) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - /* Message Type */ - packet_type = (SN_MsgType)*rx_buf++; - - if (p_packet_type) - *p_packet_type = packet_type; - - if (p_packet_id) { - switch(packet_type) { - case SN_MSG_TYPE_REGACK: - case SN_MSG_TYPE_PUBACK: - /* octet 4-5 */ - MqttDecode_Num(rx_buf + 2, p_packet_id); - break; - case SN_MSG_TYPE_PUBCOMP: - case SN_MSG_TYPE_PUBREC: - case SN_MSG_TYPE_PUBREL: - case SN_MSG_TYPE_UNSUBACK: - /* octet 2-3 */ - MqttDecode_Num(rx_buf, p_packet_id); - break; - case SN_MSG_TYPE_SUBACK: - /* octet 5-6 */ - MqttDecode_Num(rx_buf + 3, p_packet_id); - break; - case SN_MSG_TYPE_ADVERTISE: - case SN_MSG_TYPE_SEARCHGW: - case SN_MSG_TYPE_GWINFO: - case SN_MSG_TYPE_CONNECT: - case SN_MSG_TYPE_CONNACK: - case SN_MSG_TYPE_WILLTOPICREQ: - case SN_MSG_TYPE_WILLTOPIC: - case SN_MSG_TYPE_WILLMSGREQ: - case SN_MSG_TYPE_WILLMSG: - case SN_MSG_TYPE_REGISTER: - case SN_MSG_TYPE_PUBLISH: - case SN_MSG_TYPE_SUBSCRIBE: - case SN_MSG_TYPE_UNSUBSCRIBE: - case SN_MSG_TYPE_PING_REQ: - case SN_MSG_TYPE_PING_RESP: - case SN_MSG_TYPE_DISCONNECT: - case SN_MSG_TYPE_WILLTOPICUPD: - case SN_MSG_TYPE_WILLTOPICRESP: - case SN_MSG_TYPE_WILLMSGUPD: - case SN_MSG_TYPE_WILLMSGRESP: - case SN_MSG_TYPE_ENCAPMSG: - case SN_MSG_TYPE_RESERVED: - default: - *p_packet_id = 0; - break; - } - } - - return (int)total_len; -} - -int SN_Decode_Advertise(byte *rx_buf, int rx_buf_len, SN_Advertise *gw_info) -{ - int total_len; - byte *rx_payload = rx_buf, type; - - /* Validate required arguments */ - if (rx_buf == NULL || rx_buf_len <= 0) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Decode fixed header */ - total_len = *rx_payload++; - - /* Check message type */ - type = *rx_payload++; - if (total_len != 5) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); - } - - if (type != SN_MSG_TYPE_ADVERTISE) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); - } - - /* Decode gateway info */ - if (gw_info != NULL) { - gw_info->gwId = *rx_payload++; - - rx_payload += MqttDecode_Num(rx_payload, &gw_info->duration); - } - (void)rx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Encode_SearchGW(byte *tx_buf, int tx_buf_len, byte hops) -{ - int total_len; - byte *tx_payload = tx_buf; - - /* Packet length is not variable */ - total_len = 3; - - /* Validate required arguments */ - if (tx_buf == NULL || tx_buf_len < total_len) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Encode length */ - *tx_payload++ = total_len; - - /* Encode message type */ - *tx_payload++ = SN_MSG_TYPE_SEARCHGW; - - /* Encode radius */ - *tx_payload++ = hops; - (void)tx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Decode_GWInfo(byte *rx_buf, int rx_buf_len, SN_GwInfo *gw_info) -{ - int total_len; - byte *rx_payload = rx_buf, type; - - /* Validate required arguments */ - if (rx_buf == NULL || rx_buf_len <= 0) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Decode fixed header */ - total_len = *rx_payload++; - if (total_len == SN_PACKET_LEN_IND) { - /* The length is stored in the next two bytes */ - rx_payload += MqttDecode_Num(rx_payload, (word16*)&total_len); - } - - if (total_len > rx_buf_len) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - if (total_len < 3) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); - } - /* Check message type */ - type = *rx_payload++; - if (type != SN_MSG_TYPE_GWINFO) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); - } - - /* Decode gateway info */ - if (gw_info != NULL) { - gw_info->gwId = *rx_payload++; - - /* TODO: validate size of gwAddr */ - if (total_len - 3 > 0) { - /* The gateway address is only present if sent by a client */ - XMEMCPY(gw_info->gwAddr, rx_payload, total_len - 3); - } - } - (void)rx_payload; - - /* Return total length of packet */ - return total_len; -} - -/* Packet Type Encoders/Decoders */ -int SN_Encode_Connect(byte *tx_buf, int tx_buf_len, SN_Connect *mc_connect) -{ - word16 total_len, id_len; - byte flags = 0; - byte *tx_payload = tx_buf; - - /* Validate required arguments */ - if ((tx_buf == NULL) || (mc_connect == NULL) || - (mc_connect->client_id == NULL) || (mc_connect->protocol_level == 0)) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Determine packet length */ - total_len = 6; /* Len + Message Type + Flags + ProtocolID + Duration(2) */ - - /* Client ID size */ - id_len = (word16)XSTRLEN(mc_connect->client_id); - id_len = (id_len <= SN_CLIENTID_MAX_LEN) ? id_len : SN_CLIENTID_MAX_LEN; - - total_len += id_len; - - if (total_len > SN_PACKET_MAX_SMALL_SIZE) { - total_len += 2; /* Store len in three bytes */ - } - - if (total_len > tx_buf_len) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - /* Encode length */ - if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { - *tx_payload++ = (byte)total_len; - } - else { - *tx_payload++ = SN_PACKET_LEN_IND; - tx_payload += MqttEncode_Num(tx_payload, total_len); - } - - /* Encode message type */ - *tx_payload++ = SN_MSG_TYPE_CONNECT; - - /* Encode flags */ - if (mc_connect->clean_session) { - flags |= SN_PACKET_FLAG_CLEANSESSION; - } - if (mc_connect->enable_lwt) { - flags |= SN_PACKET_FLAG_WILL; - } - *tx_payload++ = flags; - - /* Protocol version */ - *tx_payload++ = mc_connect->protocol_level; - - /* Encode duration (keep-alive) */ - tx_payload += MqttEncode_Num(tx_payload, mc_connect->keep_alive_sec); - - /* Encode Client ID */ - XMEMCPY(tx_payload, mc_connect->client_id, id_len); - tx_payload += id_len; - (void)tx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Decode_WillTopicReq(byte *rx_buf, int rx_buf_len) -{ - int total_len; - byte *rx_payload = rx_buf, type; - - /* Validate required arguments */ - if (rx_buf == NULL || rx_buf_len <= 0) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Length and MsgType */ - total_len = *rx_payload++; - if (total_len != 2) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); - } - - type = *rx_payload++; - if (type != SN_MSG_TYPE_WILLTOPICREQ) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); - } - (void)rx_payload; - - /* Return total length of packet */ - return total_len; -} - -/* An empty WILLTOPIC message is a WILLTOPIC message without Flags and - WillTopic field (i.e. it is exactly 2 octets long). It is used by a client - to delete the Will topic and the Will message stored in the server */ -int SN_Encode_WillTopic(byte *tx_buf, int tx_buf_len, SN_Will *willTopic) -{ - int total_len; - byte *tx_payload, flags = 0; - - /* Validate required arguments */ - if (tx_buf == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Length and MsgType */ - total_len = 2; - - /* Determine packet length */ - if (willTopic != NULL) { - /* Will Topic is a string */ - total_len += (int)XSTRLEN(willTopic->willTopic); - - /* Flags */ - total_len++; - - if (total_len > SN_PACKET_MAX_SMALL_SIZE) { - /* Length is stored in bytes 1 and 2 */ - total_len += 2; - } - } - - if (total_len > tx_buf_len) { - /* Buffer too small */ - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - /* Encode length */ - tx_payload = tx_buf; - - if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { - *tx_payload++ = total_len; - } - else { - *tx_payload++ = SN_PACKET_LEN_IND; - tx_payload += MqttEncode_Num(tx_payload, total_len); - } - - /* Encode message type */ - *tx_payload++ = SN_MSG_TYPE_WILLTOPIC; - - if (willTopic != NULL) { - int will_len; - - /* Encode flags */ - flags |= ((willTopic->qos << SN_PACKET_FLAG_QOS_SHIFT) & - SN_PACKET_FLAG_QOS_MASK); - flags |= (willTopic->retain != 0) ? SN_PACKET_FLAG_RETAIN : 0; - *tx_payload++ = flags; - - /* Encode Will Topic */ - will_len = (int)XSTRLEN(willTopic->willTopic); - XMEMCPY(tx_payload, willTopic->willTopic, will_len); - tx_payload += will_len; - } - (void)tx_payload; - - return total_len; -} - -int SN_Decode_WillMsgReq(byte *rx_buf, int rx_buf_len) -{ - int total_len; - byte *rx_payload = rx_buf, type; - - /* Validate required arguments */ - if (rx_buf == NULL || rx_buf_len <= 0) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Decode fixed header */ - total_len = *rx_payload++; - - /* Length and MsgType */ - if (total_len != 2){ - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); - } - - /* Message Type */ - type = *rx_payload++; - if (type != SN_MSG_TYPE_WILLMSGREQ) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); - } - (void)rx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Encode_WillMsg(byte *tx_buf, int tx_buf_len, SN_Will *willMsg) -{ - int total_len; - byte *tx_payload; - - /* Validate required arguments */ - if ((tx_buf == NULL) || (willMsg == NULL)) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Length and MsgType */ - total_len = 2; - - /* Determine packet length */ - /* Add Will Message len */ - total_len += willMsg->willMsgLen; - - if (total_len > SN_PACKET_MAX_SMALL_SIZE) { - /* Length is stored in bytes 1 and 2 */ - total_len += 2; - } - - if (total_len > tx_buf_len) { - /* Buffer too small */ - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - /* Encode length */ - tx_payload = tx_buf; - - if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { - *tx_payload++ = total_len; - } - else { - *tx_payload++ = SN_PACKET_LEN_IND; - tx_payload += MqttEncode_Num(tx_payload, total_len); - } - - /* Encode message type */ - *tx_payload++ = SN_MSG_TYPE_WILLMSG; - - /* Encode Will Message */ - XMEMCPY(tx_payload, willMsg->willMsg, willMsg->willMsgLen); - tx_payload += willMsg->willMsgLen; - (void)tx_payload; - - return total_len; -} - -int SN_Encode_WillTopicUpdate(byte *tx_buf, int tx_buf_len, SN_Will *willTopic) -{ - int total_len; - byte *tx_payload, flags = 0; - - /* Validate required arguments */ - if (tx_buf == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Length and MsgType */ - total_len = 2; - - /* Determine packet length */ - if (willTopic != NULL) { - /* Will Topic is a string */ - total_len += (int)XSTRLEN(willTopic->willTopic); - - /* Flags */ - total_len++; - - if (total_len > SN_PACKET_MAX_SMALL_SIZE) { - /* Length is stored in bytes 1 and 2 */ - total_len += 2; - } - } - - if (total_len > tx_buf_len) { - /* Buffer too small */ - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - /* Encode length */ - tx_payload = tx_buf; - - if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { - *tx_payload++ = total_len; - } - else { - *tx_payload++ = SN_PACKET_LEN_IND; - tx_payload += MqttEncode_Num(tx_payload, total_len); - } - - /* Encode message type */ - *tx_payload++ = SN_MSG_TYPE_WILLTOPICUPD; - - if (willTopic != NULL) { - int will_len; - - /* Encode flags */ - flags |= ((willTopic->qos << SN_PACKET_FLAG_QOS_SHIFT) & - SN_PACKET_FLAG_QOS_MASK); - flags |= (willTopic->retain != 0) ? SN_PACKET_FLAG_RETAIN : 0; - *tx_payload++ = flags; - - /* Encode Will Topic */ - will_len = (int)XSTRLEN(willTopic->willTopic); - XMEMCPY(tx_payload, willTopic->willTopic, will_len); - tx_payload += will_len; - } - (void)tx_payload; - - return total_len; - -} - -int SN_Decode_WillTopicResponse(byte *rx_buf, int rx_buf_len, byte *ret_code) -{ - int total_len; - byte *rx_payload = rx_buf, type; - - /* Validate required arguments */ - if (rx_buf == NULL || rx_buf_len <= 0) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Decode fixed header */ - total_len = *rx_payload++; - if (total_len != 3) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); - } - - type = *rx_payload++; - if (type != SN_MSG_TYPE_WILLTOPICRESP) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); - } - - /* Return Code */ - *ret_code = *rx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Encode_WillMsgUpdate(byte *tx_buf, int tx_buf_len, SN_Will *willMsg) -{ - int total_len; - byte *tx_payload; - - /* Validate required arguments */ - if ((tx_buf == NULL) || (willMsg == NULL)) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Length and MsgType */ - total_len = 2; - - /* Determine packet length */ - /* Add Will Message len */ - total_len += willMsg->willMsgLen; - - if (total_len > SN_PACKET_MAX_SMALL_SIZE) { - /* Length is stored in bytes 1 and 2 */ - total_len += 2; - } - - if (total_len > tx_buf_len) { - /* Buffer too small */ - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - /* Encode length */ - tx_payload = tx_buf; - - if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { - *tx_payload++ = total_len; - } - else { - *tx_payload++ = SN_PACKET_LEN_IND; - tx_payload += MqttEncode_Num(tx_payload, total_len); - } - - /* Encode message type */ - *tx_payload++ = SN_MSG_TYPE_WILLMSGUPD; - - /* Encode Will Message */ - XMEMCPY(tx_payload, willMsg->willMsg, willMsg->willMsgLen); - tx_payload += willMsg->willMsgLen; - (void)tx_payload; - - return total_len; -} - -int SN_Decode_WillMsgResponse(byte *rx_buf, int rx_buf_len, byte *ret_code) -{ - int total_len; - byte *rx_payload = rx_buf, type; - - /* Validate required arguments */ - if (rx_buf == NULL || rx_buf_len <= 0) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Decode fixed header */ - total_len = *rx_payload++; - if (total_len != 3) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); - } - - type = *rx_payload++; - if (type != SN_MSG_TYPE_WILLMSGRESP) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); - } - - /* Return Code */ - *ret_code = *rx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Decode_ConnectAck(byte *rx_buf, int rx_buf_len, - SN_ConnectAck *connect_ack) -{ - int total_len; - byte *rx_payload = rx_buf, type; - - /* Validate required arguments */ - if (rx_buf == NULL || rx_buf_len <= 0) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Decode fixed header */ - total_len = *rx_payload++; - if (total_len != 3) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); - } - - type = *rx_payload++; - if (type != SN_MSG_TYPE_CONNACK) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); - } - - /* Decode variable header */ - if (connect_ack) { - connect_ack->return_code = *rx_payload++; - } - (void)rx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Encode_Register(byte *tx_buf, int tx_buf_len, SN_Register *regist) -{ - int total_len, topic_len; - byte *tx_payload; - - /* Validate required arguments */ - if (tx_buf == NULL || regist == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Determine packet length */ - /* Topic name is a string */ - total_len = (int)XSTRLEN(regist->topicName); - - /* Length, MsgType, TopicID (2), and packet_id (2) */ - total_len += 6; - - if (total_len > SN_PACKET_MAX_SMALL_SIZE) { - /* Length is stored in bytes 1 and 2 */ - total_len += 2; - } - - if (total_len > tx_buf_len) { - /* Buffer too small */ - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - /* Encode length */ - tx_payload = tx_buf; - - if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { - *tx_payload++ = total_len; - } - else { - *tx_payload++ = SN_PACKET_LEN_IND; - tx_payload += MqttEncode_Num(tx_payload, total_len); - } - - /* Encode message type */ - *tx_payload++ = SN_MSG_TYPE_REGISTER; - - /* Encode Topic ID */ - tx_payload += MqttEncode_Num(tx_payload, regist->topicId); - - /* Encode Packet ID */ - tx_payload += MqttEncode_Num(tx_payload, regist->packet_id); - - /* Encode Topic Name */ - topic_len = (int)XSTRLEN(regist->topicName); - XMEMCPY(tx_payload, regist->topicName, topic_len); - tx_payload += topic_len; - (void)tx_payload; - - return total_len; -} - -int SN_Decode_Register(byte *rx_buf, int rx_buf_len, SN_Register *regist) -{ - int total_len; - byte *rx_payload = rx_buf, type; - - /* Validate required arguments */ - if (rx_buf == NULL || rx_buf_len <= 0) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Decode fixed header */ - total_len = *rx_payload++; - if (total_len == SN_PACKET_LEN_IND) { - /* The length is stored in the next two bytes */ - rx_payload += MqttDecode_Num(rx_payload, (word16*)&total_len); - } - - if (total_len > rx_buf_len) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - if (total_len < 7) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); - } - - /* Check message type */ - type = *rx_payload++; - if (type != SN_MSG_TYPE_REGISTER) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); - } - - if (regist != NULL) { - /* Decode Topic ID assigned by GW */ - rx_payload += MqttDecode_Num(rx_payload, ®ist->topicId); - - /* Decode packet ID */ - rx_payload += MqttDecode_Num(rx_payload, ®ist->packet_id); - - /* Decode Topic Name */ - regist->topicName = (char*)rx_payload; - - /* Terminate the string */ - rx_payload[total_len-6] = '\0'; - } - (void)rx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Encode_RegAck(byte *tx_buf, int tx_buf_len, SN_RegAck *regack) -{ - int total_len; - byte *tx_payload; - - /* Validate required arguments */ - if (tx_buf == NULL || regack == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Determine packet length */ - /* Length, MsgType, TopicID (2), and MsgId (2), Return Code */ - total_len = 7; - - if (total_len > tx_buf_len) { - /* Buffer too small */ - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - tx_payload = tx_buf; - - /* Encode length */ - *tx_payload++ = total_len; - - /* Encode message type */ - *tx_payload++ = SN_MSG_TYPE_REGACK; - - /* Encode Topic ID */ - tx_payload += MqttEncode_Num(tx_payload, regack->topicId); - - /* Encode Message ID */ - tx_payload += MqttEncode_Num(tx_payload, regack->packet_id); - - /* Encode Return Code */ - *tx_payload += regack->return_code; - - (void)tx_payload; - - return total_len; -} - -int SN_Decode_RegAck(byte *rx_buf, int rx_buf_len, SN_RegAck *regack) -{ - int total_len; - byte *rx_payload = rx_buf, type; - - /* Validate required arguments */ - if (rx_buf == NULL || rx_buf_len <= 0) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Decode fixed header */ - total_len = *rx_payload++; - if (total_len != 7) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); - } - - type = *rx_payload++; - if (type != SN_MSG_TYPE_REGACK) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); - } - - if (regack != NULL) { - /* Decode Topic ID assigned by GW */ - rx_payload += MqttDecode_Num(rx_payload, ®ack->topicId); - - /* Decode packet ID */ - rx_payload += MqttDecode_Num(rx_payload, ®ack->packet_id); - - /* Decode return code */ - regack->return_code = *rx_payload++; - } - (void)rx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Encode_Subscribe(byte *tx_buf, int tx_buf_len, SN_Subscribe *subscribe) -{ - int total_len; - byte *tx_payload, flags = 0x00; - - /* Validate required arguments */ - if (tx_buf == NULL || subscribe == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Determine packet length */ - if (subscribe->topic_type == SN_TOPIC_ID_TYPE_NORMAL) { - /* Topic name is a string */ - total_len = (int)XSTRLEN(subscribe->topicNameId); - } - else { - /* Topic ID or Short name */ - total_len = 2; - } - - /* Length, MsgType, Flags, and MsgID (2) */ - total_len += 5; - - if (total_len > SN_PACKET_MAX_SMALL_SIZE) { - /* Length is stored in bytes 1 and 2 */ - total_len += 2; - } - - if (total_len > tx_buf_len) { - /* Buffer too small */ - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - /* Encode length */ - tx_payload = tx_buf; - - if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { - *tx_payload++ = total_len; - } - else { - *tx_payload++ = SN_PACKET_LEN_IND; - tx_payload += MqttEncode_Num(tx_payload, total_len); - } - - /* Encode message type */ - *tx_payload++ = SN_MSG_TYPE_SUBSCRIBE; - - /* Set flags */ - if (subscribe->duplicate) - flags |= SN_PACKET_FLAG_DUPLICATE; - flags |= (SN_PACKET_FLAG_QOS_MASK & - (subscribe->qos << SN_PACKET_FLAG_QOS_SHIFT)); - flags |= (SN_PACKET_FLAG_TOPICIDTYPE_MASK & subscribe->topic_type); - - *tx_payload++ = flags; - - /* Encode packet ID */ - tx_payload += MqttEncode_Num(tx_payload, subscribe->packet_id); - - /* Encode topic */ - if (subscribe->topic_type == SN_TOPIC_ID_TYPE_NORMAL) { - /* Topic name is a string */ - XMEMCPY(tx_payload, subscribe->topicNameId, XSTRLEN(subscribe->topicNameId)); - } - else { - /* Topic ID */ - XMEMCPY(tx_payload, subscribe->topicNameId, 2); - } - (void)tx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Decode_SubscribeAck(byte* rx_buf, int rx_buf_len, - SN_SubAck *subscribe_ack) -{ - word16 total_len; - byte* rx_payload = rx_buf, type; - - /* Validate required arguments */ - if (rx_buf == NULL || rx_buf_len <= 0) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Decode fixed header */ - total_len = *rx_payload++; - if (total_len != 8) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); - } - - type = *rx_payload++; - if (type != SN_MSG_TYPE_SUBACK) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - /* Decode SubAck fields */ - if (subscribe_ack) { - subscribe_ack->flags = *rx_payload++; - rx_payload += MqttDecode_Num(rx_payload, &subscribe_ack->topicId); - rx_payload += MqttDecode_Num(rx_payload, &subscribe_ack->packet_id); - subscribe_ack->return_code = *rx_payload++; - } - (void)rx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Encode_Publish(byte *tx_buf, int tx_buf_len, SN_Publish *publish) -{ - word16 total_len; - byte *tx_payload = tx_buf; - byte flags = 0; - - /* Validate required arguments */ - if (tx_buf == NULL || publish == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Determine packet length */ - total_len = publish->total_len; - - /* Add length, msgType, flags, topic ID (2), and msgID (2) */ - total_len += 7; - - if (total_len > SN_PACKET_MAX_SMALL_SIZE) { - /* Length is stored in bytes 1 and 2 */ - total_len += 2; - } - - if (total_len > tx_buf_len) { - /* Buffer too small */ - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - /* Encode header */ - if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { - *tx_payload++ = (byte)total_len; - } - else { - *tx_payload++ = SN_PACKET_LEN_IND; - tx_payload += MqttEncode_Num(tx_payload, total_len); - } - - *tx_payload++ = SN_MSG_TYPE_PUBLISH; - - /* Set flags */ - if (publish->duplicate) - flags |= SN_PACKET_FLAG_DUPLICATE; - flags |= (SN_PACKET_FLAG_QOS_MASK & - (publish->qos << SN_PACKET_FLAG_QOS_SHIFT)); - if (publish->retain) - flags |= SN_PACKET_FLAG_RETAIN; - flags |= (SN_PACKET_FLAG_TOPICIDTYPE_MASK & publish->topic_type); - - *tx_payload++ = flags; - - /* Encode topic */ - if ((publish->topic_type == SN_TOPIC_ID_TYPE_SHORT) || - (publish->topic_type == SN_TOPIC_ID_TYPE_PREDEF)) { - /* Short and predefined topic names are 2 chars */ - XMEMCPY(tx_payload, publish->topic_name, 2); - tx_payload += 2; - } - else { - /* Topic ID */ - tx_payload += MqttEncode_Num(tx_payload, *(word16*)publish->topic_name); - } - - tx_payload += MqttEncode_Num(tx_payload, publish->packet_id); - - /* Encode payload */ - XMEMCPY(tx_payload, publish->buffer, publish->total_len); - tx_payload += publish->total_len; - - (void)tx_payload; - - /* Return length of packet placed into tx_buf */ - return total_len; -} - -int SN_Decode_Publish(byte *rx_buf, int rx_buf_len, SN_Publish *publish) -{ - word16 total_len; - byte *rx_payload = rx_buf; - byte flags = 0, type; - - /* Validate required arguments */ - if (rx_buf == NULL || publish == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Decode fixed header */ - total_len = *rx_payload++; - if (total_len == SN_PACKET_LEN_IND) { - /* The length is stored in the next two bytes */ - rx_payload += MqttDecode_Num(rx_payload, &total_len); - } - - if (total_len > rx_buf_len) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - if (total_len < 7) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); - } - - /* Message Type */ - type = *rx_payload++; - if (type != SN_MSG_TYPE_PUBLISH) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); - } - - flags = *rx_payload++; - - publish->topic_name = (char*)rx_payload; - rx_payload += MQTT_DATA_LEN_SIZE; - publish->topic_name_len = MQTT_DATA_LEN_SIZE; - - rx_payload += MqttDecode_Num(rx_payload, &publish->packet_id); - - /* Set flags */ - publish->duplicate = flags & SN_PACKET_FLAG_DUPLICATE; - - publish->qos = (MqttQoS)((flags & SN_PACKET_FLAG_QOS_MASK) >> - SN_PACKET_FLAG_QOS_SHIFT); - - publish->retain = flags & SN_PACKET_FLAG_RETAIN; - - publish->topic_type = flags & SN_PACKET_FLAG_TOPICIDTYPE_MASK; - - /* Decode payload */ - - publish->total_len = total_len - 7; - publish->buffer = rx_payload; - publish->buffer_pos = 0; - publish->buffer_len = publish->total_len; - - /* Return length of packet read from rx_buf */ - return total_len; -} - -int SN_Encode_PublishResp(byte* tx_buf, int tx_buf_len, byte type, - SN_PublishResp *publish_resp) -{ - int total_len; - byte *tx_payload = tx_buf; - - /* Validate required arguments */ - if (tx_buf == NULL || publish_resp == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Determine packet length */ - total_len = (type == SN_MSG_TYPE_PUBACK) ? 7 : 4; - - if (total_len > tx_buf_len) - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - - /* Encode */ - *tx_payload++ = (byte)total_len; - - *tx_payload++ = type; - - if (type == SN_MSG_TYPE_PUBACK) { - tx_payload += MqttEncode_Num(tx_payload, publish_resp->topicId); - } - - tx_payload += MqttEncode_Num(tx_payload, publish_resp->packet_id); - - if (type == SN_MSG_TYPE_PUBACK) { - *tx_payload++ = publish_resp->return_code; - } - (void)tx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Decode_PublishResp(byte* rx_buf, int rx_buf_len, byte type, - SN_PublishResp *publish_resp) -{ - int total_len; - byte rec_type, *rx_payload = rx_buf; - - /* Validate required arguments */ - if (rx_buf == NULL || rx_buf_len <= 0) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Decode */ - total_len = *rx_payload++; - - if(total_len > rx_buf_len) { - /* Buffer too small */ - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - /* Validate packet type */ - rec_type = *rx_payload++; - if (rec_type != type) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); - } - - if (publish_resp) { - if (type == SN_MSG_TYPE_PUBACK) { - rx_payload += MqttDecode_Num(rx_payload, &publish_resp->topicId); - } - - rx_payload += MqttDecode_Num(rx_payload, &publish_resp->packet_id); - - if (type == SN_MSG_TYPE_PUBACK) { - publish_resp->return_code = *rx_payload++; - } - } - (void)rx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Encode_Unsubscribe(byte *tx_buf, int tx_buf_len, - SN_Unsubscribe *unsubscribe) -{ - int total_len; - byte *tx_payload, flags = 0x00; - - /* Validate required arguments */ - if (tx_buf == NULL || unsubscribe == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Determine packet length */ - if (unsubscribe->topic_type == SN_TOPIC_ID_TYPE_NORMAL) { - /* Topic name is a string */ - total_len = (int)XSTRLEN(unsubscribe->topicNameId); - } - else { - /* Topic ID or Short name */ - total_len = 2; - } - - /* Length, MsgType, Flags, and MsgID (2) */ - total_len += 5; - - if (total_len > SN_PACKET_MAX_SMALL_SIZE) { - /* Length is stored in bytes 1 and 2 */ - total_len += 2; - } - - if (total_len > tx_buf_len) { - /* Buffer too small */ - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - /* Encode header */ - tx_payload = tx_buf; - - if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { - *tx_payload++ = total_len; - } - else { - *tx_payload++ = SN_PACKET_LEN_IND; - tx_payload += MqttEncode_Num(tx_payload, total_len); - } - - *tx_payload++ = SN_MSG_TYPE_UNSUBSCRIBE; - - /* Set flags */ - if (unsubscribe->duplicate) - flags |= SN_PACKET_FLAG_DUPLICATE; - flags |= (SN_PACKET_FLAG_QOS_MASK & - (unsubscribe->qos << SN_PACKET_FLAG_QOS_SHIFT)); - flags |= (SN_PACKET_FLAG_TOPICIDTYPE_MASK & unsubscribe->topic_type); - - *tx_payload++ = flags; - - tx_payload += MqttEncode_Num(tx_payload, unsubscribe->packet_id); - - /* Encode topic */ - if (unsubscribe->topic_type == SN_TOPIC_ID_TYPE_NORMAL) { - /* Topic name is a string */ - XMEMCPY(tx_payload, unsubscribe->topicNameId, - XSTRLEN(unsubscribe->topicNameId)); - } - else { - /* Topic ID or Short name */ - XMEMCPY(tx_payload, unsubscribe->topicNameId, 2); - } - - (void)tx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Decode_UnsubscribeAck(byte *rx_buf, int rx_buf_len, - SN_UnsubscribeAck *unsubscribe_ack) -{ - word16 total_len; - byte *rx_payload = rx_buf, type; - - /* Validate required arguments */ - if (rx_buf == NULL || rx_buf_len <= 0) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Decode fixed header */ - total_len = *rx_payload++; - if (total_len != 4) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); - } - - type = *rx_payload++; - if (type != SN_MSG_TYPE_UNSUBACK) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); - } - - /* Decode SubAck fields */ - if (unsubscribe_ack) { - rx_payload += MqttDecode_Num(rx_payload, &unsubscribe_ack->packet_id); - } - (void)rx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Encode_Disconnect(byte *tx_buf, int tx_buf_len, - SN_Disconnect* disconnect) -{ - int total_len = 2; /* length and message type */ - byte *tx_payload = tx_buf;; - - /* Validate required arguments */ - if (tx_buf == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - if ((disconnect != NULL) && (disconnect->sleepTmr > 0)) { - total_len += 2; /* Sleep duration is set */ - } - - if (total_len > tx_buf_len) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - /* Encode message */ - *tx_payload++ = total_len; - - *tx_payload++ = SN_MSG_TYPE_DISCONNECT; - - if ((disconnect != NULL) && (disconnect->sleepTmr > 0)) { - tx_payload += MqttEncode_Num(tx_payload, disconnect->sleepTmr); - } - (void)tx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Decode_Disconnect(byte *rx_buf, int rx_buf_len) -{ - word16 total_len; - byte *rx_payload = rx_buf, type; - - /* Validate required arguments */ - if (rx_buf == NULL || rx_buf_len <= 0) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Decode fixed header */ - total_len = *rx_payload++; - if (total_len != 2) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); - } - - type = *rx_payload++; - if (type != SN_MSG_TYPE_DISCONNECT) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); - } - - (void)rx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Encode_Ping(byte *tx_buf, int tx_buf_len, SN_PingReq *ping, byte type) -{ - int total_len = 2, clientId_len = 0; - byte *tx_payload = tx_buf; - - /* Validate required arguments */ - if ((tx_buf == NULL) || - ((type != SN_MSG_TYPE_PING_REQ) && (type != SN_MSG_TYPE_PING_RESP))) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - if ((type == SN_MSG_TYPE_PING_REQ) && (ping != NULL) && - (ping->clientId != NULL)) { - total_len += clientId_len = (int)XSTRLEN(ping->clientId); - } - - if (total_len > tx_buf_len) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - *tx_payload++ = (byte)total_len; - - *tx_payload++ = type; - - if (clientId_len > 0) { - XMEMCPY(tx_payload, ping->clientId, clientId_len); - tx_payload += clientId_len; - } - (void)tx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Decode_Ping(byte *rx_buf, int rx_buf_len) -{ - int total_len; - byte *rx_payload = rx_buf, type; - - /* Validate required arguments */ - if (rx_buf == NULL || rx_buf_len <= 0) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - total_len = *rx_payload++; - if (total_len != 2) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); - } - - type = *rx_payload++; - if ((type != SN_MSG_TYPE_PING_REQ) && - (type != SN_MSG_TYPE_PING_RESP)) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); - } - - /* Return total length of packet */ - return total_len; -} - -/* Read return code is length when > 0 */ -int SN_Packet_Read(MqttClient *client, byte* rx_buf, int rx_buf_len, - int timeout_ms) -{ - int rc, len = 0, remain_read = 0; - word16 total_len = 0, idx = 0; - - switch (client->packet.stat) - { - case MQTT_PK_BEGIN: - { - /* Read first 2 bytes */ - if (MqttClient_Flags(client,0,0) & MQTT_CLIENT_FLAG_IS_DTLS) { - rc = MqttSocket_Read(client, rx_buf, 2, timeout_ms); - } else { - rc = MqttSocket_Peek(client, rx_buf, 2, timeout_ms); - } - if (rc < 0) { - return MqttPacket_HandleNetError(client, rc); - } - else if (rc != 2) { - return MqttPacket_HandleNetError(client, - MQTT_TRACE_ERROR(MQTT_CODE_ERROR_NETWORK)); - } - - len = rc; - - if (rx_buf[0] == SN_PACKET_LEN_IND){ - /* Read length stored in first three bytes, type in fourth */ - if (MqttClient_Flags(client,0,0) & MQTT_CLIENT_FLAG_IS_DTLS) { - rc = MqttSocket_Read(client, rx_buf+len, 2, timeout_ms); - if (rc < 0) { - return MqttPacket_HandleNetError(client, rc); - } - else if (rc != 2) { - return MqttPacket_HandleNetError(client, - MQTT_TRACE_ERROR(MQTT_CODE_ERROR_NETWORK)); - } - rc += len; - } - else { - rc = MqttSocket_Peek(client, rx_buf, 4, timeout_ms); - if (rc < 0) { - return MqttPacket_HandleNetError(client, rc); - } - else if (rc != 4) { - return MqttPacket_HandleNetError(client, - MQTT_TRACE_ERROR(MQTT_CODE_ERROR_NETWORK)); - } - len = rc; - } - - (void)MqttDecode_Num(&rx_buf[1], &total_len); - client->packet.header_len = len; - } - else { - /* Length is stored in first byte, type in second */ - total_len = rx_buf[0]; - client->packet.header_len = len; - } - } - FALL_THROUGH; - - case MQTT_PK_READ_HEAD: - { - client->packet.stat = MQTT_PK_READ_HEAD; - } - FALL_THROUGH; - - case MQTT_PK_READ: - { - client->packet.stat = MQTT_PK_READ; - - if (total_len > len) { - client->packet.remain_len = total_len - len; - } - else if ((total_len == 2) || (total_len == 4)) { - /* Handle peek */ - if (MqttClient_Flags(client,0,0) & MQTT_CLIENT_FLAG_IS_DTLS) { - client->packet.remain_len = total_len - len; - } - else { - client->packet.remain_len = total_len; - } - } - else { - client->packet.remain_len = 0; - } - - /* Make sure it does not overflow rx_buf */ - if (client->packet.remain_len > - (rx_buf_len - client->packet.header_len)) { - client->packet.remain_len = rx_buf_len - - client->packet.header_len; - } - if (MqttClient_Flags(client,0,0) & MQTT_CLIENT_FLAG_IS_DTLS) { - total_len -= client->packet.header_len; - idx = client->packet.header_len; - } - /* Read whole message */ - if (client->packet.remain_len > 0) { - rc = MqttSocket_Read(client, &rx_buf[idx], - total_len, timeout_ms); - if (rc <= 0) { - return MqttPacket_HandleNetError(client, rc); - } - remain_read = rc; - } - if (MqttClient_Flags(client,0,0) & MQTT_CLIENT_FLAG_IS_DTLS) { - remain_read += client->packet.header_len; - } - - break; - } - } /* switch (client->packet.stat) */ - - /* reset state */ - client->packet.stat = MQTT_PK_BEGIN; - - /* Return read length */ - return remain_read; -} - -#endif /* WOLFMQTT_SN */ diff --git a/src/mqtt_sn_client.c b/src/mqtt_sn_client.c new file mode 100644 index 000000000..40b7c20c4 --- /dev/null +++ b/src/mqtt_sn_client.c @@ -0,0 +1,1872 @@ +/* mqtt_sn_client.c + * + * Copyright (C) 2006-2023 wolfSSL Inc. + * + * This file is part of wolfMQTT. + * + * wolfMQTT 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; either version 2 of the License, or + * (at your option) any later version. + * + * wolfMQTT 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Include the autoconf generated config.h */ +#ifdef HAVE_CONFIG_H + #include +#endif + +#include "wolfmqtt/mqtt_sn_client.h" + +#ifdef WOLFMQTT_SN + +/* Private functions */ +static int SN_Client_HandlePacket(MqttClient* client, SN_MsgType packet_type, + void* packet_obj, int timeout) +{ + int rc = MQTT_CODE_SUCCESS; + word16 packet_id = 0; + + (void)timeout; + + switch ((int)packet_type) + { + case SN_MSG_TYPE_GWINFO: + { + SN_GwInfo info, *p_info = &info; + if (packet_obj) { + p_info = (SN_GwInfo*)packet_obj; + } + else { + XMEMSET(p_info, 0, sizeof(SN_GwInfo)); + } + + rc = SN_Decode_GWInfo(client->rx_buf, client->packet.buf_len, + p_info); + if (rc <= 0) { + return rc; + } + break; + } + case SN_MSG_TYPE_CONNACK: + { + /* Decode connect ack */ + SN_ConnectAck connect_ack, *p_connect_ack = &connect_ack; + if (packet_obj) { + p_connect_ack = (SN_ConnectAck*)packet_obj; + } + else { + XMEMSET(p_connect_ack, 0, sizeof(SN_ConnectAck)); + } + p_connect_ack->return_code = + client->rx_buf[client->packet.buf_len-1]; + + break; + } + case SN_MSG_TYPE_WILLTOPICREQ: + { + rc = SN_Decode_WillTopicReq(client->rx_buf, client->packet.buf_len); + break; + } + case SN_MSG_TYPE_WILLMSGREQ: + { + rc = SN_Decode_WillMsgReq(client->rx_buf, client->packet.buf_len); + break; + } + case SN_MSG_TYPE_REGISTER: + { + /* Decode register */ + SN_Register reg_s; + + XMEMSET(®_s, 0, sizeof(SN_Register)); + + rc = SN_Decode_Register(client->rx_buf, client->packet.buf_len, + ®_s); + + if (rc > 0) { + /* Initialize the regack */ + reg_s.regack.packet_id = reg_s.packet_id; + reg_s.regack.topicId = reg_s.topicId; + reg_s.regack.return_code = SN_RC_NOTSUPPORTED; + + /* Call the register callback to allow app to + handle new topic ID assignment. */ + if (client->reg_cb != NULL) { + rc = client->reg_cb(reg_s.topicId, + reg_s.topicName, client->reg_ctx); + /* Set the regack return code */ + reg_s.regack.return_code = (rc >= 0) ? SN_RC_ACCEPTED : + SN_RC_INVTOPICNAME; + } + + #ifdef WOLFMQTT_MULTITHREAD + /* Lock send socket mutex */ + rc = wm_SemLock(&client->lockSend); + if (rc != 0) { + return rc; + } + #endif + + /* Encode the register acknowledgment */ + rc = SN_Encode_RegAck(client->tx_buf, client->tx_buf_len, + ®_s.regack); + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d), ID %d", + rc, SN_Packet_TypeDesc(SN_MSG_TYPE_REGACK), + SN_MSG_TYPE_REGACK, reg_s.packet_id); + #endif + if (rc <= 0) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + client->write.len = rc; + + /* Send regack packet */ + rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + if (rc != client->write.len) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + } + + break; + } + case SN_MSG_TYPE_REGACK: + { + /* Decode register ack */ + SN_RegAck regack_s, *p_regack = ®ack_s; + if (packet_obj) { + p_regack = (SN_RegAck*)packet_obj; + } + else { + XMEMSET(p_regack, 0, sizeof(SN_RegAck)); + } + + rc = SN_Decode_RegAck(client->rx_buf, client->packet.buf_len, + p_regack); + if (rc > 0) { + packet_id = p_regack->packet_id; + } + + break; + } + case SN_MSG_TYPE_PUBLISH: + { + SN_Publish pub, *p_pub = &pub; + if (packet_obj) { + p_pub = (SN_Publish*)packet_obj; + } + else { + XMEMSET(p_pub, 0, sizeof(SN_Publish)); + } + + /* Decode publish message */ + rc = SN_Decode_Publish(client->rx_buf, client->packet.buf_len, + p_pub); + if (rc <= 0) { + return rc; + } + + /* Issue callback for new message */ + if (client->msg_cb) { + /* if using the temp publish message buffer, + then populate message context with client context */ + if (&client->msgSN.publish == p_pub) + p_pub->ctx = client->ctx; + rc = client->msg_cb(client, (MqttMessage*)p_pub, 1, 1); + if (rc != MQTT_CODE_SUCCESS) { + return rc; + }; + } + + /* Handle Qos */ + if (p_pub->qos > MQTT_QOS_0) { + SN_MsgType type; + + packet_id = p_pub->packet_id; + + /* Determine packet type to write */ + type = (p_pub->qos == MQTT_QOS_1) ? + SN_MSG_TYPE_PUBACK : + SN_MSG_TYPE_PUBREC; + p_pub->resp.packet_id = packet_id; + + #ifdef WOLFMQTT_MULTITHREAD + /* Lock send socket mutex */ + rc = wm_SemLock(&client->lockSend); + if (rc != 0) { + return rc; + } + #endif + + /* Encode publish response */ + rc = SN_Encode_PublishResp(client->tx_buf, + client->tx_buf_len, type, &p_pub->resp); + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d), ID %d," + " QoS %d", + rc, SN_Packet_TypeDesc(type), type, packet_id, + p_pub->qos); + #endif + if (rc <= 0) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + client->write.len = rc; + + /* Send packet */ + rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + } + break; + } + case SN_MSG_TYPE_PUBACK: + case SN_MSG_TYPE_PUBCOMP: + case SN_MSG_TYPE_PUBREC: + case SN_MSG_TYPE_PUBREL: + { + SN_PublishResp publish_resp, *p_publish_resp = &publish_resp; + if (packet_obj) { + p_publish_resp = (SN_PublishResp*)packet_obj; + } + else { + XMEMSET(p_publish_resp, 0, sizeof(SN_PublishResp)); + } + + /* Decode publish response message */ + rc = SN_Decode_PublishResp(client->rx_buf, client->packet.buf_len, + packet_type, p_publish_resp); + if (rc <= 0) { + return rc; + } + packet_id = p_publish_resp->packet_id; + + /* If Qos then send response */ + if (packet_type == SN_MSG_TYPE_PUBREC || + packet_type == SN_MSG_TYPE_PUBREL) { + + byte resp_type = (packet_type == SN_MSG_TYPE_PUBREC) ? + SN_MSG_TYPE_PUBREL : SN_MSG_TYPE_PUBCOMP; + + #ifdef WOLFMQTT_MULTITHREAD + /* Lock send socket mutex */ + rc = wm_SemLock(&client->lockSend); + if (rc != 0) { + return rc; + } + #endif + + /* Encode publish response */ + p_publish_resp->packet_id = packet_id; + rc = SN_Encode_PublishResp(client->tx_buf, + client->tx_buf_len, resp_type, p_publish_resp); + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d), ID %d", + rc, MqttPacket_TypeDesc(resp_type), resp_type, packet_id); + #endif + if (rc <= 0) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + client->write.len = rc; + + /* Send packet */ + rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + } + break; + } + case SN_MSG_TYPE_SUBACK: + { + /* Decode subscribe ack */ + SN_SubAck subscribe_ack, *p_subscribe_ack = &subscribe_ack; + if (packet_obj) { + p_subscribe_ack = (SN_SubAck*)packet_obj; + } + else { + XMEMSET(p_subscribe_ack, 0, sizeof(SN_SubAck)); + } + + rc = SN_Decode_SubscribeAck(client->rx_buf, client->packet.buf_len, + p_subscribe_ack); + if (rc <= 0) { + return rc; + } + packet_id = p_subscribe_ack->packet_id; + + break; + } + case SN_MSG_TYPE_UNSUBACK: + { + /* Decode unsubscribe ack */ + SN_UnsubscribeAck unsubscribe_ack, + *p_unsubscribe_ack = &unsubscribe_ack; + if (packet_obj) { + p_unsubscribe_ack = (SN_UnsubscribeAck*)packet_obj; + } + else { + XMEMSET(p_unsubscribe_ack, 0, sizeof(SN_UnsubscribeAck)); + } + rc = SN_Decode_UnsubscribeAck(client->rx_buf, + client->packet.buf_len, p_unsubscribe_ack); + if (rc <= 0) { + return rc; + } + packet_id = p_unsubscribe_ack->packet_id; + + break; + } + case SN_MSG_TYPE_PING_RESP: + { + /* Decode ping */ + rc = SN_Decode_Ping(client->rx_buf, client->packet.buf_len); + break; + } + case SN_MSG_TYPE_PING_REQ: + { + /* Decode ping */ + rc = SN_Decode_Ping(client->rx_buf, client->packet.buf_len); + if (rc <= 0) { return rc; } + + #ifdef WOLFMQTT_MULTITHREAD + /* Lock send socket mutex */ + rc = wm_SemLock(&client->lockSend); + if (rc != 0) { + return rc; + } + #endif + + /* Encode the ping packet as a response */ + rc = SN_Encode_Ping(client->tx_buf, client->tx_buf_len, NULL, + SN_MSG_TYPE_PING_RESP); + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d)", + rc, SN_Packet_TypeDesc(SN_MSG_TYPE_PING_RESP), + SN_MSG_TYPE_PING_RESP); + #endif + if (rc <= 0) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + client->write.len = rc; + + /* Send ping resp packet */ + rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + if (rc != client->write.len) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + + break; + } + case SN_MSG_TYPE_WILLTOPICRESP: + { + /* Decode Will Topic Response */ + SN_WillTopicResp resp_s, *resp = &resp_s; + if (packet_obj) { + resp = (SN_WillTopicResp*)packet_obj; + } + else { + XMEMSET(resp, 0, sizeof(SN_WillTopicResp)); + } + rc = SN_Decode_WillTopicResponse(client->rx_buf, + client->packet.buf_len, &resp->return_code); + break; + } + case SN_MSG_TYPE_WILLMSGRESP: + { + /* Decode Will Message Response */ + SN_WillMsgResp resp_s, *resp = &resp_s; + if (packet_obj) { + resp = (SN_WillMsgResp*)packet_obj; + } + else { + XMEMSET(resp, 0, sizeof(SN_WillMsgResp)); + } + rc = SN_Decode_WillMsgResponse(client->rx_buf, + client->packet.buf_len, &resp->return_code); + break; + } + case SN_MSG_TYPE_DISCONNECT: + { + SN_Disconnect disc_s, *disc = &disc_s; + if (packet_obj) { + disc = (SN_Disconnect*)packet_obj; + } + else { + XMEMSET(disc, 0, sizeof(SN_Disconnect)); + } + /* Decode Disconnect */ + rc = SN_Decode_Disconnect(client->rx_buf, client->packet.buf_len); + +#ifdef WOLFMQTT_DISCONNECT_CB + /* Call disconnect callback to allow handling broker disconnect */ + if ((client->disconnect_cb != NULL) && (disc->sleepTmr == 0)) { + client->disconnect_cb(client, rc, client->disconnect_ctx); + } +#endif + break; + } + + default: + { + /* Other types are server side only, ignore */ + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("SN_Client_HandlePacket: Invalid client packet type %u!", + packet_type); + #endif + break; + } + } /* switch (packet_type) */ + + (void)packet_id; + + return rc; +} + +static int SN_Client_WaitType(MqttClient *client, void* packet_obj, + byte wait_type, word16 wait_packet_id, int timeout_ms) +{ + int rc; + word16 packet_id; + SN_MsgType packet_type; +#ifdef WOLFMQTT_MULTITHREAD + MqttPendResp *pendResp; +#endif + MqttMsgStat* mms_stat; + int waitMatchFound; + void* use_packet_obj = NULL; + + if (client == NULL || packet_obj == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* all packet type structures must have MqttMsgStat at top */ + mms_stat = (MqttMsgStat*)packet_obj; + +wait_again: + + /* initialize variables */ + packet_id = 0; + packet_type = SN_MSG_TYPE_RESERVED; +#ifdef WOLFMQTT_MULTITHREAD + pendResp = NULL; +#endif + waitMatchFound = 0; + +#ifdef WOLFMQTT_DEBUG_CLIENT + #ifdef WOLFMQTT_NONBLOCK + if (client->lastRc != MQTT_CODE_CONTINUE) + #endif + { + PRINTF("SN_Client_WaitType: Type %s (%d), ID %d", + SN_Packet_TypeDesc((SN_MsgType)wait_type), + wait_type, wait_packet_id); + } +#endif + + switch (mms_stat->read) + { + case MQTT_MSG_BEGIN: + { + #ifdef WOLFMQTT_MULTITHREAD + /* Lock recv socket mutex */ + rc = wm_SemLock(&client->lockRecv); + if (rc != 0) { + PRINTF("SN_Client_WaitType recv lock error"); + return rc; + } + mms_stat->isReadLocked = 1; + MQTT_TRACE_MSG("SN lockRecv"); + #endif + + /* reset the packet state used by SN_Packet_Read */ + client->packet.stat = MQTT_PK_BEGIN; + } + FALL_THROUGH; + + case MQTT_MSG_WAIT: + { + #ifdef WOLFMQTT_MULTITHREAD + /* Check to see if packet type and id have already completed */ + pendResp = NULL; + rc = wm_SemLock(&client->lockClient); + if (rc == 0) { + if (MqttClient_RespList_Find(client, (MqttPacketType)wait_type, + wait_packet_id, &pendResp)) { + if (pendResp->packetDone) { + /* pending response is already done, so return */ + rc = pendResp->packet_ret; + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("PendResp already Done %p: Rc %d", pendResp, rc); + #endif + MqttClient_RespList_Remove(client, pendResp); + wm_SemUnlock(&client->lockClient); + MQTT_TRACE_MSG("SN unlockRecv"); + wm_SemUnlock(&client->lockRecv); + return rc; + } + } + wm_SemUnlock(&client->lockClient); + } + else { + break; /* error */ + } + #endif /* WOLFMQTT_MULTITHREAD */ + + mms_stat->read = MQTT_MSG_WAIT; + + /* Wait for packet */ + rc = SN_Packet_Read(client, client->rx_buf, client->rx_buf_len, + timeout_ms); + if (rc <= 0) { + break; + } + + client->packet.buf_len = rc; + + /* Decode header */ + rc = SN_Decode_Header(client->rx_buf, client->packet.buf_len, + &packet_type, &packet_id); + if (rc < 0) { + break; + } + + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("Read Packet: Len %d, Type %d, ID %d", + client->packet.buf_len, packet_type, packet_id); + #endif + + mms_stat->read = MQTT_MSG_HEADER; + } + FALL_THROUGH; + + case MQTT_MSG_HEADER: + case MQTT_MSG_PAYLOAD: + case MQTT_MSG_PAYLOAD2: + { + SN_MsgType use_packet_type; + + /* Determine if we received data for this request */ + if ((wait_type == SN_MSG_TYPE_ANY || wait_type == packet_type) && + (wait_packet_id == 0 || wait_packet_id == packet_id)) + { + use_packet_obj = packet_obj; + waitMatchFound = 1; + } + else { + /* use generic packet object */ + use_packet_obj = &client->msgSN; + } + use_packet_type = packet_type; + + #ifdef WOLFMQTT_MULTITHREAD + /* Check to see if we have a pending response for this packet */ + pendResp = NULL; + rc = wm_SemLock(&client->lockClient); + if (rc == 0) { + if (MqttClient_RespList_Find(client, + (MqttPacketType)packet_type, packet_id, &pendResp)) { + /* we found packet match this incoming read packet */ + pendResp->packetProcessing = 1; + if (pendResp->packet_obj != packet_obj) { + use_packet_obj = pendResp->packet_obj; + use_packet_type = (SN_MsgType)pendResp->packet_type; + /* req from another thread... not a match */ + waitMatchFound = 0; + } + } + wm_SemUnlock(&client->lockClient); + } + else { + break; /* error */ + } + #endif /* WOLFMQTT_MULTITHREAD */ + + rc = SN_Client_HandlePacket(client, use_packet_type, use_packet_obj, + timeout_ms); + + #ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE) { + break; + } + #endif + + /* handle success case */ + if (rc >= 0) { + rc = MQTT_CODE_SUCCESS; + } + + #ifdef WOLFMQTT_MULTITHREAD + if (pendResp) { + /* Mark pending response entry done */ + if (wm_SemLock(&client->lockClient) == 0) { + pendResp->packetDone = 1; + pendResp->packet_ret = rc; + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("PendResp Marked Done %p", pendResp); + #endif + pendResp = NULL; + wm_SemUnlock(&client->lockClient); + } + } + #endif /* WOLFMQTT_MULTITHREAD */ + break; + } + + case MQTT_MSG_ACK: /* ack handled in SN_Client_HandlePacket */ + case MQTT_MSG_AUTH: + default: + { + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("SN_Client_WaitType: Invalid state %d!", mms_stat->read); + #endif + rc = MQTT_TRACE_ERROR(MQTT_CODE_ERROR_STAT); + break; + } + } /* switch (msg->stat) */ + +#ifdef WOLFMQTT_DEBUG_CLIENT + if (rc != MQTT_CODE_CONTINUE) { + PRINTF("SN_Client_WaitType: rc %d, state %d", rc, mms_stat->read); + } +#endif + + if (mms_stat->read == MQTT_MSG_WAIT || rc != MQTT_CODE_CONTINUE) { + /* reset state */ + mms_stat->read = MQTT_MSG_BEGIN; + + #ifdef WOLFMQTT_MULTITHREAD + if (mms_stat->isReadLocked) { + mms_stat->isReadLocked = 0; + wm_SemUnlock(&client->lockRecv); + } + #endif + } + +#ifdef WOLFMQTT_NONBLOCK + #ifdef WOLFMQTT_DEBUG_CLIENT + client->lastRc = rc; + #endif + if (rc == MQTT_CODE_CONTINUE) { + return rc; + } +#endif + + /* Clear shared union for next call */ + if ((MqttObject*)use_packet_obj == &client->msg) { + /* reset the members, but not the stat */ + XMEMSET(((byte*)&client->msg.stat) + sizeof(client->msg.stat), 0, + sizeof(client->msg)-sizeof(client->msg.stat)); + } + + if (rc < 0) { + #ifdef WOLFMQTT_DEBUG_CLIENT + if (rc != MQTT_CODE_CONTINUE) { + PRINTF("SN_Client_WaitType: Failure: %s (%d)", + MqttClient_ReturnCodeToString(rc), rc); + } + #endif + return rc; + } + + if (!waitMatchFound) { + /* if we get here, then the we are still waiting for a packet */ + goto wait_again; + } + + return rc; +} + +/* Public Functions */ + +int SN_Client_SetRegisterCallback(MqttClient *client, + SN_ClientRegisterCb regCb, + void* ctx) +{ + int rc = MQTT_CODE_SUCCESS; + + if (client == NULL) + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + +#ifdef WOLFMQTT_MULTITHREAD + rc = wm_SemLock(&client->lockClient); + if (rc == 0) { +#endif + + client->reg_cb = regCb; + client->reg_ctx = ctx; + +#ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockClient); + } +#endif + + return rc; +} + +int SN_Client_SearchGW(MqttClient *client, SN_SearchGw *search) +{ + int rc; + + /* Validate required arguments */ + if (client == NULL || search == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + if (search->stat.write == MQTT_MSG_BEGIN) { + #ifdef WOLFMQTT_MULTITHREAD + /* Lock send socket mutex */ + rc = wm_SemLock(&client->lockSend); + if (rc != 0) { + return rc; + } + #endif + + /* Encode the search packet */ + rc = SN_Encode_SearchGW(client->tx_buf, client->tx_buf_len, + search->radius); + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d)", + rc, SN_Packet_TypeDesc(SN_MSG_TYPE_SEARCHGW), + SN_MSG_TYPE_SEARCHGW); + #endif + if (rc <= 0) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + client->write.len = rc; + + #ifdef WOLFMQTT_MULTITHREAD + rc = wm_SemLock(&client->lockClient); + if (rc == 0) { + /* inform other threads of expected response */ + rc = MqttClient_RespList_Add(client, + (MqttPacketType)SN_MSG_TYPE_GWINFO, 0, + &search->pendResp, &search->gwInfo); + wm_SemUnlock(&client->lockClient); + } + if (rc != 0) { + wm_SemUnlock(&client->lockSend); + return rc; /* Error locking client */ + } + #endif + + /* Send search for gateway packet */ + rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + if (rc != client->write.len) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &search->pendResp); + wm_SemUnlock(&client->lockClient); + } + #endif + return rc; + } + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + + search->stat.write = MQTT_MSG_WAIT; + } + + /* Wait for gateway info packet */ + rc = SN_Client_WaitType(client, &search->gwInfo, SN_MSG_TYPE_GWINFO, 0, + client->cmd_timeout_ms); +#ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE) + return rc; +#endif +#ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &search->pendResp); + wm_SemUnlock(&client->lockClient); + } +#endif + + /* reset state */ + search->stat.write = MQTT_MSG_BEGIN; + + return rc; +} + +static int SN_WillTopic(MqttClient *client, SN_Will *will) +{ + int rc; + + /* Validate required arguments */ + if (client == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + +#ifdef WOLFMQTT_MULTITHREAD + rc = wm_SemLock(&client->lockClient); + if (rc == 0) { + /* inform other threads of expected response */ + rc = MqttClient_RespList_Add(client, + (MqttPacketType)SN_MSG_TYPE_WILLTOPICREQ, 0, + &will->pendResp, &will->resp.topicResp); + wm_SemUnlock(&client->lockClient); + } + if (rc != 0) { + return rc; /* Error locking client */ + } +#endif + + /* Wait for Will Topic Request packet */ + rc = SN_Client_WaitType(client, will, + SN_MSG_TYPE_WILLTOPICREQ, 0, client->cmd_timeout_ms); +#ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE) + return rc; +#endif + +#ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &will->pendResp); + wm_SemUnlock(&client->lockClient); + } +#endif + if (rc == 0) { + #ifdef WOLFMQTT_MULTITHREAD + /* Lock send socket mutex */ + rc = wm_SemLock(&client->lockSend); + if (rc != 0) { + return rc; + } + #endif + + /* Encode Will Topic */ + rc = SN_Encode_WillTopic(client->tx_buf, client->tx_buf_len, + will); + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("EncodePacket: Len %d, Type %s (%d)", + rc, SN_Packet_TypeDesc(SN_MSG_TYPE_WILLTOPIC), + SN_MSG_TYPE_WILLTOPIC); + #endif + if (rc > 0) { + /* Send Will Topic packet */ + client->write.len = rc; + rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + if (rc == client->write.len) { + rc = 0; + } + } + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + } + + return rc; +} + +static int SN_WillMessage(MqttClient *client, SN_Will *will) +{ + int rc; + + /* Validate required arguments */ + if (client == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + +#ifdef WOLFMQTT_MULTITHREAD + rc = wm_SemLock(&client->lockClient); + if (rc == 0) { + /* inform other threads of expected response */ + rc = MqttClient_RespList_Add(client, + (MqttPacketType)SN_MSG_TYPE_WILLMSGREQ, 0, + &will->pendResp, &will->resp.msgResp); + wm_SemUnlock(&client->lockClient); + } + if (rc != 0) { + return rc; /* Error locking client */ + } +#endif + + /* Wait for Will Message Request */ + rc = SN_Client_WaitType(client, &will->resp.msgResp, + SN_MSG_TYPE_WILLMSGREQ, 0, client->cmd_timeout_ms); + +#ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE) + return rc; +#endif + +#ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &will->pendResp); + wm_SemUnlock(&client->lockClient); + } +#endif + + if (rc == 0) { + #ifdef WOLFMQTT_MULTITHREAD + /* Lock send socket mutex */ + rc = wm_SemLock(&client->lockSend); + if (rc != 0) { + return rc; + } + #endif + /* Encode Will Message */ + rc = SN_Encode_WillMsg(client->tx_buf, + client->tx_buf_len, will); + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("EncodePacket: Len %d, Type %s (%d)", + rc, SN_Packet_TypeDesc(SN_MSG_TYPE_WILLMSG), + SN_MSG_TYPE_WILLMSG); + #endif + if (rc > 0) { + /* Send Will Topic packet */ + client->write.len = rc; + rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + if (rc == client->write.len) { + rc = 0; + } + } + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + } + + return rc; +} + +int SN_Client_Connect(MqttClient *client, SN_Connect *mc_connect) +{ + int rc = 0; + static byte will_done; + + /* Validate required arguments */ + if ((client == NULL) || (mc_connect == NULL)) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + if (mc_connect->stat.write == MQTT_MSG_BEGIN) { + + will_done = 0; + + #ifdef WOLFMQTT_MULTITHREAD + /* Lock send socket mutex */ + rc = wm_SemLock(&client->lockSend); + if (rc != 0) { + return rc; + } + #endif + + /* Encode the connect packet */ + rc = SN_Encode_Connect(client->tx_buf, client->tx_buf_len, mc_connect); +#ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d), ID %d, QoS %d", + rc, SN_Packet_TypeDesc(SN_MSG_TYPE_CONNECT), + SN_MSG_TYPE_CONNECT, 0, 0); +#endif + if (rc <= 0) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + client->write.len = rc; + + #ifdef WOLFMQTT_MULTITHREAD + rc = wm_SemLock(&client->lockClient); + if (rc == 0) { + /* inform other threads of expected response */ + rc = MqttClient_RespList_Add(client, + (MqttPacketType)SN_MSG_TYPE_CONNACK, 0, + &mc_connect->pendResp, &mc_connect->ack); + wm_SemUnlock(&client->lockClient); + } + if (rc != 0) { + wm_SemUnlock(&client->lockSend); + return rc; /* Error locking client */ + } + #endif + + /* Send connect packet */ + rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + if (rc != client->write.len) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + + mc_connect->stat.write = MQTT_MSG_WAIT; + } + + if ((mc_connect->enable_lwt == 1) && (will_done != 1)) { + /* If the will is enabled, then the gateway requests the topic and + message in separate packets. */ + rc = SN_WillTopic(client, &mc_connect->will); + if (rc != 0) { + return rc; + } + + rc = SN_WillMessage(client, &mc_connect->will); + if (rc != 0) { + return rc; + } + will_done = 1; + } + + /* Wait for connect ack packet */ + rc = SN_Client_WaitType(client, &mc_connect->ack, + SN_MSG_TYPE_CONNACK, 0, client->cmd_timeout_ms); +#ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE) + return rc; +#endif + +#ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &mc_connect->pendResp); + wm_SemUnlock(&client->lockClient); + } +#endif + + /* reset state */ + mc_connect->stat.write = MQTT_MSG_BEGIN; + + return rc; +} + +int SN_Client_WillTopicUpdate(MqttClient *client, SN_Will *will) +{ + int rc = 0; + + /* Validate required arguments */ + if (client == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + if (will->stat.write == MQTT_MSG_BEGIN) { + #ifdef WOLFMQTT_MULTITHREAD + /* Lock send socket mutex */ + rc = wm_SemLock(&client->lockSend); + if (rc != 0) { + return rc; + } + #endif + + /* Encode Will Topic Update */ + rc = SN_Encode_WillTopicUpdate(client->tx_buf, + client->tx_buf_len, will); + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d)", + rc, SN_Packet_TypeDesc(SN_MSG_TYPE_WILLTOPICUPD), + SN_MSG_TYPE_WILLTOPICUPD); + #endif + if (rc <= 0) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + client->write.len = rc; + + #ifdef WOLFMQTT_MULTITHREAD + rc = wm_SemLock(&client->lockClient); + if (rc == 0) { + /* inform other threads of expected response */ + rc = MqttClient_RespList_Add(client, + (MqttPacketType)SN_MSG_TYPE_WILLTOPICRESP, + 0, &will->pendResp, &will->resp.topicResp); + wm_SemUnlock(&client->lockClient); + } + if (rc != 0) { + wm_SemUnlock(&client->lockSend); + return rc; /* Error locking client */ + } + #endif + + /* Send Will Topic Update packet */ + rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + if (rc != client->write.len) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &will->pendResp); + wm_SemUnlock(&client->lockClient); + } + #endif + } + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + + will->stat.write = MQTT_MSG_WAIT; + } + + /* Wait for Will Topic Update Response packet */ + rc = SN_Client_WaitType(client, &will->resp.topicResp, + SN_MSG_TYPE_WILLTOPICRESP, 0, client->cmd_timeout_ms); +#ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE) + return rc; +#endif +#ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &will->pendResp); + wm_SemUnlock(&client->lockClient); + } +#endif + + /* reset state */ + will->stat.write = MQTT_MSG_BEGIN; + + return rc; +} + +int SN_Client_WillMsgUpdate(MqttClient *client, SN_Will *will) +{ + int rc = 0; + + /* Validate required arguments */ + if ((client == NULL) || (will == NULL)) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + if (will->stat.write == MQTT_MSG_BEGIN) { + #ifdef WOLFMQTT_MULTITHREAD + /* Lock send socket mutex */ + rc = wm_SemLock(&client->lockSend); + if (rc != 0) { + return rc; + } + #endif + /* Encode Will Message Update */ + rc = SN_Encode_WillMsgUpdate(client->tx_buf, + client->tx_buf_len, will); + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d)", + rc, SN_Packet_TypeDesc(SN_MSG_TYPE_WILLTOPICUPD), + SN_MSG_TYPE_WILLTOPICUPD); + #endif + if (rc <= 0) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + client->write.len = rc; + + #ifdef WOLFMQTT_MULTITHREAD + rc = wm_SemLock(&client->lockClient); + if (rc == 0) { + /* inform other threads of expected response */ + rc = MqttClient_RespList_Add(client, + (MqttPacketType)SN_MSG_TYPE_WILLMSGRESP, + 0, &will->pendResp, &will->resp.msgResp); + wm_SemUnlock(&client->lockClient); + } + if (rc != 0) { + wm_SemUnlock(&client->lockSend); + return rc; /* Error locking client */ + } + #endif + + /* Send Will Message Update packet */ + rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + if (rc != client->write.len) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &will->pendResp); + wm_SemUnlock(&client->lockClient); + } + #endif + } + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + + will->stat.write = MQTT_MSG_WAIT; + } + + /* Wait for Will Message Update Response packet */ + rc = SN_Client_WaitType(client, &will->resp.msgResp, + SN_MSG_TYPE_WILLMSGRESP, 0, client->cmd_timeout_ms); +#ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE) + return rc; +#endif +#ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &will->pendResp); + wm_SemUnlock(&client->lockClient); + } +#endif + + /* reset state */ + will->stat.write = MQTT_MSG_BEGIN; + + return rc; + +} + +int SN_Client_Subscribe(MqttClient *client, SN_Subscribe *subscribe) +{ + int rc = -1; + + /* Validate required arguments */ + if (client == NULL || subscribe == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + if (subscribe->stat.write == MQTT_MSG_BEGIN) { + #ifdef WOLFMQTT_MULTITHREAD + /* Lock send socket mutex */ + rc = wm_SemLock(&client->lockSend); + if (rc != 0) { + return rc; + } + #endif + + /* Encode the subscribe packet */ + rc = SN_Encode_Subscribe(client->tx_buf, client->tx_buf_len, + subscribe); + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d), QoS %d", + rc, SN_Packet_TypeDesc(SN_MSG_TYPE_SUBSCRIBE), + SN_MSG_TYPE_SUBSCRIBE, subscribe->qos); + #endif + if (rc <= 0) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + client->write.len = rc; + + #ifdef WOLFMQTT_MULTITHREAD + rc = wm_SemLock(&client->lockClient); + if (rc == 0) { + /* inform other threads of expected response */ + rc = MqttClient_RespList_Add(client, + (MqttPacketType)SN_MSG_TYPE_SUBACK, subscribe->packet_id, + &subscribe->pendResp, &subscribe->subAck); + wm_SemUnlock(&client->lockClient); + } + if (rc != 0) { + wm_SemUnlock(&client->lockSend); + return rc; /* Error locking client */ + } + #endif + + /* Send subscribe packet */ + rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + if (rc != client->write.len) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &subscribe->pendResp); + wm_SemUnlock(&client->lockClient); + } + #endif + return rc; + } + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + + subscribe->stat.write = MQTT_MSG_WAIT; + } + + /* Wait for subscribe ack packet */ + rc = SN_Client_WaitType(client, &subscribe->subAck, + SN_MSG_TYPE_SUBACK, subscribe->packet_id, client->cmd_timeout_ms); + +#ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE) + return rc; +#endif +#ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &subscribe->pendResp); + wm_SemUnlock(&client->lockClient); + } +#endif + + /* reset state */ + subscribe->stat.write = MQTT_MSG_BEGIN; + + return rc; +} + +int SN_Client_Publish(MqttClient *client, SN_Publish *publish) +{ + int rc = MQTT_CODE_SUCCESS; + SN_MsgType resp_type; + + /* Validate required arguments */ + if (client == NULL || publish == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + switch (publish->stat.write) + { + case MQTT_MSG_BEGIN: + { + #ifdef WOLFMQTT_MULTITHREAD + /* Lock send socket mutex */ + rc = wm_SemLock(&client->lockSend); + if (rc != 0) { + return rc; + } + #endif + + /* Encode the publish packet */ + rc = SN_Encode_Publish(client->tx_buf, client->tx_buf_len, + publish); + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d), ID %d," + " QoS %d", + rc, SN_Packet_TypeDesc(SN_MSG_TYPE_PUBLISH), + SN_MSG_TYPE_PUBLISH, publish->packet_id, + publish->qos); + #endif + if (rc <= 0) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + + client->write.len = rc; + publish->buffer_pos = 0; + + #ifdef WOLFMQTT_MULTITHREAD + if ((publish->qos == MQTT_QOS_1) || + (publish->qos == MQTT_QOS_2)) { + resp_type = (publish->qos == MQTT_QOS_1) ? + SN_MSG_TYPE_PUBACK : + SN_MSG_TYPE_PUBCOMP; + + rc = wm_SemLock(&client->lockClient); + if (rc == 0) { + /* inform other threads of expected response */ + rc = MqttClient_RespList_Add(client, + (MqttPacketType)resp_type, publish->packet_id, + &publish->pendResp, &publish->resp); + wm_SemUnlock(&client->lockClient); + } + if (rc != 0) { + wm_SemUnlock(&client->lockSend); + return rc; /* Error locking client */ + } + } + #endif + + publish->stat.write = MQTT_MSG_HEADER; + } + FALL_THROUGH; + + case MQTT_MSG_HEADER: + case MQTT_MSG_PAYLOAD: + case MQTT_MSG_PAYLOAD2: + { + /* Send packet and payload */ + rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + #ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE) + return rc; + #endif + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + + if (rc < 0) { + #ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &publish->pendResp); + wm_SemUnlock(&client->lockClient); + } + #endif + return rc; + } + + if (rc == client->write.len) { + rc = MQTT_CODE_SUCCESS; + } + else { + rc = -1; + } + + /* if not expecting a reply, the reset state and exit */ + if ((publish->qos == MQTT_QOS_0) || + (publish->qos == MQTT_QOS_3)) { + break; + } + + publish->stat.write = MQTT_MSG_WAIT; + } + FALL_THROUGH; + + case MQTT_MSG_WAIT: + { + /* Handle QoS */ + if ((publish->qos == MQTT_QOS_1) || + (publish->qos == MQTT_QOS_2)) { + + /* Determine packet type to wait for */ + resp_type = (publish->qos == MQTT_QOS_1) ? + SN_MSG_TYPE_PUBACK : + SN_MSG_TYPE_PUBCOMP; + + /* Wait for publish response packet */ + rc = SN_Client_WaitType(client, &publish->resp, + resp_type, publish->packet_id, client->cmd_timeout_ms); + #ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE) + break; + #endif + #ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &publish->pendResp); + wm_SemUnlock(&client->lockClient); + } + #endif + + publish->return_code = publish->resp.return_code; + } + + break; + } + + case MQTT_MSG_ACK: + case MQTT_MSG_AUTH: + default: + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("SN_Client_Publish: Invalid state %d!", + publish->stat.write); + #endif + rc = MQTT_TRACE_ERROR(MQTT_CODE_ERROR_STAT); + break; + } /* switch (publish->stat) */ + + /* reset state */ +#ifdef WOLFMQTT_NONBLOCK + if (rc != MQTT_CODE_CONTINUE) +#endif + { + publish->stat.write = MQTT_MSG_BEGIN; + } + if (rc > 0) { + rc = MQTT_CODE_SUCCESS; + } + + return rc; +} + +int SN_Client_Unsubscribe(MqttClient *client, SN_Unsubscribe *unsubscribe) +{ + int rc; + + /* Validate required arguments */ + if (client == NULL || unsubscribe == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + if (unsubscribe->stat.write == MQTT_MSG_BEGIN) { + #ifdef WOLFMQTT_MULTITHREAD + /* Lock send socket mutex */ + rc = wm_SemLock(&client->lockSend); + if (rc != 0) { + return rc; + } + #endif + + /* Encode the subscribe packet */ + rc = SN_Encode_Unsubscribe(client->tx_buf, client->tx_buf_len, + unsubscribe); + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d)", + rc, SN_Packet_TypeDesc(SN_MSG_TYPE_UNSUBSCRIBE), + SN_MSG_TYPE_UNSUBSCRIBE); + #endif + if (rc <= 0) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + client->write.len = rc; + + #ifdef WOLFMQTT_MULTITHREAD + rc = wm_SemLock(&client->lockClient); + if (rc == 0) { + /* inform other threads of expected response */ + rc = MqttClient_RespList_Add(client, + (MqttPacketType)SN_MSG_TYPE_UNSUBACK, + 0, &unsubscribe->pendResp, &unsubscribe->ack); + wm_SemUnlock(&client->lockClient); + } + if (rc != 0) { + wm_SemUnlock(&client->lockSend); + return rc; /* Error locking client */ + } + #endif + + /* Send unsubscribe packet */ + rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + if (rc != client->write.len) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &unsubscribe->pendResp); + wm_SemUnlock(&client->lockClient); + } + #endif + } + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + + unsubscribe->stat.write = MQTT_MSG_WAIT; + } + + /* Wait for unsubscribe ack packet */ + rc = SN_Client_WaitType(client, &unsubscribe->ack, + SN_MSG_TYPE_UNSUBACK, unsubscribe->packet_id, + client->cmd_timeout_ms); + #ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE) + return rc; + #endif + #ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &unsubscribe->pendResp); + wm_SemUnlock(&client->lockClient); + } + #endif + + /* reset state */ + unsubscribe->stat.write = MQTT_MSG_BEGIN; + + return rc; +} + +int SN_Client_Register(MqttClient *client, SN_Register *regist) +{ + int rc; + + /* Validate required arguments */ + if (client == NULL || regist == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + if (regist->stat.write == MQTT_MSG_BEGIN) { + #ifdef WOLFMQTT_MULTITHREAD + /* Lock send socket mutex */ + rc = wm_SemLock(&client->lockSend); + if (rc != 0) { + return rc; + } + #endif + + /* Encode the register packet */ + rc = SN_Encode_Register(client->tx_buf, client->tx_buf_len, regist); + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d)", + rc, SN_Packet_TypeDesc(SN_MSG_TYPE_REGISTER), + SN_MSG_TYPE_REGISTER); + #endif + if (rc <= 0) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + client->write.len = rc; + + #ifdef WOLFMQTT_MULTITHREAD + rc = wm_SemLock(&client->lockClient); + if (rc == 0) { + /* inform other threads of expected response */ + rc = MqttClient_RespList_Add(client, + (MqttPacketType)SN_MSG_TYPE_REGACK, + regist->packet_id, ®ist->pendResp, ®ist->regack); + wm_SemUnlock(&client->lockClient); + } + if (rc != 0) { + wm_SemUnlock(&client->lockSend); + return rc; /* Error locking client */ + } + #endif + + /* Send register packet */ + rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + if (rc != client->write.len) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, ®ist->pendResp); + wm_SemUnlock(&client->lockClient); + } + #endif + return rc; + } + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + + regist->stat.write = MQTT_MSG_WAIT; + } + + /* Wait for register acknowledge packet */ + rc = SN_Client_WaitType(client, ®ist->regack, + SN_MSG_TYPE_REGACK, regist->packet_id, client->cmd_timeout_ms); +#ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE) + return rc; +#endif +#ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, ®ist->pendResp); + wm_SemUnlock(&client->lockClient); + } +#endif + + /* reset state */ + regist->stat.write = MQTT_MSG_BEGIN; + + return rc; +} + +int SN_Client_Ping(MqttClient *client, SN_PingReq *ping) +{ + int rc; + SN_PingReq loc_ping; + + /* Validate required arguments */ + if (client == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + if (ping == NULL) { + XMEMSET(&loc_ping, 0, sizeof(SN_PingReq)); + ping = &loc_ping; + } + + if (ping->stat.write == MQTT_MSG_BEGIN) { + #ifdef WOLFMQTT_MULTITHREAD + /* Lock send socket mutex */ + rc = wm_SemLock(&client->lockSend); + if (rc != 0) { + return rc; + } + #endif + + /* Encode the ping packet as a request */ + rc = SN_Encode_Ping(client->tx_buf, client->tx_buf_len, ping, + SN_MSG_TYPE_PING_REQ); + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d)", + rc, SN_Packet_TypeDesc(SN_MSG_TYPE_PING_REQ), + SN_MSG_TYPE_PING_REQ); + #endif + if (rc <= 0) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + client->write.len = rc; + + #ifdef WOLFMQTT_MULTITHREAD + rc = wm_SemLock(&client->lockClient); + if (rc == 0) { + /* inform other threads of expected response */ + rc = MqttClient_RespList_Add(client, + (MqttPacketType)SN_MSG_TYPE_PING_RESP, 0, + &ping->pendResp, NULL); + wm_SemUnlock(&client->lockClient); + } + if (rc != 0) { + wm_SemUnlock(&client->lockSend); + return rc; /* Error locking client */ + } + #endif + + /* Send ping req packet */ + rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + if (rc != client->write.len) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &ping->pendResp); + wm_SemUnlock(&client->lockClient); + } + #endif + return rc; + } + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + + ping->stat.write = MQTT_MSG_WAIT; + } + + /* Wait for ping resp packet */ + rc = SN_Client_WaitType(client, ping, + SN_MSG_TYPE_PING_RESP, 0, client->cmd_timeout_ms); +#ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE) + return rc; +#endif +#ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &ping->pendResp); + wm_SemUnlock(&client->lockClient); + } +#endif + + /* reset state */ + ping->stat.write = MQTT_MSG_BEGIN; + + return rc; +} + +int SN_Client_Disconnect(MqttClient *client) +{ + return SN_Client_Disconnect_ex(client, NULL); +} + +int SN_Client_Disconnect_ex(MqttClient *client, SN_Disconnect *disconnect) +{ + int rc; + + /* Validate required arguments */ + if (client == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + +#ifdef WOLFMQTT_MULTITHREAD + /* Lock send socket mutex */ + rc = wm_SemLock(&client->lockSend); + if (rc != 0) { + return rc; + } +#endif + + /* Encode the disconnect packet */ + rc = SN_Encode_Disconnect(client->tx_buf, client->tx_buf_len, disconnect); +#ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d)", + rc, SN_Packet_TypeDesc(SN_MSG_TYPE_DISCONNECT), + SN_MSG_TYPE_DISCONNECT); +#endif + if (rc <= 0) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + client->write.len = rc; + +#ifdef WOLFMQTT_MULTITHREAD + if ((disconnect != NULL) && (disconnect->sleepTmr != 0)) { + rc = wm_SemLock(&client->lockClient); + if (rc == 0) { + /* inform other threads of expected response */ + rc = MqttClient_RespList_Add(client, + (MqttPacketType)SN_MSG_TYPE_DISCONNECT, 0, + &disconnect->pendResp, NULL); + wm_SemUnlock(&client->lockClient); + } + if (rc != 0) { + wm_SemUnlock(&client->lockSend); + return rc; /* Error locking client */ + } + } +#endif + + /* Send disconnect packet */ + rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + if (rc != client->write.len) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &disconnect->pendResp); + wm_SemUnlock(&client->lockClient); + } + #endif + return rc; + } +#ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); +#endif + + rc = MQTT_CODE_SUCCESS; + + /* If sleep was set, wait for response disconnect packet */ + if ((disconnect != NULL) && (disconnect->sleepTmr != 0)) { + rc = SN_Client_WaitType(client, disconnect, + SN_MSG_TYPE_DISCONNECT, 0, client->cmd_timeout_ms); + #ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE) + return rc; + #endif + #ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &disconnect->pendResp); + wm_SemUnlock(&client->lockClient); + } + #endif + } + + return rc; +} + +int SN_Client_WaitMessage_ex(MqttClient *client, SN_Object* packet_obj, + int timeout_ms) +{ + return SN_Client_WaitType(client, packet_obj, + SN_MSG_TYPE_ANY, 0, timeout_ms); +} + +int SN_Client_WaitMessage(MqttClient *client, int timeout_ms) +{ + if (client == NULL) + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + return SN_Client_WaitMessage_ex(client, &client->msgSN, timeout_ms); +} + +#endif /* defined WOLFMQTT_SN */ diff --git a/src/mqtt_sn_packet.c b/src/mqtt_sn_packet.c new file mode 100644 index 000000000..258980ed0 --- /dev/null +++ b/src/mqtt_sn_packet.c @@ -0,0 +1,1576 @@ +/* mqtt_sn_packet.c + * + * Copyright (C) 2006-2023 wolfSSL Inc. + * + * This file is part of wolfMQTT. + * + * wolfMQTT 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; either version 2 of the License, or + * (at your option) any later version. + * + * wolfMQTT 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Include the autoconf generated config.h */ +#ifdef HAVE_CONFIG_H + #include +#endif + +#include "wolfmqtt/mqtt_sn_client.h" +#include "wolfmqtt/mqtt_sn_packet.h" + +#ifdef WOLFMQTT_SN +const char* SN_Packet_TypeDesc(SN_MsgType packet_type) +{ + switch (packet_type) { + case SN_MSG_TYPE_ADVERTISE: + return "Advertise"; + case SN_MSG_TYPE_SEARCHGW: + return "Search gateway"; + case SN_MSG_TYPE_GWINFO: + return "Gateway info"; + case SN_MSG_TYPE_CONNECT: + return "Connect"; + case SN_MSG_TYPE_CONNACK: + return "Connect Ack"; + case SN_MSG_TYPE_WILLTOPICREQ: + return "Will topic request"; + case SN_MSG_TYPE_WILLTOPIC: + return "Will topic set"; + case SN_MSG_TYPE_WILLMSGREQ: + return "Will message request"; + case SN_MSG_TYPE_WILLMSG: + return "Will message set"; + case SN_MSG_TYPE_REGISTER: + return "Register"; + case SN_MSG_TYPE_REGACK: + return "Register Ack"; + case SN_MSG_TYPE_PUBLISH: + return "Publish"; + case SN_MSG_TYPE_PUBACK: + return "Publish Ack"; + case SN_MSG_TYPE_PUBCOMP: + return "Publish complete"; + case SN_MSG_TYPE_PUBREC: + return "Publish Received"; + case SN_MSG_TYPE_PUBREL: + return "Publish Release"; + case SN_MSG_TYPE_SUBSCRIBE: + return "Subscribe"; + case SN_MSG_TYPE_SUBACK: + return "Subscribe Ack"; + case SN_MSG_TYPE_UNSUBSCRIBE: + return "Unsubscribe"; + case SN_MSG_TYPE_UNSUBACK: + return "Unsubscribe Ack"; + case SN_MSG_TYPE_PING_REQ: + return "Ping Req"; + case SN_MSG_TYPE_PING_RESP: + return "Ping Resp"; + case SN_MSG_TYPE_DISCONNECT: + return "Disconnect"; + case SN_MSG_TYPE_WILLTOPICUPD: + return "Will topic update"; + case SN_MSG_TYPE_WILLTOPICRESP: + return "WIll topic response"; + case SN_MSG_TYPE_WILLMSGUPD: + return "Will message update"; + case SN_MSG_TYPE_WILLMSGRESP: + return "Will message response"; + case SN_MSG_TYPE_ENCAPMSG: + return "Encapsulated message"; + case SN_MSG_TYPE_ANY: + return "Any"; + default: + break; + } + return "Unknown"; +} + +int SN_Decode_Header(byte *rx_buf, int rx_buf_len, + SN_MsgType* p_packet_type, word16* p_packet_id) +{ + SN_MsgType packet_type; + word16 total_len; + + if (rx_buf == NULL || rx_buf_len < MQTT_PACKET_HEADER_MIN_SIZE) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Decode fixed header */ + total_len = *rx_buf++; + if (total_len == SN_PACKET_LEN_IND) { + /* The length is stored in the next two bytes */ + rx_buf += MqttDecode_Num(rx_buf, &total_len); + } + + if (total_len > rx_buf_len) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + /* Message Type */ + packet_type = (SN_MsgType)*rx_buf++; + + if (p_packet_type) + *p_packet_type = packet_type; + + if (p_packet_id) { + switch(packet_type) { + case SN_MSG_TYPE_REGACK: + case SN_MSG_TYPE_PUBACK: + /* octet 4-5 */ + MqttDecode_Num(rx_buf + 2, p_packet_id); + break; + case SN_MSG_TYPE_PUBCOMP: + case SN_MSG_TYPE_PUBREC: + case SN_MSG_TYPE_PUBREL: + case SN_MSG_TYPE_UNSUBACK: + /* octet 2-3 */ + MqttDecode_Num(rx_buf, p_packet_id); + break; + case SN_MSG_TYPE_SUBACK: + /* octet 5-6 */ + MqttDecode_Num(rx_buf + 3, p_packet_id); + break; + case SN_MSG_TYPE_ADVERTISE: + case SN_MSG_TYPE_SEARCHGW: + case SN_MSG_TYPE_GWINFO: + case SN_MSG_TYPE_CONNECT: + case SN_MSG_TYPE_CONNACK: + case SN_MSG_TYPE_WILLTOPICREQ: + case SN_MSG_TYPE_WILLTOPIC: + case SN_MSG_TYPE_WILLMSGREQ: + case SN_MSG_TYPE_WILLMSG: + case SN_MSG_TYPE_REGISTER: + case SN_MSG_TYPE_PUBLISH: + case SN_MSG_TYPE_SUBSCRIBE: + case SN_MSG_TYPE_UNSUBSCRIBE: + case SN_MSG_TYPE_PING_REQ: + case SN_MSG_TYPE_PING_RESP: + case SN_MSG_TYPE_DISCONNECT: + case SN_MSG_TYPE_WILLTOPICUPD: + case SN_MSG_TYPE_WILLTOPICRESP: + case SN_MSG_TYPE_WILLMSGUPD: + case SN_MSG_TYPE_WILLMSGRESP: + case SN_MSG_TYPE_ENCAPMSG: + case SN_MSG_TYPE_RESERVED: + default: + *p_packet_id = 0; + break; + } + } + + return (int)total_len; +} + +int SN_Decode_Advertise(byte *rx_buf, int rx_buf_len, SN_Advertise *gw_info) +{ + int total_len; + byte *rx_payload = rx_buf, type; + + /* Validate required arguments */ + if (rx_buf == NULL || rx_buf_len <= 0) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Decode fixed header */ + total_len = *rx_payload++; + + /* Check message type */ + type = *rx_payload++; + if (total_len != 5) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); + } + + if (type != SN_MSG_TYPE_ADVERTISE) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); + } + + /* Decode gateway info */ + if (gw_info != NULL) { + gw_info->gwId = *rx_payload++; + + rx_payload += MqttDecode_Num(rx_payload, &gw_info->duration); + } + (void)rx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Encode_SearchGW(byte *tx_buf, int tx_buf_len, byte hops) +{ + int total_len; + byte *tx_payload = tx_buf; + + /* Packet length is not variable */ + total_len = 3; + + /* Validate required arguments */ + if (tx_buf == NULL || tx_buf_len < total_len) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Encode length */ + *tx_payload++ = total_len; + + /* Encode message type */ + *tx_payload++ = SN_MSG_TYPE_SEARCHGW; + + /* Encode radius */ + *tx_payload++ = hops; + (void)tx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Decode_GWInfo(byte *rx_buf, int rx_buf_len, SN_GwInfo *gw_info) +{ + int total_len; + byte *rx_payload = rx_buf, type; + + /* Validate required arguments */ + if (rx_buf == NULL || rx_buf_len <= 0) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Decode fixed header */ + total_len = *rx_payload++; + if (total_len == SN_PACKET_LEN_IND) { + /* The length is stored in the next two bytes */ + rx_payload += MqttDecode_Num(rx_payload, (word16*)&total_len); + } + + if (total_len > rx_buf_len) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + if (total_len < 3) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); + } + /* Check message type */ + type = *rx_payload++; + if (type != SN_MSG_TYPE_GWINFO) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); + } + + /* Decode gateway info */ + if (gw_info != NULL) { + gw_info->gwId = *rx_payload++; + + /* TODO: validate size of gwAddr */ + if (total_len - 3 > 0) { + /* The gateway address is only present if sent by a client */ + XMEMCPY(gw_info->gwAddr, rx_payload, total_len - 3); + } + } + (void)rx_payload; + + /* Return total length of packet */ + return total_len; +} + +/* Packet Type Encoders/Decoders */ +int SN_Encode_Connect(byte *tx_buf, int tx_buf_len, SN_Connect *mc_connect) +{ + word16 total_len, id_len; + byte flags = 0; + byte *tx_payload = tx_buf; + + /* Validate required arguments */ + if ((tx_buf == NULL) || (mc_connect == NULL) || + (mc_connect->client_id == NULL) || (mc_connect->protocol_level == 0)) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Determine packet length */ + total_len = 6; /* Len + Message Type + Flags + ProtocolID + Duration(2) */ + + /* Client ID size */ + id_len = (word16)XSTRLEN(mc_connect->client_id); + id_len = (id_len <= SN_CLIENTID_MAX_LEN) ? id_len : SN_CLIENTID_MAX_LEN; + + total_len += id_len; + + if (total_len > SN_PACKET_MAX_SMALL_SIZE) { + total_len += 2; /* Store len in three bytes */ + } + + if (total_len > tx_buf_len) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + /* Encode length */ + if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { + *tx_payload++ = (byte)total_len; + } + else { + *tx_payload++ = SN_PACKET_LEN_IND; + tx_payload += MqttEncode_Num(tx_payload, total_len); + } + + /* Encode message type */ + *tx_payload++ = SN_MSG_TYPE_CONNECT; + + /* Encode flags */ + if (mc_connect->clean_session) { + flags |= SN_PACKET_FLAG_CLEANSESSION; + } + if (mc_connect->enable_lwt) { + flags |= SN_PACKET_FLAG_WILL; + } + *tx_payload++ = flags; + + /* Protocol version */ + *tx_payload++ = mc_connect->protocol_level; + + /* Encode duration (keep-alive) */ + tx_payload += MqttEncode_Num(tx_payload, mc_connect->keep_alive_sec); + + /* Encode Client ID */ + XMEMCPY(tx_payload, mc_connect->client_id, id_len); + tx_payload += id_len; + (void)tx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Decode_WillTopicReq(byte *rx_buf, int rx_buf_len) +{ + int total_len; + byte *rx_payload = rx_buf, type; + + /* Validate required arguments */ + if (rx_buf == NULL || rx_buf_len <= 0) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Length and MsgType */ + total_len = *rx_payload++; + if (total_len != 2) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); + } + + type = *rx_payload++; + if (type != SN_MSG_TYPE_WILLTOPICREQ) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); + } + (void)rx_payload; + + /* Return total length of packet */ + return total_len; +} + +/* An empty WILLTOPIC message is a WILLTOPIC message without Flags and + WillTopic field (i.e. it is exactly 2 octets long). It is used by a client + to delete the Will topic and the Will message stored in the server */ +int SN_Encode_WillTopic(byte *tx_buf, int tx_buf_len, SN_Will *willTopic) +{ + int total_len; + byte *tx_payload, flags = 0; + + /* Validate required arguments */ + if (tx_buf == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Length and MsgType */ + total_len = 2; + + /* Determine packet length */ + if (willTopic != NULL) { + /* Will Topic is a string */ + total_len += (int)XSTRLEN(willTopic->willTopic); + + /* Flags */ + total_len++; + + if (total_len > SN_PACKET_MAX_SMALL_SIZE) { + /* Length is stored in bytes 1 and 2 */ + total_len += 2; + } + } + + if (total_len > tx_buf_len) { + /* Buffer too small */ + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + /* Encode length */ + tx_payload = tx_buf; + + if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { + *tx_payload++ = total_len; + } + else { + *tx_payload++ = SN_PACKET_LEN_IND; + tx_payload += MqttEncode_Num(tx_payload, total_len); + } + + /* Encode message type */ + *tx_payload++ = SN_MSG_TYPE_WILLTOPIC; + + if (willTopic != NULL) { + int will_len; + + /* Encode flags */ + flags |= ((willTopic->qos << SN_PACKET_FLAG_QOS_SHIFT) & + SN_PACKET_FLAG_QOS_MASK); + flags |= (willTopic->retain != 0) ? SN_PACKET_FLAG_RETAIN : 0; + *tx_payload++ = flags; + + /* Encode Will Topic */ + will_len = (int)XSTRLEN(willTopic->willTopic); + XMEMCPY(tx_payload, willTopic->willTopic, will_len); + tx_payload += will_len; + } + (void)tx_payload; + + return total_len; +} + +int SN_Decode_WillMsgReq(byte *rx_buf, int rx_buf_len) +{ + int total_len; + byte *rx_payload = rx_buf, type; + + /* Validate required arguments */ + if (rx_buf == NULL || rx_buf_len <= 0) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Decode fixed header */ + total_len = *rx_payload++; + + /* Length and MsgType */ + if (total_len != 2){ + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); + } + + /* Message Type */ + type = *rx_payload++; + if (type != SN_MSG_TYPE_WILLMSGREQ) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); + } + (void)rx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Encode_WillMsg(byte *tx_buf, int tx_buf_len, SN_Will *willMsg) +{ + int total_len; + byte *tx_payload; + + /* Validate required arguments */ + if ((tx_buf == NULL) || (willMsg == NULL)) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Length and MsgType */ + total_len = 2; + + /* Determine packet length */ + /* Add Will Message len */ + total_len += willMsg->willMsgLen; + + if (total_len > SN_PACKET_MAX_SMALL_SIZE) { + /* Length is stored in bytes 1 and 2 */ + total_len += 2; + } + + if (total_len > tx_buf_len) { + /* Buffer too small */ + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + /* Encode length */ + tx_payload = tx_buf; + + if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { + *tx_payload++ = total_len; + } + else { + *tx_payload++ = SN_PACKET_LEN_IND; + tx_payload += MqttEncode_Num(tx_payload, total_len); + } + + /* Encode message type */ + *tx_payload++ = SN_MSG_TYPE_WILLMSG; + + /* Encode Will Message */ + XMEMCPY(tx_payload, willMsg->willMsg, willMsg->willMsgLen); + tx_payload += willMsg->willMsgLen; + (void)tx_payload; + + return total_len; +} + +int SN_Encode_WillTopicUpdate(byte *tx_buf, int tx_buf_len, SN_Will *willTopic) +{ + int total_len; + byte *tx_payload, flags = 0; + + /* Validate required arguments */ + if (tx_buf == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Length and MsgType */ + total_len = 2; + + /* Determine packet length */ + if (willTopic != NULL) { + /* Will Topic is a string */ + total_len += (int)XSTRLEN(willTopic->willTopic); + + /* Flags */ + total_len++; + + if (total_len > SN_PACKET_MAX_SMALL_SIZE) { + /* Length is stored in bytes 1 and 2 */ + total_len += 2; + } + } + + if (total_len > tx_buf_len) { + /* Buffer too small */ + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + /* Encode length */ + tx_payload = tx_buf; + + if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { + *tx_payload++ = total_len; + } + else { + *tx_payload++ = SN_PACKET_LEN_IND; + tx_payload += MqttEncode_Num(tx_payload, total_len); + } + + /* Encode message type */ + *tx_payload++ = SN_MSG_TYPE_WILLTOPICUPD; + + if (willTopic != NULL) { + int will_len; + + /* Encode flags */ + flags |= ((willTopic->qos << SN_PACKET_FLAG_QOS_SHIFT) & + SN_PACKET_FLAG_QOS_MASK); + flags |= (willTopic->retain != 0) ? SN_PACKET_FLAG_RETAIN : 0; + *tx_payload++ = flags; + + /* Encode Will Topic */ + will_len = (int)XSTRLEN(willTopic->willTopic); + XMEMCPY(tx_payload, willTopic->willTopic, will_len); + tx_payload += will_len; + } + (void)tx_payload; + + return total_len; + +} + +int SN_Decode_WillTopicResponse(byte *rx_buf, int rx_buf_len, byte *ret_code) +{ + int total_len; + byte *rx_payload = rx_buf, type; + + /* Validate required arguments */ + if (rx_buf == NULL || rx_buf_len <= 0) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Decode fixed header */ + total_len = *rx_payload++; + if (total_len != 3) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); + } + + type = *rx_payload++; + if (type != SN_MSG_TYPE_WILLTOPICRESP) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); + } + + /* Return Code */ + *ret_code = *rx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Encode_WillMsgUpdate(byte *tx_buf, int tx_buf_len, SN_Will *willMsg) +{ + int total_len; + byte *tx_payload; + + /* Validate required arguments */ + if ((tx_buf == NULL) || (willMsg == NULL)) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Length and MsgType */ + total_len = 2; + + /* Determine packet length */ + /* Add Will Message len */ + total_len += willMsg->willMsgLen; + + if (total_len > SN_PACKET_MAX_SMALL_SIZE) { + /* Length is stored in bytes 1 and 2 */ + total_len += 2; + } + + if (total_len > tx_buf_len) { + /* Buffer too small */ + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + /* Encode length */ + tx_payload = tx_buf; + + if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { + *tx_payload++ = total_len; + } + else { + *tx_payload++ = SN_PACKET_LEN_IND; + tx_payload += MqttEncode_Num(tx_payload, total_len); + } + + /* Encode message type */ + *tx_payload++ = SN_MSG_TYPE_WILLMSGUPD; + + /* Encode Will Message */ + XMEMCPY(tx_payload, willMsg->willMsg, willMsg->willMsgLen); + tx_payload += willMsg->willMsgLen; + (void)tx_payload; + + return total_len; +} + +int SN_Decode_WillMsgResponse(byte *rx_buf, int rx_buf_len, byte *ret_code) +{ + int total_len; + byte *rx_payload = rx_buf, type; + + /* Validate required arguments */ + if (rx_buf == NULL || rx_buf_len <= 0) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Decode fixed header */ + total_len = *rx_payload++; + if (total_len != 3) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); + } + + type = *rx_payload++; + if (type != SN_MSG_TYPE_WILLMSGRESP) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); + } + + /* Return Code */ + *ret_code = *rx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Decode_ConnectAck(byte *rx_buf, int rx_buf_len, + SN_ConnectAck *connect_ack) +{ + int total_len; + byte *rx_payload = rx_buf, type; + + /* Validate required arguments */ + if (rx_buf == NULL || rx_buf_len <= 0) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Decode fixed header */ + total_len = *rx_payload++; + if (total_len != 3) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); + } + + type = *rx_payload++; + if (type != SN_MSG_TYPE_CONNACK) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); + } + + /* Decode variable header */ + if (connect_ack) { + connect_ack->return_code = *rx_payload++; + } + (void)rx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Encode_Register(byte *tx_buf, int tx_buf_len, SN_Register *regist) +{ + int total_len, topic_len; + byte *tx_payload; + + /* Validate required arguments */ + if (tx_buf == NULL || regist == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Determine packet length */ + /* Topic name is a string */ + total_len = (int)XSTRLEN(regist->topicName); + + /* Length, MsgType, TopicID (2), and packet_id (2) */ + total_len += 6; + + if (total_len > SN_PACKET_MAX_SMALL_SIZE) { + /* Length is stored in bytes 1 and 2 */ + total_len += 2; + } + + if (total_len > tx_buf_len) { + /* Buffer too small */ + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + /* Encode length */ + tx_payload = tx_buf; + + if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { + *tx_payload++ = total_len; + } + else { + *tx_payload++ = SN_PACKET_LEN_IND; + tx_payload += MqttEncode_Num(tx_payload, total_len); + } + + /* Encode message type */ + *tx_payload++ = SN_MSG_TYPE_REGISTER; + + /* Encode Topic ID */ + tx_payload += MqttEncode_Num(tx_payload, regist->topicId); + + /* Encode Packet ID */ + tx_payload += MqttEncode_Num(tx_payload, regist->packet_id); + + /* Encode Topic Name */ + topic_len = (int)XSTRLEN(regist->topicName); + XMEMCPY(tx_payload, regist->topicName, topic_len); + tx_payload += topic_len; + (void)tx_payload; + + return total_len; +} + +int SN_Decode_Register(byte *rx_buf, int rx_buf_len, SN_Register *regist) +{ + int total_len; + byte *rx_payload = rx_buf, type; + + /* Validate required arguments */ + if (rx_buf == NULL || rx_buf_len <= 0) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Decode fixed header */ + total_len = *rx_payload++; + if (total_len == SN_PACKET_LEN_IND) { + /* The length is stored in the next two bytes */ + rx_payload += MqttDecode_Num(rx_payload, (word16*)&total_len); + } + + if (total_len > rx_buf_len) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + if (total_len < 7) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); + } + + /* Check message type */ + type = *rx_payload++; + if (type != SN_MSG_TYPE_REGISTER) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); + } + + if (regist != NULL) { + /* Decode Topic ID assigned by GW */ + rx_payload += MqttDecode_Num(rx_payload, ®ist->topicId); + + /* Decode packet ID */ + rx_payload += MqttDecode_Num(rx_payload, ®ist->packet_id); + + /* Decode Topic Name */ + regist->topicName = (char*)rx_payload; + + /* Terminate the string */ + rx_payload[total_len-6] = '\0'; + } + (void)rx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Encode_RegAck(byte *tx_buf, int tx_buf_len, SN_RegAck *regack) +{ + int total_len; + byte *tx_payload; + + /* Validate required arguments */ + if (tx_buf == NULL || regack == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Determine packet length */ + /* Length, MsgType, TopicID (2), and MsgId (2), Return Code */ + total_len = 7; + + if (total_len > tx_buf_len) { + /* Buffer too small */ + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + tx_payload = tx_buf; + + /* Encode length */ + *tx_payload++ = total_len; + + /* Encode message type */ + *tx_payload++ = SN_MSG_TYPE_REGACK; + + /* Encode Topic ID */ + tx_payload += MqttEncode_Num(tx_payload, regack->topicId); + + /* Encode Message ID */ + tx_payload += MqttEncode_Num(tx_payload, regack->packet_id); + + /* Encode Return Code */ + *tx_payload += regack->return_code; + + (void)tx_payload; + + return total_len; +} + +int SN_Decode_RegAck(byte *rx_buf, int rx_buf_len, SN_RegAck *regack) +{ + int total_len; + byte *rx_payload = rx_buf, type; + + /* Validate required arguments */ + if (rx_buf == NULL || rx_buf_len <= 0) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Decode fixed header */ + total_len = *rx_payload++; + if (total_len != 7) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); + } + + type = *rx_payload++; + if (type != SN_MSG_TYPE_REGACK) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); + } + + if (regack != NULL) { + /* Decode Topic ID assigned by GW */ + rx_payload += MqttDecode_Num(rx_payload, ®ack->topicId); + + /* Decode packet ID */ + rx_payload += MqttDecode_Num(rx_payload, ®ack->packet_id); + + /* Decode return code */ + regack->return_code = *rx_payload++; + } + (void)rx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Encode_Subscribe(byte *tx_buf, int tx_buf_len, SN_Subscribe *subscribe) +{ + int total_len; + byte *tx_payload, flags = 0x00; + + /* Validate required arguments */ + if (tx_buf == NULL || subscribe == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Determine packet length */ + if (subscribe->topic_type == SN_TOPIC_ID_TYPE_NORMAL) { + /* Topic name is a string */ + total_len = (int)XSTRLEN(subscribe->topicNameId); + } + else { + /* Topic ID or Short name */ + total_len = 2; + } + + /* Length, MsgType, Flags, and MsgID (2) */ + total_len += 5; + + if (total_len > SN_PACKET_MAX_SMALL_SIZE) { + /* Length is stored in bytes 1 and 2 */ + total_len += 2; + } + + if (total_len > tx_buf_len) { + /* Buffer too small */ + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + /* Encode length */ + tx_payload = tx_buf; + + if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { + *tx_payload++ = total_len; + } + else { + *tx_payload++ = SN_PACKET_LEN_IND; + tx_payload += MqttEncode_Num(tx_payload, total_len); + } + + /* Encode message type */ + *tx_payload++ = SN_MSG_TYPE_SUBSCRIBE; + + /* Set flags */ + if (subscribe->duplicate) + flags |= SN_PACKET_FLAG_DUPLICATE; + flags |= (SN_PACKET_FLAG_QOS_MASK & + (subscribe->qos << SN_PACKET_FLAG_QOS_SHIFT)); + flags |= (SN_PACKET_FLAG_TOPICIDTYPE_MASK & subscribe->topic_type); + + *tx_payload++ = flags; + + /* Encode packet ID */ + tx_payload += MqttEncode_Num(tx_payload, subscribe->packet_id); + + /* Encode topic */ + if (subscribe->topic_type == SN_TOPIC_ID_TYPE_NORMAL) { + /* Topic name is a string */ + XMEMCPY(tx_payload, subscribe->topicNameId, XSTRLEN(subscribe->topicNameId)); + } + else { + /* Topic ID */ + XMEMCPY(tx_payload, subscribe->topicNameId, 2); + } + (void)tx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Decode_SubscribeAck(byte* rx_buf, int rx_buf_len, + SN_SubAck *subscribe_ack) +{ + word16 total_len; + byte* rx_payload = rx_buf, type; + + /* Validate required arguments */ + if (rx_buf == NULL || rx_buf_len <= 0) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Decode fixed header */ + total_len = *rx_payload++; + if (total_len != 8) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); + } + + type = *rx_payload++; + if (type != SN_MSG_TYPE_SUBACK) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + /* Decode SubAck fields */ + if (subscribe_ack) { + subscribe_ack->flags = *rx_payload++; + rx_payload += MqttDecode_Num(rx_payload, &subscribe_ack->topicId); + rx_payload += MqttDecode_Num(rx_payload, &subscribe_ack->packet_id); + subscribe_ack->return_code = *rx_payload++; + } + (void)rx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Encode_Publish(byte *tx_buf, int tx_buf_len, SN_Publish *publish) +{ + word16 total_len; + byte *tx_payload = tx_buf; + byte flags = 0; + + /* Validate required arguments */ + if (tx_buf == NULL || publish == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Determine packet length */ + total_len = publish->total_len; + + /* Add length, msgType, flags, topic ID (2), and msgID (2) */ + total_len += 7; + + if (total_len > SN_PACKET_MAX_SMALL_SIZE) { + /* Length is stored in bytes 1 and 2 */ + total_len += 2; + } + + if (total_len > tx_buf_len) { + /* Buffer too small */ + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + /* Encode header */ + if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { + *tx_payload++ = (byte)total_len; + } + else { + *tx_payload++ = SN_PACKET_LEN_IND; + tx_payload += MqttEncode_Num(tx_payload, total_len); + } + + *tx_payload++ = SN_MSG_TYPE_PUBLISH; + + /* Set flags */ + if (publish->duplicate) + flags |= SN_PACKET_FLAG_DUPLICATE; + flags |= (SN_PACKET_FLAG_QOS_MASK & + (publish->qos << SN_PACKET_FLAG_QOS_SHIFT)); + if (publish->retain) + flags |= SN_PACKET_FLAG_RETAIN; + flags |= (SN_PACKET_FLAG_TOPICIDTYPE_MASK & publish->topic_type); + + *tx_payload++ = flags; + + /* Encode topic */ + if ((publish->topic_type == SN_TOPIC_ID_TYPE_SHORT) || + (publish->topic_type == SN_TOPIC_ID_TYPE_PREDEF)) { + /* Short and predefined topic names are 2 chars */ + XMEMCPY(tx_payload, publish->topic_name, 2); + tx_payload += 2; + } + else { + /* Topic ID */ + tx_payload += MqttEncode_Num(tx_payload, *(word16*)publish->topic_name); + } + + tx_payload += MqttEncode_Num(tx_payload, publish->packet_id); + + /* Encode payload */ + XMEMCPY(tx_payload, publish->buffer, publish->total_len); + tx_payload += publish->total_len; + + (void)tx_payload; + + /* Return length of packet placed into tx_buf */ + return total_len; +} + +int SN_Decode_Publish(byte *rx_buf, int rx_buf_len, SN_Publish *publish) +{ + word16 total_len; + byte *rx_payload = rx_buf; + byte flags = 0, type; + + /* Validate required arguments */ + if (rx_buf == NULL || publish == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Decode fixed header */ + total_len = *rx_payload++; + if (total_len == SN_PACKET_LEN_IND) { + /* The length is stored in the next two bytes */ + rx_payload += MqttDecode_Num(rx_payload, &total_len); + } + + if (total_len > rx_buf_len) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + if (total_len < 7) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); + } + + /* Message Type */ + type = *rx_payload++; + if (type != SN_MSG_TYPE_PUBLISH) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); + } + + flags = *rx_payload++; + + publish->topic_name = (char*)rx_payload; + rx_payload += MQTT_DATA_LEN_SIZE; + publish->topic_name_len = MQTT_DATA_LEN_SIZE; + + rx_payload += MqttDecode_Num(rx_payload, &publish->packet_id); + + /* Set flags */ + publish->duplicate = flags & SN_PACKET_FLAG_DUPLICATE; + + publish->qos = (MqttQoS)((flags & SN_PACKET_FLAG_QOS_MASK) >> + SN_PACKET_FLAG_QOS_SHIFT); + + publish->retain = flags & SN_PACKET_FLAG_RETAIN; + + publish->topic_type = flags & SN_PACKET_FLAG_TOPICIDTYPE_MASK; + + /* Decode payload */ + + publish->total_len = total_len - 7; + publish->buffer = rx_payload; + publish->buffer_pos = 0; + publish->buffer_len = publish->total_len; + + /* Return length of packet read from rx_buf */ + return total_len; +} + +int SN_Encode_PublishResp(byte* tx_buf, int tx_buf_len, byte type, + SN_PublishResp *publish_resp) +{ + int total_len; + byte *tx_payload = tx_buf; + + /* Validate required arguments */ + if (tx_buf == NULL || publish_resp == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Determine packet length */ + total_len = (type == SN_MSG_TYPE_PUBACK) ? 7 : 4; + + if (total_len > tx_buf_len) + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + + /* Encode */ + *tx_payload++ = (byte)total_len; + + *tx_payload++ = type; + + if (type == SN_MSG_TYPE_PUBACK) { + tx_payload += MqttEncode_Num(tx_payload, publish_resp->topicId); + } + + tx_payload += MqttEncode_Num(tx_payload, publish_resp->packet_id); + + if (type == SN_MSG_TYPE_PUBACK) { + *tx_payload++ = publish_resp->return_code; + } + (void)tx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Decode_PublishResp(byte* rx_buf, int rx_buf_len, byte type, + SN_PublishResp *publish_resp) +{ + int total_len; + byte rec_type, *rx_payload = rx_buf; + + /* Validate required arguments */ + if (rx_buf == NULL || rx_buf_len <= 0) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Decode */ + total_len = *rx_payload++; + + if(total_len > rx_buf_len) { + /* Buffer too small */ + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + /* Validate packet type */ + rec_type = *rx_payload++; + if (rec_type != type) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); + } + + if (publish_resp) { + if (type == SN_MSG_TYPE_PUBACK) { + rx_payload += MqttDecode_Num(rx_payload, &publish_resp->topicId); + } + + rx_payload += MqttDecode_Num(rx_payload, &publish_resp->packet_id); + + if (type == SN_MSG_TYPE_PUBACK) { + publish_resp->return_code = *rx_payload++; + } + } + (void)rx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Encode_Unsubscribe(byte *tx_buf, int tx_buf_len, + SN_Unsubscribe *unsubscribe) +{ + int total_len; + byte *tx_payload, flags = 0x00; + + /* Validate required arguments */ + if (tx_buf == NULL || unsubscribe == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Determine packet length */ + if (unsubscribe->topic_type == SN_TOPIC_ID_TYPE_NORMAL) { + /* Topic name is a string */ + total_len = (int)XSTRLEN(unsubscribe->topicNameId); + } + else { + /* Topic ID or Short name */ + total_len = 2; + } + + /* Length, MsgType, Flags, and MsgID (2) */ + total_len += 5; + + if (total_len > SN_PACKET_MAX_SMALL_SIZE) { + /* Length is stored in bytes 1 and 2 */ + total_len += 2; + } + + if (total_len > tx_buf_len) { + /* Buffer too small */ + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + /* Encode header */ + tx_payload = tx_buf; + + if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { + *tx_payload++ = total_len; + } + else { + *tx_payload++ = SN_PACKET_LEN_IND; + tx_payload += MqttEncode_Num(tx_payload, total_len); + } + + *tx_payload++ = SN_MSG_TYPE_UNSUBSCRIBE; + + /* Set flags */ + if (unsubscribe->duplicate) + flags |= SN_PACKET_FLAG_DUPLICATE; + flags |= (SN_PACKET_FLAG_QOS_MASK & + (unsubscribe->qos << SN_PACKET_FLAG_QOS_SHIFT)); + flags |= (SN_PACKET_FLAG_TOPICIDTYPE_MASK & unsubscribe->topic_type); + + *tx_payload++ = flags; + + tx_payload += MqttEncode_Num(tx_payload, unsubscribe->packet_id); + + /* Encode topic */ + if (unsubscribe->topic_type == SN_TOPIC_ID_TYPE_NORMAL) { + /* Topic name is a string */ + XMEMCPY(tx_payload, unsubscribe->topicNameId, + XSTRLEN(unsubscribe->topicNameId)); + } + else { + /* Topic ID or Short name */ + XMEMCPY(tx_payload, unsubscribe->topicNameId, 2); + } + + (void)tx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Decode_UnsubscribeAck(byte *rx_buf, int rx_buf_len, + SN_UnsubscribeAck *unsubscribe_ack) +{ + word16 total_len; + byte *rx_payload = rx_buf, type; + + /* Validate required arguments */ + if (rx_buf == NULL || rx_buf_len <= 0) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Decode fixed header */ + total_len = *rx_payload++; + if (total_len != 4) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); + } + + type = *rx_payload++; + if (type != SN_MSG_TYPE_UNSUBACK) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); + } + + /* Decode SubAck fields */ + if (unsubscribe_ack) { + rx_payload += MqttDecode_Num(rx_payload, &unsubscribe_ack->packet_id); + } + (void)rx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Encode_Disconnect(byte *tx_buf, int tx_buf_len, + SN_Disconnect* disconnect) +{ + int total_len = 2; /* length and message type */ + byte *tx_payload = tx_buf;; + + /* Validate required arguments */ + if (tx_buf == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + if ((disconnect != NULL) && (disconnect->sleepTmr > 0)) { + total_len += 2; /* Sleep duration is set */ + } + + if (total_len > tx_buf_len) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + /* Encode message */ + *tx_payload++ = total_len; + + *tx_payload++ = SN_MSG_TYPE_DISCONNECT; + + if ((disconnect != NULL) && (disconnect->sleepTmr > 0)) { + tx_payload += MqttEncode_Num(tx_payload, disconnect->sleepTmr); + } + (void)tx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Decode_Disconnect(byte *rx_buf, int rx_buf_len) +{ + word16 total_len; + byte *rx_payload = rx_buf, type; + + /* Validate required arguments */ + if (rx_buf == NULL || rx_buf_len <= 0) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Decode fixed header */ + total_len = *rx_payload++; + if (total_len != 2) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); + } + + type = *rx_payload++; + if (type != SN_MSG_TYPE_DISCONNECT) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); + } + + (void)rx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Encode_Ping(byte *tx_buf, int tx_buf_len, SN_PingReq *ping, byte type) +{ + int total_len = 2, clientId_len = 0; + byte *tx_payload = tx_buf; + + /* Validate required arguments */ + if ((tx_buf == NULL) || + ((type != SN_MSG_TYPE_PING_REQ) && (type != SN_MSG_TYPE_PING_RESP))) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + if ((type == SN_MSG_TYPE_PING_REQ) && (ping != NULL) && + (ping->clientId != NULL)) { + total_len += clientId_len = (int)XSTRLEN(ping->clientId); + } + + if (total_len > tx_buf_len) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + *tx_payload++ = (byte)total_len; + + *tx_payload++ = type; + + if (clientId_len > 0) { + XMEMCPY(tx_payload, ping->clientId, clientId_len); + tx_payload += clientId_len; + } + (void)tx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Decode_Ping(byte *rx_buf, int rx_buf_len) +{ + int total_len; + byte *rx_payload = rx_buf, type; + + /* Validate required arguments */ + if (rx_buf == NULL || rx_buf_len <= 0) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + total_len = *rx_payload++; + if (total_len != 2) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); + } + + type = *rx_payload++; + if ((type != SN_MSG_TYPE_PING_REQ) && + (type != SN_MSG_TYPE_PING_RESP)) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); + } + + /* Return total length of packet */ + return total_len; +} + +/* Read return code is length when > 0 */ +int SN_Packet_Read(MqttClient *client, byte* rx_buf, int rx_buf_len, + int timeout_ms) +{ + int rc, len = 0, remain_read = 0; + word16 total_len = 0, idx = 0; + + switch (client->packet.stat) + { + case MQTT_PK_BEGIN: + { + /* Read first 2 bytes */ + if (MqttClient_Flags(client,0,0) & MQTT_CLIENT_FLAG_IS_DTLS) { + rc = MqttSocket_Read(client, rx_buf, 2, timeout_ms); + } else { + rc = MqttSocket_Peek(client, rx_buf, 2, timeout_ms); + } + if (rc < 0) { + return MqttPacket_HandleNetError(client, rc); + } + else if (rc != 2) { + return MqttPacket_HandleNetError(client, + MQTT_TRACE_ERROR(MQTT_CODE_ERROR_NETWORK)); + } + + len = rc; + + if (rx_buf[0] == SN_PACKET_LEN_IND){ + /* Read length stored in first three bytes, type in fourth */ + if (MqttClient_Flags(client,0,0) & MQTT_CLIENT_FLAG_IS_DTLS) { + rc = MqttSocket_Read(client, rx_buf+len, 2, timeout_ms); + if (rc < 0) { + return MqttPacket_HandleNetError(client, rc); + } + else if (rc != 2) { + return MqttPacket_HandleNetError(client, + MQTT_TRACE_ERROR(MQTT_CODE_ERROR_NETWORK)); + } + rc += len; + } + else { + rc = MqttSocket_Peek(client, rx_buf, 4, timeout_ms); + if (rc < 0) { + return MqttPacket_HandleNetError(client, rc); + } + else if (rc != 4) { + return MqttPacket_HandleNetError(client, + MQTT_TRACE_ERROR(MQTT_CODE_ERROR_NETWORK)); + } + len = rc; + } + + (void)MqttDecode_Num(&rx_buf[1], &total_len); + client->packet.header_len = len; + } + else { + /* Length is stored in first byte, type in second */ + total_len = rx_buf[0]; + client->packet.header_len = len; + } + } + FALL_THROUGH; + + case MQTT_PK_READ_HEAD: + { + client->packet.stat = MQTT_PK_READ_HEAD; + } + FALL_THROUGH; + + case MQTT_PK_READ: + { + client->packet.stat = MQTT_PK_READ; + + if (total_len > len) { + client->packet.remain_len = total_len - len; + } + else if ((total_len == 2) || (total_len == 4)) { + /* Handle peek */ + if (MqttClient_Flags(client,0,0) & MQTT_CLIENT_FLAG_IS_DTLS) { + client->packet.remain_len = total_len - len; + } + else { + client->packet.remain_len = total_len; + } + } + else { + client->packet.remain_len = 0; + } + + /* Make sure it does not overflow rx_buf */ + if (client->packet.remain_len > + (rx_buf_len - client->packet.header_len)) { + client->packet.remain_len = rx_buf_len - + client->packet.header_len; + } + if (MqttClient_Flags(client,0,0) & MQTT_CLIENT_FLAG_IS_DTLS) { + total_len -= client->packet.header_len; + idx = client->packet.header_len; + } + /* Read whole message */ + if (client->packet.remain_len > 0) { + rc = MqttSocket_Read(client, &rx_buf[idx], + total_len, timeout_ms); + if (rc <= 0) { + return MqttPacket_HandleNetError(client, rc); + } + remain_read = rc; + } + if (MqttClient_Flags(client,0,0) & MQTT_CLIENT_FLAG_IS_DTLS) { + remain_read += client->packet.header_len; + } + + break; + } + } /* switch (client->packet.stat) */ + + /* reset state */ + client->packet.stat = MQTT_PK_BEGIN; + + /* Return read length */ + return remain_read; +} + +#endif /* WOLFMQTT_SN */ diff --git a/wolfmqtt.vcxproj b/wolfmqtt.vcxproj index 9f79e47e0..406d8842b 100755 --- a/wolfmqtt.vcxproj +++ b/wolfmqtt.vcxproj @@ -145,10 +145,14 @@ + + + + diff --git a/wolfmqtt/include.am b/wolfmqtt/include.am index 0096389b2..77dfcc9da 100644 --- a/wolfmqtt/include.am +++ b/wolfmqtt/include.am @@ -11,3 +11,8 @@ nobase_include_HEADERS+= \ wolfmqtt/visibility.h \ wolfmqtt/options.h \ wolfmqtt/vs_settings.h + +if BUILD_SN +nobase_include_HEADERS+= wolfmqtt/mqtt_sn_client.h \ + wolfmqtt/mqtt_sn_packet.h +endif diff --git a/wolfmqtt/mqtt_client.h b/wolfmqtt/mqtt_client.h index 8c8346067..f381d2f8f 100644 --- a/wolfmqtt/mqtt_client.h +++ b/wolfmqtt/mqtt_client.h @@ -42,6 +42,11 @@ #include "wolfmqtt/mqtt_packet.h" #include "wolfmqtt/mqtt_socket.h" +#ifdef WOLFMQTT_SN +#include "wolfmqtt/mqtt_sn_packet.h" +#endif + + /* This macro allows the disconnect callback to be triggered when * MqttClient_Disconnect_ex is called. Normally the CB is only used to handle * errors from MqttPacket_HandleNetError. @@ -133,6 +138,8 @@ typedef struct _MqttSk { #ifdef WOLFMQTT_PROPERTY_CB typedef int (*MqttPropertyCb)(struct _MqttClient* client, MqttProp* head, void* ctx); #endif + + #ifdef WOLFMQTT_SN /*! \brief Mqtt-SN Register Callback. * A GW sends a REGISTER message to a client if it wants to @@ -206,6 +213,9 @@ typedef struct _MqttClient { #endif } MqttClient; +#ifdef WOLFMQTT_SN +#include "wolfmqtt/mqtt_sn_client.h" +#endif /* Application Interfaces */ @@ -567,190 +577,17 @@ WOLFMQTT_API const char* MqttClient_ReturnCodeToString( "not compiled in" #endif /* WOLFMQTT_NO_ERROR_STRINGS */ -#ifdef WOLFMQTT_SN -/*! \brief Encodes and sends the a message to search for a gateway and - waits for the gateway info response message. - * \note This is a blocking function that will wait for MqttNet.read - * \param client Pointer to MqttClient structure - * \param search Pointer to SN_SearchGW structure initialized - with hop radius. - * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* - (see enum MqttPacketResponseCodes) - */ -WOLFMQTT_API int SN_Client_SearchGW( - MqttClient *client, - SN_SearchGw *search); - -/*! \brief Encodes and sends the Connect packet and waits for the - Connect Acknowledgment packet. If Will is enabled, the gateway - prompts for LWT Topic and Message. Sending an empty will topic - indicates that the client wishes to delete the Will topic and - the Will message stored in the server. - * \note This is a blocking function that will wait for MqttNet.read - * \param client Pointer to MqttClient structure - * \param connect Pointer to SN_Connect structure initialized - with connect parameters - * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* - (see enum MqttPacketResponseCodes) - */ -WOLFMQTT_API int SN_Client_Connect( - MqttClient *client, - SN_Connect *connect); - -/*! \brief Encodes and sends the MQTT-SN Will Topic Update packet. Sending - a NULL 'will' indicates that the client wishes to delete the - Will topic and the Will message stored in the server. - * \note This is a blocking function that will wait for MqttNet.read - * \param client Pointer to MqttClient structure - * \param will Pointer to SN_Will structure initialized - with topic and message parameters. NULL is valid. - * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* - (see enum MqttPacketResponseCodes) - */ -WOLFMQTT_API int SN_Client_WillTopicUpdate(MqttClient *client, SN_Will *will); - -/*! \brief Encodes and sends the MQTT-SN Will Message Update packet. - * \note This is a blocking function that will wait for MqttNet.read - * \param client Pointer to MqttClient structure - * \param will Pointer to SN_Will structure initialized - with topic and message parameters. NULL is valid. - * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* - (see enum MqttPacketResponseCodes) - */ -WOLFMQTT_API int SN_Client_WillMsgUpdate(MqttClient *client, SN_Will *will); - -/*! \brief Encodes and sends the MQTT-SN Register packet and waits for the - Register Acknowledge packet. The Register packet is sent by a - client to a GW for requesting a topic id value for the included - topic name. It is also sent by a GW to inform a client about - the topic id value it has assigned to the included topic name. - * \note This is a blocking function that will wait for MqttNet.read - * \param client Pointer to MqttClient structure - * \param regist Pointer to SN_Register structure - * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* - (see enum MqttPacketResponseCodes) - */ -WOLFMQTT_API int SN_Client_Register( - MqttClient *client, - SN_Register *regist); - - -/*! \brief Sets a register callback with custom context - * \param client Pointer to MqttClient structure - (uninitialized is okay) - * \param regCb Pointer to register callback function - * \param ctx Pointer to your own context - * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_BAD_ARG - */ -WOLFMQTT_API int SN_Client_SetRegisterCallback( - MqttClient *client, - SN_ClientRegisterCb regCb, - void* ctx); - - -/*! \brief Encodes and sends the MQTT-SN Publish packet and waits for the - Publish response (if QoS > 0). - * \note This is a blocking function that will wait for MqttNet.read - * If QoS level = 1 then will wait for PUBLISH_ACK. - * If QoS level = 2 then will wait for PUBLISH_REC then send - PUBLISH_REL and wait for PUBLISH_COMP. - * \param client Pointer to MqttClient structure - * \param publish Pointer to SN_Publish structure initialized - with message data - * Note: SN_Publish and MqttMessage are same - structure. - * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* - (see enum MqttPacketResponseCodes) - */ -WOLFMQTT_API int SN_Client_Publish( - MqttClient *client, - SN_Publish *publish); - -/*! \brief Encodes and sends the MQTT-SN Subscribe packet and waits for the - Subscribe Acknowledgment packet containing the assigned - topic ID. - * \note This is a blocking function that will wait for MqttNet.read - * \param client Pointer to MqttClient structure - * \param subscribe Pointer to SN_Subscribe structure initialized with - subscription topic list and desired QoS. - * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* - (see enum MqttPacketResponseCodes) - */ -WOLFMQTT_API int SN_Client_Subscribe( - MqttClient *client, - SN_Subscribe *subscribe); - -/*! \brief Encodes and sends the MQTT-SN Unsubscribe packet and waits for - the Unsubscribe Acknowledgment packet - * \note This is a blocking function that will wait for MqttNet.read - * \param client Pointer to MqttClient structure - * \param unsubscribe Pointer to SN_Unsubscribe structure initialized - with topic ID. - * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* - (see enum MqttPacketResponseCodes) - */ -WOLFMQTT_API int SN_Client_Unsubscribe( - MqttClient *client, - SN_Unsubscribe *unsubscribe); - -/*! \brief Encodes and sends the MQTT-SN Disconnect packet. Client may - send the disconnect with a duration to indicate the client is - entering the "asleep" state. - * \note This is a non-blocking function that will try and send using - MqttNet.write - * \param client Pointer to MqttClient structure - * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* - (see enum MqttPacketResponseCodes) - */ -WOLFMQTT_API int SN_Client_Disconnect( - MqttClient *client); - -/*! \brief Encodes and sends the MQTT-SN Disconnect packet. Client may - send the disconnect with a duration to indicate the client is - entering the "asleep" state. - * \note This is a non-blocking function that will try and send using - MqttNet.write - * \param client Pointer to MqttClient structure - * \param disconnect Pointer to SN_Disconnect structure. NULL is valid. - * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* - (see enum MqttPacketResponseCodes) - */ -WOLFMQTT_API int SN_Client_Disconnect_ex( - MqttClient *client, - SN_Disconnect *disconnect); - - -/*! \brief Encodes and sends the MQTT-SN Ping Request packet and waits - for the Ping Response packet. If client is in the "asleep" - state and wants to notify the gateway that it is entering the - "awake" state, it should add it's client ID to the ping - request. - * \note This is a blocking function that will wait for MqttNet.read - * \param client Pointer to MqttClient structure - * \param ping Pointer to SN_PingReq structure. NULL is valid. - * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* - (see enum MqttPacketResponseCodes) - */ -WOLFMQTT_API int SN_Client_Ping( - MqttClient *client, - SN_PingReq *ping); - -/*! \brief Waits for packets to arrive. Incoming publish messages - will arrive via callback provided in MqttClient_Init. - * \note This is a blocking function that will wait for MqttNet.read - * \param client Pointer to MqttClient structure - * \param timeout_ms Milliseconds until read timeout - * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* - (see enum MqttPacketResponseCodes) - */ -WOLFMQTT_API int SN_Client_WaitMessage( - MqttClient *client, - int timeout_ms); - -WOLFMQTT_API int SN_Client_WaitMessage_ex(MqttClient *client, SN_Object* packet_obj, - int timeout_ms); - -#endif /* WOLFMQTT_SN */ +/* Internal functions */ +#ifdef WOLFMQTT_MULTITHREAD +WOLFMQTT_LOCAL int MqttClient_RespList_Find(MqttClient *client, + MqttPacketType packet_type, word16 packet_id, MqttPendResp **retResp); +WOLFMQTT_LOCAL void MqttClient_RespList_Remove(MqttClient *client, + MqttPendResp *rmResp); +WOLFMQTT_LOCAL int MqttClient_RespList_Add(MqttClient *client, + MqttPacketType packet_type, word16 packet_id, MqttPendResp *newResp, + void *packet_obj); +#endif +WOLFMQTT_LOCAL int MqttPacket_HandleNetError(MqttClient *client, int rc); #ifdef __cplusplus } /* extern "C" */ diff --git a/wolfmqtt/mqtt_packet.h b/wolfmqtt/mqtt_packet.h index 73e6e0a96..251a7c6d2 100644 --- a/wolfmqtt/mqtt_packet.h +++ b/wolfmqtt/mqtt_packet.h @@ -720,463 +720,6 @@ WOLFMQTT_LOCAL MqttProp* MqttProps_FindType(MqttProp *head, #define MqttPacket_TypeDesc(x) "not compiled in" #endif - -#ifdef WOLFMQTT_SN - -/* Note that because MQTT-SN does not support message fragmentation and - reassembly, the maximum message length that could be used in a network is - governed by the maximum packet size that is supported by that network, - and not by the maximum length that could be encoded by MQTT-SN. */ -#ifndef WOLFMQTT_SN_MAXPACKET_SIZE -#define WOLFMQTT_SN_MAXPACKET_SIZE 1024 -#endif - -/* The SN_GwAddr field has a variable length and contains the address of a GW. - Its depends on the network over which MQTT-SN operates and is indicated in - the first octet of this field. For example, in a ZigBee network the network - address is 2-octet long. */ -typedef word16 SN_GwAddr ; - -/* RETURN CODE values */ -enum SN_ReturnCodes { - SN_RC_ACCEPTED = 0x00, - SN_RC_CONGESTION = 0x01, - SN_RC_INVTOPICNAME = 0x02, - SN_RC_NOTSUPPORTED = 0x03 - /* 0x04 - 0xFF reserved */ -}; - -/* MESSAGE HEADER */ -/* Message types: Located in last byte of header */ -typedef enum _SN_MsgType { - SN_MSG_TYPE_ADVERTISE = 0x00, - SN_MSG_TYPE_SEARCHGW = 0x01, - SN_MSG_TYPE_GWINFO = 0x02, - /* 0x03 reserved */ - SN_MSG_TYPE_CONNECT = 0x04, - SN_MSG_TYPE_CONNACK = 0x05, - SN_MSG_TYPE_WILLTOPICREQ = 0x06, - SN_MSG_TYPE_WILLTOPIC = 0x07, - SN_MSG_TYPE_WILLMSGREQ = 0x08, - SN_MSG_TYPE_WILLMSG = 0x09, - SN_MSG_TYPE_REGISTER = 0x0A, - SN_MSG_TYPE_REGACK = 0x0B, - SN_MSG_TYPE_PUBLISH = 0x0C, - SN_MSG_TYPE_PUBACK = 0x0D, - SN_MSG_TYPE_PUBCOMP = 0x0E, - SN_MSG_TYPE_PUBREC = 0x0F, - SN_MSG_TYPE_PUBREL = 0x10, - /* 0x11 reserved */ - SN_MSG_TYPE_SUBSCRIBE = 0x12, - SN_MSG_TYPE_SUBACK = 0x13, - SN_MSG_TYPE_UNSUBSCRIBE = 0x14, - SN_MSG_TYPE_UNSUBACK = 0x15, - SN_MSG_TYPE_PING_REQ = 0x16, - SN_MSG_TYPE_PING_RESP = 0x17, - SN_MSG_TYPE_DISCONNECT = 0x18, - /* 0x19 reserved */ - SN_MSG_TYPE_WILLTOPICUPD = 0x1A, - SN_MSG_TYPE_WILLTOPICRESP = 0x1B, - SN_MSG_TYPE_WILLMSGUPD = 0x1C, - SN_MSG_TYPE_WILLMSGRESP = 0x1D, - /* 0x1E - 0xFD reserved */ - SN_MSG_TYPE_ENCAPMSG = 0xFE, /* Encapsulated message */ - /* 0xFF reserved */ - SN_MSG_TYPE_RESERVED = 0xFF, - SN_MSG_TYPE_ANY = 0xFF -} SN_MsgType; - -/* Topic ID types */ -enum SN_TopicId_Types { - SN_TOPIC_ID_TYPE_NORMAL = 0x0, - SN_TOPIC_ID_TYPE_PREDEF = 0x1, - SN_TOPIC_ID_TYPE_SHORT = 0x2 -}; - -enum SN_PacketFlags { - SN_PACKET_FLAG_TOPICIDTYPE_MASK = 0x3, - SN_PACKET_FLAG_CLEANSESSION = 0x4, - SN_PACKET_FLAG_WILL = 0x8, - SN_PACKET_FLAG_RETAIN = 0x10, - SN_PACKET_FLAG_QOS_MASK = 0x60, - SN_PACKET_FLAG_QOS_SHIFT = 0x5, - SN_PACKET_FLAG_DUPLICATE = 0x80 -}; - -/* Message Header: Size is variable 2 - 4 bytes */ - -/* If the first byte of the packet len is 0x01, then the packet size is - greater than 0xFF and is stored in the next two bytes */ -#define SN_PACKET_LEN_IND 0x01 - -#define SN_PACKET_MAX_SMALL_SIZE 0xFF - -/* Gateway (GW) messages */ -/* Advertise message */ -typedef struct _SN_AdvertiseMsg { - MqttMsgStat stat; - - byte gwId; /* ID of the gateway that sent this message */ - word16 duration; /* Seconds until next Advertise - is broadcast by this gateway */ -} SN_Advertise; - -typedef struct _SN_GwInfo { - MqttMsgStat stat; - - byte gwId; /* ID of the gateway that sent this message */ - SN_GwAddr* gwAddr; /* Address of the indicated gateway */ -} SN_GwInfo; - -typedef struct _SN_SearchGw { - MqttMsgStat stat; -#ifdef WOLFMQTT_MULTITHREAD - MqttPendResp pendResp; -#endif - - byte radius; /* Broadcast radius (in hops) */ - SN_GwInfo gwInfo; -} SN_SearchGw; - -/* Connect Protocol */ -#define SN_PROTOCOL_ID_1 0x01 -#define SN_PROTOCOL_ID SN_PROTOCOL_ID_1 - -#define SN_CLIENTID_MAX_LEN 23 - -/* Connect Ack message structure */ -typedef struct _SN_ConnectAck { - MqttMsgStat stat; - - byte return_code; -} SN_ConnectAck; - -/* WILL TOPIC */ -typedef struct _SN_WillTopicUpd { - MqttMsgStat stat; -#ifdef WOLFMQTT_MULTITHREAD - MqttPendResp pendResp; -#endif - - byte flags; - char* willTopic; /* contains the Will topic name */ -} SN_WillTopicUpd; - -typedef struct _SN_WillMsgUpd { - MqttMsgStat stat; -#ifdef WOLFMQTT_MULTITHREAD - MqttPendResp pendResp; -#endif - - char* willMsg; -} SN_WillMsgUpd; - -typedef struct _SN_WillTopicResp { - MqttMsgStat stat; - - byte return_code; -} SN_WillTopicResp; - -typedef SN_WillTopicResp SN_WillMsgResp; - -typedef union _SN_WillResp { - SN_WillMsgResp msgResp; - SN_WillMsgUpd msgUpd; - SN_WillTopicResp topicResp; - SN_WillTopicUpd topicUpd; -} SN_WillResp; - -typedef struct _SN_Will { - MqttMsgStat stat; -#ifdef WOLFMQTT_MULTITHREAD - MqttPendResp pendResp; -#endif - - byte qos; - byte retain; - const char* willTopic; - byte* willMsg; - word16 willMsgLen; - - SN_WillResp resp; -} SN_Will; - -/* Connect */ -typedef struct _SN_Connect { - MqttMsgStat stat; -#ifdef WOLFMQTT_MULTITHREAD - MqttPendResp pendResp; -#endif - - word16 keep_alive_sec; - byte clean_session; - const char *client_id; - - /* Protocol version: 1=v1.2 (default) */ - byte protocol_level; - - /* Optional Last will and testament */ - byte enable_lwt; - SN_Will will; - - /* Ack data */ - SN_ConnectAck ack; -} SN_Connect; - -/* REGISTER protocol */ -typedef struct _SN_RegAck { - MqttMsgStat stat; - - word16 topicId; - word16 packet_id; - byte return_code; -} SN_RegAck; - -typedef struct _SN_Register { - MqttMsgStat stat; -#ifdef WOLFMQTT_MULTITHREAD - MqttPendResp pendResp; -#endif - - word16 topicId; - word16 packet_id; - const char* topicName; - SN_RegAck regack; -} SN_Register; - -/* PUBLISH RESPONSE */ -/* This is the response struct for PUBREC, PUBREL, and PUBCOMP */ -/* If QoS = 0: No response */ -/* If QoS = 1: Expect response packet with type = SN_MSG_TYPE_PUBACK */ -/* If QoS = 2: Expect response packet with type = SN_MSG_TYPE_PUBREC */ -/* Message ID required if QoS is 1 or 2 */ -/* If QoS = 2: Send SN_MSG_TYPE_PUBREL with msgId to complete - QoS2 protocol exchange */ -/* Expect response packet with type = SN_MSG_TYPE_PUBCOMP */ -typedef struct _SN_PublishResp { - MqttMsgStat stat; - - word16 packet_id; - word16 topicId; /* PUBACK Only */ - byte return_code; /* PUBACK Only */ -} SN_PublishResp; - -/* PUBLISH protocol */ -typedef struct _SN_Publish { - MqttMsgStat stat; /* must be first member at top */ -#ifdef WOLFMQTT_MULTITHREAD - MqttPendResp pendResp; -#endif - - /* BEGIN: THIS SECTION NEEDS TO MATCH MqttMessage */ - word16 packet_id; - byte type; - MqttQoS qos; - byte retain; - byte duplicate; - byte topic_type; - byte return_code; - - const char *topic_name; /* Pointer is valid only when - msg_new set in callback */ - word16 topic_name_len; - word32 total_len; /* Payload total length */ - byte *buffer; /* Payload buffer */ - word32 buffer_len; /* Payload buffer length */ - word32 buffer_pos; /* Payload buffer position */ - - /* Used internally for TX/RX buffers */ - byte buffer_new; /* flag to indicate new message */ - word32 intBuf_len; /* Buffer length */ - word32 intBuf_pos; /* Buffer position */ - - void* ctx; /* user supplied context for publish callbacks */ - /* END: THIS SECTION NEEDS TO MATCH MqttMessage */ - - SN_PublishResp resp; -} SN_Publish; - -/* SUBSCRIBE ACK */ -typedef struct _SN_SubAck { - MqttMsgStat stat; - - byte flags; - word16 topicId; - word16 packet_id; - byte return_code; -} SN_SubAck; - -/* SUBSCRIBE */ -typedef struct _SN_Subscribe { - MqttMsgStat stat; -#ifdef WOLFMQTT_MULTITHREAD - MqttPendResp pendResp; -#endif - - byte duplicate; - byte qos; - word16 packet_id; - byte topic_type; - /* 5.3.12 TopicName - The TopicName field has a variable length and contains an UTF8-encoded - string that specifies the topic name.*/ - const char* topicNameId; /* Contains topic name, ID, - or short name as indicated in topic type */ - SN_SubAck subAck; -} SN_Subscribe; - -/* UNSUBSCRIBE RESPONSE ACK */ -typedef struct _SN_UnsubscribeAck { - MqttMsgStat stat; /* must be first member at top */ - - word16 packet_id; -} SN_UnsubscribeAck; - -/* UNSUBSCRIBE */ -typedef struct _SN_Unsubscribe { - MqttMsgStat stat; -#ifdef WOLFMQTT_MULTITHREAD - MqttPendResp pendResp; -#endif - - byte duplicate; - byte qos; - word16 packet_id; - byte topic_type; - /* 5.3.12 TopicName - The TopicName field has a variable length and contains an UTF8-encoded - string that specifies the topic name.*/ - const char* topicNameId; /* Contains topic name, ID, - or short name as indicated in topic type */ - SN_UnsubscribeAck ack; -} SN_Unsubscribe; - -/* PING REQUEST / PING RESPONSE */ -typedef struct _SN_PingReq { - MqttMsgStat stat; -#ifdef WOLFMQTT_MULTITHREAD - MqttPendResp pendResp; -#endif - - /* clientId is optional and is included by a “sleeping” client when it - goes to the “awake” state and is waiting for messages sent by the - server/gateway. */ - char *clientId; -} SN_PingReq; - -/* DISCONNECT */ -typedef struct _SN_Disconnect { - MqttMsgStat stat; -#ifdef WOLFMQTT_MULTITHREAD - MqttPendResp pendResp; -#endif - - /* sleepTmr is optional and is included by a “sleeping” client - that wants to go the “asleep” state. The receipt of this message - is also acknowledged by the gateway by means of a DISCONNECT message - (without a duration field).*/ - word16 sleepTmr; -} SN_Disconnect; - -typedef union _SN_Object { - SN_Advertise advertise; - SN_GwInfo gwInfo; - SN_SearchGw searchGw; - - SN_Will will; - SN_Connect connect; - SN_ConnectAck connectAck; - - SN_Register reg; - SN_RegAck regAck; - - SN_Publish publish; - SN_PublishResp publishResp; - - SN_Subscribe sub; - SN_SubAck subAck; - SN_Unsubscribe unSub; - SN_UnsubscribeAck unSubAck; - - SN_PingReq pingReq; - SN_Disconnect disconnect; - - SN_WillMsgUpd willMsgUpd; - SN_WillMsgResp willMsgResp; - - SN_WillTopicUpd willTopicUpd; - SN_WillTopicResp willTopicResp; -} SN_Object; - - -/* Forward Encapsulation */ -// TODO - -WOLFMQTT_LOCAL int SN_Decode_Header(byte *rx_buf, int rx_buf_len, - SN_MsgType* p_packet_type, word16* p_packet_id); -WOLFMQTT_LOCAL int SN_Decode_Advertise(byte *rx_buf, int rx_buf_len, - SN_Advertise *gw_info); -WOLFMQTT_LOCAL int SN_Encode_SearchGW(byte *tx_buf, int tx_buf_len, byte hops); -WOLFMQTT_LOCAL int SN_Decode_GWInfo(byte *rx_buf, int rx_buf_len, - SN_GwInfo *gw_info); -WOLFMQTT_LOCAL int SN_Encode_Connect(byte *tx_buf, int tx_buf_len, - SN_Connect *connect); -WOLFMQTT_LOCAL int SN_Decode_ConnectAck(byte *rx_buf, int rx_buf_len, - SN_ConnectAck *connect_ack); -WOLFMQTT_LOCAL int SN_Decode_WillTopicReq(byte *rx_buf, int rx_buf_len); -WOLFMQTT_LOCAL int SN_Encode_WillTopic(byte *tx_buf, int tx_buf_len, - SN_Will *willTopic); -WOLFMQTT_LOCAL int SN_Decode_WillMsgReq(byte *rx_buf, int rx_buf_len); -WOLFMQTT_LOCAL int SN_Encode_WillMsg(byte *tx_buf, int tx_buf_len, - SN_Will *willMsg); -WOLFMQTT_LOCAL int SN_Encode_WillTopicUpdate(byte *tx_buf, int tx_buf_len, - SN_Will *willTopic); -WOLFMQTT_LOCAL int SN_Decode_WillTopicResponse(byte *rx_buf, int rx_buf_len, - byte *ret_code); -WOLFMQTT_LOCAL int SN_Encode_WillMsgUpdate(byte *tx_buf, int tx_buf_len, - SN_Will *willMsg); -WOLFMQTT_LOCAL int SN_Decode_WillMsgResponse(byte *rx_buf, int rx_buf_len, - byte *ret_code); -WOLFMQTT_LOCAL int SN_Encode_Register(byte *tx_buf, int tx_buf_len, - SN_Register *regist); -WOLFMQTT_LOCAL int SN_Decode_Register(byte *rx_buf, int rx_buf_len, - SN_Register *regist); -WOLFMQTT_LOCAL int SN_Encode_RegAck(byte *tx_buf, int tx_buf_len, - SN_RegAck *regack); -WOLFMQTT_LOCAL int SN_Decode_RegAck(byte *rx_buf, int rx_buf_len, - SN_RegAck *regack); -WOLFMQTT_LOCAL int SN_Encode_Subscribe(byte *tx_buf, int tx_buf_len, - SN_Subscribe *subscribe); -WOLFMQTT_LOCAL int SN_Decode_SubscribeAck(byte* rx_buf, int rx_buf_len, - SN_SubAck *subscribe_ack); -WOLFMQTT_LOCAL int SN_Encode_Publish(byte *tx_buf, int tx_buf_len, - SN_Publish *publish); -WOLFMQTT_LOCAL int SN_Decode_Publish(byte *rx_buf, int rx_buf_len, - SN_Publish *publish); -WOLFMQTT_LOCAL int SN_Encode_PublishResp(byte* tx_buf, int tx_buf_len, - byte type, SN_PublishResp *publish_resp); -WOLFMQTT_LOCAL int SN_Decode_PublishResp(byte* rx_buf, int rx_buf_len, - byte type, SN_PublishResp *publish_resp); -WOLFMQTT_LOCAL int SN_Encode_Unsubscribe(byte *tx_buf, int tx_buf_len, - SN_Unsubscribe *unsubscribe); -WOLFMQTT_LOCAL int SN_Decode_UnsubscribeAck(byte *rx_buf, int rx_buf_len, - SN_UnsubscribeAck *unsubscribe_ack); -WOLFMQTT_LOCAL int SN_Encode_Disconnect(byte *tx_buf, int tx_buf_len, - SN_Disconnect* disconnect); -WOLFMQTT_LOCAL int SN_Decode_Disconnect(byte *rx_buf, int rx_buf_len); -WOLFMQTT_LOCAL int SN_Encode_Ping(byte *tx_buf, int tx_buf_len, - SN_PingReq *ping, byte type); -WOLFMQTT_LOCAL int SN_Decode_Ping(byte *rx_buf, int rx_buf_len); -WOLFMQTT_LOCAL int SN_Packet_Read(struct _MqttClient *client, byte* rx_buf, - int rx_buf_len, int timeout_ms); - -#ifndef WOLFMQTT_NO_ERROR_STRINGS - WOLFMQTT_LOCAL const char* SN_Packet_TypeDesc(SN_MsgType packet_type); -#else - #define SN_Packet_TypeDesc(x) "not compiled in" -#endif -#endif /* WOLFMQTT_SN */ - #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/wolfmqtt/mqtt_sn_client.h b/wolfmqtt/mqtt_sn_client.h new file mode 100644 index 000000000..68124afad --- /dev/null +++ b/wolfmqtt/mqtt_sn_client.h @@ -0,0 +1,236 @@ +/* mqtt_sn_client.h + * + * Copyright (C) 2006-2023 wolfSSL Inc. + * + * This file is part of wolfMQTT. + * + * wolfMQTT 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; either version 2 of the License, or + * (at your option) any later version. + * + * wolfMQTT 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Implementation by: David Garske + * Based on specification for MQTT-SN v1.2 + * See http://mqtt.org/documentation for additional MQTT-SN documentation. + */ + +#ifndef WOLFMQTT_SN_CLIENT_H +#define WOLFMQTT_SN_CLIENT_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* Windows uses the vs_settings.h file included vis mqtt_types.h */ +#if !defined(WOLFMQTT_USER_SETTINGS) && \ + !defined(_WIN32) && !defined(USE_WINDOWS_API) + /* If options.h is missing use the "./configure" script. Otherwise, copy + * the template "wolfmqtt/options.h.in" into "wolfmqtt/options.h" */ + #include +#endif +#include "wolfmqtt/mqtt_client.h" +#include "wolfmqtt/mqtt_types.h" +#include "wolfmqtt/mqtt_packet.h" +#include "wolfmqtt/mqtt_socket.h" + +#ifdef WOLFMQTT_SN +/*! \brief Encodes and sends the a message to search for a gateway and + waits for the gateway info response message. + * \note This is a blocking function that will wait for MqttNet.read + * \param client Pointer to MqttClient structure + * \param search Pointer to SN_SearchGW structure initialized + with hop radius. + * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* + (see enum MqttPacketResponseCodes) + */ +WOLFMQTT_API int SN_Client_SearchGW( + MqttClient *client, + SN_SearchGw *search); + +/*! \brief Encodes and sends the Connect packet and waits for the + Connect Acknowledgment packet. If Will is enabled, the gateway + prompts for LWT Topic and Message. Sending an empty will topic + indicates that the client wishes to delete the Will topic and + the Will message stored in the server. + * \note This is a blocking function that will wait for MqttNet.read + * \param client Pointer to MqttClient structure + * \param connect Pointer to SN_Connect structure initialized + with connect parameters + * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* + (see enum MqttPacketResponseCodes) + */ +WOLFMQTT_API int SN_Client_Connect( + MqttClient *client, + SN_Connect *connect); + +/*! \brief Encodes and sends the MQTT-SN Will Topic Update packet. Sending + a NULL 'will' indicates that the client wishes to delete the + Will topic and the Will message stored in the server. + * \note This is a blocking function that will wait for MqttNet.read + * \param client Pointer to MqttClient structure + * \param will Pointer to SN_Will structure initialized + with topic and message parameters. NULL is valid. + * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* + (see enum MqttPacketResponseCodes) + */ +WOLFMQTT_API int SN_Client_WillTopicUpdate(MqttClient *client, SN_Will *will); + +/*! \brief Encodes and sends the MQTT-SN Will Message Update packet. + * \note This is a blocking function that will wait for MqttNet.read + * \param client Pointer to MqttClient structure + * \param will Pointer to SN_Will structure initialized + with topic and message parameters. NULL is valid. + * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* + (see enum MqttPacketResponseCodes) + */ +WOLFMQTT_API int SN_Client_WillMsgUpdate(MqttClient *client, SN_Will *will); + +/*! \brief Encodes and sends the MQTT-SN Register packet and waits for the + Register Acknowledge packet. The Register packet is sent by a + client to a GW for requesting a topic id value for the included + topic name. It is also sent by a GW to inform a client about + the topic id value it has assigned to the included topic name. + * \note This is a blocking function that will wait for MqttNet.read + * \param client Pointer to MqttClient structure + * \param regist Pointer to SN_Register structure + * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* + (see enum MqttPacketResponseCodes) + */ +WOLFMQTT_API int SN_Client_Register( + MqttClient *client, + SN_Register *regist); + + +/*! \brief Sets a register callback with custom context + * \param client Pointer to MqttClient structure + (uninitialized is okay) + * \param regCb Pointer to register callback function + * \param ctx Pointer to your own context + * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_BAD_ARG + */ +WOLFMQTT_API int SN_Client_SetRegisterCallback( + MqttClient *client, + SN_ClientRegisterCb regCb, + void* ctx); + + +/*! \brief Encodes and sends the MQTT-SN Publish packet and waits for the + Publish response (if QoS > 0). + * \note This is a blocking function that will wait for MqttNet.read + * If QoS level = 1 then will wait for PUBLISH_ACK. + * If QoS level = 2 then will wait for PUBLISH_REC then send + PUBLISH_REL and wait for PUBLISH_COMP. + * \param client Pointer to MqttClient structure + * \param publish Pointer to SN_Publish structure initialized + with message data + * Note: SN_Publish and MqttMessage are same + structure. + * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* + (see enum MqttPacketResponseCodes) + */ +WOLFMQTT_API int SN_Client_Publish( + MqttClient *client, + SN_Publish *publish); + +/*! \brief Encodes and sends the MQTT-SN Subscribe packet and waits for the + Subscribe Acknowledgment packet containing the assigned + topic ID. + * \note This is a blocking function that will wait for MqttNet.read + * \param client Pointer to MqttClient structure + * \param subscribe Pointer to SN_Subscribe structure initialized with + subscription topic list and desired QoS. + * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* + (see enum MqttPacketResponseCodes) + */ +WOLFMQTT_API int SN_Client_Subscribe( + MqttClient *client, + SN_Subscribe *subscribe); + +/*! \brief Encodes and sends the MQTT-SN Unsubscribe packet and waits for + the Unsubscribe Acknowledgment packet + * \note This is a blocking function that will wait for MqttNet.read + * \param client Pointer to MqttClient structure + * \param unsubscribe Pointer to SN_Unsubscribe structure initialized + with topic ID. + * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* + (see enum MqttPacketResponseCodes) + */ +WOLFMQTT_API int SN_Client_Unsubscribe( + MqttClient *client, + SN_Unsubscribe *unsubscribe); + +/*! \brief Encodes and sends the MQTT-SN Disconnect packet. Client may + send the disconnect with a duration to indicate the client is + entering the "asleep" state. + * \note This is a non-blocking function that will try and send using + MqttNet.write + * \param client Pointer to MqttClient structure + * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* + (see enum MqttPacketResponseCodes) + */ +WOLFMQTT_API int SN_Client_Disconnect( + MqttClient *client); + +/*! \brief Encodes and sends the MQTT-SN Disconnect packet. Client may + send the disconnect with a duration to indicate the client is + entering the "asleep" state. + * \note This is a non-blocking function that will try and send using + MqttNet.write + * \param client Pointer to MqttClient structure + * \param disconnect Pointer to SN_Disconnect structure. NULL is valid. + * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* + (see enum MqttPacketResponseCodes) + */ +WOLFMQTT_API int SN_Client_Disconnect_ex( + MqttClient *client, + SN_Disconnect *disconnect); + + +/*! \brief Encodes and sends the MQTT-SN Ping Request packet and waits + for the Ping Response packet. If client is in the "asleep" + state and wants to notify the gateway that it is entering the + "awake" state, it should add it's client ID to the ping + request. + * \note This is a blocking function that will wait for MqttNet.read + * \param client Pointer to MqttClient structure + * \param ping Pointer to SN_PingReq structure. NULL is valid. + * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* + (see enum MqttPacketResponseCodes) + */ +WOLFMQTT_API int SN_Client_Ping( + MqttClient *client, + SN_PingReq *ping); + +/*! \brief Waits for packets to arrive. Incoming publish messages + will arrive via callback provided in MqttClient_Init. + * \note This is a blocking function that will wait for MqttNet.read + * \param client Pointer to MqttClient structure + * \param timeout_ms Milliseconds until read timeout + * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* + (see enum MqttPacketResponseCodes) + */ +WOLFMQTT_API int SN_Client_WaitMessage( + MqttClient *client, + int timeout_ms); + +WOLFMQTT_API int SN_Client_WaitMessage_ex(MqttClient *client, SN_Object* packet_obj, + int timeout_ms); + +#endif /* WOLFMQTT_SN */ + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif /* WOLFMQTT_SN_CLIENT_H */ + diff --git a/wolfmqtt/mqtt_sn_packet.h b/wolfmqtt/mqtt_sn_packet.h new file mode 100644 index 000000000..6729bc4f5 --- /dev/null +++ b/wolfmqtt/mqtt_sn_packet.h @@ -0,0 +1,497 @@ +/* mqtt_sn_packet.h + * + * Copyright (C) 2006-2023 wolfSSL Inc. + * + * This file is part of wolfMQTT. + * + * wolfMQTT 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; either version 2 of the License, or + * (at your option) any later version. + * + * wolfMQTT 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Implementation by: David Garske + * Based on specification for MQTT-SN v1.2 + * See http://mqtt.org/documentation for additional MQTT documentation. + */ + +#ifndef WOLFMQTT_SN_PACKET_H +#define WOLFMQTT_SN_PACKET_H + +#ifdef __cplusplus + extern "C" { +#endif + +#include "wolfmqtt/mqtt_types.h" +#include "wolfmqtt/mqtt_socket.h" + +#ifdef WOLFMQTT_SN + +/* Note that because MQTT-SN does not support message fragmentation and + reassembly, the maximum message length that could be used in a network is + governed by the maximum packet size that is supported by that network, + and not by the maximum length that could be encoded by MQTT-SN. */ +#ifndef WOLFMQTT_SN_MAXPACKET_SIZE +#define WOLFMQTT_SN_MAXPACKET_SIZE 1024 +#endif + +/* The SN_GwAddr field has a variable length and contains the address of a GW. + Its depends on the network over which MQTT-SN operates and is indicated in + the first octet of this field. For example, in a ZigBee network the network + address is 2-octet long. */ +typedef word16 SN_GwAddr ; + +/* RETURN CODE values */ +enum SN_ReturnCodes { + SN_RC_ACCEPTED = 0x00, + SN_RC_CONGESTION = 0x01, + SN_RC_INVTOPICNAME = 0x02, + SN_RC_NOTSUPPORTED = 0x03 + /* 0x04 - 0xFF reserved */ +}; + +/* MESSAGE HEADER */ +/* Message types: Located in last byte of header */ +typedef enum _SN_MsgType { + SN_MSG_TYPE_ADVERTISE = 0x00, + SN_MSG_TYPE_SEARCHGW = 0x01, + SN_MSG_TYPE_GWINFO = 0x02, + /* 0x03 reserved */ + SN_MSG_TYPE_CONNECT = 0x04, + SN_MSG_TYPE_CONNACK = 0x05, + SN_MSG_TYPE_WILLTOPICREQ = 0x06, + SN_MSG_TYPE_WILLTOPIC = 0x07, + SN_MSG_TYPE_WILLMSGREQ = 0x08, + SN_MSG_TYPE_WILLMSG = 0x09, + SN_MSG_TYPE_REGISTER = 0x0A, + SN_MSG_TYPE_REGACK = 0x0B, + SN_MSG_TYPE_PUBLISH = 0x0C, + SN_MSG_TYPE_PUBACK = 0x0D, + SN_MSG_TYPE_PUBCOMP = 0x0E, + SN_MSG_TYPE_PUBREC = 0x0F, + SN_MSG_TYPE_PUBREL = 0x10, + /* 0x11 reserved */ + SN_MSG_TYPE_SUBSCRIBE = 0x12, + SN_MSG_TYPE_SUBACK = 0x13, + SN_MSG_TYPE_UNSUBSCRIBE = 0x14, + SN_MSG_TYPE_UNSUBACK = 0x15, + SN_MSG_TYPE_PING_REQ = 0x16, + SN_MSG_TYPE_PING_RESP = 0x17, + SN_MSG_TYPE_DISCONNECT = 0x18, + /* 0x19 reserved */ + SN_MSG_TYPE_WILLTOPICUPD = 0x1A, + SN_MSG_TYPE_WILLTOPICRESP = 0x1B, + SN_MSG_TYPE_WILLMSGUPD = 0x1C, + SN_MSG_TYPE_WILLMSGRESP = 0x1D, + /* 0x1E - 0xFD reserved */ + SN_MSG_TYPE_ENCAPMSG = 0xFE, /* Encapsulated message */ + /* 0xFF reserved */ + SN_MSG_TYPE_RESERVED = 0xFF, + SN_MSG_TYPE_ANY = 0xFF +} SN_MsgType; + +/* Topic ID types */ +enum SN_TopicId_Types { + SN_TOPIC_ID_TYPE_NORMAL = 0x0, + SN_TOPIC_ID_TYPE_PREDEF = 0x1, + SN_TOPIC_ID_TYPE_SHORT = 0x2 +}; + +enum SN_PacketFlags { + SN_PACKET_FLAG_TOPICIDTYPE_MASK = 0x3, + SN_PACKET_FLAG_CLEANSESSION = 0x4, + SN_PACKET_FLAG_WILL = 0x8, + SN_PACKET_FLAG_RETAIN = 0x10, + SN_PACKET_FLAG_QOS_MASK = 0x60, + SN_PACKET_FLAG_QOS_SHIFT = 0x5, + SN_PACKET_FLAG_DUPLICATE = 0x80 +}; + +/* Message Header: Size is variable 2 - 4 bytes */ + +/* If the first byte of the packet len is 0x01, then the packet size is + greater than 0xFF and is stored in the next two bytes */ +#define SN_PACKET_LEN_IND 0x01 + +#define SN_PACKET_MAX_SMALL_SIZE 0xFF + +/* Gateway (GW) messages */ +/* Advertise message */ +typedef struct _SN_AdvertiseMsg { + MqttMsgStat stat; + + byte gwId; /* ID of the gateway that sent this message */ + word16 duration; /* Seconds until next Advertise + is broadcast by this gateway */ +} SN_Advertise; + +typedef struct _SN_GwInfo { + MqttMsgStat stat; + + byte gwId; /* ID of the gateway that sent this message */ + SN_GwAddr* gwAddr; /* Address of the indicated gateway */ +} SN_GwInfo; + +typedef struct _SN_SearchGw { + MqttMsgStat stat; +#ifdef WOLFMQTT_MULTITHREAD + MqttPendResp pendResp; +#endif + + byte radius; /* Broadcast radius (in hops) */ + SN_GwInfo gwInfo; +} SN_SearchGw; + +/* Connect Protocol */ +#define SN_PROTOCOL_ID_1 0x01 +#define SN_PROTOCOL_ID SN_PROTOCOL_ID_1 + +#define SN_CLIENTID_MAX_LEN 23 + +/* Connect Ack message structure */ +typedef struct _SN_ConnectAck { + MqttMsgStat stat; + + byte return_code; +} SN_ConnectAck; + +/* WILL TOPIC */ +typedef struct _SN_WillTopicUpd { + MqttMsgStat stat; +#ifdef WOLFMQTT_MULTITHREAD + MqttPendResp pendResp; +#endif + + byte flags; + char* willTopic; /* contains the Will topic name */ +} SN_WillTopicUpd; + +typedef struct _SN_WillMsgUpd { + MqttMsgStat stat; +#ifdef WOLFMQTT_MULTITHREAD + MqttPendResp pendResp; +#endif + + char* willMsg; +} SN_WillMsgUpd; + +typedef struct _SN_WillTopicResp { + MqttMsgStat stat; + + byte return_code; +} SN_WillTopicResp; + +typedef SN_WillTopicResp SN_WillMsgResp; + +typedef union _SN_WillResp { + SN_WillMsgResp msgResp; + SN_WillMsgUpd msgUpd; + SN_WillTopicResp topicResp; + SN_WillTopicUpd topicUpd; +} SN_WillResp; + +typedef struct _SN_Will { + MqttMsgStat stat; +#ifdef WOLFMQTT_MULTITHREAD + MqttPendResp pendResp; +#endif + + byte qos; + byte retain; + const char* willTopic; + byte* willMsg; + word16 willMsgLen; + + SN_WillResp resp; +} SN_Will; + +/* Connect */ +typedef struct _SN_Connect { + MqttMsgStat stat; +#ifdef WOLFMQTT_MULTITHREAD + MqttPendResp pendResp; +#endif + + word16 keep_alive_sec; + byte clean_session; + const char *client_id; + + /* Protocol version: 1=v1.2 (default) */ + byte protocol_level; + + /* Optional Last will and testament */ + byte enable_lwt; + SN_Will will; + + /* Ack data */ + SN_ConnectAck ack; +} SN_Connect; + +/* REGISTER protocol */ +typedef struct _SN_RegAck { + MqttMsgStat stat; + + word16 topicId; + word16 packet_id; + byte return_code; +} SN_RegAck; + +typedef struct _SN_Register { + MqttMsgStat stat; +#ifdef WOLFMQTT_MULTITHREAD + MqttPendResp pendResp; +#endif + + word16 topicId; + word16 packet_id; + const char* topicName; + SN_RegAck regack; +} SN_Register; + +/* PUBLISH RESPONSE */ +/* This is the response struct for PUBREC, PUBREL, and PUBCOMP */ +/* If QoS = 0: No response */ +/* If QoS = 1: Expect response packet with type = SN_MSG_TYPE_PUBACK */ +/* If QoS = 2: Expect response packet with type = SN_MSG_TYPE_PUBREC */ +/* Message ID required if QoS is 1 or 2 */ +/* If QoS = 2: Send SN_MSG_TYPE_PUBREL with msgId to complete + QoS2 protocol exchange */ +/* Expect response packet with type = SN_MSG_TYPE_PUBCOMP */ +typedef struct _SN_PublishResp { + MqttMsgStat stat; + + word16 packet_id; + word16 topicId; /* PUBACK Only */ + byte return_code; /* PUBACK Only */ +} SN_PublishResp; + +/* PUBLISH protocol */ +typedef struct _SN_Publish { + MqttMsgStat stat; /* must be first member at top */ +#ifdef WOLFMQTT_MULTITHREAD + MqttPendResp pendResp; +#endif + + /* BEGIN: THIS SECTION NEEDS TO MATCH MqttMessage */ + word16 packet_id; + byte type; + MqttQoS qos; + byte retain; + byte duplicate; + byte topic_type; + byte return_code; + + const char *topic_name; /* Pointer is valid only when + msg_new set in callback */ + word16 topic_name_len; + word32 total_len; /* Payload total length */ + byte *buffer; /* Payload buffer */ + word32 buffer_len; /* Payload buffer length */ + word32 buffer_pos; /* Payload buffer position */ + + /* Used internally for TX/RX buffers */ + byte buffer_new; /* flag to indicate new message */ + word32 intBuf_len; /* Buffer length */ + word32 intBuf_pos; /* Buffer position */ + + void* ctx; /* user supplied context for publish callbacks */ + /* END: THIS SECTION NEEDS TO MATCH MqttMessage */ + + SN_PublishResp resp; +} SN_Publish; + +/* SUBSCRIBE ACK */ +typedef struct _SN_SubAck { + MqttMsgStat stat; + + byte flags; + word16 topicId; + word16 packet_id; + byte return_code; +} SN_SubAck; + +/* SUBSCRIBE */ +typedef struct _SN_Subscribe { + MqttMsgStat stat; +#ifdef WOLFMQTT_MULTITHREAD + MqttPendResp pendResp; +#endif + + byte duplicate; + byte qos; + word16 packet_id; + byte topic_type; + /* 5.3.12 TopicName + The TopicName field has a variable length and contains an UTF8-encoded + string that specifies the topic name.*/ + const char* topicNameId; /* Contains topic name, ID, + or short name as indicated in topic type */ + SN_SubAck subAck; +} SN_Subscribe; + +/* UNSUBSCRIBE RESPONSE ACK */ +typedef struct _SN_UnsubscribeAck { + MqttMsgStat stat; /* must be first member at top */ + + word16 packet_id; +} SN_UnsubscribeAck; + +/* UNSUBSCRIBE */ +typedef struct _SN_Unsubscribe { + MqttMsgStat stat; +#ifdef WOLFMQTT_MULTITHREAD + MqttPendResp pendResp; +#endif + + byte duplicate; + byte qos; + word16 packet_id; + byte topic_type; + /* 5.3.12 TopicName + The TopicName field has a variable length and contains an UTF8-encoded + string that specifies the topic name.*/ + const char* topicNameId; /* Contains topic name, ID, + or short name as indicated in topic type */ + SN_UnsubscribeAck ack; +} SN_Unsubscribe; + +/* PING REQUEST / PING RESPONSE */ +typedef struct _SN_PingReq { + MqttMsgStat stat; +#ifdef WOLFMQTT_MULTITHREAD + MqttPendResp pendResp; +#endif + + /* clientId is optional and is included by a “sleeping” client when it + goes to the “awake” state and is waiting for messages sent by the + server/gateway. */ + char *clientId; +} SN_PingReq; + +/* DISCONNECT */ +typedef struct _SN_Disconnect { + MqttMsgStat stat; +#ifdef WOLFMQTT_MULTITHREAD + MqttPendResp pendResp; +#endif + + /* sleepTmr is optional and is included by a “sleeping” client + that wants to go the “asleep” state. The receipt of this message + is also acknowledged by the gateway by means of a DISCONNECT message + (without a duration field).*/ + word16 sleepTmr; +} SN_Disconnect; + +typedef union _SN_Object { + SN_Advertise advertise; + SN_GwInfo gwInfo; + SN_SearchGw searchGw; + + SN_Will will; + SN_Connect connect; + SN_ConnectAck connectAck; + + SN_Register reg; + SN_RegAck regAck; + + SN_Publish publish; + SN_PublishResp publishResp; + + SN_Subscribe sub; + SN_SubAck subAck; + SN_Unsubscribe unSub; + SN_UnsubscribeAck unSubAck; + + SN_PingReq pingReq; + SN_Disconnect disconnect; + + SN_WillMsgUpd willMsgUpd; + SN_WillMsgResp willMsgResp; + + SN_WillTopicUpd willTopicUpd; + SN_WillTopicResp willTopicResp; +} SN_Object; + + +/* Forward Encapsulation */ +// TODO + +WOLFMQTT_LOCAL int SN_Decode_Header(byte *rx_buf, int rx_buf_len, + SN_MsgType* p_packet_type, word16* p_packet_id); +WOLFMQTT_LOCAL int SN_Decode_Advertise(byte *rx_buf, int rx_buf_len, + SN_Advertise *gw_info); +WOLFMQTT_LOCAL int SN_Encode_SearchGW(byte *tx_buf, int tx_buf_len, byte hops); +WOLFMQTT_LOCAL int SN_Decode_GWInfo(byte *rx_buf, int rx_buf_len, + SN_GwInfo *gw_info); +WOLFMQTT_LOCAL int SN_Encode_Connect(byte *tx_buf, int tx_buf_len, + SN_Connect *connect); +WOLFMQTT_LOCAL int SN_Decode_ConnectAck(byte *rx_buf, int rx_buf_len, + SN_ConnectAck *connect_ack); +WOLFMQTT_LOCAL int SN_Decode_WillTopicReq(byte *rx_buf, int rx_buf_len); +WOLFMQTT_LOCAL int SN_Encode_WillTopic(byte *tx_buf, int tx_buf_len, + SN_Will *willTopic); +WOLFMQTT_LOCAL int SN_Decode_WillMsgReq(byte *rx_buf, int rx_buf_len); +WOLFMQTT_LOCAL int SN_Encode_WillMsg(byte *tx_buf, int tx_buf_len, + SN_Will *willMsg); +WOLFMQTT_LOCAL int SN_Encode_WillTopicUpdate(byte *tx_buf, int tx_buf_len, + SN_Will *willTopic); +WOLFMQTT_LOCAL int SN_Decode_WillTopicResponse(byte *rx_buf, int rx_buf_len, + byte *ret_code); +WOLFMQTT_LOCAL int SN_Encode_WillMsgUpdate(byte *tx_buf, int tx_buf_len, + SN_Will *willMsg); +WOLFMQTT_LOCAL int SN_Decode_WillMsgResponse(byte *rx_buf, int rx_buf_len, + byte *ret_code); +WOLFMQTT_LOCAL int SN_Encode_Register(byte *tx_buf, int tx_buf_len, + SN_Register *regist); +WOLFMQTT_LOCAL int SN_Decode_Register(byte *rx_buf, int rx_buf_len, + SN_Register *regist); +WOLFMQTT_LOCAL int SN_Encode_RegAck(byte *tx_buf, int tx_buf_len, + SN_RegAck *regack); +WOLFMQTT_LOCAL int SN_Decode_RegAck(byte *rx_buf, int rx_buf_len, + SN_RegAck *regack); +WOLFMQTT_LOCAL int SN_Encode_Subscribe(byte *tx_buf, int tx_buf_len, + SN_Subscribe *subscribe); +WOLFMQTT_LOCAL int SN_Decode_SubscribeAck(byte* rx_buf, int rx_buf_len, + SN_SubAck *subscribe_ack); +WOLFMQTT_LOCAL int SN_Encode_Publish(byte *tx_buf, int tx_buf_len, + SN_Publish *publish); +WOLFMQTT_LOCAL int SN_Decode_Publish(byte *rx_buf, int rx_buf_len, + SN_Publish *publish); +WOLFMQTT_LOCAL int SN_Encode_PublishResp(byte* tx_buf, int tx_buf_len, + byte type, SN_PublishResp *publish_resp); +WOLFMQTT_LOCAL int SN_Decode_PublishResp(byte* rx_buf, int rx_buf_len, + byte type, SN_PublishResp *publish_resp); +WOLFMQTT_LOCAL int SN_Encode_Unsubscribe(byte *tx_buf, int tx_buf_len, + SN_Unsubscribe *unsubscribe); +WOLFMQTT_LOCAL int SN_Decode_UnsubscribeAck(byte *rx_buf, int rx_buf_len, + SN_UnsubscribeAck *unsubscribe_ack); +WOLFMQTT_LOCAL int SN_Encode_Disconnect(byte *tx_buf, int tx_buf_len, + SN_Disconnect* disconnect); +WOLFMQTT_LOCAL int SN_Decode_Disconnect(byte *rx_buf, int rx_buf_len); +WOLFMQTT_LOCAL int SN_Encode_Ping(byte *tx_buf, int tx_buf_len, + SN_PingReq *ping, byte type); +WOLFMQTT_LOCAL int SN_Decode_Ping(byte *rx_buf, int rx_buf_len); +WOLFMQTT_LOCAL int SN_Packet_Read(struct _MqttClient *client, byte* rx_buf, + int rx_buf_len, int timeout_ms); + +#ifndef WOLFMQTT_NO_ERROR_STRINGS + WOLFMQTT_LOCAL const char* SN_Packet_TypeDesc(SN_MsgType packet_type); +#else + #define SN_Packet_TypeDesc(x) "not compiled in" +#endif +#endif /* WOLFMQTT_SN */ + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif /* WOLFMQTT_SN_PACKET_H */ From 9bc0925734ec3b47712ce679f1ab8756a842627f Mon Sep 17 00:00:00 2001 From: Eric Blankenhorn Date: Wed, 1 Nov 2023 10:01:19 -0500 Subject: [PATCH 11/62] wolfMQTT Release v1.17.0 preparation --- CMakeLists.txt | 2 +- ChangeLog.md | 19 ++++ configure.ac | 4 +- scripts/broker_test/ca-cert.pem | 97 ++++++++-------- scripts/broker_test/server-cert.pem | 169 ++++++++++++++-------------- wolfmqtt/version.h | 4 +- 6 files changed, 158 insertions(+), 137 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f38bffdc9..aab5eff0e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,7 +21,7 @@ cmake_minimum_required(VERSION 3.16) -project(wolfMQTT VERSION 1.16.0 LANGUAGES C) +project(wolfMQTT VERSION 1.17.0 LANGUAGES C) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set(MQTT_SOURCES diff --git a/ChangeLog.md b/ChangeLog.md index 482331ae9..595761320 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,5 +1,24 @@ ## Release Notes +### v1.16.0 (11/2/2023) +* Fix for declaration after executable block by @lealem47 in #341 +* Add QNX IDE, Makefile, and remove source code exec bit by @JacobBarthelmeh + in #317 +* update for cmake after wolfssl added NAMESPACE by @JacobBarthelmeh in #343 +* Add mutex protection to MqttClient_NetDisconnect by @embhorn in #342 +* Add DTLS support to MQTT-SN client by @embhorn in #348 +* Tie zephyr tests to a release by @julek-wolfssl in #350 +* add documentation link to README by @gojimmypi in #355 +* Possible patch for POSIX conditional wait issue by @dgarske in #356 +* Fix publish with topic ID >=127 by @embhorn in #351 +* Adding publish and subscribe atomic client examples by @embhorn in #347 +* Allow disabling the posix conditional signal by @dgarske in #360 +* Exclude CI tests with external brokers by @embhorn in #362 +* Improvements for client property stack by @dgarske in #361 +* Add mosquitto to CI tests by @embhorn in #365 +* Fixes for non-blocking edge cases by @dgarske in #363 +* Refactor MQTT-SN code by @embhorn in #366 + ### v1.16.0 (6/29/2023) * Add testing for TLS mutual auth, and fsanitize gh test by @lealem47 in #321 * Add support for pkcs8 keys to mqtt client by @lealem47 in #322 diff --git a/configure.ac b/configure.ac index abdbd0b74..1e0ff92f7 100644 --- a/configure.ac +++ b/configure.ac @@ -3,7 +3,7 @@ # All right reserved. AC_COPYRIGHT([Copyright (C) 2014-2023 wolfSSL Inc.]) -AC_INIT([wolfmqtt],[1.16.0],[https://github.com/wolfssl/wolfMQTT/issues],[wolfmqtt],[http://www.wolfssl.com]) +AC_INIT([wolfmqtt],[1.17.0],[https://github.com/wolfssl/wolfMQTT/issues],[wolfmqtt],[http://www.wolfssl.com]) AC_PREREQ([2.63]) AC_CONFIG_AUX_DIR([build-aux]) @@ -24,7 +24,7 @@ AC_ARG_PROGRAM AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_HEADERS([src/config.h]) -WOLFMQTT_LIBRARY_VERSION=15:0:0 +WOLFMQTT_LIBRARY_VERSION=16:0:0 # | | | # +------+ | +---+ # | | | diff --git a/scripts/broker_test/ca-cert.pem b/scripts/broker_test/ca-cert.pem index 47a3ba0a4..58688a0eb 100644 --- a/scripts/broker_test/ca-cert.pem +++ b/scripts/broker_test/ca-cert.pem @@ -1,16 +1,17 @@ Certificate: Data: Version: 3 (0x2) - Serial Number: 12309252214903945037 (0xaad33fac180a374d) - Signature Algorithm: sha256WithRSAEncryption - Issuer: C=US, ST=Montana, L=Bozeman, O=Sawtooth, OU=Consulting, CN=www.wolfssl.com/emailAddress=info@wolfssl.com + Serial Number: + 2c:80:ce:db:47:9d:07:66:92:3d:68:d7:ca:ac:90:4f:ca:69:41:4b + Signature Algorithm: sha256WithRSAEncryption + Issuer: C = US, ST = Montana, L = Bozeman, O = Sawtooth, OU = Consulting, CN = www.wolfssl.com, emailAddress = info@wolfssl.com Validity - Not Before: Feb 10 19:49:52 2021 GMT - Not After : Nov 7 19:49:52 2023 GMT - Subject: C=US, ST=Montana, L=Bozeman, O=Sawtooth, OU=Consulting, CN=www.wolfssl.com/emailAddress=info@wolfssl.com + Not Before: Dec 16 21:17:49 2022 GMT + Not After : Sep 11 21:17:49 2025 GMT + Subject: C = US, ST = Montana, L = Bozeman, O = Sawtooth, OU = Consulting, CN = www.wolfssl.com, emailAddress = info@wolfssl.com Subject Public Key Info: Public Key Algorithm: rsaEncryption - Public-Key: (2048 bit) + RSA Public-Key: (2048 bit) Modulus: 00:bf:0c:ca:2d:14:b2:1e:84:42:5b:cd:38:1f:4a: f2:4d:75:10:f1:b6:35:9f:df:ca:7d:03:98:d3:ac: @@ -37,7 +38,7 @@ Certificate: X509v3 Authority Key Identifier: keyid:27:8E:67:11:74:C3:26:1D:3F:ED:33:63:B3:A4:D8:1D:30:E5:E8:D5 DirName:/C=US/ST=Montana/L=Bozeman/O=Sawtooth/OU=Consulting/CN=www.wolfssl.com/emailAddress=info@wolfssl.com - serial:AA:D3:3F:AC:18:0A:37:4D + serial:2C:80:CE:DB:47:9D:07:66:92:3D:68:D7:CA:AC:90:4F:CA:69:41:4B X509v3 Basic Constraints: CA:TRUE @@ -46,47 +47,47 @@ Certificate: X509v3 Extended Key Usage: TLS Web Server Authentication, TLS Web Client Authentication Signature Algorithm: sha256WithRSAEncryption - 62:98:c8:58:cf:56:03:86:5b:1b:71:49:7d:05:03:5d:e0:08: - 86:ad:db:4a:de:ab:22:96:a8:c3:59:68:c1:37:90:40:df:bd: - 89:d0:bc:da:8e:ef:87:b2:c2:62:52:e1:1a:29:17:6a:96:99: - c8:4e:d8:32:fe:b8:d1:5c:3b:0a:c2:3c:5f:a1:1e:98:7f:ce: - 89:26:21:1f:64:9c:15:7a:9c:ef:fb:1d:85:6a:fa:98:ce:a8: - a9:ab:c3:a2:c0:eb:87:ed:bc:21:df:f3:07:5b:ae:fd:40:d4: - ae:20:d0:76:8a:31:0a:a2:62:7c:61:0d:ce:5d:9a:1e:e4:20: - 88:51:49:fb:77:a9:cd:4d:c6:bf:54:99:33:ef:4b:a0:73:70: - 6d:2e:d9:3d:08:f6:12:39:31:68:c6:61:5c:41:b5:1b:f4:38: - 7d:fc:be:73:66:2d:f7:ca:5b:2c:5b:31:aa:cf:f6:7f:30:e4: - 12:2c:8e:d6:38:51:e6:45:ee:d5:da:c3:83:d6:ed:5e:ec:d6: - b6:14:b3:93:59:e1:55:4a:7f:04:df:ce:65:d4:df:18:4f:dd: - b4:45:7f:a6:56:30:c4:05:44:98:9d:4f:26:6d:84:80:a0:5e: - ed:23:d1:48:87:0e:05:06:91:3b:b0:3c:bb:8c:8f:3c:7b:4c: - 4f:a1:ca:98 + ae:b0:a4:35:8e:8a:1b:a6:eb:b3:a2:57:cf:3a:1f:dc:6e:bc: + d2:d0:a6:4a:8f:88:0a:6e:74:d5:d1:7c:d1:44:b1:d4:3b:17: + 03:09:5a:46:ed:08:08:cf:f1:fd:20:07:67:c0:97:ec:35:f3: + 75:ca:20:61:98:3e:f5:4d:be:e6:9d:75:1e:e4:03:ad:8c:a6: + 1e:3d:ec:e4:1a:92:5b:f9:a3:ad:83:ca:4f:cd:aa:38:bb:6e: + ae:ad:fa:a7:46:f1:8b:73:ec:09:23:bc:f2:18:e5:b7:92:86: + 3e:a4:75:60:c7:3d:0f:3f:83:00:c3:06:08:9c:d1:54:d6:ba: + 6d:95:3d:34:a1:be:24:91:cc:20:03:11:5b:72:1c:d4:65:d0: + 11:88:75:26:04:26:ef:66:70:e6:3b:38:87:9c:53:71:1b:09: + 51:70:50:99:4c:31:0c:62:44:57:30:60:04:fc:12:2c:a3:24: + b4:f7:11:d5:0e:b5:21:0b:ed:86:11:67:4d:36:fa:57:a0:59: + 55:21:b3:6d:e4:77:5e:ec:7e:f0:09:13:8e:99:98:b2:e1:82: + b6:4b:3e:0f:41:a6:0c:cd:49:99:7e:e4:8a:cb:37:ed:53:cf: + 86:5d:a9:26:a8:e5:01:25:5a:b4:bc:25:35:f1:fa:5a:5c:ce: + d4:b8:9a:2c -----BEGIN CERTIFICATE----- -MIIE6TCCA9GgAwIBAgIJAKrTP6wYCjdNMA0GCSqGSIb3DQEBCwUAMIGUMQswCQYD +MIIE/zCCA+egAwIBAgIULIDO20edB2aSPWjXyqyQT8ppQUswDQYJKoZIhvcNAQEL +BQAwgZQxCzAJBgNVBAYTAlVTMRAwDgYDVQQIDAdNb250YW5hMRAwDgYDVQQHDAdC +b3plbWFuMREwDwYDVQQKDAhTYXd0b290aDETMBEGA1UECwwKQ29uc3VsdGluZzEY +MBYGA1UEAwwPd3d3LndvbGZzc2wuY29tMR8wHQYJKoZIhvcNAQkBFhBpbmZvQHdv +bGZzc2wuY29tMB4XDTIyMTIxNjIxMTc0OVoXDTI1MDkxMTIxMTc0OVowgZQxCzAJ +BgNVBAYTAlVTMRAwDgYDVQQIDAdNb250YW5hMRAwDgYDVQQHDAdCb3plbWFuMREw +DwYDVQQKDAhTYXd0b290aDETMBEGA1UECwwKQ29uc3VsdGluZzEYMBYGA1UEAwwP +d3d3LndvbGZzc2wuY29tMR8wHQYJKoZIhvcNAQkBFhBpbmZvQHdvbGZzc2wuY29t +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvwzKLRSyHoRCW804H0ry +TXUQ8bY1n9/KfQOY06zeA2buKvHYsH1uB1QLEJghTYDLEiDnzE/eRX3Jcncy6sqQ +u2lSEAMvqPOVxfGLYlYb72dvpBBBla0Km+OlwLDScHZQMFuo6AgsfO2nonqNOCkc +rMft8nyVsJWCfUlcOM13Je+9gHVTlDw9ymNbnxW10x0TLxnRPNt2Osy4fcnlwtfa +QG/YIdxzG0ItU5z+Gvx9q3o2P5jehHwFZ85qFDiHqfGMtWjLaH9xICv1oGP1Vi+j +JtK3b7FaF9c4mQj+k1hv/sMTSQgWC6dNZwBSMWcjTpjtUUUduQTZC+zYKLNLve02 +eQIDAQABo4IBRTCCAUEwHQYDVR0OBBYEFCeOZxF0wyYdP+0zY7Ok2B0w5ejVMIHU +BgNVHSMEgcwwgcmAFCeOZxF0wyYdP+0zY7Ok2B0w5ejVoYGapIGXMIGUMQswCQYD VQQGEwJVUzEQMA4GA1UECAwHTW9udGFuYTEQMA4GA1UEBwwHQm96ZW1hbjERMA8G A1UECgwIU2F3dG9vdGgxEzARBgNVBAsMCkNvbnN1bHRpbmcxGDAWBgNVBAMMD3d3 -dy53b2xmc3NsLmNvbTEfMB0GCSqGSIb3DQEJARYQaW5mb0B3b2xmc3NsLmNvbTAe -Fw0yMTAyMTAxOTQ5NTJaFw0yMzExMDcxOTQ5NTJaMIGUMQswCQYDVQQGEwJVUzEQ -MA4GA1UECAwHTW9udGFuYTEQMA4GA1UEBwwHQm96ZW1hbjERMA8GA1UECgwIU2F3 -dG9vdGgxEzARBgNVBAsMCkNvbnN1bHRpbmcxGDAWBgNVBAMMD3d3dy53b2xmc3Ns -LmNvbTEfMB0GCSqGSIb3DQEJARYQaW5mb0B3b2xmc3NsLmNvbTCCASIwDQYJKoZI -hvcNAQEBBQADggEPADCCAQoCggEBAL8Myi0Ush6EQlvNOB9K8k11EPG2NZ/fyn0D -mNOs3gNm7irx2LB9bgdUCxCYIU2AyxIg58xP3kV9yXJ3MurKkLtpUhADL6jzlcXx -i2JWG+9nb6QQQZWtCpvjpcCw0nB2UDBbqOgILHztp6J6jTgpHKzH7fJ8lbCVgn1J -XDjNdyXvvYB1U5Q8PcpjW58VtdMdEy8Z0TzbdjrMuH3J5cLX2kBv2CHccxtCLVOc -/hr8fat6Nj+Y3oR8BWfOahQ4h6nxjLVoy2h/cSAr9aBj9VYvoybSt2+xWhfXOJkI -/pNYb/7DE0kIFgunTWcAUjFnI06Y7VFFHbkE2Qvs2CizS73tNnkCAwEAAaOCATow -ggE2MB0GA1UdDgQWBBQnjmcRdMMmHT/tM2OzpNgdMOXo1TCByQYDVR0jBIHBMIG+ -gBQnjmcRdMMmHT/tM2OzpNgdMOXo1aGBmqSBlzCBlDELMAkGA1UEBhMCVVMxEDAO -BgNVBAgMB01vbnRhbmExEDAOBgNVBAcMB0JvemVtYW4xETAPBgNVBAoMCFNhd3Rv -b3RoMRMwEQYDVQQLDApDb25zdWx0aW5nMRgwFgYDVQQDDA93d3cud29sZnNzbC5j -b20xHzAdBgkqhkiG9w0BCQEWEGluZm9Ad29sZnNzbC5jb22CCQCq0z+sGAo3TTAM -BgNVHRMEBTADAQH/MBwGA1UdEQQVMBOCC2V4YW1wbGUuY29thwR/AAABMB0GA1Ud -JQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjANBgkqhkiG9w0BAQsFAAOCAQEAYpjI -WM9WA4ZbG3FJfQUDXeAIhq3bSt6rIpaow1lowTeQQN+9idC82o7vh7LCYlLhGikX -apaZyE7YMv640Vw7CsI8X6EemH/OiSYhH2ScFXqc7/sdhWr6mM6oqavDosDrh+28 -Id/zB1uu/UDUriDQdooxCqJifGENzl2aHuQgiFFJ+3epzU3Gv1SZM+9LoHNwbS7Z -PQj2EjkxaMZhXEG1G/Q4ffy+c2Yt98pbLFsxqs/2fzDkEiyO1jhR5kXu1drDg9bt -XuzWthSzk1nhVUp/BN/OZdTfGE/dtEV/plYwxAVEmJ1PJm2EgKBe7SPRSIcOBQaR -O7A8u4yPPHtMT6HKmA== +dy53b2xmc3NsLmNvbTEfMB0GCSqGSIb3DQEJARYQaW5mb0B3b2xmc3NsLmNvbYIU +LIDO20edB2aSPWjXyqyQT8ppQUswDAYDVR0TBAUwAwEB/zAcBgNVHREEFTATggtl +eGFtcGxlLmNvbYcEfwAAATAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIw +DQYJKoZIhvcNAQELBQADggEBAK6wpDWOihum67OiV886H9xuvNLQpkqPiApudNXR +fNFEsdQ7FwMJWkbtCAjP8f0gB2fAl+w183XKIGGYPvVNvuaddR7kA62Mph497OQa +klv5o62Dyk/Nqji7bq6t+qdG8Ytz7AkjvPIY5beShj6kdWDHPQ8/gwDDBgic0VTW +um2VPTShviSRzCADEVtyHNRl0BGIdSYEJu9mcOY7OIecU3EbCVFwUJlMMQxiRFcw +YAT8EiyjJLT3EdUOtSEL7YYRZ002+legWVUhs23kd17sfvAJE46ZmLLhgrZLPg9B +pgzNSZl+5IrLN+1Tz4ZdqSao5QElWrS8JTXx+lpcztS4miw= -----END CERTIFICATE----- diff --git a/scripts/broker_test/server-cert.pem b/scripts/broker_test/server-cert.pem index 54dd74e32..de754a263 100644 --- a/scripts/broker_test/server-cert.pem +++ b/scripts/broker_test/server-cert.pem @@ -2,15 +2,15 @@ Certificate: Data: Version: 3 (0x2) Serial Number: 1 (0x1) - Signature Algorithm: sha256WithRSAEncryption - Issuer: C=US, ST=Montana, L=Bozeman, O=Sawtooth, OU=Consulting, CN=www.wolfssl.com/emailAddress=info@wolfssl.com + Signature Algorithm: sha256WithRSAEncryption + Issuer: C = US, ST = Montana, L = Bozeman, O = Sawtooth, OU = Consulting, CN = www.wolfssl.com, emailAddress = info@wolfssl.com Validity - Not Before: Feb 10 19:49:53 2021 GMT - Not After : Nov 7 19:49:53 2023 GMT - Subject: C=US, ST=Montana, L=Bozeman, O=wolfSSL, OU=Support, CN=www.wolfssl.com/emailAddress=info@wolfssl.com + Not Before: Dec 16 21:17:49 2022 GMT + Not After : Sep 11 21:17:49 2025 GMT + Subject: C = US, ST = Montana, L = Bozeman, O = wolfSSL, OU = Support, CN = www.wolfssl.com, emailAddress = info@wolfssl.com Subject Public Key Info: Public Key Algorithm: rsaEncryption - Public-Key: (2048 bit) + RSA Public-Key: (2048 bit) Modulus: 00:c0:95:08:e1:57:41:f2:71:6d:b7:d2:45:41:27: 01:65:c6:45:ae:f2:bc:24:30:b8:95:ce:2f:4e:d6: @@ -37,7 +37,7 @@ Certificate: X509v3 Authority Key Identifier: keyid:27:8E:67:11:74:C3:26:1D:3F:ED:33:63:B3:A4:D8:1D:30:E5:E8:D5 DirName:/C=US/ST=Montana/L=Bozeman/O=Sawtooth/OU=Consulting/CN=www.wolfssl.com/emailAddress=info@wolfssl.com - serial:AA:D3:3F:AC:18:0A:37:4D + serial:2C:80:CE:DB:47:9D:07:66:92:3D:68:D7:CA:AC:90:4F:CA:69:41:4B X509v3 Basic Constraints: CA:TRUE @@ -46,27 +46,27 @@ Certificate: X509v3 Extended Key Usage: TLS Web Server Authentication, TLS Web Client Authentication Signature Algorithm: sha256WithRSAEncryption - 1b:0d:a6:44:93:0d:0e:0c:35:28:26:40:31:d2:eb:26:4c:47: - 5b:19:fb:ad:fe:3a:f5:30:3a:28:d7:aa:69:a4:15:e7:26:6e: - b7:33:56:ac:8f:34:3d:f3:21:2f:53:58:91:d0:3e:b4:39:48: - bf:93:11:74:36:d3:87:49:c3:34:0d:30:30:ab:f4:4c:27:19: - d5:c4:0c:ad:49:bd:91:f8:da:9e:c8:2d:2a:ac:e2:75:8e:aa: - 08:d9:bf:65:ff:a3:b1:4f:f0:60:6f:4d:95:c4:06:7f:af:66: - 6a:23:3b:3a:a4:61:b6:6c:ca:be:e1:b0:77:f3:ec:83:d5:8c: - 1d:85:7f:8d:74:c8:ec:1e:49:ec:57:4a:cc:fd:e2:3a:3e:54: - 50:ae:67:cd:17:b0:67:a5:53:7f:c3:0e:3e:a7:58:e8:df:d5: - 0c:f2:64:f3:ad:12:70:e3:b9:42:bc:08:60:76:d5:0c:a5:31: - 77:50:e0:c8:f3:3a:3d:45:cf:32:75:ef:10:dd:b5:ed:6e:d2: - 2d:57:82:95:38:bc:7d:54:c4:84:5e:fb:7e:83:f5:f1:2d:9c: - 98:ac:73:e3:a7:d2:02:30:d6:1f:06:1e:d0:dc:3a:ac:f4:c2: - c2:be:72:40:9a:ea:cf:35:21:3b:56:6d:e1:52:f2:80:d7:35: - 83:97:07:cc + b9:10:f0:be:fe:c8:67:5e:7d:0f:36:33:c7:17:2a:01:c4:bb: + 74:83:4c:bc:bb:e2:ba:92:82:3a:d9:2d:8c:0e:e3:75:1b:c0: + 14:aa:40:1e:a8:11:7d:94:9c:3d:74:7a:3b:16:7b:d8:9d:f0: + e8:7d:1d:fa:3b:14:42:20:e3:05:a3:fd:b1:0c:f1:2a:c4:00: + 50:8d:1e:97:93:6a:de:82:13:24:9e:2b:fa:08:85:e3:4f:40: + fd:63:c7:3d:e9:bd:6f:7c:03:98:85:fe:b4:51:5d:7f:8c:83: + b3:ad:4a:88:e9:f3:4c:33:84:77:d3:02:35:59:e3:4e:64:a1: + b7:bb:fb:f8:fb:14:2a:ae:36:bf:d9:82:e7:cb:98:48:16:c8: + 81:d6:a0:f1:74:14:e3:74:4a:72:4a:f1:6f:dd:be:86:1e:20: + f3:05:16:83:1f:aa:7c:59:35:97:24:b8:27:b7:56:9f:30:2e: + 90:e0:19:e0:21:ca:9d:3f:da:99:07:94:79:49:53:14:5c:a2: + 2c:56:5b:b2:55:68:5c:1f:91:58:9a:cd:53:b5:ea:63:5a:72: + 49:41:cc:76:9f:88:35:86:0d:60:5d:e5:91:bd:ac:6f:cf:d5: + 92:27:72:4a:21:f4:58:98:8e:3b:d2:29:e6:ee:fa:e6:b0:6c: + 8b:1e:e0:54 -----BEGIN CERTIFICATE----- -MIIE3TCCA8WgAwIBAgIBATANBgkqhkiG9w0BAQsFADCBlDELMAkGA1UEBhMCVVMx +MIIE6DCCA9CgAwIBAgIBATANBgkqhkiG9w0BAQsFADCBlDELMAkGA1UEBhMCVVMx EDAOBgNVBAgMB01vbnRhbmExEDAOBgNVBAcMB0JvemVtYW4xETAPBgNVBAoMCFNh d3Rvb3RoMRMwEQYDVQQLDApDb25zdWx0aW5nMRgwFgYDVQQDDA93d3cud29sZnNz -bC5jb20xHzAdBgkqhkiG9w0BCQEWEGluZm9Ad29sZnNzbC5jb20wHhcNMjEwMjEw -MTk0OTUzWhcNMjMxMTA3MTk0OTUzWjCBkDELMAkGA1UEBhMCVVMxEDAOBgNVBAgM +bC5jb20xHzAdBgkqhkiG9w0BCQEWEGluZm9Ad29sZnNzbC5jb20wHhcNMjIxMjE2 +MjExNzQ5WhcNMjUwOTExMjExNzQ5WjCBkDELMAkGA1UEBhMCVVMxEDAOBgNVBAgM B01vbnRhbmExEDAOBgNVBAcMB0JvemVtYW4xEDAOBgNVBAoMB3dvbGZTU0wxEDAO BgNVBAsMB1N1cHBvcnQxGDAWBgNVBAMMD3d3dy53b2xmc3NsLmNvbTEfMB0GCSqG SIb3DQEJARYQaW5mb0B3b2xmc3NsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEP @@ -75,34 +75,35 @@ f/5cnFF194rKB+c1L4/hvXvAL3yrZKgX/Mpde7rgIeVyLm8uhtiVc9qsG1O5Xz/X GQ0lT+FjY1GLC2Q/rUO4pRxcNLOuAKBjxfZ/C1loeHOmjBipAm2vwxkBLrgQ48bM QLRpo0YzaYduxLsXpvPo3a1zvHsvIbX9ZlEMvVSz4W1fHLwjc9EJA4kU0hC5ZMMq 0KGWSrzh1Bpbx6DAwWN4D0Q3MDKWgDIjlaF3uhPSl3PiXSXJag3DOWCktLBpQkIJ -6dgIvDMgs1gip6rrxOHmYYPF0pbf2dBPrdcCAwEAAaOCATowggE2MB0GA1UdDgQW -BBSzETLJkpiE4sn40DtuA0LKHw6OPDCByQYDVR0jBIHBMIG+gBQnjmcRdMMmHT/t +6dgIvDMgs1gip6rrxOHmYYPF0pbf2dBPrdcCAwEAAaOCAUUwggFBMB0GA1UdDgQW +BBSzETLJkpiE4sn40DtuA0LKHw6OPDCB1AYDVR0jBIHMMIHJgBQnjmcRdMMmHT/t M2OzpNgdMOXo1aGBmqSBlzCBlDELMAkGA1UEBhMCVVMxEDAOBgNVBAgMB01vbnRh bmExEDAOBgNVBAcMB0JvemVtYW4xETAPBgNVBAoMCFNhd3Rvb3RoMRMwEQYDVQQL DApDb25zdWx0aW5nMRgwFgYDVQQDDA93d3cud29sZnNzbC5jb20xHzAdBgkqhkiG -9w0BCQEWEGluZm9Ad29sZnNzbC5jb22CCQCq0z+sGAo3TTAMBgNVHRMEBTADAQH/ -MBwGA1UdEQQVMBOCC2V4YW1wbGUuY29thwR/AAABMB0GA1UdJQQWMBQGCCsGAQUF -BwMBBggrBgEFBQcDAjANBgkqhkiG9w0BAQsFAAOCAQEAGw2mRJMNDgw1KCZAMdLr -JkxHWxn7rf469TA6KNeqaaQV5yZutzNWrI80PfMhL1NYkdA+tDlIv5MRdDbTh0nD -NA0wMKv0TCcZ1cQMrUm9kfjansgtKqzidY6qCNm/Zf+jsU/wYG9NlcQGf69maiM7 -OqRhtmzKvuGwd/Psg9WMHYV/jXTI7B5J7FdKzP3iOj5UUK5nzRewZ6VTf8MOPqdY -6N/VDPJk860ScOO5QrwIYHbVDKUxd1DgyPM6PUXPMnXvEN217W7SLVeClTi8fVTE -hF77foP18S2cmKxz46fSAjDWHwYe0Nw6rPTCwr5yQJrqzzUhO1Zt4VLygNc1g5cH -zA== +9w0BCQEWEGluZm9Ad29sZnNzbC5jb22CFCyAzttHnQdmkj1o18qskE/KaUFLMAwG +A1UdEwQFMAMBAf8wHAYDVR0RBBUwE4ILZXhhbXBsZS5jb22HBH8AAAEwHQYDVR0l +BBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMA0GCSqGSIb3DQEBCwUAA4IBAQC5EPC+ +/shnXn0PNjPHFyoBxLt0g0y8u+K6koI62S2MDuN1G8AUqkAeqBF9lJw9dHo7FnvY +nfDofR36OxRCIOMFo/2xDPEqxABQjR6Xk2reghMkniv6CIXjT0D9Y8c96b1vfAOY +hf60UV1/jIOzrUqI6fNMM4R30wI1WeNOZKG3u/v4+xQqrja/2YLny5hIFsiB1qDx +dBTjdEpySvFv3b6GHiDzBRaDH6p8WTWXJLgnt1afMC6Q4BngIcqdP9qZB5R5SVMU +XKIsVluyVWhcH5FYms1TtepjWnJJQcx2n4g1hg1gXeWRvaxvz9WSJ3JKIfRYmI47 +0inm7vrmsGyLHuBU -----END CERTIFICATE----- Certificate: Data: Version: 3 (0x2) - Serial Number: 12309252214903945037 (0xaad33fac180a374d) - Signature Algorithm: sha256WithRSAEncryption - Issuer: C=US, ST=Montana, L=Bozeman, O=Sawtooth, OU=Consulting, CN=www.wolfssl.com/emailAddress=info@wolfssl.com + Serial Number: + 2c:80:ce:db:47:9d:07:66:92:3d:68:d7:ca:ac:90:4f:ca:69:41:4b + Signature Algorithm: sha256WithRSAEncryption + Issuer: C = US, ST = Montana, L = Bozeman, O = Sawtooth, OU = Consulting, CN = www.wolfssl.com, emailAddress = info@wolfssl.com Validity - Not Before: Feb 10 19:49:52 2021 GMT - Not After : Nov 7 19:49:52 2023 GMT - Subject: C=US, ST=Montana, L=Bozeman, O=Sawtooth, OU=Consulting, CN=www.wolfssl.com/emailAddress=info@wolfssl.com + Not Before: Dec 16 21:17:49 2022 GMT + Not After : Sep 11 21:17:49 2025 GMT + Subject: C = US, ST = Montana, L = Bozeman, O = Sawtooth, OU = Consulting, CN = www.wolfssl.com, emailAddress = info@wolfssl.com Subject Public Key Info: Public Key Algorithm: rsaEncryption - Public-Key: (2048 bit) + RSA Public-Key: (2048 bit) Modulus: 00:bf:0c:ca:2d:14:b2:1e:84:42:5b:cd:38:1f:4a: f2:4d:75:10:f1:b6:35:9f:df:ca:7d:03:98:d3:ac: @@ -129,7 +130,7 @@ Certificate: X509v3 Authority Key Identifier: keyid:27:8E:67:11:74:C3:26:1D:3F:ED:33:63:B3:A4:D8:1D:30:E5:E8:D5 DirName:/C=US/ST=Montana/L=Bozeman/O=Sawtooth/OU=Consulting/CN=www.wolfssl.com/emailAddress=info@wolfssl.com - serial:AA:D3:3F:AC:18:0A:37:4D + serial:2C:80:CE:DB:47:9D:07:66:92:3D:68:D7:CA:AC:90:4F:CA:69:41:4B X509v3 Basic Constraints: CA:TRUE @@ -138,47 +139,47 @@ Certificate: X509v3 Extended Key Usage: TLS Web Server Authentication, TLS Web Client Authentication Signature Algorithm: sha256WithRSAEncryption - 62:98:c8:58:cf:56:03:86:5b:1b:71:49:7d:05:03:5d:e0:08: - 86:ad:db:4a:de:ab:22:96:a8:c3:59:68:c1:37:90:40:df:bd: - 89:d0:bc:da:8e:ef:87:b2:c2:62:52:e1:1a:29:17:6a:96:99: - c8:4e:d8:32:fe:b8:d1:5c:3b:0a:c2:3c:5f:a1:1e:98:7f:ce: - 89:26:21:1f:64:9c:15:7a:9c:ef:fb:1d:85:6a:fa:98:ce:a8: - a9:ab:c3:a2:c0:eb:87:ed:bc:21:df:f3:07:5b:ae:fd:40:d4: - ae:20:d0:76:8a:31:0a:a2:62:7c:61:0d:ce:5d:9a:1e:e4:20: - 88:51:49:fb:77:a9:cd:4d:c6:bf:54:99:33:ef:4b:a0:73:70: - 6d:2e:d9:3d:08:f6:12:39:31:68:c6:61:5c:41:b5:1b:f4:38: - 7d:fc:be:73:66:2d:f7:ca:5b:2c:5b:31:aa:cf:f6:7f:30:e4: - 12:2c:8e:d6:38:51:e6:45:ee:d5:da:c3:83:d6:ed:5e:ec:d6: - b6:14:b3:93:59:e1:55:4a:7f:04:df:ce:65:d4:df:18:4f:dd: - b4:45:7f:a6:56:30:c4:05:44:98:9d:4f:26:6d:84:80:a0:5e: - ed:23:d1:48:87:0e:05:06:91:3b:b0:3c:bb:8c:8f:3c:7b:4c: - 4f:a1:ca:98 + ae:b0:a4:35:8e:8a:1b:a6:eb:b3:a2:57:cf:3a:1f:dc:6e:bc: + d2:d0:a6:4a:8f:88:0a:6e:74:d5:d1:7c:d1:44:b1:d4:3b:17: + 03:09:5a:46:ed:08:08:cf:f1:fd:20:07:67:c0:97:ec:35:f3: + 75:ca:20:61:98:3e:f5:4d:be:e6:9d:75:1e:e4:03:ad:8c:a6: + 1e:3d:ec:e4:1a:92:5b:f9:a3:ad:83:ca:4f:cd:aa:38:bb:6e: + ae:ad:fa:a7:46:f1:8b:73:ec:09:23:bc:f2:18:e5:b7:92:86: + 3e:a4:75:60:c7:3d:0f:3f:83:00:c3:06:08:9c:d1:54:d6:ba: + 6d:95:3d:34:a1:be:24:91:cc:20:03:11:5b:72:1c:d4:65:d0: + 11:88:75:26:04:26:ef:66:70:e6:3b:38:87:9c:53:71:1b:09: + 51:70:50:99:4c:31:0c:62:44:57:30:60:04:fc:12:2c:a3:24: + b4:f7:11:d5:0e:b5:21:0b:ed:86:11:67:4d:36:fa:57:a0:59: + 55:21:b3:6d:e4:77:5e:ec:7e:f0:09:13:8e:99:98:b2:e1:82: + b6:4b:3e:0f:41:a6:0c:cd:49:99:7e:e4:8a:cb:37:ed:53:cf: + 86:5d:a9:26:a8:e5:01:25:5a:b4:bc:25:35:f1:fa:5a:5c:ce: + d4:b8:9a:2c -----BEGIN CERTIFICATE----- -MIIE6TCCA9GgAwIBAgIJAKrTP6wYCjdNMA0GCSqGSIb3DQEBCwUAMIGUMQswCQYD +MIIE/zCCA+egAwIBAgIULIDO20edB2aSPWjXyqyQT8ppQUswDQYJKoZIhvcNAQEL +BQAwgZQxCzAJBgNVBAYTAlVTMRAwDgYDVQQIDAdNb250YW5hMRAwDgYDVQQHDAdC +b3plbWFuMREwDwYDVQQKDAhTYXd0b290aDETMBEGA1UECwwKQ29uc3VsdGluZzEY +MBYGA1UEAwwPd3d3LndvbGZzc2wuY29tMR8wHQYJKoZIhvcNAQkBFhBpbmZvQHdv +bGZzc2wuY29tMB4XDTIyMTIxNjIxMTc0OVoXDTI1MDkxMTIxMTc0OVowgZQxCzAJ +BgNVBAYTAlVTMRAwDgYDVQQIDAdNb250YW5hMRAwDgYDVQQHDAdCb3plbWFuMREw +DwYDVQQKDAhTYXd0b290aDETMBEGA1UECwwKQ29uc3VsdGluZzEYMBYGA1UEAwwP +d3d3LndvbGZzc2wuY29tMR8wHQYJKoZIhvcNAQkBFhBpbmZvQHdvbGZzc2wuY29t +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvwzKLRSyHoRCW804H0ry +TXUQ8bY1n9/KfQOY06zeA2buKvHYsH1uB1QLEJghTYDLEiDnzE/eRX3Jcncy6sqQ +u2lSEAMvqPOVxfGLYlYb72dvpBBBla0Km+OlwLDScHZQMFuo6AgsfO2nonqNOCkc +rMft8nyVsJWCfUlcOM13Je+9gHVTlDw9ymNbnxW10x0TLxnRPNt2Osy4fcnlwtfa +QG/YIdxzG0ItU5z+Gvx9q3o2P5jehHwFZ85qFDiHqfGMtWjLaH9xICv1oGP1Vi+j +JtK3b7FaF9c4mQj+k1hv/sMTSQgWC6dNZwBSMWcjTpjtUUUduQTZC+zYKLNLve02 +eQIDAQABo4IBRTCCAUEwHQYDVR0OBBYEFCeOZxF0wyYdP+0zY7Ok2B0w5ejVMIHU +BgNVHSMEgcwwgcmAFCeOZxF0wyYdP+0zY7Ok2B0w5ejVoYGapIGXMIGUMQswCQYD VQQGEwJVUzEQMA4GA1UECAwHTW9udGFuYTEQMA4GA1UEBwwHQm96ZW1hbjERMA8G A1UECgwIU2F3dG9vdGgxEzARBgNVBAsMCkNvbnN1bHRpbmcxGDAWBgNVBAMMD3d3 -dy53b2xmc3NsLmNvbTEfMB0GCSqGSIb3DQEJARYQaW5mb0B3b2xmc3NsLmNvbTAe -Fw0yMTAyMTAxOTQ5NTJaFw0yMzExMDcxOTQ5NTJaMIGUMQswCQYDVQQGEwJVUzEQ -MA4GA1UECAwHTW9udGFuYTEQMA4GA1UEBwwHQm96ZW1hbjERMA8GA1UECgwIU2F3 -dG9vdGgxEzARBgNVBAsMCkNvbnN1bHRpbmcxGDAWBgNVBAMMD3d3dy53b2xmc3Ns -LmNvbTEfMB0GCSqGSIb3DQEJARYQaW5mb0B3b2xmc3NsLmNvbTCCASIwDQYJKoZI -hvcNAQEBBQADggEPADCCAQoCggEBAL8Myi0Ush6EQlvNOB9K8k11EPG2NZ/fyn0D -mNOs3gNm7irx2LB9bgdUCxCYIU2AyxIg58xP3kV9yXJ3MurKkLtpUhADL6jzlcXx -i2JWG+9nb6QQQZWtCpvjpcCw0nB2UDBbqOgILHztp6J6jTgpHKzH7fJ8lbCVgn1J -XDjNdyXvvYB1U5Q8PcpjW58VtdMdEy8Z0TzbdjrMuH3J5cLX2kBv2CHccxtCLVOc -/hr8fat6Nj+Y3oR8BWfOahQ4h6nxjLVoy2h/cSAr9aBj9VYvoybSt2+xWhfXOJkI -/pNYb/7DE0kIFgunTWcAUjFnI06Y7VFFHbkE2Qvs2CizS73tNnkCAwEAAaOCATow -ggE2MB0GA1UdDgQWBBQnjmcRdMMmHT/tM2OzpNgdMOXo1TCByQYDVR0jBIHBMIG+ -gBQnjmcRdMMmHT/tM2OzpNgdMOXo1aGBmqSBlzCBlDELMAkGA1UEBhMCVVMxEDAO -BgNVBAgMB01vbnRhbmExEDAOBgNVBAcMB0JvemVtYW4xETAPBgNVBAoMCFNhd3Rv -b3RoMRMwEQYDVQQLDApDb25zdWx0aW5nMRgwFgYDVQQDDA93d3cud29sZnNzbC5j -b20xHzAdBgkqhkiG9w0BCQEWEGluZm9Ad29sZnNzbC5jb22CCQCq0z+sGAo3TTAM -BgNVHRMEBTADAQH/MBwGA1UdEQQVMBOCC2V4YW1wbGUuY29thwR/AAABMB0GA1Ud -JQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjANBgkqhkiG9w0BAQsFAAOCAQEAYpjI -WM9WA4ZbG3FJfQUDXeAIhq3bSt6rIpaow1lowTeQQN+9idC82o7vh7LCYlLhGikX -apaZyE7YMv640Vw7CsI8X6EemH/OiSYhH2ScFXqc7/sdhWr6mM6oqavDosDrh+28 -Id/zB1uu/UDUriDQdooxCqJifGENzl2aHuQgiFFJ+3epzU3Gv1SZM+9LoHNwbS7Z -PQj2EjkxaMZhXEG1G/Q4ffy+c2Yt98pbLFsxqs/2fzDkEiyO1jhR5kXu1drDg9bt -XuzWthSzk1nhVUp/BN/OZdTfGE/dtEV/plYwxAVEmJ1PJm2EgKBe7SPRSIcOBQaR -O7A8u4yPPHtMT6HKmA== +dy53b2xmc3NsLmNvbTEfMB0GCSqGSIb3DQEJARYQaW5mb0B3b2xmc3NsLmNvbYIU +LIDO20edB2aSPWjXyqyQT8ppQUswDAYDVR0TBAUwAwEB/zAcBgNVHREEFTATggtl +eGFtcGxlLmNvbYcEfwAAATAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIw +DQYJKoZIhvcNAQELBQADggEBAK6wpDWOihum67OiV886H9xuvNLQpkqPiApudNXR +fNFEsdQ7FwMJWkbtCAjP8f0gB2fAl+w183XKIGGYPvVNvuaddR7kA62Mph497OQa +klv5o62Dyk/Nqji7bq6t+qdG8Ytz7AkjvPIY5beShj6kdWDHPQ8/gwDDBgic0VTW +um2VPTShviSRzCADEVtyHNRl0BGIdSYEJu9mcOY7OIecU3EbCVFwUJlMMQxiRFcw +YAT8EiyjJLT3EdUOtSEL7YYRZ002+legWVUhs23kd17sfvAJE46ZmLLhgrZLPg9B +pgzNSZl+5IrLN+1Tz4ZdqSao5QElWrS8JTXx+lpcztS4miw= -----END CERTIFICATE----- diff --git a/wolfmqtt/version.h b/wolfmqtt/version.h index 170511f37..8b501a23d 100644 --- a/wolfmqtt/version.h +++ b/wolfmqtt/version.h @@ -34,8 +34,8 @@ extern "C" { #endif -#define LIBWOLFMQTT_VERSION_STRING "1.16.0" -#define LIBWOLFMQTT_VERSION_HEX 0x01016000 +#define LIBWOLFMQTT_VERSION_STRING "1.17.0" +#define LIBWOLFMQTT_VERSION_HEX 0x01017000 #ifdef __cplusplus } From 65f3241505db18d78ec96260905273615a5b7d26 Mon Sep 17 00:00:00 2001 From: Eric Blankenhorn Date: Wed, 1 Nov 2023 16:09:44 -0500 Subject: [PATCH 12/62] Fixes for release --- ChangeLog.md | 5 ++++- examples/pub-sub/mqtt-pub.c | 1 - scripts/firmware.test | 3 +++ src/mqtt_sn_packet.c | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/ChangeLog.md b/ChangeLog.md index 595761320..9c26a858e 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,6 +1,9 @@ ## Release Notes -### v1.16.0 (11/2/2023) +### v1.17.0 (11/2/2023) +Release 1.17.0 has been developed according to wolfSSL's development and QA process (see link below) and successfully passed the quality criteria. +https://www.wolfssl.com/about/wolfssl-software-development-process-quality-assurance + * Fix for declaration after executable block by @lealem47 in #341 * Add QNX IDE, Makefile, and remove source code exec bit by @JacobBarthelmeh in #317 diff --git a/examples/pub-sub/mqtt-pub.c b/examples/pub-sub/mqtt-pub.c index b994cb68c..467fccee5 100644 --- a/examples/pub-sub/mqtt-pub.c +++ b/examples/pub-sub/mqtt-pub.c @@ -461,7 +461,6 @@ int pub_client(MQTTCtx *mqttCtx) static BOOL CtrlHandler(DWORD fdwCtrlType) { if (fdwCtrlType == CTRL_C_EVENT) { - mStopRead = 1; PRINTF("Received Ctrl+c"); return TRUE; } diff --git a/scripts/firmware.test b/scripts/firmware.test index 52926617b..9284a8e78 100755 --- a/scripts/firmware.test +++ b/scripts/firmware.test @@ -67,6 +67,9 @@ sleep 0.5 server_result=$? [ $server_result -ne 0 ] && echo -e "\n\nMQTT Example fwpush failed!" && do_cleanup "-1" +# give some time for the client complete +sleep 0.5 + if [ $ENABLE_MQTT_TLS -ne 1 ]; then # Compare files echo "Comparing files" diff --git a/src/mqtt_sn_packet.c b/src/mqtt_sn_packet.c index 258980ed0..287142a2b 100644 --- a/src/mqtt_sn_packet.c +++ b/src/mqtt_sn_packet.c @@ -1488,7 +1488,7 @@ int SN_Packet_Read(MqttClient *client, byte* rx_buf, int rx_buf_len, return MqttPacket_HandleNetError(client, MQTT_TRACE_ERROR(MQTT_CODE_ERROR_NETWORK)); } - rc += len; + len += rc; } else { rc = MqttSocket_Peek(client, rx_buf, 4, timeout_ms); From f6df185e9dbf11ed1ad381f8afab988d7acfbd73 Mon Sep 17 00:00:00 2001 From: Lealem Amedie Date: Fri, 3 Nov 2023 11:54:02 -0600 Subject: [PATCH 13/62] Include stdint in userio_template.h --- IDE/STM32CUBE/userio_template.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/IDE/STM32CUBE/userio_template.h b/IDE/STM32CUBE/userio_template.h index e61720c7c..d902850c2 100644 --- a/IDE/STM32CUBE/userio_template.h +++ b/IDE/STM32CUBE/userio_template.h @@ -24,6 +24,8 @@ #ifdef WOLFMQTT_USER_IO +#include + #define SOCK_STREAM 1 #define SOCK_DGRAM 2 #define SOCK_RAW 3 From 4013ae76897243135082c77dc69e704d334e6ea9 Mon Sep 17 00:00:00 2001 From: Lealem Amedie Date: Fri, 3 Nov 2023 11:56:25 -0600 Subject: [PATCH 14/62] Remove comma from last enum --- wolfmqtt/mqtt_packet.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wolfmqtt/mqtt_packet.h b/wolfmqtt/mqtt_packet.h index 251a7c6d2..4c4ba5d0c 100644 --- a/wolfmqtt/mqtt_packet.h +++ b/wolfmqtt/mqtt_packet.h @@ -254,7 +254,7 @@ enum MqttPacketFlags { MQTT_PACKET_FLAG_RETAIN = 0x1, MQTT_PACKET_FLAG_QOS_SHIFT = 0x1, MQTT_PACKET_FLAG_QOS_MASK = 0x6, - MQTT_PACKET_FLAG_DUPLICATE = 0x8, + MQTT_PACKET_FLAG_DUPLICATE = 0x8 }; /* Packet Header: Size is variable 2 - 5 bytes */ From c9d41db3abce35a0695c89526cc8fbaa9ebcf74a Mon Sep 17 00:00:00 2001 From: David Garske Date: Wed, 1 Nov 2023 14:52:24 -0700 Subject: [PATCH 15/62] Improvements to multithread example and tests. --- examples/multithread/multithread.c | 18 ++++++++++++++++-- src/mqtt_socket.c | 14 ++++++++++---- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/examples/multithread/multithread.c b/examples/multithread/multithread.c index b00365b95..983f9fc14 100755 --- a/examples/multithread/multithread.c +++ b/examples/multithread/multithread.c @@ -109,7 +109,7 @@ static int check_response(MQTTCtx* mqttCtx, int rc, word32* startSec, /* check for test mode */ if (mqtt_stop_get()) { PRINTF("MQTT Exiting Thread..."); - return MQTT_CODE_SUCCESS; + return MQTT_CODE_ERROR_SYSTEM; } #ifdef WOLFMQTT_NONBLOCK @@ -387,6 +387,11 @@ static int multithread_test_finish(MQTTCtx *mqttCtx) PRINTF("MQTT Client Done: %d", mqttCtx->return_code); + if (mStopRead && mqttCtx->return_code == MQTT_CODE_ERROR_SYSTEM) { + /* this is okay, we requested termination */ + mqttCtx->return_code = MQTT_CODE_SUCCESS; + } + return mqttCtx->return_code; } @@ -507,12 +512,17 @@ static void *waitMessage_task(void *param) } /* Try and read packet */ - rc = MqttClient_WaitMessage(&mqttCtx->client, cmd_timeout_ms); + rc = MqttClient_WaitMessage_ex(&mqttCtx->client, &mqttCtx->client.msg, + cmd_timeout_ms); if (mqttCtx->test_mode && rc == MQTT_CODE_ERROR_TIMEOUT) { rc = 0; } rc = check_response(mqttCtx, rc, &startSec, MQTT_PACKET_TYPE_ANY, cmd_timeout_ms); + if (rc != MQTT_CODE_SUCCESS && rc != MQTT_CODE_CONTINUE) { + MqttClient_CancelMessage(&mqttCtx->client, + (MqttObject*)&mqttCtx->client.msg); + } /* check return code */ if (rc == MQTT_CODE_CONTINUE) { @@ -540,6 +550,10 @@ static void *waitMessage_task(void *param) rc = MqttClient_Publish(&mqttCtx->client, &mqttCtx->publish); } while (rc == MQTT_CODE_CONTINUE); + if (rc != MQTT_CODE_SUCCESS) { + MqttClient_CancelMessage(&mqttCtx->client, + (MqttObject*)&mqttCtx->publish); + } PRINTF("MQTT Publish: Topic %s, %s (%d)", mqttCtx->publish.topic_name, MqttClient_ReturnCodeToString(rc), rc); diff --git a/src/mqtt_socket.c b/src/mqtt_socket.c index f8888d254..86ae1822b 100644 --- a/src/mqtt_socket.c +++ b/src/mqtt_socket.c @@ -41,6 +41,12 @@ #ifdef WOLFMQTT_NO_STDIO #undef WOLFMQTT_DEBUG_SOCKET #endif + +/* #define WOLFMQTT_TEST_NONBLOCK */ +#ifdef WOLFMQTT_TEST_NONBLOCK + #define WOLFMQTT_TEST_NONBLOCK_TIMES 1 +#endif + /* lwip */ #ifdef WOLFSSL_LWIP #undef read @@ -125,8 +131,8 @@ static int MqttSocket_WriteDo(MqttClient *client, const byte* buf, int buf_len, #if defined(WOLFMQTT_NONBLOCK) && defined(WOLFMQTT_TEST_NONBLOCK) static int testNbWriteAlt = 0; - if (!testNbWriteAlt) { - testNbWriteAlt = 1; + if (testNbWriteAlt < WOLFMQTT_TEST_NONBLOCK_TIMES) { + testNbWriteAlt++; return MQTT_CODE_CONTINUE; } testNbWriteAlt = 0; @@ -247,8 +253,8 @@ static int MqttSocket_ReadDo(MqttClient *client, byte* buf, int buf_len, #if defined(WOLFMQTT_NONBLOCK) && defined(WOLFMQTT_TEST_NONBLOCK) static int testNbReadAlt = 0; - if (!testNbReadAlt) { - testNbReadAlt = 1; + if (testNbReadAlt < WOLFMQTT_TEST_NONBLOCK_TIMES) { + testNbReadAlt++; return MQTT_CODE_CONTINUE; } testNbReadAlt = 0; From 01db5ca100b82f06db549cdcea1e8d74ade866d0 Mon Sep 17 00:00:00 2001 From: David Garske Date: Fri, 3 Nov 2023 13:48:07 -0700 Subject: [PATCH 16/62] Refactor of locking to better detect and handle edge cases. Allow partial writes on all packet types. --- examples/multithread/multithread.c | 2 +- src/mqtt_client.c | 515 ++++++++++++++++++----------- src/mqtt_socket.c | 4 + wolfmqtt/mqtt_client.h | 11 +- 4 files changed, 326 insertions(+), 206 deletions(-) diff --git a/examples/multithread/multithread.c b/examples/multithread/multithread.c index 983f9fc14..fbb6522b3 100755 --- a/examples/multithread/multithread.c +++ b/examples/multithread/multithread.c @@ -619,11 +619,11 @@ static void *publish_task(void *param) MqttClient_CancelMessage(&mqttCtx->client, (MqttObject*)&publish); } - wm_SemLock(&mtLock); PRINTF("MQTT Publish: Topic %s, %s (%d)", publish.topic_name, MqttClient_ReturnCodeToString(rc), rc); + wm_SemLock(&mtLock); mNumMsgsDone++; wm_SemUnlock(&mtLock); diff --git a/src/mqtt_client.c b/src/mqtt_client.c index de2bc6c57..d2f0e6112 100644 --- a/src/mqtt_client.c +++ b/src/mqtt_client.c @@ -49,7 +49,7 @@ static int MqttClient_Publish_ReadPayload(MqttClient* client, int wm_SemInit(wm_Sem *s){ /* dispatch_release() fails hard, with Trace/BPT trap signal, if the * sem's internal count is less than the value passed in with - * dispatch_semaphore_create(). work around this by initing + * dispatch_semaphore_create(). work around this by initializing * with 0, then incrementing it afterwards. */ s->sem = dispatch_semaphore_create(0); @@ -159,6 +159,97 @@ static int MqttClient_Publish_ReadPayload(MqttClient* client, #endif +#ifdef WOLFMQTT_MULTITHREAD +static int MqttLockSend(MqttClient* client, MqttMsgStat* stat) +{ + int rc = 0; + +#ifdef WOLFMQTT_DEBUG_CLIENT + if (stat->isWriteLocked) { + MQTT_TRACE_MSG("Warning, send already locked!"); + rc = MQTT_CODE_ERROR_SYSTEM; + } + /* detect if a write is already in progress */ + if (wm_SemLock(&client->lockClient) == 0) { + if (client->write.total > 0) { + MQTT_TRACE_MSG("Partial write in progress!"); + rc = MQTT_CODE_CONTINUE; /* can't write yet */ + } + wm_SemUnlock(&client->lockClient); + } + if (rc != 0) { + return rc; + } +#endif + + rc = wm_SemLock(&client->lockSend); + if (rc == 0) { + stat->isWriteLocked = 1; + MQTT_TRACE_MSG("lockSend"); + } + return rc; +} +static void MqttUnlockSend(MqttClient* client, MqttMsgStat* stat) +{ +#ifdef WOLFMQTT_DEBUG_CLIENT + if (!stat->isWriteLocked) { + MQTT_TRACE_MSG("Warning, send not locked!"); + return; + } +#endif + + if (stat->isWriteLocked) { + MQTT_TRACE_MSG("unlockSend"); + stat->isWriteLocked = 0; + wm_SemUnlock(&client->lockSend); + } +} + +static int MqttLockRecv(MqttClient* client, MqttMsgStat* stat) +{ + int rc = 0; + +#ifdef WOLFMQTT_DEBUG_CLIENT + if (stat->isReadLocked) { + MQTT_TRACE_MSG("Warning, recv already locked!"); + rc = MQTT_CODE_ERROR_SYSTEM; + } + /* detect if a write is already in progress */ + if (wm_SemLock(&client->lockClient) == 0) { + if (client->read.total > 0) { + MQTT_TRACE_MSG("Partial read in progress!"); + rc = MQTT_CODE_CONTINUE; /* can't read yet */ + } + wm_SemUnlock(&client->lockClient); + } + if (rc != 0) + return rc; +#endif + + rc = wm_SemLock(&client->lockRecv); + if (rc == 0) { + stat->isReadLocked = 1; + MQTT_TRACE_MSG("lockRecv"); + } + return rc; +} +static void MqttUnlockRecv(MqttClient* client, MqttMsgStat* stat) +{ +#ifdef WOLFMQTT_DEBUG_CLIENT + if (!stat->isReadLocked) { + MQTT_TRACE_MSG("Warning, recv not locked!"); + return; + } +#endif + + if (stat->isReadLocked) { + stat->isReadLocked = 0; + MQTT_TRACE_MSG("unlockRecv"); + wm_SemUnlock(&client->lockRecv); + } +} +#endif /* WOLFMQTT_MULTITHREAD */ + /* These RespList functions assume caller has locked client->lockClient mutex */ int MqttClient_RespList_Add(MqttClient *client, MqttPacketType packet_type, word16 packet_id, MqttPendResp *newResp, @@ -954,20 +1045,14 @@ static int MqttClient_WaitType(MqttClient *client, void *packet_obj, #ifdef WOLFMQTT_MULTITHREAD /* Check to see if packet type and id have already completed */ rc = MqttClient_CheckPendResp(client, wait_type, wait_packet_id); - if (rc != MQTT_CODE_ERROR_NOT_FOUND - && rc != MQTT_CODE_CONTINUE - ) { + if (rc != MQTT_CODE_ERROR_NOT_FOUND && rc != MQTT_CODE_CONTINUE) { return rc; } /* Lock recv socket mutex */ - rc = wm_SemLock(&client->lockRecv); - if (rc != 0) { - PRINTF("MqttClient_WaitType: recv lock error!"); + if ((rc = MqttLockRecv(client, mms_stat)) != 0) { return rc; } - mms_stat->isReadLocked = 1; - MQTT_TRACE_MSG("lockRecv"); #endif /* reset the packet state used by MqttPacket_Read */ @@ -1037,7 +1122,8 @@ static int MqttClient_WaitType(MqttClient *client, void *packet_obj, #ifdef WOLFMQTT_DEBUG_CLIENT PRINTF("Using INCOMING packet_obj %p", use_packet_obj); #endif - if (packet_type == wait_type || wait_type == MQTT_PACKET_TYPE_ANY) { + if (packet_type == wait_type || + wait_type == MQTT_PACKET_TYPE_ANY) { /* Only stop waiting when matched or waiting for "any" */ waitMatchFound = 1; } @@ -1149,12 +1235,9 @@ static int MqttClient_WaitType(MqttClient *client, void *packet_obj, #ifdef WOLFMQTT_MULTITHREAD /* release read lock, done reading */ - if (mms_stat->isReadLocked) { - mms_stat->isReadLocked = 0; - MQTT_TRACE_MSG("unlockRecv"); - wm_SemUnlock(&client->lockRecv); - } + MqttUnlockRecv(client, mms_stat); #endif + XMEMSET(&client->read, 0, sizeof(client->read)); /* reset read */ /* if error, leave */ if (rc != MQTT_CODE_SUCCESS) { @@ -1168,10 +1251,9 @@ static int MqttClient_WaitType(MqttClient *client, void *packet_obj, #ifdef WOLFMQTT_MULTITHREAD /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) break; - mms_stat->isWriteLocked = 1; - MQTT_TRACE_MSG("lockSend"); + if ((rc = MqttLockSend(client, mms_stat)) != 0) { + break; + } #endif /* setup ACK in shared context */ @@ -1209,15 +1291,6 @@ static int MqttClient_WaitType(MqttClient *client, void *packet_obj, case MQTT_MSG_ACK: { - #ifdef WOLFMQTT_MULTITHREAD - if (!mms_stat->isWriteLocked) { - rc = wm_SemLock(&client->lockSend); - if (rc != 0) break; - mms_stat->isWriteLocked = 1; - MQTT_TRACE_MSG("lockSend"); - } - #endif - /* send ack */ rc = MqttEncode_PublishResp(client->tx_buf, client->tx_buf_len, client->packetAck.packet_type, &client->packetAck); @@ -1226,34 +1299,46 @@ static int MqttClient_WaitType(MqttClient *client, void *packet_obj, rc, MqttPacket_TypeDesc(client->packetAck.packet_type), client->packetAck.packet_type, client->packetAck.packet_id); #endif - if (rc > 0) { - client->write.len = rc; - - /* Send publish response packet */ - rc = MqttPacket_Write(client, client->tx_buf, - client->write.len); - #ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE) { - /* keep send mutex locked and return to caller */ - return rc; - } + if (rc < 0) { + #ifdef WOLFMQTT_MULTITHREAD + MqttUnlockSend(client, mms_stat); #endif - if (rc == client->write.len) { - rc = 0; /* success */ - } + break; } - mms_stat->write = MQTT_MSG_BEGIN; + client->write.len = rc; + rc = 0; + + mms_stat->write = MQTT_MSG_HEADER; + } + FALL_THROUGH; + + case MQTT_MSG_HEADER: + { + int xfer = client->write.len; + + /* Send publish response packet */ + rc = MqttPacket_Write(client, client->tx_buf, xfer); + #ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE) { + /* keep send mutex locked and return to caller */ + /* must keep send locked */ + return rc; + } + #endif + XMEMSET(&client->write, 0, sizeof(client->write)); /* reset write */ #ifdef WOLFMQTT_MULTITHREAD - MQTT_TRACE_MSG("unlockSend"); - mms_stat->isWriteLocked = 0; - wm_SemUnlock(&client->lockSend); + MqttUnlockSend(client, mms_stat); #endif + if (rc == xfer) { + rc = 0; /* success */ + } + + mms_stat->write = MQTT_MSG_BEGIN; /* reset write state */ break; } case MQTT_MSG_AUTH: - case MQTT_MSG_HEADER: case MQTT_MSG_PAYLOAD: case MQTT_MSG_PAYLOAD2: default: @@ -1285,12 +1370,9 @@ static int MqttClient_WaitType(MqttClient *client, void *packet_obj, #endif #ifdef WOLFMQTT_MULTITHREAD - if (mms_stat->isReadLocked) { - mms_stat->isReadLocked = 0; - MQTT_TRACE_MSG("unlockRecv"); - wm_SemUnlock(&client->lockRecv); - } + MqttUnlockRecv(client, mms_stat); #endif + XMEMSET(&client->read, 0, sizeof(client->read)); /* reset read */ #ifdef WOLFMQTT_NONBLOCK #ifdef WOLFMQTT_DEBUG_CLIENT @@ -1441,8 +1523,7 @@ int MqttClient_Connect(MqttClient *client, MqttConnect *mc_connect) if (mc_connect->stat.write == MQTT_MSG_BEGIN) { #ifdef WOLFMQTT_MULTITHREAD /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { + if ((rc = MqttLockSend(client, &mc_connect->stat)) != 0) { return rc; } #endif @@ -1460,9 +1541,9 @@ int MqttClient_Connect(MqttClient *client, MqttConnect *mc_connect) MQTT_PACKET_TYPE_CONNECT, 0, 0); #endif if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif + #ifdef WOLFMQTT_MULTITHREAD + MqttUnlockSend(client, &mc_connect->stat); + #endif return rc; } client->write.len = rc; @@ -1476,26 +1557,34 @@ int MqttClient_Connect(MqttClient *client, MqttConnect *mc_connect) wm_SemUnlock(&client->lockClient); } if (rc != 0) { - wm_SemUnlock(&client->lockSend); + #ifdef WOLFMQTT_MULTITHREAD + MqttUnlockSend(client, &mc_connect->stat); + #endif return rc; /* Error locking client */ } + mc_connect->stat.write = MQTT_MSG_HEADER; #endif + } + if (mc_connect->stat.write == MQTT_MSG_HEADER) { + int xfer = client->write.len; /* Send connect packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc != client->write.len) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &mc_connect->pendResp); - wm_SemUnlock(&client->lockClient); - } - #endif + rc = MqttPacket_Write(client, client->tx_buf, xfer); + #ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE && client->write.total > 0) { + /* keep send locked and return early */ return rc; } + #endif + XMEMSET(&client->write, 0, sizeof(client->write)); /* reset write */ #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); + MqttUnlockSend(client, &mc_connect->stat); #endif + if (rc != xfer) { + MqttClient_CancelMessage(client, (MqttObject*)mc_connect); + return rc; + } + #ifdef WOLFMQTT_V5 /* Enhanced authentication */ if (client->enable_eauth == 1) { @@ -1559,6 +1648,7 @@ int MqttClient_Connect(MqttClient *client, MqttConnect *mc_connect) #endif return rc; } + mc_connect->stat.write = MQTT_MSG_WAIT; } #endif /* WOLFMQTT_V5 */ @@ -1849,12 +1939,9 @@ static int MqttPublishMsg(MqttClient *client, MqttPublish *publish, { #ifdef WOLFMQTT_MULTITHREAD /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { + if ((rc = MqttLockSend(client, &publish->stat)) != 0) { return rc; } - publish->stat.isWriteLocked = 1; - MQTT_TRACE_MSG("lockSend"); #endif /* Encode the publish packet */ @@ -1869,9 +1956,7 @@ static int MqttPublishMsg(MqttClient *client, MqttPublish *publish, #endif if (rc <= 0) { #ifdef WOLFMQTT_MULTITHREAD - MQTT_TRACE_MSG("unlockSend"); - publish->stat.isWriteLocked = 0; - wm_SemUnlock(&client->lockSend); + MqttUnlockSend(client, &publish->stat); #endif return rc; } @@ -1891,9 +1976,9 @@ static int MqttPublishMsg(MqttClient *client, MqttPublish *publish, wm_SemUnlock(&client->lockClient); } if (rc != 0) { - MQTT_TRACE_MSG("unlockSend"); - publish->stat.isWriteLocked = 0; - wm_SemUnlock(&client->lockSend); + #ifdef WOLFMQTT_MULTITHREAD + MqttUnlockSend(client, &publish->stat); + #endif return rc; /* Error locking client */ } } @@ -1905,30 +1990,26 @@ static int MqttPublishMsg(MqttClient *client, MqttPublish *publish, case MQTT_MSG_HEADER: { - /* Send packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + int xfer = client->write.len; + + /* Send publish packet */ + rc = MqttPacket_Write(client, client->tx_buf, xfer); #ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE) + if (rc == MQTT_CODE_CONTINUE && client->write.total > 0) + /* keep send locked and return early */ return rc; #endif - if (rc < 0) { - #ifdef WOLFMQTT_MULTITHREAD - MQTT_TRACE_MSG("unlockSend"); - publish->stat.isWriteLocked = 0; - wm_SemUnlock(&client->lockSend); - #endif + client->write.len = 0; /* reset len, so publish chunk resets */ + + /* if failure or no data was written yet */ + if (rc != xfer) { #ifdef WOLFMQTT_MULTITHREAD - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &publish->pendResp); - wm_SemUnlock(&client->lockClient); - } + MqttUnlockSend(client, &publish->stat); #endif + MqttClient_CancelMessage(client, (MqttObject*)&publish); return rc; } - /* reset client->write.len */ - client->write.len = 0; - /* advance state */ publish->stat.write = MQTT_MSG_PAYLOAD; } @@ -1941,19 +2022,12 @@ static int MqttPublishMsg(MqttClient *client, MqttPublish *publish, if (rc == MQTT_CODE_CONTINUE || rc == MQTT_CODE_PUB_CONTINUE) return rc; #endif + XMEMSET(&client->write, 0, sizeof(client->write)); /* reset write */ #ifdef WOLFMQTT_MULTITHREAD - MQTT_TRACE_MSG("unlockSend"); - publish->stat.isWriteLocked = 0; - wm_SemUnlock(&client->lockSend); + MqttUnlockSend(client, &publish->stat); #endif - if (rc < 0) { - #ifdef WOLFMQTT_MULTITHREAD - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &publish->pendResp); - wm_SemUnlock(&client->lockClient); - } - #endif + MqttClient_CancelMessage(client, (MqttObject*)&publish); break; } @@ -2068,8 +2142,7 @@ int MqttClient_Subscribe(MqttClient *client, MqttSubscribe *subscribe) if (subscribe->stat.write == MQTT_MSG_BEGIN) { #ifdef WOLFMQTT_MULTITHREAD /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { + if ((rc = MqttLockSend(client, &subscribe->stat)) != 0) { return rc; } #endif @@ -2084,7 +2157,7 @@ int MqttClient_Subscribe(MqttClient *client, MqttSubscribe *subscribe) #endif if (rc <= 0) { #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); + MqttUnlockSend(client, &subscribe->stat); #endif return rc; } @@ -2099,26 +2172,34 @@ int MqttClient_Subscribe(MqttClient *client, MqttSubscribe *subscribe) wm_SemUnlock(&client->lockClient); } if (rc != 0) { - wm_SemUnlock(&client->lockSend); + #ifdef WOLFMQTT_MULTITHREAD + MqttUnlockSend(client, &subscribe->stat); + #endif return rc; /* Error locking client */ } #endif + subscribe->stat.write = MQTT_MSG_HEADER; + } + if (subscribe->stat.write == MQTT_MSG_HEADER) { + int xfer = client->write.len; + /* Send subscribe packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc != client->write.len) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &subscribe->pendResp); - wm_SemUnlock(&client->lockClient); - } - #endif + rc = MqttPacket_Write(client, client->tx_buf, xfer); + #ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE && client->write.total > 0) { + /* keep send locked and return early */ return rc; } + #endif + XMEMSET(&client->write, 0, sizeof(client->write)); /* reset write */ #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); + MqttUnlockSend(client, &subscribe->stat); #endif + if (rc != xfer) { + MqttClient_CancelMessage(client, (MqttObject*)subscribe); + return rc; + } subscribe->stat.write = MQTT_MSG_WAIT; } @@ -2170,8 +2251,7 @@ int MqttClient_Unsubscribe(MqttClient *client, MqttUnsubscribe *unsubscribe) if (unsubscribe->stat.write == MQTT_MSG_BEGIN) { #ifdef WOLFMQTT_MULTITHREAD /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { + if ((rc = MqttLockSend(client, &unsubscribe->stat)) != 0) { return rc; } #endif @@ -2186,7 +2266,7 @@ int MqttClient_Unsubscribe(MqttClient *client, MqttUnsubscribe *unsubscribe) #endif if (rc <= 0) { #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); + MqttUnlockSend(client, &unsubscribe->stat); #endif return rc; } @@ -2202,26 +2282,33 @@ int MqttClient_Unsubscribe(MqttClient *client, MqttUnsubscribe *unsubscribe) wm_SemUnlock(&client->lockClient); } if (rc != 0) { - wm_SemUnlock(&client->lockSend); /* Error locking client */ + #ifdef WOLFMQTT_MULTITHREAD + MqttUnlockSend(client, &unsubscribe->stat); + #endif return rc; } #endif + unsubscribe->stat.write = MQTT_MSG_HEADER; + } + if (unsubscribe->stat.write == MQTT_MSG_HEADER) { + int xfer = client->write.len; /* Send unsubscribe packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc != client->write.len) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &unsubscribe->pendResp); - wm_SemUnlock(&client->lockClient); - } - #endif + rc = MqttPacket_Write(client, client->tx_buf, xfer); + #ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE && client->write.total > 0) { + /* keep send locked and return early */ return rc; } + #endif + XMEMSET(&client->write, 0, sizeof(client->write)); /* reset write */ #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); + MqttUnlockSend(client, &unsubscribe->stat); #endif + if (rc != xfer) { + MqttClient_CancelMessage(client, (MqttObject*)unsubscribe); + return rc; + } unsubscribe->stat.write = MQTT_MSG_WAIT; } @@ -2267,8 +2354,7 @@ int MqttClient_Ping_ex(MqttClient *client, MqttPing* ping) if (ping->stat.write == MQTT_MSG_BEGIN) { #ifdef WOLFMQTT_MULTITHREAD /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { + if ((rc = MqttLockSend(client, &ping->stat)) != 0) { return rc; } #endif @@ -2282,7 +2368,7 @@ int MqttClient_Ping_ex(MqttClient *client, MqttPing* ping) #endif if (rc <= 0) { #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); + MqttUnlockSend(client, &ping->stat); #endif return rc; } @@ -2297,26 +2383,34 @@ int MqttClient_Ping_ex(MqttClient *client, MqttPing* ping) wm_SemUnlock(&client->lockClient); } if (rc != 0) { - wm_SemUnlock(&client->lockSend); + #ifdef WOLFMQTT_MULTITHREAD + MqttUnlockSend(client, &ping->stat); + #endif return rc; /* Error locking client */ } #endif + ping->stat.write = MQTT_MSG_HEADER; + } + if (ping->stat.write == MQTT_MSG_HEADER) { + int xfer = client->write.len; /* Send ping req packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc != client->write.len) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &ping->pendResp); - wm_SemUnlock(&client->lockClient); - } - #endif + rc = MqttPacket_Write(client, client->tx_buf, xfer); + #ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE && client->write.total > 0) { + /* keep send locked and return early */ return rc; } + #endif + XMEMSET(&client->write, 0, sizeof(client->write)); /* reset write */ #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); + MqttUnlockSend(client, &ping->stat); #endif + if (rc != xfer) { + MqttClient_CancelMessage(client, (MqttObject*)ping); + return rc; + } + ping->stat.write = MQTT_MSG_WAIT; } @@ -2353,54 +2447,64 @@ int MqttClient_Disconnect(MqttClient *client) int MqttClient_Disconnect_ex(MqttClient *client, MqttDisconnect *disconnect) { - int rc; + int rc, xfer; /* Validate required arguments */ if (client == NULL) { return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); } -#ifdef WOLFMQTT_V5 - if (disconnect != NULL) { - /* Use specified protocol version if set */ - disconnect->protocol_level = client->protocol_level; - } -#endif - -#ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } -#endif + if (disconnect->stat.write == MQTT_MSG_BEGIN) { + #ifdef WOLFMQTT_V5 + if (disconnect != NULL) { + /* Use specified protocol version if set */ + disconnect->protocol_level = client->protocol_level; + } + #endif - /* Encode the disconnect packet */ - rc = MqttEncode_Disconnect(client->tx_buf, client->tx_buf_len, disconnect); -#ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d), ID %d, QoS %d", - rc, MqttPacket_TypeDesc(MQTT_PACKET_TYPE_DISCONNECT), - MQTT_PACKET_TYPE_DISCONNECT, 0, 0); -#endif - if (rc <= 0) { #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); + /* Lock send socket mutex */ + if ((rc = MqttLockSend(client, &disconnect->stat)) != 0) { + return rc; + } #endif - return rc; + + /* Encode the disconnect packet */ + rc = MqttEncode_Disconnect(client->tx_buf, client->tx_buf_len, + disconnect); + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d), ID %d, QoS %d", + rc, MqttPacket_TypeDesc(MQTT_PACKET_TYPE_DISCONNECT), + MQTT_PACKET_TYPE_DISCONNECT, 0, 0); + #endif + if (rc <= 0) { + #ifdef WOLFMQTT_MULTITHREAD + MqttUnlockSend(client, &disconnect->stat); + #endif + return rc; + } + client->write.len = rc; + + disconnect->stat.write = MQTT_MSG_HEADER; } - client->write.len = rc; /* Send disconnect packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc != client->write.len) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif + xfer = client->write.len; + rc = MqttPacket_Write(client, client->tx_buf, xfer); +#ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE && client->write.total > 0) { + /* keep send locked and return early */ return rc; } +#endif + XMEMSET(&client->write, 0, sizeof(client->write)); /* reset write */ #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); + MqttUnlockSend(client, &disconnect->stat); #endif + if (rc != xfer) { + disconnect->stat.write = MQTT_MSG_BEGIN; /* reset state */ + return rc; + } rc = MQTT_CODE_SUCCESS; @@ -2412,6 +2516,9 @@ int MqttClient_Disconnect_ex(MqttClient *client, MqttDisconnect *disconnect) /* No response for MQTT disconnect packet */ + /* reset state */ + disconnect->stat.write = MQTT_MSG_BEGIN; + return rc; } @@ -2428,8 +2535,7 @@ int MqttClient_Auth(MqttClient *client, MqttAuth* auth) if (auth->stat.write == MQTT_MSG_BEGIN) { #ifdef WOLFMQTT_MULTITHREAD /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { + if ((rc = MqttLockSend(client, &auth->stat)) != 0) { return rc; } #endif @@ -2443,7 +2549,7 @@ int MqttClient_Auth(MqttClient *client, MqttAuth* auth) #endif if (rc <= 0) { #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); + MqttUnlockSend(client, &auth->stat); #endif return rc; } @@ -2458,26 +2564,33 @@ int MqttClient_Auth(MqttClient *client, MqttAuth* auth) wm_SemUnlock(&client->lockClient); } if (rc != 0) { - wm_SemUnlock(&client->lockSend); + #ifdef WOLFMQTT_MULTITHREAD + MqttUnlockSend(client, &auth->stat); + #endif return rc; /* Error locking client */ } #endif + auth->stat.write = MQTT_MSG_HEADER; + } + if (auth->stat.write == MQTT_MSG_BEGIN) { + int xfer = client->write.len; /* Send authentication packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc != client->write.len) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &auth->pendResp); - wm_SemUnlock(&client->lockClient); - } - #endif + rc = MqttPacket_Write(client, client->tx_buf, xfer); + #ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE && client->write.total > 0) { + /* keep send locked and return early */ return rc; } + #endif + XMEMSET(&client->write, 0, sizeof(client->write)); /* reset write */ #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); + MqttUnlockSend(client, &auth->stat); #endif + if (rc != xfer) { + MqttClient_CancelMessage(client, (MqttObject*)auth); + return rc; + } auth->stat.write = MQTT_MSG_WAIT; } @@ -2528,10 +2641,10 @@ int MqttClient_WaitMessage(MqttClient *client, int timeout_ms) return MqttClient_WaitMessage_ex(client, &client->msg, timeout_ms); } -#if defined(WOLFMQTT_MULTITHREAD) || defined(WOLFMQTT_NONBLOCK) int MqttClient_CancelMessage(MqttClient *client, MqttObject* msg) { int rc = MQTT_CODE_SUCCESS; + #ifdef WOLFMQTT_MULTITHREAD MqttPendResp* tmpResp; MqttMsgStat* mms_stat; @@ -2549,7 +2662,11 @@ int MqttClient_CancelMessage(MqttClient *client, MqttObject* msg) PRINTF("Cancel Msg: %p", msg); #endif - /* Find pending response entry and remove */ + /* reset states */ + mms_stat->write = MQTT_MSG_BEGIN; + mms_stat->read = MQTT_MSG_BEGIN; + + /* Remove any pending responses expected */ rc = wm_SemLock(&client->lockClient); if (rc != MQTT_CODE_SUCCESS) { return rc; @@ -2582,26 +2699,26 @@ int MqttClient_CancelMessage(MqttClient *client, MqttObject* msg) } wm_SemUnlock(&client->lockClient); - /* clear any locks */ + /* cancel any locks */ if (mms_stat->isReadLocked) { #ifdef WOLFMQTT_DEBUG_CLIENT PRINTF("Cancel Read Lock"); #endif mms_stat->isReadLocked = 0; - wm_SemUnlock(&client->lockRecv); + XMEMSET(&client->read, 0, sizeof(client->read)); + MqttUnlockRecv(client, mms_stat); } if (mms_stat->isWriteLocked) { #ifdef WOLFMQTT_DEBUG_CLIENT PRINTF("Cancel Write Lock"); #endif - client->write.pos = 0; /* reset current write position */ mms_stat->isWriteLocked = 0; - wm_SemUnlock(&client->lockSend); + XMEMSET(&client->write, 0, sizeof(client->write)); + MqttUnlockSend(client, mms_stat); } #endif return rc; } -#endif /* WOLFMQTT_MULTITHREAD || WOLFMQTT_NONBLOCK */ #ifdef WOLFMQTT_NONBLOCK static inline int IsMessageActive(MqttObject *msg) diff --git a/src/mqtt_socket.c b/src/mqtt_socket.c index 86ae1822b..8002d2480 100644 --- a/src/mqtt_socket.c +++ b/src/mqtt_socket.c @@ -217,6 +217,7 @@ int MqttSocket_Write(MqttClient *client, const byte* buf, int buf_len, buf_len - client->write.pos, timeout_ms); if (rc >= 0) { client->write.pos += rc; + client->write.total += rc; if (client->write.pos < buf_len) { rc = MQTT_CODE_CONTINUE; } @@ -233,6 +234,7 @@ int MqttSocket_Write(MqttClient *client, const byte* buf, int buf_len, break; } client->write.pos += rc; + client->write.total += rc; } while (client->write.pos < buf_len); #endif /* WOLFMQTT_NONBLOCK */ @@ -339,6 +341,7 @@ int MqttSocket_Read(MqttClient *client, byte* buf, int buf_len, int timeout_ms) buf_len - client->read.pos, timeout_ms); if (rc >= 0) { client->read.pos += rc; + client->read.total += rc; if (client->read.pos < buf_len) { rc = MQTT_CODE_CONTINUE; } @@ -355,6 +358,7 @@ int MqttSocket_Read(MqttClient *client, byte* buf, int buf_len, int timeout_ms) break; } client->read.pos += rc; + client->read.total += rc; } while (client->read.pos < buf_len); #endif /* WOLFMQTT_NONBLOCK */ diff --git a/wolfmqtt/mqtt_client.h b/wolfmqtt/mqtt_client.h index f381d2f8f..7fc976afc 100644 --- a/wolfmqtt/mqtt_client.h +++ b/wolfmqtt/mqtt_client.h @@ -128,8 +128,9 @@ typedef struct _MqttPkRead { } MqttPkRead; typedef struct _MqttSk { - int pos; - int len; + int pos; /* position inside current buffer */ + int len; /* length of current segment being sent */ + int total; /* number bytes sent or received */ } MqttSk; #ifdef WOLFMQTT_DISCONNECT_CB @@ -496,9 +497,8 @@ WOLFMQTT_API int MqttClient_WaitMessage_ex( MqttObject *msg, int timeout_ms); -#if defined(WOLFMQTT_MULTITHREAD) || defined(WOLFMQTT_NONBLOCK) -/*! \brief In a multi-threaded and non-blocking mode this allows you to - cancel an MQTT object that was previously submitted. +/*! \brief Cancel a partially sent message. Applies to multi-threaded or + non-blocking mode * \note This is a blocking function that will wait for MqttNet.read * \param client Pointer to MqttClient structure * \param msg Pointer to MqttObject structure @@ -508,7 +508,6 @@ WOLFMQTT_API int MqttClient_WaitMessage_ex( WOLFMQTT_API int MqttClient_CancelMessage( MqttClient *client, MqttObject *msg); -#endif #ifdef WOLFMQTT_NONBLOCK /*! \brief In a non-blocking mode this checks if the message has a read From 26de3118ba07339db84baf4e51dc126775c19269 Mon Sep 17 00:00:00 2001 From: David Garske Date: Mon, 6 Nov 2023 10:38:17 -0800 Subject: [PATCH 17/62] Cleanups from refactor of locking. --- examples/mqttnet.c | 2 +- examples/multithread/multithread.c | 8 +- src/mqtt_client.c | 284 ++++++++++++----------------- src/mqtt_sn_client.c | 6 +- wolfmqtt/mqtt_packet.h | 6 +- 5 files changed, 125 insertions(+), 181 deletions(-) diff --git a/examples/mqttnet.c b/examples/mqttnet.c index 6b6b4cf74..acd9d3340 100644 --- a/examples/mqttnet.c +++ b/examples/mqttnet.c @@ -800,7 +800,7 @@ static int NetRead_ex(void *context, byte* buf, int buf_len, if (do_read) { /* Try and read number of buf_len provided, - minus what's already been read */ + * minus what's already been read */ rc = (int)SOCK_RECV(sock->fd, &buf[bytes], buf_len - bytes, diff --git a/examples/multithread/multithread.c b/examples/multithread/multithread.c index fbb6522b3..91515d769 100755 --- a/examples/multithread/multithread.c +++ b/examples/multithread/multithread.c @@ -562,8 +562,8 @@ static void *waitMessage_task(void *param) #endif else if (rc == MQTT_CODE_ERROR_TIMEOUT) { if (mqttCtx->test_mode) { - mqtt_stop_set(); /* timeout in test mode should exit */ + mqtt_stop_set(); PRINTF("MQTT Exiting timeout..."); break; } @@ -748,11 +748,11 @@ int multithread_test(MQTTCtx *mqttCtx) /* Join threads - wait for completion */ if (THREAD_JOIN(threadList, threadCount)) { #ifdef __GLIBC__ - /* %m is specific to glibc/uclibc/musl, and recently (2018) - * added to FreeBSD */ + /* "%m" is specific to glibc/uclibc/musl, and FreeBSD (as of 2018). + * Uses errno and not argument required */ PRINTF("THREAD_JOIN failed: %m"); #else - PRINTF("THREAD_JOIN failed: %d",errno); + PRINTF("THREAD_JOIN failed: %d", errno); #endif } diff --git a/src/mqtt_client.c b/src/mqtt_client.c index d2f0e6112..0af4d05d5 100644 --- a/src/mqtt_client.c +++ b/src/mqtt_client.c @@ -157,15 +157,16 @@ static int MqttClient_Publish_ReadPayload(MqttClient* client, return 0; } -#endif +#endif /* MUTEX */ +#endif /* WOLFMQTT_MULTITHREAD */ -#ifdef WOLFMQTT_MULTITHREAD -static int MqttLockSend(MqttClient* client, MqttMsgStat* stat) +static int MqttWriteStart(MqttClient* client, MqttMsgStat* stat) { int rc = 0; -#ifdef WOLFMQTT_DEBUG_CLIENT - if (stat->isWriteLocked) { +#ifdef WOLFMQTT_MULTITHREAD + #ifdef WOLFMQTT_DEBUG_CLIENT + if (stat->isWriteActive) { MQTT_TRACE_MSG("Warning, send already locked!"); rc = MQTT_CODE_ERROR_SYSTEM; } @@ -180,37 +181,47 @@ static int MqttLockSend(MqttClient* client, MqttMsgStat* stat) if (rc != 0) { return rc; } -#endif + #endif /* WOLFMQTT_DEBUG_CLIENT */ rc = wm_SemLock(&client->lockSend); +#endif /* WOLFMQTT_MULTITHREAD */ if (rc == 0) { - stat->isWriteLocked = 1; + stat->isWriteActive = 1; MQTT_TRACE_MSG("lockSend"); } + + (void)client; + return rc; } -static void MqttUnlockSend(MqttClient* client, MqttMsgStat* stat) +static void MqttWriteStop(MqttClient* client, MqttMsgStat* stat) { #ifdef WOLFMQTT_DEBUG_CLIENT - if (!stat->isWriteLocked) { + if (!stat->isWriteActive) { MQTT_TRACE_MSG("Warning, send not locked!"); return; } #endif - if (stat->isWriteLocked) { + /* reset write */ + XMEMSET(&client->write, 0, sizeof(client->write)); + + if (stat->isWriteActive) { MQTT_TRACE_MSG("unlockSend"); - stat->isWriteLocked = 0; + stat->isWriteActive = 0; + #ifdef WOLFMQTT_MULTITHREAD wm_SemUnlock(&client->lockSend); + #endif } } -static int MqttLockRecv(MqttClient* client, MqttMsgStat* stat) +static int MqttReadStart(MqttClient* client, MqttMsgStat* stat) { int rc = 0; -#ifdef WOLFMQTT_DEBUG_CLIENT - if (stat->isReadLocked) { +#ifdef WOLFMQTT_MULTITHREAD + #ifdef WOLFMQTT_DEBUG_CLIENT + if (stat->isReadActive) { MQTT_TRACE_MSG("Warning, recv already locked!"); rc = MQTT_CODE_ERROR_SYSTEM; } @@ -224,31 +235,39 @@ static int MqttLockRecv(MqttClient* client, MqttMsgStat* stat) } if (rc != 0) return rc; -#endif + #endif /* WOLFMQTT_DEBUG_CLIENT */ rc = wm_SemLock(&client->lockRecv); +#endif /* WOLFMQTT_MULTITHREAD */ if (rc == 0) { - stat->isReadLocked = 1; + stat->isReadActive = 1; MQTT_TRACE_MSG("lockRecv"); } + (void)client; return rc; } -static void MqttUnlockRecv(MqttClient* client, MqttMsgStat* stat) +static void MqttReadStop(MqttClient* client, MqttMsgStat* stat) { #ifdef WOLFMQTT_DEBUG_CLIENT - if (!stat->isReadLocked) { + if (!stat->isReadActive) { MQTT_TRACE_MSG("Warning, recv not locked!"); return; } #endif - if (stat->isReadLocked) { - stat->isReadLocked = 0; + /* reset read */ + XMEMSET(&client->read, 0, sizeof(client->read)); + + if (stat->isReadActive) { + stat->isReadActive = 0; MQTT_TRACE_MSG("unlockRecv"); + #ifdef WOLFMQTT_MULTITHREAD wm_SemUnlock(&client->lockRecv); + #endif } } -#endif /* WOLFMQTT_MULTITHREAD */ + +#ifdef WOLFMQTT_MULTITHREAD /* These RespList functions assume caller has locked client->lockClient mutex */ int MqttClient_RespList_Add(MqttClient *client, @@ -1048,12 +1067,11 @@ static int MqttClient_WaitType(MqttClient *client, void *packet_obj, if (rc != MQTT_CODE_ERROR_NOT_FOUND && rc != MQTT_CODE_CONTINUE) { return rc; } + #endif - /* Lock recv socket mutex */ - if ((rc = MqttLockRecv(client, mms_stat)) != 0) { + if ((rc = MqttReadStart(client, mms_stat)) != 0) { return rc; } - #endif /* reset the packet state used by MqttPacket_Read */ client->packet.stat = MQTT_PK_BEGIN; @@ -1233,11 +1251,8 @@ static int MqttClient_WaitType(MqttClient *client, void *packet_obj, mms_stat->read = MQTT_MSG_BEGIN; } - #ifdef WOLFMQTT_MULTITHREAD - /* release read lock, done reading */ - MqttUnlockRecv(client, mms_stat); - #endif - XMEMSET(&client->read, 0, sizeof(client->read)); /* reset read */ + /* done reading */ + MqttReadStop(client, mms_stat); /* if error, leave */ if (rc != MQTT_CODE_SUCCESS) { @@ -1249,12 +1264,10 @@ static int MqttClient_WaitType(MqttClient *client, void *packet_obj, break; } - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - if ((rc = MqttLockSend(client, mms_stat)) != 0) { + /* Flag write active / lock mutex */ + if ((rc = MqttWriteStart(client, mms_stat)) != 0) { break; } - #endif /* setup ACK in shared context */ XMEMCPY(&client->packetAck, &resp, sizeof(MqttPublishResp)); @@ -1300,9 +1313,7 @@ static int MqttClient_WaitType(MqttClient *client, void *packet_obj, client->packetAck.packet_type, client->packetAck.packet_id); #endif if (rc < 0) { - #ifdef WOLFMQTT_MULTITHREAD - MqttUnlockSend(client, mms_stat); - #endif + MqttWriteStop(client, mms_stat); break; } @@ -1326,10 +1337,7 @@ static int MqttClient_WaitType(MqttClient *client, void *packet_obj, return rc; } #endif - XMEMSET(&client->write, 0, sizeof(client->write)); /* reset write */ - #ifdef WOLFMQTT_MULTITHREAD - MqttUnlockSend(client, mms_stat); - #endif + MqttWriteStop(client, mms_stat); if (rc == xfer) { rc = 0; /* success */ } @@ -1369,10 +1377,7 @@ static int MqttClient_WaitType(MqttClient *client, void *packet_obj, } #endif -#ifdef WOLFMQTT_MULTITHREAD - MqttUnlockRecv(client, mms_stat); -#endif - XMEMSET(&client->read, 0, sizeof(client->read)); /* reset read */ + MqttReadStop(client, mms_stat); #ifdef WOLFMQTT_NONBLOCK #ifdef WOLFMQTT_DEBUG_CLIENT @@ -1521,12 +1526,10 @@ int MqttClient_Connect(MqttClient *client, MqttConnect *mc_connect) } if (mc_connect->stat.write == MQTT_MSG_BEGIN) { - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - if ((rc = MqttLockSend(client, &mc_connect->stat)) != 0) { + /* Flag write active / lock mutex */ + if ((rc = MqttWriteStart(client, &mc_connect->stat)) != 0) { return rc; } - #endif #ifdef WOLFMQTT_V5 /* Use specified protocol version if set */ @@ -1541,9 +1544,7 @@ int MqttClient_Connect(MqttClient *client, MqttConnect *mc_connect) MQTT_PACKET_TYPE_CONNECT, 0, 0); #endif if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - MqttUnlockSend(client, &mc_connect->stat); - #endif + MqttWriteStop(client, &mc_connect->stat); return rc; } client->write.len = rc; @@ -1557,13 +1558,12 @@ int MqttClient_Connect(MqttClient *client, MqttConnect *mc_connect) wm_SemUnlock(&client->lockClient); } if (rc != 0) { - #ifdef WOLFMQTT_MULTITHREAD - MqttUnlockSend(client, &mc_connect->stat); - #endif + MqttWriteStop(client, &mc_connect->stat); return rc; /* Error locking client */ } - mc_connect->stat.write = MQTT_MSG_HEADER; #endif + + mc_connect->stat.write = MQTT_MSG_HEADER; } if (mc_connect->stat.write == MQTT_MSG_HEADER) { int xfer = client->write.len; @@ -1576,10 +1576,7 @@ int MqttClient_Connect(MqttClient *client, MqttConnect *mc_connect) return rc; } #endif - XMEMSET(&client->write, 0, sizeof(client->write)); /* reset write */ - #ifdef WOLFMQTT_MULTITHREAD - MqttUnlockSend(client, &mc_connect->stat); - #endif + MqttWriteStop(client, &mc_connect->stat); if (rc != xfer) { MqttClient_CancelMessage(client, (MqttObject*)mc_connect); return rc; @@ -1937,12 +1934,10 @@ static int MqttPublishMsg(MqttClient *client, MqttPublish *publish, { case MQTT_MSG_BEGIN: { - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - if ((rc = MqttLockSend(client, &publish->stat)) != 0) { + /* Flag write active / lock mutex */ + if ((rc = MqttWriteStart(client, &publish->stat)) != 0) { return rc; } - #endif /* Encode the publish packet */ rc = MqttEncode_Publish(client->tx_buf, client->tx_buf_len, @@ -1955,9 +1950,7 @@ static int MqttPublishMsg(MqttClient *client, MqttPublish *publish, publish->qos); #endif if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - MqttUnlockSend(client, &publish->stat); - #endif + MqttWriteStop(client, &publish->stat); return rc; } client->write.len = rc; @@ -1976,9 +1969,7 @@ static int MqttPublishMsg(MqttClient *client, MqttPublish *publish, wm_SemUnlock(&client->lockClient); } if (rc != 0) { - #ifdef WOLFMQTT_MULTITHREAD - MqttUnlockSend(client, &publish->stat); - #endif + MqttWriteStop(client, &publish->stat); return rc; /* Error locking client */ } } @@ -2003,10 +1994,8 @@ static int MqttPublishMsg(MqttClient *client, MqttPublish *publish, /* if failure or no data was written yet */ if (rc != xfer) { - #ifdef WOLFMQTT_MULTITHREAD - MqttUnlockSend(client, &publish->stat); - #endif - MqttClient_CancelMessage(client, (MqttObject*)&publish); + MqttWriteStop(client, &publish->stat); + MqttClient_CancelMessage(client, (MqttObject*)publish); return rc; } @@ -2022,12 +2011,9 @@ static int MqttPublishMsg(MqttClient *client, MqttPublish *publish, if (rc == MQTT_CODE_CONTINUE || rc == MQTT_CODE_PUB_CONTINUE) return rc; #endif - XMEMSET(&client->write, 0, sizeof(client->write)); /* reset write */ - #ifdef WOLFMQTT_MULTITHREAD - MqttUnlockSend(client, &publish->stat); - #endif + MqttWriteStop(client, &publish->stat); if (rc < 0) { - MqttClient_CancelMessage(client, (MqttObject*)&publish); + MqttClient_CancelMessage(client, (MqttObject*)publish); break; } @@ -2140,12 +2126,10 @@ int MqttClient_Subscribe(MqttClient *client, MqttSubscribe *subscribe) #endif if (subscribe->stat.write == MQTT_MSG_BEGIN) { - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - if ((rc = MqttLockSend(client, &subscribe->stat)) != 0) { + /* Flag write active / lock mutex */ + if ((rc = MqttWriteStart(client, &subscribe->stat)) != 0) { return rc; } - #endif /* Encode the subscribe packet */ rc = MqttEncode_Subscribe(client->tx_buf, client->tx_buf_len, @@ -2156,9 +2140,7 @@ int MqttClient_Subscribe(MqttClient *client, MqttSubscribe *subscribe) MQTT_PACKET_TYPE_SUBSCRIBE, subscribe->packet_id); #endif if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - MqttUnlockSend(client, &subscribe->stat); - #endif + MqttWriteStop(client, &subscribe->stat); return rc; } client->write.len = rc; @@ -2172,9 +2154,7 @@ int MqttClient_Subscribe(MqttClient *client, MqttSubscribe *subscribe) wm_SemUnlock(&client->lockClient); } if (rc != 0) { - #ifdef WOLFMQTT_MULTITHREAD - MqttUnlockSend(client, &subscribe->stat); - #endif + MqttWriteStop(client, &subscribe->stat); return rc; /* Error locking client */ } #endif @@ -2192,10 +2172,7 @@ int MqttClient_Subscribe(MqttClient *client, MqttSubscribe *subscribe) return rc; } #endif - XMEMSET(&client->write, 0, sizeof(client->write)); /* reset write */ - #ifdef WOLFMQTT_MULTITHREAD - MqttUnlockSend(client, &subscribe->stat); - #endif + MqttWriteStop(client, &subscribe->stat); if (rc != xfer) { MqttClient_CancelMessage(client, (MqttObject*)subscribe); return rc; @@ -2249,12 +2226,10 @@ int MqttClient_Unsubscribe(MqttClient *client, MqttUnsubscribe *unsubscribe) #endif if (unsubscribe->stat.write == MQTT_MSG_BEGIN) { - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - if ((rc = MqttLockSend(client, &unsubscribe->stat)) != 0) { + /* Flag write active / lock mutex */ + if ((rc = MqttWriteStart(client, &unsubscribe->stat)) != 0) { return rc; } - #endif /* Encode the subscribe packet */ rc = MqttEncode_Unsubscribe(client->tx_buf, client->tx_buf_len, @@ -2265,9 +2240,7 @@ int MqttClient_Unsubscribe(MqttClient *client, MqttUnsubscribe *unsubscribe) MQTT_PACKET_TYPE_UNSUBSCRIBE, unsubscribe->packet_id, 0); #endif if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - MqttUnlockSend(client, &unsubscribe->stat); - #endif + MqttWriteStop(client, &unsubscribe->stat); return rc; } client->write.len = rc; @@ -2282,12 +2255,11 @@ int MqttClient_Unsubscribe(MqttClient *client, MqttUnsubscribe *unsubscribe) wm_SemUnlock(&client->lockClient); } if (rc != 0) { - #ifdef WOLFMQTT_MULTITHREAD - MqttUnlockSend(client, &unsubscribe->stat); - #endif + MqttWriteStop(client, &unsubscribe->stat); return rc; } #endif + unsubscribe->stat.write = MQTT_MSG_HEADER; } if (unsubscribe->stat.write == MQTT_MSG_HEADER) { @@ -2301,10 +2273,7 @@ int MqttClient_Unsubscribe(MqttClient *client, MqttUnsubscribe *unsubscribe) return rc; } #endif - XMEMSET(&client->write, 0, sizeof(client->write)); /* reset write */ - #ifdef WOLFMQTT_MULTITHREAD - MqttUnlockSend(client, &unsubscribe->stat); - #endif + MqttWriteStop(client, &unsubscribe->stat); if (rc != xfer) { MqttClient_CancelMessage(client, (MqttObject*)unsubscribe); return rc; @@ -2352,12 +2321,10 @@ int MqttClient_Ping_ex(MqttClient *client, MqttPing* ping) } if (ping->stat.write == MQTT_MSG_BEGIN) { - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - if ((rc = MqttLockSend(client, &ping->stat)) != 0) { + /* Flag write active / lock mutex */ + if ((rc = MqttWriteStart(client, &ping->stat)) != 0) { return rc; } - #endif /* Encode the subscribe packet */ rc = MqttEncode_Ping(client->tx_buf, client->tx_buf_len, ping); @@ -2367,9 +2334,7 @@ int MqttClient_Ping_ex(MqttClient *client, MqttPing* ping) MQTT_PACKET_TYPE_PING_REQ, 0, 0); #endif if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - MqttUnlockSend(client, &ping->stat); - #endif + MqttWriteStop(client, &ping->stat); return rc; } client->write.len = rc; @@ -2383,12 +2348,11 @@ int MqttClient_Ping_ex(MqttClient *client, MqttPing* ping) wm_SemUnlock(&client->lockClient); } if (rc != 0) { - #ifdef WOLFMQTT_MULTITHREAD - MqttUnlockSend(client, &ping->stat); - #endif + MqttWriteStop(client, &ping->stat); return rc; /* Error locking client */ } #endif + ping->stat.write = MQTT_MSG_HEADER; } if (ping->stat.write == MQTT_MSG_HEADER) { @@ -2402,10 +2366,7 @@ int MqttClient_Ping_ex(MqttClient *client, MqttPing* ping) return rc; } #endif - XMEMSET(&client->write, 0, sizeof(client->write)); /* reset write */ - #ifdef WOLFMQTT_MULTITHREAD - MqttUnlockSend(client, &ping->stat); - #endif + MqttWriteStop(client, &ping->stat); if (rc != xfer) { MqttClient_CancelMessage(client, (MqttObject*)ping); return rc; @@ -2445,29 +2406,30 @@ int MqttClient_Disconnect(MqttClient *client) return MqttClient_Disconnect_ex(client, NULL); } -int MqttClient_Disconnect_ex(MqttClient *client, MqttDisconnect *disconnect) +int MqttClient_Disconnect_ex(MqttClient *client, MqttDisconnect *p_disconnect) { int rc, xfer; + MqttDisconnect *disconnect = p_disconnect, lcl_disconnect; /* Validate required arguments */ if (client == NULL) { return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); } + if (disconnect == NULL) { + disconnect = &lcl_disconnect; + XMEMSET(disconnect, 0, sizeof(*disconnect)); + } if (disconnect->stat.write == MQTT_MSG_BEGIN) { #ifdef WOLFMQTT_V5 - if (disconnect != NULL) { - /* Use specified protocol version if set */ - disconnect->protocol_level = client->protocol_level; - } + /* Use specified protocol version if set */ + disconnect->protocol_level = client->protocol_level; #endif - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - if ((rc = MqttLockSend(client, &disconnect->stat)) != 0) { + /* Flag write active / lock mutex */ + if ((rc = MqttWriteStart(client, &disconnect->stat)) != 0) { return rc; } - #endif /* Encode the disconnect packet */ rc = MqttEncode_Disconnect(client->tx_buf, client->tx_buf_len, @@ -2478,9 +2440,7 @@ int MqttClient_Disconnect_ex(MqttClient *client, MqttDisconnect *disconnect) MQTT_PACKET_TYPE_DISCONNECT, 0, 0); #endif if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - MqttUnlockSend(client, &disconnect->stat); - #endif + MqttWriteStop(client, &disconnect->stat); return rc; } client->write.len = rc; @@ -2492,22 +2452,18 @@ int MqttClient_Disconnect_ex(MqttClient *client, MqttDisconnect *disconnect) xfer = client->write.len; rc = MqttPacket_Write(client, client->tx_buf, xfer); #ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE && client->write.total > 0) { + /* if disconnect context avail allow partial write in non-blocking mode */ + if (p_disconnect != NULL && + rc == MQTT_CODE_CONTINUE && client->write.total > 0) { /* keep send locked and return early */ return rc; } #endif - XMEMSET(&client->write, 0, sizeof(client->write)); /* reset write */ -#ifdef WOLFMQTT_MULTITHREAD - MqttUnlockSend(client, &disconnect->stat); -#endif - if (rc != xfer) { - disconnect->stat.write = MQTT_MSG_BEGIN; /* reset state */ - return rc; + MqttWriteStop(client, &disconnect->stat); + if (rc == xfer) { + rc = MQTT_CODE_SUCCESS; } - rc = MQTT_CODE_SUCCESS; - #if defined(WOLFMQTT_DISCONNECT_CB) && defined(WOLFMQTT_USE_CB_ON_DISCONNECT) /* Trigger disconnect callback */ if (client->disconnect_cb) @@ -2533,12 +2489,10 @@ int MqttClient_Auth(MqttClient *client, MqttAuth* auth) } if (auth->stat.write == MQTT_MSG_BEGIN) { - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - if ((rc = MqttLockSend(client, &auth->stat)) != 0) { + /* Flag write active / lock mutex */ + if ((rc = MqttWriteStart(client, &auth->stat)) != 0) { return rc; } - #endif /* Encode the authentication packet */ rc = MqttEncode_Auth(client->tx_buf, client->tx_buf_len, auth); @@ -2548,9 +2502,7 @@ int MqttClient_Auth(MqttClient *client, MqttAuth* auth) MQTT_PACKET_TYPE_AUTH, 0, 0); #endif if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - MqttUnlockSend(client, &auth->stat); - #endif + MqttWriteStop(client, &auth->stat); return rc; } client->write.len = rc; @@ -2564,12 +2516,11 @@ int MqttClient_Auth(MqttClient *client, MqttAuth* auth) wm_SemUnlock(&client->lockClient); } if (rc != 0) { - #ifdef WOLFMQTT_MULTITHREAD - MqttUnlockSend(client, &auth->stat); - #endif + MqttWriteStop(client, &auth->stat); return rc; /* Error locking client */ } #endif + auth->stat.write = MQTT_MSG_HEADER; } if (auth->stat.write == MQTT_MSG_BEGIN) { @@ -2583,10 +2534,7 @@ int MqttClient_Auth(MqttClient *client, MqttAuth* auth) return rc; } #endif - XMEMSET(&client->write, 0, sizeof(client->write)); /* reset write */ - #ifdef WOLFMQTT_MULTITHREAD - MqttUnlockSend(client, &auth->stat); - #endif + MqttWriteStop(client, &auth->stat); if (rc != xfer) { MqttClient_CancelMessage(client, (MqttObject*)auth); return rc; @@ -2644,17 +2592,15 @@ int MqttClient_WaitMessage(MqttClient *client, int timeout_ms) int MqttClient_CancelMessage(MqttClient *client, MqttObject* msg) { int rc = MQTT_CODE_SUCCESS; - + MqttMsgStat* mms_stat; #ifdef WOLFMQTT_MULTITHREAD MqttPendResp* tmpResp; - MqttMsgStat* mms_stat; #endif if (client == NULL || msg == NULL) { return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); } -#ifdef WOLFMQTT_MULTITHREAD /* all packet type structures must have MqttMsgStat at top */ mms_stat = (MqttMsgStat*)msg; @@ -2666,6 +2612,7 @@ int MqttClient_CancelMessage(MqttClient *client, MqttObject* msg) mms_stat->write = MQTT_MSG_BEGIN; mms_stat->read = MQTT_MSG_BEGIN; +#ifdef WOLFMQTT_MULTITHREAD /* Remove any pending responses expected */ rc = wm_SemLock(&client->lockClient); if (rc != MQTT_CODE_SUCCESS) { @@ -2698,25 +2645,24 @@ int MqttClient_CancelMessage(MqttClient *client, MqttObject* msg) } } wm_SemUnlock(&client->lockClient); +#endif /* WOLFMQTT_MULTITHREAD */ - /* cancel any locks */ - if (mms_stat->isReadLocked) { + /* cancel any active flags / locks */ + if (mms_stat->isReadActive) { #ifdef WOLFMQTT_DEBUG_CLIENT PRINTF("Cancel Read Lock"); #endif - mms_stat->isReadLocked = 0; - XMEMSET(&client->read, 0, sizeof(client->read)); - MqttUnlockRecv(client, mms_stat); + mms_stat->isReadActive = 0; + MqttReadStop(client, mms_stat); } - if (mms_stat->isWriteLocked) { + if (mms_stat->isWriteActive) { #ifdef WOLFMQTT_DEBUG_CLIENT PRINTF("Cancel Write Lock"); #endif - mms_stat->isWriteLocked = 0; - XMEMSET(&client->write, 0, sizeof(client->write)); - MqttUnlockSend(client, mms_stat); + mms_stat->isWriteActive = 0; + MqttWriteStop(client, mms_stat); } -#endif + return rc; } diff --git a/src/mqtt_sn_client.c b/src/mqtt_sn_client.c index 40b7c20c4..1144d6362 100644 --- a/src/mqtt_sn_client.c +++ b/src/mqtt_sn_client.c @@ -506,7 +506,7 @@ static int SN_Client_WaitType(MqttClient *client, void* packet_obj, PRINTF("SN_Client_WaitType recv lock error"); return rc; } - mms_stat->isReadLocked = 1; + mms_stat->isReadActive = 1; MQTT_TRACE_MSG("SN lockRecv"); #endif @@ -667,8 +667,8 @@ static int SN_Client_WaitType(MqttClient *client, void* packet_obj, mms_stat->read = MQTT_MSG_BEGIN; #ifdef WOLFMQTT_MULTITHREAD - if (mms_stat->isReadLocked) { - mms_stat->isReadLocked = 0; + if (mms_stat->isReadActive) { + mms_stat->isReadActive = 0; wm_SemUnlock(&client->lockRecv); } #endif diff --git a/wolfmqtt/mqtt_packet.h b/wolfmqtt/mqtt_packet.h index 251a7c6d2..c99b6b9af 100644 --- a/wolfmqtt/mqtt_packet.h +++ b/wolfmqtt/mqtt_packet.h @@ -292,10 +292,8 @@ typedef struct _MqttMsgStat { MqttMsgState read; MqttMsgState write; -#ifdef WOLFMQTT_MULTITHREAD - byte isReadLocked:1; - byte isWriteLocked:1; -#endif + byte isReadActive:1; + byte isWriteActive:1; } MqttMsgStat; #ifdef WOLFMQTT_MULTITHREAD From 43779337ea7802e9d49837772a3764ce737470ff Mon Sep 17 00:00:00 2001 From: David Garske Date: Wed, 8 Nov 2023 06:37:12 -0800 Subject: [PATCH 18/62] Do not call disconnect callback for "continue". Allow unsubscribe after "exit" in multi-threaded example. --- examples/multithread/multithread.c | 7 ++++--- src/mqtt_client.c | 7 ++++++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/examples/multithread/multithread.c b/examples/multithread/multithread.c index 91515d769..4114d4549 100755 --- a/examples/multithread/multithread.c +++ b/examples/multithread/multithread.c @@ -103,13 +103,14 @@ static int mqtt_stop_get(void) return rc; } +#define MQTT_CODE_TEST_EXIT -200 static int check_response(MQTTCtx* mqttCtx, int rc, word32* startSec, int packet_type, word32 timeoutMs) { /* check for test mode */ - if (mqtt_stop_get()) { + if (mqtt_stop_get() && packet_type != MQTT_PACKET_TYPE_UNSUBSCRIBE) { PRINTF("MQTT Exiting Thread..."); - return MQTT_CODE_ERROR_SYSTEM; + return MQTT_CODE_TEST_EXIT; } #ifdef WOLFMQTT_NONBLOCK @@ -387,7 +388,7 @@ static int multithread_test_finish(MQTTCtx *mqttCtx) PRINTF("MQTT Client Done: %d", mqttCtx->return_code); - if (mStopRead && mqttCtx->return_code == MQTT_CODE_ERROR_SYSTEM) { + if (mStopRead && mqttCtx->return_code == MQTT_CODE_TEST_EXIT) { /* this is okay, we requested termination */ mqttCtx->return_code = MQTT_CODE_SUCCESS; } diff --git a/src/mqtt_client.c b/src/mqtt_client.c index 0af4d05d5..fbd7044f1 100644 --- a/src/mqtt_client.c +++ b/src/mqtt_client.c @@ -2466,8 +2466,13 @@ int MqttClient_Disconnect_ex(MqttClient *client, MqttDisconnect *p_disconnect) #if defined(WOLFMQTT_DISCONNECT_CB) && defined(WOLFMQTT_USE_CB_ON_DISCONNECT) /* Trigger disconnect callback */ - if (client->disconnect_cb) + if (client->disconnect_cb + #ifdef WOLFMQTT_NONBLOCK + && rc != MQTT_CODE_CONTINUE + #endif + ) { client->disconnect_cb(client, rc, client->disconnect_ctx); + } #endif /* No response for MQTT disconnect packet */ From cb249ab4013926e4c8339b6dc9dbd79e3ebd4303 Mon Sep 17 00:00:00 2001 From: David Garske Date: Wed, 8 Nov 2023 06:58:36 -0800 Subject: [PATCH 19/62] Fix issue with publish write only incorrectly removing pending response. ZD 16769. Remove extra test non-block in `MqttClient_Publish_ReadPayload` (already handled in `MqttSocket_Read`). Cleanup return code 0=success. --- src/mqtt_client.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/src/mqtt_client.c b/src/mqtt_client.c index fbd7044f1..e904401cd 100644 --- a/src/mqtt_client.c +++ b/src/mqtt_client.c @@ -162,7 +162,7 @@ static int MqttClient_Publish_ReadPayload(MqttClient* client, static int MqttWriteStart(MqttClient* client, MqttMsgStat* stat) { - int rc = 0; + int rc = MQTT_CODE_SUCCESS; #ifdef WOLFMQTT_MULTITHREAD #ifdef WOLFMQTT_DEBUG_CLIENT @@ -217,7 +217,7 @@ static void MqttWriteStop(MqttClient* client, MqttMsgStat* stat) static int MqttReadStart(MqttClient* client, MqttMsgStat* stat) { - int rc = 0; + int rc = MQTT_CODE_SUCCESS; #ifdef WOLFMQTT_MULTITHREAD #ifdef WOLFMQTT_DEBUG_CLIENT @@ -315,7 +315,7 @@ int MqttClient_RespList_Add(MqttClient *client, client->lastPendResp->next = newResp; client->lastPendResp = newResp; } - return 0; + return MQTT_CODE_SUCCESS; } void MqttClient_RespList_Remove(MqttClient *client, MqttPendResp *rmResp) @@ -362,6 +362,7 @@ void MqttClient_RespList_Remove(MqttClient *client, MqttPendResp *rmResp) #endif } +/* return codes: 0=not found, 1=found */ int MqttClient_RespList_Find(MqttClient *client, MqttPacketType packet_type, word16 packet_id, MqttPendResp **retResp) { @@ -1318,7 +1319,7 @@ static int MqttClient_WaitType(MqttClient *client, void *packet_obj, } client->write.len = rc; - rc = 0; + rc = MQTT_CODE_SUCCESS; mms_stat->write = MQTT_MSG_HEADER; } @@ -1339,7 +1340,7 @@ static int MqttClient_WaitType(MqttClient *client, void *packet_obj, #endif MqttWriteStop(client, mms_stat); if (rc == xfer) { - rc = 0; /* success */ + rc = MQTT_CODE_SUCCESS; /* success */ } mms_stat->write = MQTT_MSG_BEGIN; /* reset write state */ @@ -1722,15 +1723,6 @@ static int MqttClient_Publish_ReadPayload(MqttClient* client, /* make sure there is something to read */ if (msg_len > 0) { - #if defined(WOLFMQTT_NONBLOCK) && defined(WOLFMQTT_TEST_NONBLOCK) - static int testNbAlt = 0; - if (!testNbAlt) { - testNbAlt = 1; - return MQTT_CODE_CONTINUE; - } - testNbAlt = 0; - #endif - rc = MqttSocket_Read(client, client->rx_buf, msg_len, timeout_ms); if (rc < 0) { @@ -2036,9 +2028,14 @@ static int MqttPublishMsg(MqttClient *client, MqttPublish *publish, #ifdef WOLFMQTT_MULTITHREAD if (writeOnly) { - /* another thread will handle the wait type */ + /* another thread will handle response */ + /* check if response already received from other thread */ rc = MqttClient_CheckPendResp(client, resp_type, publish->packet_id); + if (rc == MQTT_CODE_CONTINUE) { + /* mark success, let other thread handle response */ + rc = MQTT_CODE_SUCCESS; + } } else #endif @@ -2049,6 +2046,7 @@ static int MqttPublishMsg(MqttClient *client, MqttPublish *publish, rc = MqttClient_WaitType(client, &publish->resp, resp_type, publish->packet_id, client->cmd_timeout_ms); } + #if defined(WOLFMQTT_NONBLOCK) || defined(WOLFMQTT_MULTITHREAD) if (rc == MQTT_CODE_CONTINUE) break; From d8ec3cc8fbd681657383380ec916e9b9a042199f Mon Sep 17 00:00:00 2001 From: David Garske Date: Thu, 9 Nov 2023 18:17:35 -0800 Subject: [PATCH 20/62] Fix for issue processing wait for a publish with ack on a different packet type causing the write state to get reset. --- src/mqtt_client.c | 18 +++++++++--------- wolfmqtt/mqtt_packet.h | 1 + 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/mqtt_client.c b/src/mqtt_client.c index e904401cd..b86b873e0 100644 --- a/src/mqtt_client.c +++ b/src/mqtt_client.c @@ -1276,7 +1276,7 @@ static int MqttClient_WaitType(MqttClient *client, void *packet_obj, client->packetAck.protocol_level = client->protocol_level; #endif - mms_stat->write = MQTT_MSG_ACK; + mms_stat->ack = MQTT_MSG_ACK; break; } @@ -1296,7 +1296,7 @@ static int MqttClient_WaitType(MqttClient *client, void *packet_obj, } } /* switch (mms_stat->read) */ - switch (mms_stat->write) + switch (mms_stat->ack) { case MQTT_MSG_BEGIN: case MQTT_MSG_WAIT: @@ -1321,7 +1321,7 @@ static int MqttClient_WaitType(MqttClient *client, void *packet_obj, client->write.len = rc; rc = MQTT_CODE_SUCCESS; - mms_stat->write = MQTT_MSG_HEADER; + mms_stat->ack = MQTT_MSG_HEADER; } FALL_THROUGH; @@ -1343,7 +1343,7 @@ static int MqttClient_WaitType(MqttClient *client, void *packet_obj, rc = MQTT_CODE_SUCCESS; /* success */ } - mms_stat->write = MQTT_MSG_BEGIN; /* reset write state */ + mms_stat->ack = MQTT_MSG_BEGIN; /* reset write state */ break; } @@ -1352,17 +1352,17 @@ static int MqttClient_WaitType(MqttClient *client, void *packet_obj, case MQTT_MSG_PAYLOAD2: default: #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("MqttClient_WaitType: Invalid write state %d!", - mms_stat->write); + PRINTF("MqttClient_WaitType: Invalid ack state %d!", + mms_stat->ack); #endif rc = MQTT_TRACE_ERROR(MQTT_CODE_ERROR_STAT); break; - } /* switch (mms_stat->write) */ + } /* switch (mms_stat->ack) */ #ifdef WOLFMQTT_DEBUG_CLIENT if (rc != MQTT_CODE_CONTINUE) { - PRINTF("MqttClient_WaitType: rc %d, state %d-%d", - rc, mms_stat->read, mms_stat->write); + PRINTF("MqttClient_WaitType: rc %d, state %d-%d-%d", + rc, mms_stat->read, mms_stat->write, mms_stat->ack); } #endif diff --git a/wolfmqtt/mqtt_packet.h b/wolfmqtt/mqtt_packet.h index c99b6b9af..f45efb7ac 100644 --- a/wolfmqtt/mqtt_packet.h +++ b/wolfmqtt/mqtt_packet.h @@ -291,6 +291,7 @@ typedef enum _MqttMsgState { typedef struct _MqttMsgStat { MqttMsgState read; MqttMsgState write; + MqttMsgState ack; byte isReadActive:1; byte isWriteActive:1; From 07f13fcb52470e297c611e2a4a40cd154cb650c9 Mon Sep 17 00:00:00 2001 From: David Garske Date: Tue, 14 Nov 2023 09:59:14 -0800 Subject: [PATCH 21/62] For publish write only, we still need to wait for it to be done in multi-threaded. If not using non-blocking mode consider it "done". --- src/mqtt_client.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mqtt_client.c b/src/mqtt_client.c index b86b873e0..6cb65a2d3 100644 --- a/src/mqtt_client.c +++ b/src/mqtt_client.c @@ -2032,10 +2032,12 @@ static int MqttPublishMsg(MqttClient *client, MqttPublish *publish, /* check if response already received from other thread */ rc = MqttClient_CheckPendResp(client, resp_type, publish->packet_id); + #ifndef WOLFMQTT_NONBLOCK if (rc == MQTT_CODE_CONTINUE) { /* mark success, let other thread handle response */ rc = MQTT_CODE_SUCCESS; } + #endif } else #endif From 92045977c109cd2b2dbc81f0e65716a492efe35d Mon Sep 17 00:00:00 2001 From: David Garske Date: Tue, 14 Nov 2023 11:56:12 -0800 Subject: [PATCH 22/62] Add note about the disconnect callback and use of `WOLFMQTT_USE_CB_ON_DISCONNECT`. --- src/mqtt_client.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/mqtt_client.c b/src/mqtt_client.c index 6cb65a2d3..ffe0ff999 100644 --- a/src/mqtt_client.c +++ b/src/mqtt_client.c @@ -2465,7 +2465,9 @@ int MqttClient_Disconnect_ex(MqttClient *client, MqttDisconnect *p_disconnect) } #if defined(WOLFMQTT_DISCONNECT_CB) && defined(WOLFMQTT_USE_CB_ON_DISCONNECT) - /* Trigger disconnect callback */ + /* Trigger disconnect callback - for intentional disconnect + * This callback may occur on a network failure during an intentional + * disconnect if the transport/socket is not setup yet. */ if (client->disconnect_cb #ifdef WOLFMQTT_NONBLOCK && rc != MQTT_CODE_CONTINUE From 8e3185ff6e4acde6c2b1f97a421337c871c24467 Mon Sep 17 00:00:00 2001 From: David Garske Date: Wed, 15 Nov 2023 20:48:18 -0800 Subject: [PATCH 23/62] Removing invalid detect if write is already in progress (its valid to encounter this between two thread, just not on same thread). --- src/mqtt_client.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/mqtt_client.c b/src/mqtt_client.c index ffe0ff999..d38f4e6c6 100644 --- a/src/mqtt_client.c +++ b/src/mqtt_client.c @@ -170,14 +170,6 @@ static int MqttWriteStart(MqttClient* client, MqttMsgStat* stat) MQTT_TRACE_MSG("Warning, send already locked!"); rc = MQTT_CODE_ERROR_SYSTEM; } - /* detect if a write is already in progress */ - if (wm_SemLock(&client->lockClient) == 0) { - if (client->write.total > 0) { - MQTT_TRACE_MSG("Partial write in progress!"); - rc = MQTT_CODE_CONTINUE; /* can't write yet */ - } - wm_SemUnlock(&client->lockClient); - } if (rc != 0) { return rc; } From 694e37997d99b571fe2afce4f0ac84addf913abe Mon Sep 17 00:00:00 2001 From: David Garske Date: Thu, 16 Nov 2023 07:46:32 -0800 Subject: [PATCH 24/62] Peer review feedback. Keep cancel private function unless non-block or multi-thread. Fix for `--disable-tls` and added tests. --- .github/workflows/fsanitize-check.yml | 7 ++++ .github/workflows/macos-check.yml | 7 ++++ .github/workflows/ubuntu-check.yml | 9 +++++ examples/mqttexample.c | 7 +++- scripts/awsiot.test | 11 +++++- scripts/azureiothub.test | 11 +++++- scripts/client.test | 54 ++++++++++++++++----------- scripts/firmware.test | 14 ++++++- scripts/multithread.test | 37 +++++++++++------- scripts/nbclient.test | 38 ++++++++++++------- src/mqtt_client.c | 7 ++++ wolfmqtt/mqtt_client.h | 6 ++- 12 files changed, 151 insertions(+), 57 deletions(-) diff --git a/.github/workflows/fsanitize-check.yml b/.github/workflows/fsanitize-check.yml index 073496907..0c0a0d858 100644 --- a/.github/workflows/fsanitize-check.yml +++ b/.github/workflows/fsanitize-check.yml @@ -63,6 +63,13 @@ jobs: - name: make check run: make check + - name: configure without TLS + run: ./configure --disable-tls + - name: make + run: make + - name: make check + run: make check + - name: configure with SN Enabled run: ./configure --enable-sn CC="gcc -fsanitize=address" - name: make diff --git a/.github/workflows/macos-check.yml b/.github/workflows/macos-check.yml index b79b5af59..b382b377e 100644 --- a/.github/workflows/macos-check.yml +++ b/.github/workflows/macos-check.yml @@ -45,6 +45,13 @@ jobs: - name: make check run: make check + - name: configure without TLS + run: ./configure --disable-tls + - name: make + run: make + - name: make check + run: make check + - name: configure with SN Enabled run: ./configure --enable-sn - name: make diff --git a/.github/workflows/ubuntu-check.yml b/.github/workflows/ubuntu-check.yml index cd1ecf65b..4611278cc 100644 --- a/.github/workflows/ubuntu-check.yml +++ b/.github/workflows/ubuntu-check.yml @@ -63,6 +63,15 @@ jobs: - name: wolfmqtt make check run: make check + - name: wolfmqtt configure without TLS + env: + WOLFMQTT_NO_EXTERNAL_BROKER_TESTS: 1 + run: ./configure --disable-tls + - name: wolfmqtt make + run: make + - name: wolfmqtt make check + run: make check + - name: wolfmqtt configure with SN Enabled env: WOLFMQTT_NO_EXTERNAL_BROKER_TESTS: 1 diff --git a/examples/mqttexample.c b/examples/mqttexample.c index fdfa604a6..07d6bf374 100644 --- a/examples/mqttexample.c +++ b/examples/mqttexample.c @@ -150,6 +150,7 @@ static int mqtt_get_rand(byte* data, word32 len) for (i = 0; i Port to connect on, default: Normal %d, TLS %d", MQTT_DEFAULT_PORT, MQTT_SECURE_PORT); - PRINTF("-t Enable TLS"); + PRINTF("-t Enable TLS"); /* Note: this string is used in test + * scripts to detect TLS feature */ PRINTF("-A Load CA (validate peer)"); PRINTF("-K Use private key (for TLS mutual auth)"); PRINTF("-c Use certificate (for TLS mutual auth)"); diff --git a/scripts/awsiot.test b/scripts/awsiot.test index ce7e90472..ec65c8122 100755 --- a/scripts/awsiot.test +++ b/scripts/awsiot.test @@ -5,8 +5,15 @@ # Check for application [ ! -x ./examples/aws/awsiot ] && echo -e "\n\nAWS IoT MQTT Client doesn't exist" && exit 1 -if test -n "$WOLFMQTT_NO_EXTERNAL_BROKER_TESTS"; then - echo "WOLFMQTT_NO_EXTERNAL_BROKER_TESTS set, won't run" +# Check for TLS support +has_tls=no +./examples/aws/awsiot -? 2>&1 | grep -- 'Enable TLS' +if [ $? -eq 0 ]; then + has_tls=yes +fi + +if test -n "$WOLFMQTT_NO_EXTERNAL_BROKER_TESTS" && test $has_tls == yes; then + echo "WOLFMQTT_NO_EXTERNAL_BROKER_TESTS set or no TLS, won't run" else def_args="-T -C 2000" diff --git a/scripts/azureiothub.test b/scripts/azureiothub.test index 9616946a5..2c8a37d3d 100755 --- a/scripts/azureiothub.test +++ b/scripts/azureiothub.test @@ -5,8 +5,15 @@ # Check for application [ ! -x ./examples/azure/azureiothub ] && echo -e "\n\nAzureIotHub MQTT Client doesn't exist" && exit 1 -if test -n "$WOLFMQTT_NO_EXTERNAL_BROKER_TESTS"; then - echo "WOLFMQTT_NO_EXTERNAL_BROKER_TESTS set, won't run" +# Check for TLS support +has_tls=no +./examples/azure/azureiothub -? 2>&1 | grep -- 'Enable TLS' +if [ $? -eq 0 ]; then + has_tls=yes +fi + +if test -n "$WOLFMQTT_NO_EXTERNAL_BROKER_TESTS" && test $has_tls == yes; then + echo "WOLFMQTT_NO_EXTERNAL_BROKER_TESTS set or no TLS, won't run" else # Use short timeout here, since we can't get a publish response to complete test # So use the timeout and ping response to complete test diff --git a/scripts/client.test b/scripts/client.test index 482427790..de6be6cef 100755 --- a/scripts/client.test +++ b/scripts/client.test @@ -22,6 +22,13 @@ do_cleanup() { # Check for application [ ! -x ./examples/mqttclient/mqttclient ] && echo -e "\n\nMQTT Client doesn't exist" && exit 1 +# Check for TLS support +has_tls=no +./examples/mqttclient/mqttclient -? 2>&1 | grep -- 'Enable TLS' +if [ $? -eq 0 ]; then + has_tls=yes +fi + def_args="-T -C 2000" # Check for mosquitto @@ -47,8 +54,7 @@ then ecc_mutual_auth_args="-c certs/client-ecc-cert.pem -K certs/ecc-client-key.pem" fi -# Run with and without TLS and QoS 0-2 - +# Run without TLS and QoS 0-2 ./examples/mqttclient/mqttclient $def_args $port_args -q 0 $1 RESULT=$? [ $RESULT -ne 0 ] && echo -e "\n\nMQTT Client failed! TLS=Off, QoS=0" && do_cleanup "-1" @@ -61,29 +67,33 @@ RESULT=$? RESULT=$? [ $RESULT -ne 0 ] && echo -e "\n\nMQTT Client failed! TLS=Off, QoS=2" && do_cleanup "-1" -./examples/mqttclient/mqttclient $def_args $tls_port_args -t -q 0 $1 -RESULT=$? -[ $RESULT -ne 0 ] && echo -e "\n\nMQTT Client failed! TLS=On, QoS=0" && do_cleanup "-1" - -./examples/mqttclient/mqttclient $def_args $tls_port_args -t -q 1 $1 -RESULT=$? -[ $RESULT -ne 0 ] && echo -e "\n\nMQTT Client failed! TLS=On, QoS=1" && do_cleanup "-1" - -./examples/mqttclient/mqttclient $def_args $tls_port_args -t -q 2 $1 -RESULT=$? -[ $RESULT -ne 0 ] && echo -e "\n\nMQTT Client failed! TLS=On, QoS=2" && do_cleanup "-1" - -./examples/mqttclient/mqttclient $def_args $mutual_auth_args $tls_port_args -t -q 0 $1 -RESULT=$? -[ $RESULT -ne 0 ] && echo -e "\n\nMQTT Client failed! TLS=On, QoS=0, RSA mutual auth" && do_cleanup "-1" - -./examples/mqttclient/mqttclient $def_args $ecc_mutual_auth_args $tls_port_args -t -q 0 $1 -RESULT=$? -[ $RESULT -ne 0 ] && echo -e "\n\nMQTT Client failed! TLS=On, QoS=0, ECC mutual auth" && do_cleanup "-1" +if test $has_tls == yes +then + # Run with TLS and QoS 0-2 + ./examples/mqttclient/mqttclient $def_args $tls_port_args -t -q 0 $1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "\n\nMQTT Client failed! TLS=On, QoS=0" && do_cleanup "-1" + + ./examples/mqttclient/mqttclient $def_args $tls_port_args -t -q 1 $1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "\n\nMQTT Client failed! TLS=On, QoS=1" && do_cleanup "-1" + + ./examples/mqttclient/mqttclient $def_args $tls_port_args -t -q 2 $1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "\n\nMQTT Client failed! TLS=On, QoS=2" && do_cleanup "-1" + + ./examples/mqttclient/mqttclient $def_args $mutual_auth_args $tls_port_args -t -q 0 $1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "\n\nMQTT Client failed! TLS=On, QoS=0, RSA mutual auth" && do_cleanup "-1" + + ./examples/mqttclient/mqttclient $def_args $ecc_mutual_auth_args $tls_port_args -t -q 0 $1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "\n\nMQTT Client failed! TLS=On, QoS=0, ECC mutual auth" && do_cleanup "-1" +fi # End broker do_cleanup "0" - + echo -e "\n\nMQTT Client Tests Passed" exit 0 diff --git a/scripts/firmware.test b/scripts/firmware.test index 9284a8e78..92b57874f 100755 --- a/scripts/firmware.test +++ b/scripts/firmware.test @@ -28,10 +28,22 @@ do_cleanup() { [ ! -x ./examples/firmware/fwpush ] && echo -e "\n\nMQTT Example fwpush doesn't exist" && exit 1 [ ! -x ./examples/firmware/fwclient ] && echo -e "\n\nMQTT Example fwclient doesn't exist" && exit 1 -def_args="-t -T -C 5000 -n wolfMQTT/example/firmware_$((RANDOM))" +# Check for TLS support +has_tls=no +./examples/mqttclient/mqttclient -? 2>&1 | grep -- 'Enable TLS' +if [ $? -eq 0 ]; then + has_tls=yes +fi + +def_args="-T -C 5000 -n wolfMQTT/example/firmware_$((RANDOM))" +if test $has_tls == yes +then + def_args="${def_args} -t" +fi filein=./examples/publish.dat fileout=./examples/publish.dat.trs + # Check for mosquitto if command -v mosquitto then diff --git a/scripts/multithread.test b/scripts/multithread.test index 72f671df1..cd5f0b9f1 100755 --- a/scripts/multithread.test +++ b/scripts/multithread.test @@ -22,6 +22,13 @@ do_cleanup() { # Check for application [ ! -x ./examples/multithread/multithread ] && echo -e "\n\nMultithread Client doesn't exist" && exit 1 +# Check for TLS support +has_tls=no +./examples/multithread/multithread -? 2>&1 | grep -- 'Enable TLS' +if [ $? -eq 0 ]; then + has_tls=yes +fi + def_args="-T -C 2000" # Check for mosquitto @@ -45,8 +52,8 @@ then port_args="-p 11883" fi -# Run with and without TLS and QoS 0-2 +# Run without TLS and QoS 0-2 ./examples/multithread/multithread $def_args $port_args -q 0 $1 RESULT=$? [ $RESULT -ne 0 ] && echo -e "\n\nMultithread Client failed! TLS=Off, QoS=0" && do_cleanup "-1" @@ -59,21 +66,25 @@ RESULT=$? RESULT=$? [ $RESULT -ne 0 ] && echo -e "\n\nMultithread Client failed! TLS=Off, QoS=2" && do_cleanup "-1" -./examples/multithread/multithread $def_args $tls_port_args -t -q 0 $1 -RESULT=$? -[ $RESULT -ne 0 ] && echo -e "\n\nMultithread Client failed! TLS=On, QoS=0" && do_cleanup "-1" - -./examples/multithread/multithread $def_args $tls_port_args -t -q 1 $1 -RESULT=$? -[ $RESULT -ne 0 ] && echo -e "\n\nMultithread Client failed! TLS=On, QoS=1" && do_cleanup "-1" - -./examples/multithread/multithread $def_args $tls_port_args -t -q 2 $1 -RESULT=$? -[ $RESULT -ne 0 ] && echo -e "\n\nMultithread Client failed! TLS=On, QoS=2" && do_cleanup "-1" +if test $has_tls == yes +then + # Run with TLS and QoS 0-2 + ./examples/multithread/multithread $def_args $tls_port_args -t -q 0 $1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "\n\nMultithread Client failed! TLS=On, QoS=0" && do_cleanup "-1" + + ./examples/multithread/multithread $def_args $tls_port_args -t -q 1 $1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "\n\nMultithread Client failed! TLS=On, QoS=1" && do_cleanup "-1" + + ./examples/multithread/multithread $def_args $tls_port_args -t -q 2 $1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "\n\nMultithread Client failed! TLS=On, QoS=2" && do_cleanup "-1" +fi # End broker do_cleanup "0" - + echo -e "\n\nMultithread MQTT Client Tests Passed" exit 0 diff --git a/scripts/nbclient.test b/scripts/nbclient.test index 8d760d083..5a1389870 100755 --- a/scripts/nbclient.test +++ b/scripts/nbclient.test @@ -22,6 +22,13 @@ do_cleanup() { # Check for application [ ! -x ./examples/nbclient/nbclient ] && echo -e "\n\nNon-blocking Client doesn't exist" && exit 1 +# Check for TLS support +has_tls=no +./examples/multithread/multithread -? 2>&1 | grep -- 'Enable TLS' +if [ $? -eq 0 ]; then + has_tls=yes +fi + # Use minimum of 2 seconds # The check timout will sometimes incorrectly trigger if 1 second is used def_args="-T -C 2000" @@ -47,8 +54,7 @@ then port_args="-p 11883" fi -# Run with and without TLS and QoS 0-2 - +# Run without TLS and QoS 0-2 ./examples/nbclient/nbclient $def_args $port_args -q 0 $1 RESULT=$? [ $RESULT -ne 0 ] && echo -e "\n\nNon-blocking Client failed! TLS=Off, QoS=0" && do_cleanup "-1" @@ -61,21 +67,25 @@ RESULT=$? RESULT=$? [ $RESULT -ne 0 ] && echo -e "\n\nNon-blocking Client failed! TLS=Off, QoS=2" && do_cleanup "-1" -./examples/nbclient/nbclient $def_args $tls_port_args -t -q 0 $1 -RESULT=$? -[ $RESULT -ne 0 ] && echo -e "\n\nNon-blocking Client failed! TLS=On, QoS=0" && do_cleanup "-1" - -./examples/nbclient/nbclient $def_args $tls_port_args -t -q 1 $1 -RESULT=$? -[ $RESULT -ne 0 ] && echo -e "\n\nNon-blocking Client failed! TLS=On, QoS=1" && do_cleanup "-1" - -./examples/nbclient/nbclient $def_args $tls_port_args -t -q 2 $1 -RESULT=$? -[ $RESULT -ne 0 ] && echo -e "\n\nNon-blocking Client failed! TLS=On, QoS=2" && do_cleanup "-1" +if test $has_tls == yes +then + # Run with TLS and QoS 0-2 + ./examples/nbclient/nbclient $def_args $tls_port_args -t -q 0 $1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "\n\nNon-blocking Client failed! TLS=On, QoS=0" && do_cleanup "-1" + + ./examples/nbclient/nbclient $def_args $tls_port_args -t -q 1 $1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "\n\nNon-blocking Client failed! TLS=On, QoS=1" && do_cleanup "-1" + + ./examples/nbclient/nbclient $def_args $tls_port_args -t -q 2 $1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "\n\nNon-blocking Client failed! TLS=On, QoS=2" && do_cleanup "-1" +fi # End broker do_cleanup "0" - + echo -e "\n\nNon-blocking MQTT Client Tests Passed" exit 0 diff --git a/src/mqtt_client.c b/src/mqtt_client.c index d38f4e6c6..88102c250 100644 --- a/src/mqtt_client.c +++ b/src/mqtt_client.c @@ -31,6 +31,10 @@ /* forward declarations */ static int MqttClient_Publish_ReadPayload(MqttClient* client, MqttPublish* publish, int timeout_ms); +#if !defined(WOLFMQTT_MULTITHREAD) && !defined(WOLFMQTT_NONBLOCK) +static int MqttClient_CancelMessage(MqttClient *client, MqttObject* msg); +#endif + #ifdef WOLFMQTT_MULTITHREAD @@ -2588,6 +2592,9 @@ int MqttClient_WaitMessage(MqttClient *client, int timeout_ms) return MqttClient_WaitMessage_ex(client, &client->msg, timeout_ms); } +#if !defined(WOLFMQTT_MULTITHREAD) && !defined(WOLFMQTT_NONBLOCK) +static +#endif int MqttClient_CancelMessage(MqttClient *client, MqttObject* msg) { int rc = MQTT_CODE_SUCCESS; diff --git a/wolfmqtt/mqtt_client.h b/wolfmqtt/mqtt_client.h index 7fc976afc..e8ad0299c 100644 --- a/wolfmqtt/mqtt_client.h +++ b/wolfmqtt/mqtt_client.h @@ -497,8 +497,9 @@ WOLFMQTT_API int MqttClient_WaitMessage_ex( MqttObject *msg, int timeout_ms); -/*! \brief Cancel a partially sent message. Applies to multi-threaded or - non-blocking mode +#if defined(WOLFMQTT_MULTITHREAD) || defined(WOLFMQTT_NONBLOCK) +/*! \brief In a multi-threaded and non-blocking mode this allows you to + cancel an MQTT object that was previously submitted. * \note This is a blocking function that will wait for MqttNet.read * \param client Pointer to MqttClient structure * \param msg Pointer to MqttObject structure @@ -508,6 +509,7 @@ WOLFMQTT_API int MqttClient_WaitMessage_ex( WOLFMQTT_API int MqttClient_CancelMessage( MqttClient *client, MqttObject *msg); +#endif #ifdef WOLFMQTT_NONBLOCK /*! \brief In a non-blocking mode this checks if the message has a read From 13ee5a5f29aa42c4c3fe19849cc027cbcc6d1847 Mon Sep 17 00:00:00 2001 From: David Garske Date: Thu, 16 Nov 2023 07:56:46 -0800 Subject: [PATCH 25/62] Add debugging for NetDisconnect pending response cleanup. --- src/mqtt_client.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/mqtt_client.c b/src/mqtt_client.c index 88102c250..8022746d6 100644 --- a/src/mqtt_client.c +++ b/src/mqtt_client.c @@ -2733,9 +2733,19 @@ int MqttClient_NetDisconnect(MqttClient *client) /* Get client lock on to ensure no other threads are active */ wm_SemLock(&client->lockClient); +#ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("Net Disconnect: Removing pending responses"); +#endif for (tmpResp = client->firstPendResp; tmpResp != NULL; tmpResp = tmpResp->next) { + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("\tPendResp: %p (obj %p), Type %s (%d), ID %d, InProc %d, Done %d", + tmpResp, tmpResp->packet_obj, + MqttPacket_TypeDesc(tmpResp->packet_type), + tmpResp->packet_type, tmpResp->packet_id, + tmpResp->packetProcessing, tmpResp->packetDone); + #endif MqttClient_RespList_Remove(client, tmpResp); } wm_SemUnlock(&client->lockClient); From fa17fe27b0d182bf050584d4ed1d0de976683e5a Mon Sep 17 00:00:00 2001 From: David Garske Date: Thu, 16 Nov 2023 08:07:04 -0800 Subject: [PATCH 26/62] Fix for static analyzer warning. --- src/mqtt_client.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/mqtt_client.c b/src/mqtt_client.c index 8022746d6..7cce1e58d 100644 --- a/src/mqtt_client.c +++ b/src/mqtt_client.c @@ -1315,7 +1315,10 @@ static int MqttClient_WaitType(MqttClient *client, void *packet_obj, } client->write.len = rc; + /* Note: static analyzer complains about set, but not used here. + * Keeping it to ensure no future issues with rc > 0 */ rc = MQTT_CODE_SUCCESS; + (void)rc; /* inhibit clang-analyzer-deadcode.DeadStores */ mms_stat->ack = MQTT_MSG_HEADER; } From 1571d2b4f413168faa27d57c0202d9b4aad7799d Mon Sep 17 00:00:00 2001 From: David Garske Date: Thu, 16 Nov 2023 09:34:47 -0800 Subject: [PATCH 27/62] Fix for cancelling publish to also attempt to cancel any publish responses expected (Qos 1 and 2). --- src/mqtt_client.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/mqtt_client.c b/src/mqtt_client.c index 7cce1e58d..61c2ed483 100644 --- a/src/mqtt_client.c +++ b/src/mqtt_client.c @@ -2649,6 +2649,13 @@ int MqttClient_CancelMessage(MqttClient *client, MqttObject* msg) tmpResp->packet_type, tmpResp->packet_id, tmpResp->packetProcessing, tmpResp->packetDone); #endif + if (tmpResp->packet_type == MQTT_PACKET_TYPE_PUBLISH) { + /* Also cancel any publish responses */ + MqttPublish* publish = (MqttPublish*)msg; + if (publish) { + MqttClient_RespList_Remove(client, &publish->resp.pendResp); + } + } MqttClient_RespList_Remove(client, tmpResp); break; } From 2f85fa1cda54f605cb441925d275ab66f47ee931 Mon Sep 17 00:00:00 2001 From: David Garske Date: Fri, 17 Nov 2023 12:25:03 -0800 Subject: [PATCH 28/62] Revert 7ed741b. The ack uses the publish for packet_obj, so canceling works correctly during testing. Customer is seeing a cancel for an ACK become strayed in the pending response list (investigating). --- src/mqtt_client.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/mqtt_client.c b/src/mqtt_client.c index 61c2ed483..7cce1e58d 100644 --- a/src/mqtt_client.c +++ b/src/mqtt_client.c @@ -2649,13 +2649,6 @@ int MqttClient_CancelMessage(MqttClient *client, MqttObject* msg) tmpResp->packet_type, tmpResp->packet_id, tmpResp->packetProcessing, tmpResp->packetDone); #endif - if (tmpResp->packet_type == MQTT_PACKET_TYPE_PUBLISH) { - /* Also cancel any publish responses */ - MqttPublish* publish = (MqttPublish*)msg; - if (publish) { - MqttClient_RespList_Remove(client, &publish->resp.pendResp); - } - } MqttClient_RespList_Remove(client, tmpResp); break; } From f559c181452ca8f2da2dd393390f8693d1ec2fb3 Mon Sep 17 00:00:00 2001 From: David Garske Date: Mon, 20 Nov 2023 14:30:44 -0800 Subject: [PATCH 29/62] Expand disable TLS test. --- .github/workflows/fsanitize-check.yml | 2 +- .github/workflows/macos-check.yml | 12 +++++++++--- .github/workflows/ubuntu-check.yml | 2 +- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/.github/workflows/fsanitize-check.yml b/.github/workflows/fsanitize-check.yml index 0c0a0d858..29fa5430e 100644 --- a/.github/workflows/fsanitize-check.yml +++ b/.github/workflows/fsanitize-check.yml @@ -64,7 +64,7 @@ jobs: run: make check - name: configure without TLS - run: ./configure --disable-tls + run: ./configure --enable-all --disable-tls - name: make run: make - name: make check diff --git a/.github/workflows/macos-check.yml b/.github/workflows/macos-check.yml index b382b377e..8c52d3238 100644 --- a/.github/workflows/macos-check.yml +++ b/.github/workflows/macos-check.yml @@ -11,8 +11,6 @@ jobs: runs-on: macos-latest timeout-minutes: 10 - env: - WOLFMQTT_NO_EXTERNAL_BROKER_TESTS: 1 steps: - uses: actions/checkout@master with: @@ -46,13 +44,17 @@ jobs: run: make check - name: configure without TLS - run: ./configure --disable-tls + env: + WOLFMQTT_NO_EXTERNAL_BROKER_TESTS: 1 + run: ./configure --enable-all --disable-tls - name: make run: make - name: make check run: make check - name: configure with SN Enabled + env: + WOLFMQTT_NO_EXTERNAL_BROKER_TESTS: 1 run: ./configure --enable-sn - name: make run: make @@ -60,6 +62,8 @@ jobs: run: make check - name: configure with Non-Block + env: + WOLFMQTT_NO_EXTERNAL_BROKER_TESTS: 1 run: ./configure --enable-nonblock CFLAGS="-DWOLFMQTT_TEST_NONBLOCK" - name: make run: make @@ -67,6 +71,8 @@ jobs: run: make check - name: configure with Non-Block and Multi-threading + env: + WOLFMQTT_NO_EXTERNAL_BROKER_TESTS: 1 run: ./configure --enable-mt --enable-nonblock CFLAGS="-DWOLFMQTT_TEST_NONBLOCK" - name: make run: make diff --git a/.github/workflows/ubuntu-check.yml b/.github/workflows/ubuntu-check.yml index 4611278cc..537a848fc 100644 --- a/.github/workflows/ubuntu-check.yml +++ b/.github/workflows/ubuntu-check.yml @@ -66,7 +66,7 @@ jobs: - name: wolfmqtt configure without TLS env: WOLFMQTT_NO_EXTERNAL_BROKER_TESTS: 1 - run: ./configure --disable-tls + run: ./configure --enable-all --disable-tls - name: wolfmqtt make run: make - name: wolfmqtt make check From 6ff557ec22794196918e963ca9b190f58747a933 Mon Sep 17 00:00:00 2001 From: David Garske Date: Tue, 21 Nov 2023 08:15:49 -0800 Subject: [PATCH 30/62] Fix TSAN report with WOLFMQTT_DEBUG_CLIENT set around the use of client->lastRc used for debugging. --- src/mqtt_client.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/mqtt_client.c b/src/mqtt_client.c index 7cce1e58d..a13c64325 100644 --- a/src/mqtt_client.c +++ b/src/mqtt_client.c @@ -1381,8 +1381,16 @@ static int MqttClient_WaitType(MqttClient *client, void *packet_obj, #ifdef WOLFMQTT_NONBLOCK #ifdef WOLFMQTT_DEBUG_CLIENT - client->lastRc = rc; + #ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) #endif + { + client->lastRc = rc; + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockClient); + #endif + } + #endif /* WOLFMQTT_DEBUG_CLIENT */ if (rc == MQTT_CODE_CONTINUE) { return rc; } From acead5b7f185a1e76aeb84bea842b8f29a820a8c Mon Sep 17 00:00:00 2001 From: David Garske Date: Tue, 21 Nov 2023 11:56:22 -0800 Subject: [PATCH 31/62] Fix for read of fixed portion (2 bytes) of header when only 1 is returned. --- src/mqtt_client.c | 3 ++- src/mqtt_packet.c | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mqtt_client.c b/src/mqtt_client.c index a13c64325..f284b0207 100644 --- a/src/mqtt_client.c +++ b/src/mqtt_client.c @@ -1087,7 +1087,8 @@ static int MqttClient_WaitType(MqttClient *client, void *packet_obj, if (rc <= 0) { #ifdef WOLFMQTT_NONBLOCK if (rc == MQTT_CODE_CONTINUE && - client->packet.stat > MQTT_PK_BEGIN) { + (client->packet.stat > MQTT_PK_BEGIN || + client->read.total > 0)) { /* advance state, since we received some data */ mms_stat->read = MQTT_MSG_HEADER; } diff --git a/src/mqtt_packet.c b/src/mqtt_packet.c index cafa8e842..1b9fd0008 100644 --- a/src/mqtt_packet.c +++ b/src/mqtt_packet.c @@ -1963,7 +1963,6 @@ int MqttPacket_Read(MqttClient *client, byte* rx_buf, int rx_buf_len, { case MQTT_PK_BEGIN: { - client->read.pos = 0; client->packet.header_len = MQTT_PACKET_HEADER_MIN_SIZE; client->packet.remain_len = 0; From bffe15c83940b83fbbc2de34be134b5b12eceb34 Mon Sep 17 00:00:00 2001 From: David Garske Date: Tue, 21 Nov 2023 13:50:49 -0800 Subject: [PATCH 32/62] Improvements to the tests. MacOS local broker. Improve `WOLFMQTT_TEST_NONBLOCK`. --- .github/workflows/fsanitize-check.yml | 8 ++++---- .github/workflows/macos-check.yml | 18 ++++++++---------- .github/workflows/ubuntu-check.yml | 2 +- scripts/client.test | 2 ++ scripts/firmware.test | 2 ++ scripts/multithread.test | 1 + scripts/nbclient.test | 2 ++ scripts/wiot.test | 21 ++++++++++++++++----- src/mqtt_socket.c | 6 ++---- 9 files changed, 38 insertions(+), 24 deletions(-) diff --git a/.github/workflows/fsanitize-check.yml b/.github/workflows/fsanitize-check.yml index 29fa5430e..cb78fcb4e 100644 --- a/.github/workflows/fsanitize-check.yml +++ b/.github/workflows/fsanitize-check.yml @@ -64,28 +64,28 @@ jobs: run: make check - name: configure without TLS - run: ./configure --enable-all --disable-tls + run: ./configure CC="gcc -fsanitize=address" --enable-all --disable-tls CFLAGS="-DWOLFMQTT_TEST_NONBLOCK" - name: make run: make - name: make check run: make check - name: configure with SN Enabled - run: ./configure --enable-sn CC="gcc -fsanitize=address" + run: ./configure CC="gcc -fsanitize=address" --enable-sn - name: make run: make - name: make check run: make check - name: configure with Non-Block - run: ./configure --enable-nonblock CFLAGS="-DWOLFMQTT_TEST_NONBLOCK" CC="gcc -fsanitize=address" + run: ./configure CC="gcc -fsanitize=address" --enable-nonblock CFLAGS="-DWOLFMQTT_TEST_NONBLOCK" - name: make run: make - name: make check run: make check - name: configure with Non-Block and Multi-threading - run: ./configure --enable-mt --enable-nonblock CFLAGS="-DWOLFMQTT_TEST_NONBLOCK" CC="gcc -fsanitize=address" + run: ./configure CC="gcc -fsanitize=address" --enable-mt --enable-nonblock CFLAGS="-DWOLFMQTT_TEST_NONBLOCK" - name: make run: make - name: make check diff --git a/.github/workflows/macos-check.yml b/.github/workflows/macos-check.yml index 8c52d3238..a0b129074 100644 --- a/.github/workflows/macos-check.yml +++ b/.github/workflows/macos-check.yml @@ -6,6 +6,9 @@ on: pull_request: branches: [ '*' ] +env: + WOLFMQTT_NO_EXTERNAL_BROKER_TESTS: 1 + jobs: build: @@ -17,7 +20,10 @@ jobs: repository: wolfssl/wolfssl path: wolfssl - name: brew - run: brew install automake libtool md5sha1sum + run: | + brew install automake libtool md5sha1sum mosquitto + echo "/usr/local/sbin/" >> $GITHUB_PATH + echo "/usr/local/opt/mosquitto/sbin/" >> $GITHUB_PATH - name: wolfssl autogen working-directory: ./wolfssl @@ -44,17 +50,13 @@ jobs: run: make check - name: configure without TLS - env: - WOLFMQTT_NO_EXTERNAL_BROKER_TESTS: 1 - run: ./configure --enable-all --disable-tls + run: ./configure --enable-all --disable-tls CFLAGS="-DWOLFMQTT_TEST_NONBLOCK" - name: make run: make - name: make check run: make check - name: configure with SN Enabled - env: - WOLFMQTT_NO_EXTERNAL_BROKER_TESTS: 1 run: ./configure --enable-sn - name: make run: make @@ -62,8 +64,6 @@ jobs: run: make check - name: configure with Non-Block - env: - WOLFMQTT_NO_EXTERNAL_BROKER_TESTS: 1 run: ./configure --enable-nonblock CFLAGS="-DWOLFMQTT_TEST_NONBLOCK" - name: make run: make @@ -71,8 +71,6 @@ jobs: run: make check - name: configure with Non-Block and Multi-threading - env: - WOLFMQTT_NO_EXTERNAL_BROKER_TESTS: 1 run: ./configure --enable-mt --enable-nonblock CFLAGS="-DWOLFMQTT_TEST_NONBLOCK" - name: make run: make diff --git a/.github/workflows/ubuntu-check.yml b/.github/workflows/ubuntu-check.yml index 537a848fc..e9366b0a9 100644 --- a/.github/workflows/ubuntu-check.yml +++ b/.github/workflows/ubuntu-check.yml @@ -66,7 +66,7 @@ jobs: - name: wolfmqtt configure without TLS env: WOLFMQTT_NO_EXTERNAL_BROKER_TESTS: 1 - run: ./configure --enable-all --disable-tls + run: ./configure --enable-all --disable-tls CFLAGS="-DWOLFMQTT_TEST_NONBLOCK" - name: wolfmqtt make run: make - name: wolfmqtt make check diff --git a/scripts/client.test b/scripts/client.test index de6be6cef..26ba7fdd6 100755 --- a/scripts/client.test +++ b/scripts/client.test @@ -54,6 +54,8 @@ then ecc_mutual_auth_args="-c certs/client-ecc-cert.pem -K certs/ecc-client-key.pem" fi +echo -e "Base args: $def_args $port_args" + # Run without TLS and QoS 0-2 ./examples/mqttclient/mqttclient $def_args $port_args -q 0 $1 RESULT=$? diff --git a/scripts/firmware.test b/scripts/firmware.test index 92b57874f..6317abe81 100755 --- a/scripts/firmware.test +++ b/scripts/firmware.test @@ -66,6 +66,8 @@ fi grep -F -e 'ENABLE_MQTT_TLS' ./wolfmqtt/options.h ENABLE_MQTT_TLS=$? +echo -e "Base args: $def_args" + # Start firmware client ./examples/firmware/fwclient $def_args -f $fileout $1 & client_result=$? diff --git a/scripts/multithread.test b/scripts/multithread.test index cd5f0b9f1..acae30f5c 100755 --- a/scripts/multithread.test +++ b/scripts/multithread.test @@ -52,6 +52,7 @@ then port_args="-p 11883" fi +echo -e "Base args: $def_args $port_args" # Run without TLS and QoS 0-2 ./examples/multithread/multithread $def_args $port_args -q 0 $1 diff --git a/scripts/nbclient.test b/scripts/nbclient.test index 5a1389870..e867ddc04 100755 --- a/scripts/nbclient.test +++ b/scripts/nbclient.test @@ -54,6 +54,8 @@ then port_args="-p 11883" fi +echo -e "Base args: $def_args $port_args" + # Run without TLS and QoS 0-2 ./examples/nbclient/nbclient $def_args $port_args -q 0 $1 RESULT=$? diff --git a/scripts/wiot.test b/scripts/wiot.test index 9d1e9493b..0d14e930a 100755 --- a/scripts/wiot.test +++ b/scripts/wiot.test @@ -7,14 +7,25 @@ # Check for application [ ! -x ./examples/wiot/wiot ] && echo -e "\n\nWatson IoT MQTT Client doesn't exist" && exit 1 +# Check for TLS support +has_tls=no +./examples/azure/azureiothub -? 2>&1 | grep -- 'Enable TLS' +if [ $? -eq 0 ]; then + has_tls=yes +fi + def_args="-T -C 2000" -# Run +if test -n "$WOLFMQTT_NO_EXTERNAL_BROKER_TESTS" && test $has_tls == yes; then + echo "WOLFMQTT_NO_EXTERNAL_BROKER_TESTS set or no TLS, won't run" +else + # Run -./examples/wiot/wiot $def_args $1 -RESULT=$? -[ $RESULT -ne 0 ] && echo -e "\n\nWatson IoT MQTT Client failed! TLS=On, QoS=0" && exit 1 + ./examples/wiot/wiot $def_args $1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "\n\nWatson IoT MQTT Client failed! TLS=On, QoS=0" && exit 1 -echo -e "\n\nWatson IoT MQTT Client Tests Passed" + echo -e "\n\nWatson IoT MQTT Client Tests Passed" +fi exit 0 diff --git a/src/mqtt_socket.c b/src/mqtt_socket.c index 8002d2480..299577e80 100644 --- a/src/mqtt_socket.c +++ b/src/mqtt_socket.c @@ -173,9 +173,8 @@ static int MqttSocket_WriteDo(MqttClient *client, const byte* buf, int buf_len, #if defined(WOLFMQTT_NONBLOCK) && defined(WOLFMQTT_TEST_NONBLOCK) static int testSmallerWrite = 0; if (!testSmallerWrite) { - if (buf_len > 100) { + if (buf_len > 1) buf_len /= 2; - } testSmallerWrite = 1; } else { @@ -300,9 +299,8 @@ static int MqttSocket_ReadDo(MqttClient *client, byte* buf, int buf_len, #if defined(WOLFMQTT_NONBLOCK) && defined(WOLFMQTT_TEST_NONBLOCK) static int testSmallerRead = 0; if (!testSmallerRead) { - if (buf_len > 100) { + if (buf_len > 1) buf_len /= 2; - } testSmallerRead = 1; } else { From e46c2e950710308440fc5859ab0e7818e2431e81 Mon Sep 17 00:00:00 2001 From: David Garske Date: Tue, 21 Nov 2023 14:44:34 -0800 Subject: [PATCH 33/62] For systems without bwrap use a random port and no TLS. --- scripts/client.test | 38 ++++++++++++++++++----- scripts/firmware.test | 66 +++++++++++++++++++++++++--------------- scripts/multithread.test | 37 +++++++++++++++++----- scripts/nbclient.test | 37 +++++++++++++++++----- 4 files changed, 129 insertions(+), 49 deletions(-) diff --git a/scripts/client.test b/scripts/client.test index 26ba7fdd6..d31ca0e8c 100755 --- a/scripts/client.test +++ b/scripts/client.test @@ -19,6 +19,19 @@ do_cleanup() { fi } +generate_port() { # function to produce a random port number + if [[ "$OSTYPE" == "linux"* ]]; then + port=$(($(od -An -N2 /dev/urandom) % (65535-49152) + 49152)) + elif [[ "$OSTYPE" == "darwin"* ]]; then + port=$(($(od -An -N2 /dev/random) % (65535-49152) + 49152)) + else + echo "Unknown OS TYPE" + exit 1 + fi + echo -e "Using port $port" +} + + # Check for application [ ! -x ./examples/mqttclient/mqttclient ] && echo -e "\n\nMQTT Client doesn't exist" && exit 1 @@ -34,22 +47,31 @@ def_args="-T -C 2000" # Check for mosquitto if command -v mosquitto then - # bwrap only if using a local mosquitto instance - if [ "${AM_BWRAPPED-}" != "yes" ]; then - bwrap_path="$(command -v bwrap)" - if [ -n "$bwrap_path" ]; then - echo "Client test using bwrap" + bwrap_path="$(command -v bwrap)" + if [ -n "$bwrap_path" ]; then + # bwrap only if using a local mosquitto instance + if [ "${AM_BWRAPPED-}" != "yes" ]; then + echo "Using bwrap" export AM_BWRAPPED=yes exec "$bwrap_path" --unshare-net --dev-bind / / "$0" "$@" fi + + broker_args="-c scripts/broker_test/mosquitto.conf" + port=11883 + else + # mosquitto broker custom port non-TLS only + has_tls=no + generate_port + broker_args="-p $port" fi - # Run mosquitto broker - mosquitto -c scripts/broker_test/mosquitto.conf & + mosquitto $broker_args & broker_pid=$! echo "Broker PID is $broker_pid" + sleep 0.1 + def_args="${def_args} -h localhost" tls_port_args="-p 18883" - port_args="-p 11883" + port_args="-p ${port}" mutual_auth_args="-c certs/client-cert.pem -K certs/client-key.pem" ecc_mutual_auth_args="-c certs/client-ecc-cert.pem -K certs/ecc-client-key.pem" fi diff --git a/scripts/firmware.test b/scripts/firmware.test index 6317abe81..8e0348ada 100755 --- a/scripts/firmware.test +++ b/scripts/firmware.test @@ -6,10 +6,8 @@ no_pid=-1 broker_pid=$no_pid do_cleanup() { - if [ $ENABLE_MQTT_TLS -ne 1 ]; then - # Delete file - rm $fileout - fi + # Delete file + rm $fileout if [ $broker_pid != $no_pid ] then @@ -24,6 +22,19 @@ do_cleanup() { fi } +generate_port() { # function to produce a random port number + if [[ "$OSTYPE" == "linux"* ]]; then + port=$(($(od -An -N2 /dev/urandom) % (65535-49152) + 49152)) + elif [[ "$OSTYPE" == "darwin"* ]]; then + port=$(($(od -An -N2 /dev/random) % (65535-49152) + 49152)) + else + echo "Unknown OS TYPE" + exit 1 + fi + echo -e "Using port $port" +} + + # Check for application [ ! -x ./examples/firmware/fwpush ] && echo -e "\n\nMQTT Example fwpush doesn't exist" && exit 1 [ ! -x ./examples/firmware/fwclient ] && echo -e "\n\nMQTT Example fwclient doesn't exist" && exit 1 @@ -36,10 +47,6 @@ if [ $? -eq 0 ]; then fi def_args="-T -C 5000 -n wolfMQTT/example/firmware_$((RANDOM))" -if test $has_tls == yes -then - def_args="${def_args} -t" -fi filein=./examples/publish.dat fileout=./examples/publish.dat.trs @@ -47,24 +54,35 @@ fileout=./examples/publish.dat.trs # Check for mosquitto if command -v mosquitto then - # bwrap only if using a local mosquitto instance - if [ "${AM_BWRAPPED-}" != "yes" ]; then - bwrap_path="$(command -v bwrap)" - if [ -n "$bwrap_path" ]; then - echo "Firmware test using bwrap" + bwrap_path="$(command -v bwrap)" + if [ -n "$bwrap_path" ]; then + # bwrap only if using a local mosquitto instance + if [ "${AM_BWRAPPED-}" != "yes" ]; then + echo "Using bwrap" export AM_BWRAPPED=yes exec "$bwrap_path" --unshare-net --dev-bind / / "$0" "$@" fi + + broker_args="-c scripts/broker_test/mosquitto.conf" + port=11883 + else + # mosquitto broker custom port non-TLS only + has_tls=no + generate_port + broker_args="-p $port" fi - # Run mosquitto broker - mosquitto -c scripts/broker_test/mosquitto.conf & + mosquitto $broker_args & broker_pid=$! echo "Broker PID is $broker_pid" - def_args="${def_args} -h localhost -p 18883" + sleep 0.1 + + def_args="${def_args} -h localhost -p ${port}" fi -grep -F -e 'ENABLE_MQTT_TLS' ./wolfmqtt/options.h -ENABLE_MQTT_TLS=$? +if test $has_tls == yes +then + def_args="${def_args} -t" +fi echo -e "Base args: $def_args" @@ -84,13 +102,11 @@ server_result=$? # give some time for the client complete sleep 0.5 -if [ $ENABLE_MQTT_TLS -ne 1 ]; then - # Compare files - echo "Comparing files" - md5sum -b $filein $fileout - compare_result=$? - [ $compare_result -ne 0 ] && echo -e "\n\nMQTT Example firmware compare failed!" && do_cleanup "-1" -fi +# Compare files +echo "Comparing files" +md5sum -b $filein $fileout +compare_result=$? +[ $compare_result -ne 0 ] && echo -e "\n\nMQTT Example firmware compare failed!" && do_cleanup "-1" # End broker do_cleanup "0" diff --git a/scripts/multithread.test b/scripts/multithread.test index acae30f5c..7b1f6326e 100755 --- a/scripts/multithread.test +++ b/scripts/multithread.test @@ -19,6 +19,18 @@ do_cleanup() { fi } +generate_port() { # function to produce a random port number + if [[ "$OSTYPE" == "linux"* ]]; then + port=$(($(od -An -N2 /dev/urandom) % (65535-49152) + 49152)) + elif [[ "$OSTYPE" == "darwin"* ]]; then + port=$(($(od -An -N2 /dev/random) % (65535-49152) + 49152)) + else + echo "Unknown OS TYPE" + exit 1 + fi + echo -e "Using port $port" +} + # Check for application [ ! -x ./examples/multithread/multithread ] && echo -e "\n\nMultithread Client doesn't exist" && exit 1 @@ -34,22 +46,31 @@ def_args="-T -C 2000" # Check for mosquitto if command -v mosquitto then - # bwrap only if using a local mosquitto instance - if [ "${AM_BWRAPPED-}" != "yes" ]; then - bwrap_path="$(command -v bwrap)" - if [ -n "$bwrap_path" ]; then - echo "multithread test using bwrap" + bwrap_path="$(command -v bwrap)" + if [ -n "$bwrap_path" ]; then + # bwrap only if using a local mosquitto instance + if [ "${AM_BWRAPPED-}" != "yes" ]; then + echo "Using bwrap" export AM_BWRAPPED=yes exec "$bwrap_path" --unshare-net --dev-bind / / "$0" "$@" fi + + broker_args="-c scripts/broker_test/mosquitto.conf" + port=11883 + else + # mosquitto broker custom port non-TLS only + has_tls=no + generate_port + broker_args="-p $port" fi - # Run mosquitto broker - mosquitto -c scripts/broker_test/mosquitto.conf & + mosquitto $broker_args & broker_pid=$! echo "Broker PID is $broker_pid" + sleep 0.1 + def_args="${def_args} -h localhost" tls_port_args="-p 18883" - port_args="-p 11883" + port_args="-p ${port}" fi echo -e "Base args: $def_args $port_args" diff --git a/scripts/nbclient.test b/scripts/nbclient.test index e867ddc04..e6a1fc456 100755 --- a/scripts/nbclient.test +++ b/scripts/nbclient.test @@ -19,6 +19,18 @@ do_cleanup() { fi } +generate_port() { # function to produce a random port number + if [[ "$OSTYPE" == "linux"* ]]; then + port=$(($(od -An -N2 /dev/urandom) % (65535-49152) + 49152)) + elif [[ "$OSTYPE" == "darwin"* ]]; then + port=$(($(od -An -N2 /dev/random) % (65535-49152) + 49152)) + else + echo "Unknown OS TYPE" + exit 1 + fi + echo -e "Using port $port" +} + # Check for application [ ! -x ./examples/nbclient/nbclient ] && echo -e "\n\nNon-blocking Client doesn't exist" && exit 1 @@ -36,22 +48,31 @@ def_args="-T -C 2000" # Check for mosquitto if command -v mosquitto then - # bwrap only if using a local mosquitto instance - if [ "${AM_BWRAPPED-}" != "yes" ]; then - bwrap_path="$(command -v bwrap)" - if [ -n "$bwrap_path" ]; then - echo "nbclient test using bwrap" + bwrap_path="$(command -v bwrap)" + if [ -n "$bwrap_path" ]; then + # bwrap only if using a local mosquitto instance + if [ "${AM_BWRAPPED-}" != "yes" ]; then + echo "Using bwrap" export AM_BWRAPPED=yes exec "$bwrap_path" --unshare-net --dev-bind / / "$0" "$@" fi + + broker_args="-c scripts/broker_test/mosquitto.conf" + port=11883 + else + # mosquitto broker custom port non-TLS only + has_tls=no + generate_port + broker_args="-p $port" fi - # Run mosquitto broker - mosquitto -c scripts/broker_test/mosquitto.conf & + mosquitto $broker_args & broker_pid=$! echo "Broker PID is $broker_pid" + sleep 0.1 + def_args="${def_args} -h localhost" tls_port_args="-p 18883" - port_args="-p 11883" + port_args="-p ${port}" fi echo -e "Base args: $def_args $port_args" From da5f1989f7f00954528a1bce0d22954afede5ebe Mon Sep 17 00:00:00 2001 From: David Garske Date: Tue, 21 Nov 2023 15:23:58 -0800 Subject: [PATCH 34/62] Enable firmware test without TLS support. --- examples/firmware/fwclient.c | 151 ++++++++++++++++----------------- examples/firmware/fwpush.c | 156 +++++++++++++++++------------------ examples/firmware/fwpush.h | 4 + scripts/firmware.test | 7 +- 4 files changed, 161 insertions(+), 157 deletions(-) diff --git a/examples/firmware/fwclient.c b/examples/firmware/fwclient.c index b28914ef7..351eaf4b4 100644 --- a/examples/firmware/fwclient.c +++ b/examples/firmware/fwclient.c @@ -36,18 +36,18 @@ /* The signature wrapper for this example was added in wolfSSL after 3.7.1 */ #if defined(LIBWOLFSSL_VERSION_HEX) && LIBWOLFSSL_VERSION_HEX > 0x03007001 \ && defined(HAVE_ECC) && !defined(NO_SIG_WRAPPER) - #undef ENABLE_FIRMWARE_EXAMPLE - #define ENABLE_FIRMWARE_EXAMPLE + #undef ENABLE_FIRMWARE_SIG + #define ENABLE_FIRMWARE_SIG #endif #endif -#if defined(ENABLE_FIRMWARE_EXAMPLE) - +#if defined(ENABLE_FIRMWARE_SIG) #include #include #include #include +#endif #include "fwclient.h" #include "firmware.h" @@ -109,10 +109,12 @@ static int fwfile_save(const char* filePath, byte* fileBuf, int fileLen) static int fw_message_process(MQTTCtx *mqttCtx, byte* buffer, word32 len) { - int rc; + int rc = 0; FirmwareHeader* header = (FirmwareHeader*)buffer; byte *sigBuf, *pubKeyBuf, *fwBuf; +#ifdef ENABLE_FIRMWARE_SIG ecc_key eccKey; +#endif word32 check_len = sizeof(FirmwareHeader) + header->sigLen + header->pubKeyLen + header->fwLen; @@ -129,6 +131,7 @@ static int fw_message_process(MQTTCtx *mqttCtx, byte* buffer, word32 len) fwBuf = (buffer + sizeof(FirmwareHeader) + header->sigLen + header->pubKeyLen); +#ifdef ENABLE_FIRMWARE_SIG /* Import the public key */ wc_ecc_init(&eccKey); rc = wc_ecc_import_x963(pubKeyBuf, header->pubKeyLen, &eccKey); @@ -141,17 +144,22 @@ static int fw_message_process(MQTTCtx *mqttCtx, byte* buffer, word32 len) &eccKey, sizeof(eccKey)); PRINTF("Firmware Signature Verification: %s (%d)", (rc == 0) ? "Pass" : "Fail", rc); - +#else + (void)pubKeyBuf; + (void)sigBuf; +#endif if (rc == 0) { /* TODO: Process firmware image */ /* For example, save to disk using topic name */ fwfile_save(mqttCtx->pub_file, fwBuf, header->fwLen); } +#ifdef ENABLE_FIRMWARE_SIG } else { PRINTF("ECC public key import failed! %d", rc); } wc_ecc_free(&eccKey); +#endif return rc; } @@ -484,89 +492,76 @@ int fwclient_test(MQTTCtx *mqttCtx) return rc; } -#endif /* ENABLE_FIRMWARE_EXAMPLE */ /* so overall tests can pull in test function */ - #ifdef USE_WINDOWS_API - #include /* for ctrl handler */ +#ifdef USE_WINDOWS_API + #include /* for ctrl handler */ - static BOOL CtrlHandler(DWORD fdwCtrlType) - { - if (fdwCtrlType == CTRL_C_EVENT) { - #if defined(ENABLE_FIRMWARE_EXAMPLE) - mStopRead = 1; - #endif - PRINTF("Received Ctrl+c"); - return TRUE; - } - return FALSE; + static BOOL CtrlHandler(DWORD fdwCtrlType) + { + if (fdwCtrlType == CTRL_C_EVENT) { + #if defined(ENABLE_FIRMWARE_SIG) + mStopRead = 1; + #endif + PRINTF("Received Ctrl+c"); + return TRUE; } - #elif HAVE_SIGNAL - #include - static void sig_handler(int signo) - { - if (signo == SIGINT) { - #if defined(ENABLE_FIRMWARE_EXAMPLE) - mStopRead = 1; - #endif - PRINTF("Received SIGINT"); - } + return FALSE; + } +#elif HAVE_SIGNAL + #include + static void sig_handler(int signo) + { + if (signo == SIGINT) { + #if defined(ENABLE_FIRMWARE_SIG) + mStopRead = 1; + #endif + PRINTF("Received SIGINT"); } - #endif + } +#endif #if defined(NO_MAIN_DRIVER) - int fwclient_main(int argc, char** argv) +int fwclient_main(int argc, char** argv) #else - int main(int argc, char** argv) +int main(int argc, char** argv) #endif - { - int rc; - #ifdef ENABLE_FIRMWARE_EXAMPLE - MQTTCtx mqttCtx; - - /* init defaults */ - mqtt_init_ctx(&mqttCtx); - mqttCtx.app_name = "fwclient"; - mqttCtx.client_id = mqtt_append_random(FIRMWARE_CLIIENT_ID, - (word32)XSTRLEN(FIRMWARE_CLIIENT_ID)); - mqttCtx.dynamicClientId = 1; - mqttCtx.topic_name = FIRMWARE_TOPIC_NAME; - mqttCtx.qos = FIRMWARE_MQTT_QOS; - mqttCtx.pub_file = FIRMWARE_DEF_SAVE_AS; - - /* parse arguments */ - rc = mqtt_parse_args(&mqttCtx, argc, argv); - if (rc != 0) { - return rc; - } - #endif - - #ifdef USE_WINDOWS_API - if (SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE) == FALSE) { - PRINTF("Error setting Ctrl Handler! Error %d", (int)GetLastError()); - } - #elif HAVE_SIGNAL - if (signal(SIGINT, sig_handler) == SIG_ERR) { - PRINTF("Can't catch SIGINT"); - } - #endif - - #ifdef ENABLE_FIRMWARE_EXAMPLE - do { - rc = fwclient_test(&mqttCtx); - } while (!mStopRead && rc == MQTT_CODE_CONTINUE); +{ + int rc; + MQTTCtx mqttCtx; + + /* init defaults */ + mqtt_init_ctx(&mqttCtx); + mqttCtx.app_name = "fwclient"; + mqttCtx.client_id = mqtt_append_random(FIRMWARE_CLIIENT_ID, + (word32)XSTRLEN(FIRMWARE_CLIIENT_ID)); + mqttCtx.dynamicClientId = 1; + mqttCtx.topic_name = FIRMWARE_TOPIC_NAME; + mqttCtx.qos = FIRMWARE_MQTT_QOS; + mqttCtx.pub_file = FIRMWARE_DEF_SAVE_AS; + + /* parse arguments */ + rc = mqtt_parse_args(&mqttCtx, argc, argv); + if (rc != 0) { + return rc; + } - mqtt_free_ctx(&mqttCtx); - #else - (void)argc; - (void)argv; +#ifdef USE_WINDOWS_API + if (SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE) == FALSE) { + PRINTF("Error setting Ctrl Handler! Error %d", (int)GetLastError()); + } +#elif HAVE_SIGNAL + if (signal(SIGINT, sig_handler) == SIG_ERR) { + PRINTF("Can't catch SIGINT"); + } +#endif - /* This example requires wolfSSL after 3.7.1 for signature wrapper */ - PRINTF("Example not compiled in!"); - rc = 0; /* return success, so make check passes with TLS disabled */ - #endif + do { + rc = fwclient_test(&mqttCtx); + } while (!mStopRead && rc == MQTT_CODE_CONTINUE); - return (rc == 0) ? 0 : EXIT_FAILURE; - } + mqtt_free_ctx(&mqttCtx); + return (rc == 0) ? 0 : EXIT_FAILURE; +} diff --git a/examples/firmware/fwpush.c b/examples/firmware/fwpush.c index c16bbb77e..7b2ee2ad6 100644 --- a/examples/firmware/fwpush.c +++ b/examples/firmware/fwpush.c @@ -36,18 +36,19 @@ /* The signature wrapper for this example was added in wolfSSL after 3.7.1 */ #if defined(LIBWOLFSSL_VERSION_HEX) && LIBWOLFSSL_VERSION_HEX > 0x03007001 \ && defined(HAVE_ECC) && !defined(NO_SIG_WRAPPER) - #undef ENABLE_FIRMWARE_EXAMPLE - #define ENABLE_FIRMWARE_EXAMPLE + #undef ENABLE_FIRMWARE_SIG + #define ENABLE_FIRMWARE_SIG #endif #endif -#if defined(ENABLE_FIRMWARE_EXAMPLE) +#ifdef ENABLE_FIRMWARE_SIG #include #include #include #include +#endif #include "fwpush.h" #include "firmware.h" @@ -143,10 +144,12 @@ static int fw_message_build(MQTTCtx *mqttCtx, const char* fwFile, int msgLen = 0, fwLen = 0; word32 keyLen = 0, sigLen = 0; FirmwareHeader *header; +#ifdef ENABLE_FIRMWARE_SIG ecc_key eccKey; WC_RNG rng; wc_InitRng(&rng); +#endif /* Verify file can be loaded */ rc = mqtt_file_load(fwFile, &fwBuf, &fwLen); @@ -157,6 +160,7 @@ static int fw_message_build(MQTTCtx *mqttCtx, const char* fwFile, } PRINTF("Firmware File %s is %d bytes", fwFile, fwLen); +#ifdef ENABLE_FIRMWARE_SIG /* Generate Key */ /* Note: Real implementation would use previously exchanged/signed key */ wc_ecc_init(&eccKey); @@ -192,11 +196,13 @@ static int fw_message_build(MQTTCtx *mqttCtx, const char* fwFile, rc = EXIT_FAILURE; goto exit; } +#endif /* Display lengths */ PRINTF("Firmware Message: Sig %d bytes, Key %d bytes, File %d bytes", sigLen, keyLen, fwLen); +#ifdef ENABLE_FIRMWARE_SIG /* Generate Signature */ rc = wc_SignatureGenerate( FIRMWARE_HASH_TYPE, FIRMWARE_SIG_TYPE, @@ -209,6 +215,7 @@ static int fw_message_build(MQTTCtx *mqttCtx, const char* fwFile, rc = EXIT_FAILURE; goto exit; } +#endif /* Assemble message */ msgLen = sizeof(FirmwareHeader) + sigLen + keyLen + fwLen; @@ -225,8 +232,10 @@ static int fw_message_build(MQTTCtx *mqttCtx, const char* fwFile, header->sigLen = sigLen; header->pubKeyLen = keyLen; header->fwLen = fwLen; - XMEMCPY(&msgBuf[sizeof(FirmwareHeader)], sigBuf, sigLen); - XMEMCPY(&msgBuf[sizeof(FirmwareHeader) + sigLen], keyBuf, keyLen); + if (sigLen > 0) + XMEMCPY(&msgBuf[sizeof(FirmwareHeader)], sigBuf, sigLen); + if (keyLen > 0) + XMEMCPY(&msgBuf[sizeof(FirmwareHeader) + sigLen], keyBuf, keyLen); rc = 0; @@ -246,8 +255,10 @@ static int fw_message_build(MQTTCtx *mqttCtx, const char* fwFile, if (sigBuf) WOLFMQTT_FREE(sigBuf); if (fwBuf) WOLFMQTT_FREE(fwBuf); +#ifdef ENABLE_FIRMWARE_SIG wc_ecc_free(&eccKey); wc_FreeRng(&rng); +#endif return rc; } @@ -272,7 +283,7 @@ int fwpush_test(MQTTCtx *mqttCtx) goto disconn; } - switch(mqttCtx->stat) + switch (mqttCtx->stat) { case WMQ_BEGIN: { @@ -522,87 +533,76 @@ int fwpush_test(MQTTCtx *mqttCtx) return rc; } -#endif /* ENABLE_FIRMWARE_EXAMPLE */ /* so overall tests can pull in test function */ -#ifndef NO_MAIN_DRIVER - #ifdef USE_WINDOWS_API - #include /* for ctrl handler */ +#ifdef USE_WINDOWS_API + #include /* for ctrl handler */ - static BOOL CtrlHandler(DWORD fdwCtrlType) - { - if (fdwCtrlType == CTRL_C_EVENT) { - #if defined(ENABLE_FIRMWARE_EXAMPLE) - mStopRead = 1; - #endif - PRINTF("Received Ctrl+c"); - return TRUE; - } - return FALSE; - } - #elif HAVE_SIGNAL - #include - static void sig_handler(int signo) - { - if (signo == SIGINT) { - #if defined(ENABLE_FIRMWARE_EXAMPLE) - mStopRead = 1; - #endif - PRINTF("Received SIGINT"); - } - } - #endif - - int main(int argc, char** argv) + static BOOL CtrlHandler(DWORD fdwCtrlType) { - int rc; - #ifdef ENABLE_FIRMWARE_EXAMPLE - MQTTCtx mqttCtx; - - /* init defaults */ - mqtt_init_ctx(&mqttCtx); - mqttCtx.app_name = "fwpush"; - mqttCtx.client_id = mqtt_append_random(FIRMWARE_PUSH_CLIENT_ID, - (word32)XSTRLEN(FIRMWARE_PUSH_CLIENT_ID)); - mqttCtx.dynamicClientId = 1; - mqttCtx.topic_name = FIRMWARE_TOPIC_NAME; - mqttCtx.qos = FIRMWARE_MQTT_QOS; - mqttCtx.pub_file = FIRMWARE_PUSH_DEF_FILE; - - /* parse arguments */ - rc = mqtt_parse_args(&mqttCtx, argc, argv); - if (rc != 0) { - return rc; + if (fdwCtrlType == CTRL_C_EVENT) { + #if defined(ENABLE_FIRMWARE_SIG) + mStopRead = 1; + #endif + PRINTF("Received Ctrl+c"); + return TRUE; } - #endif - - #ifdef USE_WINDOWS_API - if (SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE) == FALSE) { - PRINTF("Error setting Ctrl Handler! Error %d", (int)GetLastError()); - } - #elif HAVE_SIGNAL - if (signal(SIGINT, sig_handler) == SIG_ERR) { - PRINTF("Can't catch SIGINT"); + return FALSE; + } +#elif HAVE_SIGNAL + #include + static void sig_handler(int signo) + { + if (signo == SIGINT) { + #if defined(ENABLE_FIRMWARE_SIG) + mStopRead = 1; + #endif + PRINTF("Received SIGINT"); } - #endif + } +#endif - #ifdef ENABLE_FIRMWARE_EXAMPLE - do { - rc = fwpush_test(&mqttCtx); - } while (!mStopRead && rc == MQTT_CODE_CONTINUE); +#if defined(NO_MAIN_DRIVER) +int fwpush_main(int argc, char** argv) +#else +int main(int argc, char** argv) +#endif +{ + int rc; + MQTTCtx mqttCtx; + + /* init defaults */ + mqtt_init_ctx(&mqttCtx); + mqttCtx.app_name = "fwpush"; + mqttCtx.client_id = mqtt_append_random(FIRMWARE_PUSH_CLIENT_ID, + (word32)XSTRLEN(FIRMWARE_PUSH_CLIENT_ID)); + mqttCtx.dynamicClientId = 1; + mqttCtx.topic_name = FIRMWARE_TOPIC_NAME; + mqttCtx.qos = FIRMWARE_MQTT_QOS; + mqttCtx.pub_file = FIRMWARE_PUSH_DEF_FILE; + + /* parse arguments */ + rc = mqtt_parse_args(&mqttCtx, argc, argv); + if (rc != 0) { + return rc; + } - mqtt_free_ctx(&mqttCtx); - #else - (void)argc; - (void)argv; +#ifdef USE_WINDOWS_API + if (SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE) == FALSE) { + PRINTF("Error setting Ctrl Handler! Error %d", (int)GetLastError()); + } +#elif HAVE_SIGNAL + if (signal(SIGINT, sig_handler) == SIG_ERR) { + PRINTF("Can't catch SIGINT"); + } +#endif - /* This example requires wolfSSL after 3.7.1 for signature wrapper */ - PRINTF("Example not compiled in!"); - rc = 0; /* return success, so make check passes with TLS disabled */ - #endif + do { + rc = fwpush_test(&mqttCtx); + } while (!mStopRead && rc == MQTT_CODE_CONTINUE); - return (rc == 0) ? 0 : EXIT_FAILURE; - } + mqtt_free_ctx(&mqttCtx); -#endif /* NO_MAIN_DRIVER */ + return (rc == 0) ? 0 : EXIT_FAILURE; +} diff --git a/examples/firmware/fwpush.h b/examples/firmware/fwpush.h index 270b593bc..3230652de 100644 --- a/examples/firmware/fwpush.h +++ b/examples/firmware/fwpush.h @@ -38,4 +38,8 @@ typedef struct FwpushCBdata_s { /* Exposed functions */ int fwpush_test(MQTTCtx *mqttCtx); +#if defined(NO_MAIN_DRIVER) +int fwpush_main(int argc, char** argv); +#endif + #endif /* WOLFMQTT_FWPUSH_H */ diff --git a/scripts/firmware.test b/scripts/firmware.test index 8e0348ada..92bd646e5 100755 --- a/scripts/firmware.test +++ b/scripts/firmware.test @@ -64,7 +64,12 @@ then fi broker_args="-c scripts/broker_test/mosquitto.conf" - port=11883 + if test $has_tls == yes + then + port=18883 + else + port=11883 + fi else # mosquitto broker custom port non-TLS only has_tls=no From 2f7506a8ca70d7a91cc66c7e0a7821330bf7a31a Mon Sep 17 00:00:00 2001 From: David Garske Date: Tue, 21 Nov 2023 16:08:54 -0800 Subject: [PATCH 35/62] More logs on failure/timeout. --- .github/workflows/fsanitize-check.yml | 1 + .github/workflows/macos-check.yml | 1 + .github/workflows/ubuntu-check.yml | 1 + scripts/client.test | 1 + scripts/firmware.test | 1 + scripts/multithread.test | 1 + scripts/nbclient.test | 1 + 7 files changed, 7 insertions(+) diff --git a/.github/workflows/fsanitize-check.yml b/.github/workflows/fsanitize-check.yml index cb78fcb4e..c334c5328 100644 --- a/.github/workflows/fsanitize-check.yml +++ b/.github/workflows/fsanitize-check.yml @@ -96,3 +96,4 @@ jobs: if: failure() || cancelled() run: | cat test-suite.log + cat scripts/*.log diff --git a/.github/workflows/macos-check.yml b/.github/workflows/macos-check.yml index a0b129074..7732f98a0 100644 --- a/.github/workflows/macos-check.yml +++ b/.github/workflows/macos-check.yml @@ -82,3 +82,4 @@ jobs: if: failure() || cancelled() run: | cat test-suite.log + cat scripts/*.log diff --git a/.github/workflows/ubuntu-check.yml b/.github/workflows/ubuntu-check.yml index e9366b0a9..f09b8b57b 100644 --- a/.github/workflows/ubuntu-check.yml +++ b/.github/workflows/ubuntu-check.yml @@ -113,3 +113,4 @@ jobs: if: failure() || cancelled() run: | cat test-suite.log + cat scripts/*.log diff --git a/scripts/client.test b/scripts/client.test index d31ca0e8c..e86752c26 100755 --- a/scripts/client.test +++ b/scripts/client.test @@ -55,6 +55,7 @@ then export AM_BWRAPPED=yes exec "$bwrap_path" --unshare-net --dev-bind / / "$0" "$@" fi + unset AM_BWRAPPED broker_args="-c scripts/broker_test/mosquitto.conf" port=11883 diff --git a/scripts/firmware.test b/scripts/firmware.test index 92bd646e5..13d743b9e 100755 --- a/scripts/firmware.test +++ b/scripts/firmware.test @@ -62,6 +62,7 @@ then export AM_BWRAPPED=yes exec "$bwrap_path" --unshare-net --dev-bind / / "$0" "$@" fi + unset AM_BWRAPPED broker_args="-c scripts/broker_test/mosquitto.conf" if test $has_tls == yes diff --git a/scripts/multithread.test b/scripts/multithread.test index 7b1f6326e..0f382e717 100755 --- a/scripts/multithread.test +++ b/scripts/multithread.test @@ -54,6 +54,7 @@ then export AM_BWRAPPED=yes exec "$bwrap_path" --unshare-net --dev-bind / / "$0" "$@" fi + unset AM_BWRAPPED broker_args="-c scripts/broker_test/mosquitto.conf" port=11883 diff --git a/scripts/nbclient.test b/scripts/nbclient.test index e6a1fc456..9c15f786b 100755 --- a/scripts/nbclient.test +++ b/scripts/nbclient.test @@ -56,6 +56,7 @@ then export AM_BWRAPPED=yes exec "$bwrap_path" --unshare-net --dev-bind / / "$0" "$@" fi + unset AM_BWRAPPED broker_args="-c scripts/broker_test/mosquitto.conf" port=11883 From f5b05e44a31f32185e53f6cb171949ab220dd73e Mon Sep 17 00:00:00 2001 From: David Garske Date: Tue, 21 Nov 2023 16:35:31 -0800 Subject: [PATCH 36/62] Don't use non-block test build option unless TLS is enabled. Not all examples are setup to support non-blocking. --- .github/workflows/fsanitize-check.yml | 2 +- .github/workflows/macos-check.yml | 2 +- .github/workflows/ubuntu-check.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/fsanitize-check.yml b/.github/workflows/fsanitize-check.yml index c334c5328..fdba323c9 100644 --- a/.github/workflows/fsanitize-check.yml +++ b/.github/workflows/fsanitize-check.yml @@ -64,7 +64,7 @@ jobs: run: make check - name: configure without TLS - run: ./configure CC="gcc -fsanitize=address" --enable-all --disable-tls CFLAGS="-DWOLFMQTT_TEST_NONBLOCK" + run: ./configure CC="gcc -fsanitize=address" --enable-all --disable-tls - name: make run: make - name: make check diff --git a/.github/workflows/macos-check.yml b/.github/workflows/macos-check.yml index 7732f98a0..18c724a22 100644 --- a/.github/workflows/macos-check.yml +++ b/.github/workflows/macos-check.yml @@ -50,7 +50,7 @@ jobs: run: make check - name: configure without TLS - run: ./configure --enable-all --disable-tls CFLAGS="-DWOLFMQTT_TEST_NONBLOCK" + run: ./configure --enable-all --disable-tls - name: make run: make - name: make check diff --git a/.github/workflows/ubuntu-check.yml b/.github/workflows/ubuntu-check.yml index f09b8b57b..5f6bef603 100644 --- a/.github/workflows/ubuntu-check.yml +++ b/.github/workflows/ubuntu-check.yml @@ -66,7 +66,7 @@ jobs: - name: wolfmqtt configure without TLS env: WOLFMQTT_NO_EXTERNAL_BROKER_TESTS: 1 - run: ./configure --enable-all --disable-tls CFLAGS="-DWOLFMQTT_TEST_NONBLOCK" + run: ./configure --enable-all --disable-tls - name: wolfmqtt make run: make - name: wolfmqtt make check From eae8e4798cb6c0514ca0509e4f8d0c05056dd3c9 Mon Sep 17 00:00:00 2001 From: jordan Date: Wed, 22 Nov 2023 10:45:46 -0600 Subject: [PATCH 37/62] Cleanup executable status on src files. --- examples/multithread/multithread.c | 0 examples/wiot/wiot.h | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 examples/multithread/multithread.c mode change 100755 => 100644 examples/wiot/wiot.h diff --git a/examples/multithread/multithread.c b/examples/multithread/multithread.c old mode 100755 new mode 100644 diff --git a/examples/wiot/wiot.h b/examples/wiot/wiot.h old mode 100755 new mode 100644 From 25e27e6698a3dd36985745722e0a3c1a97909af4 Mon Sep 17 00:00:00 2001 From: David Garske Date: Wed, 22 Nov 2023 13:01:51 -0800 Subject: [PATCH 38/62] Fix for continue during variable part of the header. A continue in the middle of variable would not correctly process for large messages. --- src/mqtt_packet.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/mqtt_packet.c b/src/mqtt_packet.c index 1b9fd0008..5f105ab3d 100644 --- a/src/mqtt_packet.c +++ b/src/mqtt_packet.c @@ -1984,7 +1984,9 @@ int MqttPacket_Read(MqttClient *client, byte* rx_buf, int rx_buf_len, int i; client->packet.stat = MQTT_PK_READ_HEAD; - for (i = 0; i < MQTT_PACKET_MAX_LEN_BYTES; i++) { + for (i = (client->packet.header_len - MQTT_PACKET_HEADER_MIN_SIZE); + i < MQTT_PACKET_MAX_LEN_BYTES; + i++) { /* Check if another byte is needed */ if ((header->len[i] & MQTT_PACKET_LEN_ENCODE_MASK) == 0) { /* Variable byte length can be determined */ From 8e86413e91212017b84882c50223aa8dff994b2e Mon Sep 17 00:00:00 2001 From: David Garske Date: Wed, 22 Nov 2023 13:02:02 -0800 Subject: [PATCH 39/62] Further improve WOLFMQTT_TEST_NONBLOCK. --- src/mqtt_socket.c | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/mqtt_socket.c b/src/mqtt_socket.c index 299577e80..c5700ca4e 100644 --- a/src/mqtt_socket.c +++ b/src/mqtt_socket.c @@ -64,6 +64,18 @@ int MqttSocket_TlsSocketReceive(WOLFSSL* ssl, char *buf, int sz, MqttClient *client = (MqttClient*)ptr; (void)ssl; /* Not used */ +#if defined(WOLFMQTT_NONBLOCK) && defined(WOLFMQTT_TEST_NONBLOCK) + static int testSmallerTlsRead = 0; + if (!testSmallerTlsRead) { + if (sz > 2) + sz /= 2; + testSmallerTlsRead = 1; + } + else { + testSmallerTlsRead = 0; + } +#endif + rc = client->net->read(client->net->context, (byte*)buf, sz, client->tls.timeout_ms); @@ -87,6 +99,18 @@ int MqttSocket_TlsSocketSend(WOLFSSL* ssl, char *buf, int sz, MqttClient *client = (MqttClient*)ptr; (void)ssl; /* Not used */ +#if defined(WOLFMQTT_NONBLOCK) && defined(WOLFMQTT_TEST_NONBLOCK) + static int testSmallerTlsWrite = 0; + if (!testSmallerTlsWrite) { + if (sz > 2) + sz /= 2; + testSmallerTlsWrite = 1; + } + else { + testSmallerTlsWrite = 0; + } +#endif + rc = client->net->write(client->net->context, (byte*)buf, sz, client->tls.timeout_ms); @@ -173,7 +197,7 @@ static int MqttSocket_WriteDo(MqttClient *client, const byte* buf, int buf_len, #if defined(WOLFMQTT_NONBLOCK) && defined(WOLFMQTT_TEST_NONBLOCK) static int testSmallerWrite = 0; if (!testSmallerWrite) { - if (buf_len > 1) + if (buf_len > 2) buf_len /= 2; testSmallerWrite = 1; } @@ -299,7 +323,7 @@ static int MqttSocket_ReadDo(MqttClient *client, byte* buf, int buf_len, #if defined(WOLFMQTT_NONBLOCK) && defined(WOLFMQTT_TEST_NONBLOCK) static int testSmallerRead = 0; if (!testSmallerRead) { - if (buf_len > 1) + if (buf_len > 2) buf_len /= 2; testSmallerRead = 1; } From 97cb820562e5a3e189bc71c737ab365333a1797b Mon Sep 17 00:00:00 2001 From: David Garske Date: Wed, 22 Nov 2023 13:02:14 -0800 Subject: [PATCH 40/62] Fixes for using the `-f [file]` option with examples. Allow build-time override of `MAX_BUFFER_SIZE` in examples. --- examples/aws/awsiot.c | 2 ++ examples/azure/azureiothub.c | 2 ++ examples/firmware/fwclient.c | 2 ++ examples/firmware/fwpush.c | 2 ++ examples/mqttclient/mqttclient.c | 4 ++++ examples/multithread/multithread.c | 2 ++ examples/nbclient/nbclient.c | 4 ++++ examples/pub-sub/mqtt-pub.c | 6 +++++- examples/pub-sub/mqtt-sub.c | 4 +++- examples/sn-client/sn-client.c | 12 +++++++----- examples/sn-client/sn-client_qos-1.c | 2 ++ examples/sn-client/sn-multithread.c | 2 ++ examples/wiot/wiot.c | 2 ++ 13 files changed, 39 insertions(+), 7 deletions(-) diff --git a/examples/aws/awsiot.c b/examples/aws/awsiot.c index cd77c3807..09c3a05ad 100644 --- a/examples/aws/awsiot.c +++ b/examples/aws/awsiot.c @@ -60,7 +60,9 @@ static int mTestDone = 0; #define APP_HARDWARE "wolf_aws_iot_demo" #define APP_FIRMWARE_VERSION LIBWOLFMQTT_VERSION_STRING +#ifndef MAX_BUFFER_SIZE #define MAX_BUFFER_SIZE 512 /* Maximum size for network read/write callbacks */ +#endif #define AWSIOT_HOST "a2dujmi05ideo2-ats.iot.us-west-2.amazonaws.com" #define AWSIOT_DEVICE_ID "demoDevice" #define AWSIOT_QOS MQTT_QOS_1 diff --git a/examples/azure/azureiothub.c b/examples/azure/azureiothub.c index 463e67256..dc8205b65 100644 --- a/examples/azure/azureiothub.c +++ b/examples/azure/azureiothub.c @@ -76,7 +76,9 @@ static int mTestDone = 0; * https://azure.microsoft.com/en-us/documentation/articles/iot-hub-sas-tokens/#using-sas-tokens-as-a-device * https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-mqtt-support */ +#ifndef MAX_BUFFER_SIZE #define MAX_BUFFER_SIZE 1024 /* Maximum size for network read/write callbacks */ +#endif #define AZURE_API_VERSION "?api-version=2018-06-30" #define AZURE_HOST "wolfMQTT.azure-devices.net" #define AZURE_DEVICE_ID "demoDevice" diff --git a/examples/firmware/fwclient.c b/examples/firmware/fwclient.c index 351eaf4b4..22259fd8d 100644 --- a/examples/firmware/fwclient.c +++ b/examples/firmware/fwclient.c @@ -55,7 +55,9 @@ #include "examples/mqttnet.h" /* Configuration */ +#ifndef MAX_BUFFER_SIZE #define MAX_BUFFER_SIZE FIRMWARE_MAX_PACKET +#endif /* Locals */ static int mStopRead = 0; diff --git a/examples/firmware/fwpush.c b/examples/firmware/fwpush.c index 7b2ee2ad6..676ee95e7 100644 --- a/examples/firmware/fwpush.c +++ b/examples/firmware/fwpush.c @@ -56,7 +56,9 @@ #include "examples/mqttnet.h" /* Configuration */ +#ifndef MAX_BUFFER_SIZE #define MAX_BUFFER_SIZE FIRMWARE_MAX_PACKET +#endif /* Locals */ static int mStopRead = 0; diff --git a/examples/mqttclient/mqttclient.c b/examples/mqttclient/mqttclient.c index 8c65813a2..e390b94b7 100644 --- a/examples/mqttclient/mqttclient.c +++ b/examples/mqttclient/mqttclient.c @@ -34,7 +34,9 @@ static int mStopRead = 0; /* Maximum size for network read/write callbacks. There is also a v5 define that describes the max MQTT control packet size, DEFAULT_MAX_PKT_SZ. */ +#ifndef MAX_BUFFER_SIZE #define MAX_BUFFER_SIZE 1024 +#endif #ifdef WOLFMQTT_PROPERTY_CB #define MAX_CLIENT_ID_LEN 64 @@ -504,6 +506,8 @@ int mqttclient_test(MQTTCtx *mqttCtx) if ((mqttCtx->pub_file) && (mqttCtx->publish.buffer)) { WOLFMQTT_FREE(mqttCtx->publish.buffer); + mqttCtx->publish.buffer = NULL; + mqttCtx->pub_file = NULL; /* don't try and send file again */ } PRINTF("MQTT Publish: Topic %s, %s (%d)", diff --git a/examples/multithread/multithread.c b/examples/multithread/multithread.c index 4114d4549..da4939802 100644 --- a/examples/multithread/multithread.c +++ b/examples/multithread/multithread.c @@ -42,7 +42,9 @@ /* Maximum size for network read/write callbacks. There is also a v5 define that describes the max MQTT control packet size, DEFAULT_MAX_PKT_SZ. */ +#ifndef MAX_BUFFER_SIZE #define MAX_BUFFER_SIZE 1024 +#endif /* Total size of test message to build */ #define TEST_MESSAGE_SIZE 1048 /* span more than one max packet */ diff --git a/examples/nbclient/nbclient.c b/examples/nbclient/nbclient.c index cf337310d..ed438aa89 100644 --- a/examples/nbclient/nbclient.c +++ b/examples/nbclient/nbclient.c @@ -38,7 +38,9 @@ static int mTestDone = 0; /* Configuration */ /* Maximum size for network read/write callbacks. */ +#ifndef MAX_BUFFER_SIZE #define MAX_BUFFER_SIZE 1024 +#endif #ifdef WOLFMQTT_PROPERTY_CB #define MAX_CLIENT_ID_LEN 64 @@ -444,6 +446,8 @@ int mqttclient_test(MQTTCtx *mqttCtx) if ((mqttCtx->pub_file) && (mqttCtx->publish.buffer)) { WOLFMQTT_FREE(mqttCtx->publish.buffer); + mqttCtx->publish.buffer = NULL; + mqttCtx->pub_file = NULL; /* don't try and send file again */ } PRINTF("MQTT Publish: Topic %s, %s (%d)", diff --git a/examples/pub-sub/mqtt-pub.c b/examples/pub-sub/mqtt-pub.c index 467fccee5..b6d220d39 100644 --- a/examples/pub-sub/mqtt-pub.c +++ b/examples/pub-sub/mqtt-pub.c @@ -30,8 +30,10 @@ /* Configuration */ /* Maximum size for network read/write callbacks. There is also a v5 define that - describes the max MQTT control packet size, DEFAULT_MAX_PKT_SZ. */ + * describes the max MQTT control packet size, DEFAULT_MAX_PKT_SZ. */ +#ifndef MAX_BUFFER_SIZE #define MAX_BUFFER_SIZE 1024 +#endif #ifdef WOLFMQTT_PROPERTY_CB #define MAX_CLIENT_ID_LEN 64 @@ -381,6 +383,8 @@ int pub_client(MQTTCtx *mqttCtx) if ((mqttCtx->pub_file) && (mqttCtx->publish.buffer)) { WOLFMQTT_FREE(mqttCtx->publish.buffer); + mqttCtx->publish.buffer = NULL; + mqttCtx->pub_file = NULL; /* don't try and send file again */ } if (mqttCtx->debug_on) { PRINTF("MQTT Publish: Topic %s, %s (%d)", diff --git a/examples/pub-sub/mqtt-sub.c b/examples/pub-sub/mqtt-sub.c index cbace383c..84d791e10 100644 --- a/examples/pub-sub/mqtt-sub.c +++ b/examples/pub-sub/mqtt-sub.c @@ -33,8 +33,10 @@ static int mStopRead = 0; /* Configuration */ /* Maximum size for network read/write callbacks. There is also a v5 define that - describes the max MQTT control packet size, DEFAULT_MAX_PKT_SZ. */ + * describes the max MQTT control packet size, DEFAULT_MAX_PKT_SZ. */ +#ifndef MAX_BUFFER_SIZE #define MAX_BUFFER_SIZE 1024 +#endif #ifdef WOLFMQTT_PROPERTY_CB #define MAX_CLIENT_ID_LEN 64 diff --git a/examples/sn-client/sn-client.c b/examples/sn-client/sn-client.c index e60c1f5af..15b5a9bf1 100644 --- a/examples/sn-client/sn-client.c +++ b/examples/sn-client/sn-client.c @@ -36,7 +36,9 @@ static int mStopRead = 0; /* Configuration */ /* Maximum size for network read/write callbacks. */ +#ifndef MAX_BUFFER_SIZE #define MAX_BUFFER_SIZE 1024 +#endif #define TEST_MESSAGE "test" #define SHORT_TOPIC_NAME "s1" @@ -215,11 +217,11 @@ int sn_test(MQTTCtx *mqttCtx) /* Send Connect and wait for Connect Ack */ rc = SN_Client_Connect(&mqttCtx->client, connect); - if (rc != MQTT_CODE_SUCCESS) { - PRINTF("MQTT-SN Connect: %s (%d)", - MqttClient_ReturnCodeToString(rc), rc); - goto disconn; - } + if (rc != MQTT_CODE_SUCCESS) { + PRINTF("MQTT-SN Connect: %s (%d)", + MqttClient_ReturnCodeToString(rc), rc); + goto disconn; + } /* Validate Connect Ack info */ PRINTF("....MQTT-SN Connect Ack: Return Code %u", diff --git a/examples/sn-client/sn-client_qos-1.c b/examples/sn-client/sn-client_qos-1.c index cee87110a..ed57b09b2 100644 --- a/examples/sn-client/sn-client_qos-1.c +++ b/examples/sn-client/sn-client_qos-1.c @@ -40,7 +40,9 @@ static int mStopRead = 0; /* Configuration */ /* Maximum size for network read/write callbacks. */ +#ifndef MAX_BUFFER_SIZE #define MAX_BUFFER_SIZE 1024 +#endif #define TEST_MESSAGE "QoS-1 test message" char SHORT_TOPIC_NAME[] = {1}; diff --git a/examples/sn-client/sn-multithread.c b/examples/sn-client/sn-multithread.c index 4e7d934ee..7ca310201 100644 --- a/examples/sn-client/sn-multithread.c +++ b/examples/sn-client/sn-multithread.c @@ -36,7 +36,9 @@ /* Configuration */ /* Maximum size for network read/write callbacks. */ +#ifndef MAX_BUFFER_SIZE #define MAX_BUFFER_SIZE 1024 +#endif #define TEST_MESSAGE "test00" /* Number of publish tasks. Each will send a unique message to the broker. */ #define NUM_PUB_TASKS 10 diff --git a/examples/wiot/wiot.c b/examples/wiot/wiot.c index 9c4e59da8..96b6348cc 100644 --- a/examples/wiot/wiot.c +++ b/examples/wiot/wiot.c @@ -41,7 +41,9 @@ static int mStopRead = 0; static int mTestDone = 0; /* Configuration */ +#ifndef MAX_BUFFER_SIZE #define MAX_BUFFER_SIZE 1024 /* Maximum size for network read/write callbacks */ +#endif /* Undefine if using an IBM WIOT Platform account that you created. */ #define WIOT_USE_QUICKSTART From dd9973c3d61028136af04340f8a1273d52e7e5f5 Mon Sep 17 00:00:00 2001 From: David Garske Date: Fri, 24 Nov 2023 08:24:33 -0800 Subject: [PATCH 41/62] Move the test non-block code into the network callbacks, which know if the non-blocking is allowed. --- examples/mqttnet.c | 52 ++++++++++++++++++++++++++++++++++ src/mqtt_socket.c | 70 ---------------------------------------------- 2 files changed, 52 insertions(+), 70 deletions(-) diff --git a/examples/mqttnet.c b/examples/mqttnet.c index acd9d3340..0a73a2f30 100644 --- a/examples/mqttnet.c +++ b/examples/mqttnet.c @@ -32,6 +32,10 @@ typedef struct MulticastCtx { } MulticastCtx; #endif +#ifndef WOLFMQTT_TEST_NONBLOCK_TIMES +#define WOLFMQTT_TEST_NONBLOCK_TIMES 1 +#endif + /* Private functions */ /* -------------------------------------------------------------------------- */ @@ -643,11 +647,16 @@ static int NetWrite(void *context, const byte* buf, int buf_len, int timeout_ms) { SocketContext *sock = (SocketContext*)context; + MQTTCtx* mqttCtx; int rc; SOERROR_T so_error = 0; #ifndef WOLFMQTT_NO_TIMEOUT struct timeval tv; #endif +#if defined(WOLFMQTT_NONBLOCK) && defined(WOLFMQTT_TEST_NONBLOCK) + static int testNbWriteAlt = 0; + static int testSmallerWrite = 0; +#endif if (context == NULL || buf == NULL || buf_len <= 0) { return MQTT_CODE_ERROR_BAD_ARG; @@ -656,6 +665,27 @@ static int NetWrite(void *context, const byte* buf, int buf_len, if (sock->fd == SOCKET_INVALID) return MQTT_CODE_ERROR_BAD_ARG; + mqttCtx = sock->mqttCtx; + (void)mqttCtx; + +#if defined(WOLFMQTT_NONBLOCK) && defined(WOLFMQTT_TEST_NONBLOCK) + if (mqttCtx->useNonBlockMode) { + if (testNbWriteAlt < WOLFMQTT_TEST_NONBLOCK_TIMES) { + testNbWriteAlt++; + return MQTT_CODE_CONTINUE; + } + testNbWriteAlt = 0; + if (!testSmallerWrite) { + if (buf_len > 2) + buf_len /= 2; + testSmallerWrite = 1; + } + else { + testSmallerWrite = 0; + } + } +#endif + #ifndef WOLFMQTT_NO_TIMEOUT /* Setup timeout */ setup_timeout(&tv, timeout_ms); @@ -706,6 +736,10 @@ static int NetRead_ex(void *context, byte* buf, int buf_len, fd_set errfds; struct timeval tv; #endif +#if defined(WOLFMQTT_NONBLOCK) && defined(WOLFMQTT_TEST_NONBLOCK) + static int testNbReadAlt = 0; + static int testSmallerRead = 0; +#endif if (context == NULL || buf == NULL || buf_len <= 0) { return MQTT_CODE_ERROR_BAD_ARG; @@ -721,6 +755,24 @@ static int NetRead_ex(void *context, byte* buf, int buf_len, mqttCtx = sock->mqttCtx; (void)mqttCtx; +#if defined(WOLFMQTT_NONBLOCK) && defined(WOLFMQTT_TEST_NONBLOCK) + if (mqttCtx->useNonBlockMode) { + if (testNbReadAlt < WOLFMQTT_TEST_NONBLOCK_TIMES) { + testNbReadAlt++; + return MQTT_CODE_CONTINUE; + } + testNbReadAlt = 0; + if (!testSmallerRead) { + if (buf_len > 2) + buf_len /= 2; + testSmallerRead = 1; + } + else { + testSmallerRead = 0; + } + } +#endif + #ifndef WOLFMQTT_NO_TIMEOUT /* Setup timeout */ setup_timeout(&tv, timeout_ms); diff --git a/src/mqtt_socket.c b/src/mqtt_socket.c index c5700ca4e..7fa70caf9 100644 --- a/src/mqtt_socket.c +++ b/src/mqtt_socket.c @@ -42,11 +42,6 @@ #undef WOLFMQTT_DEBUG_SOCKET #endif -/* #define WOLFMQTT_TEST_NONBLOCK */ -#ifdef WOLFMQTT_TEST_NONBLOCK - #define WOLFMQTT_TEST_NONBLOCK_TIMES 1 -#endif - /* lwip */ #ifdef WOLFSSL_LWIP #undef read @@ -64,18 +59,6 @@ int MqttSocket_TlsSocketReceive(WOLFSSL* ssl, char *buf, int sz, MqttClient *client = (MqttClient*)ptr; (void)ssl; /* Not used */ -#if defined(WOLFMQTT_NONBLOCK) && defined(WOLFMQTT_TEST_NONBLOCK) - static int testSmallerTlsRead = 0; - if (!testSmallerTlsRead) { - if (sz > 2) - sz /= 2; - testSmallerTlsRead = 1; - } - else { - testSmallerTlsRead = 0; - } -#endif - rc = client->net->read(client->net->context, (byte*)buf, sz, client->tls.timeout_ms); @@ -99,18 +82,6 @@ int MqttSocket_TlsSocketSend(WOLFSSL* ssl, char *buf, int sz, MqttClient *client = (MqttClient*)ptr; (void)ssl; /* Not used */ -#if defined(WOLFMQTT_NONBLOCK) && defined(WOLFMQTT_TEST_NONBLOCK) - static int testSmallerTlsWrite = 0; - if (!testSmallerTlsWrite) { - if (sz > 2) - sz /= 2; - testSmallerTlsWrite = 1; - } - else { - testSmallerTlsWrite = 0; - } -#endif - rc = client->net->write(client->net->context, (byte*)buf, sz, client->tls.timeout_ms); @@ -153,15 +124,6 @@ static int MqttSocket_WriteDo(MqttClient *client, const byte* buf, int buf_len, { int rc; -#if defined(WOLFMQTT_NONBLOCK) && defined(WOLFMQTT_TEST_NONBLOCK) - static int testNbWriteAlt = 0; - if (testNbWriteAlt < WOLFMQTT_TEST_NONBLOCK_TIMES) { - testNbWriteAlt++; - return MQTT_CODE_CONTINUE; - } - testNbWriteAlt = 0; -#endif - #ifdef ENABLE_MQTT_TLS if (MqttClient_Flags(client,0,0) & MQTT_CLIENT_FLAG_IS_TLS) { client->tls.timeout_ms = timeout_ms; @@ -194,18 +156,6 @@ static int MqttSocket_WriteDo(MqttClient *client, const byte* buf, int buf_len, else #endif /* ENABLE_MQTT_TLS */ { - #if defined(WOLFMQTT_NONBLOCK) && defined(WOLFMQTT_TEST_NONBLOCK) - static int testSmallerWrite = 0; - if (!testSmallerWrite) { - if (buf_len > 2) - buf_len /= 2; - testSmallerWrite = 1; - } - else { - testSmallerWrite = 0; - } - #endif - rc = client->net->write(client->net->context, buf, buf_len, timeout_ms); } @@ -276,15 +226,6 @@ static int MqttSocket_ReadDo(MqttClient *client, byte* buf, int buf_len, { int rc; -#if defined(WOLFMQTT_NONBLOCK) && defined(WOLFMQTT_TEST_NONBLOCK) - static int testNbReadAlt = 0; - if (testNbReadAlt < WOLFMQTT_TEST_NONBLOCK_TIMES) { - testNbReadAlt++; - return MQTT_CODE_CONTINUE; - } - testNbReadAlt = 0; -#endif - #ifdef ENABLE_MQTT_TLS if (MqttClient_Flags(client,0,0) & MQTT_CLIENT_FLAG_IS_TLS) { client->tls.timeout_ms = timeout_ms; @@ -320,17 +261,6 @@ static int MqttSocket_ReadDo(MqttClient *client, byte* buf, int buf_len, else #endif /* ENABLE_MQTT_TLS */ { - #if defined(WOLFMQTT_NONBLOCK) && defined(WOLFMQTT_TEST_NONBLOCK) - static int testSmallerRead = 0; - if (!testSmallerRead) { - if (buf_len > 2) - buf_len /= 2; - testSmallerRead = 1; - } - else { - testSmallerRead = 0; - } - #endif rc = client->net->read(client->net->context, buf, buf_len, timeout_ms); } From 072f5b89ab77c783dcdb1cf73bbb57569d3eaa2b Mon Sep 17 00:00:00 2001 From: David Garske Date: Fri, 24 Nov 2023 08:56:46 -0800 Subject: [PATCH 42/62] By default keep mutex locked if we tried to write. The wolfSSL TLS engine requires an SSL_Write that returns WANT_WRITE to be called with the same buffer/sz, not a different one, even if no data was sent. If user wants to enable the feature anyways they can use `WOLFMQTT_ALLOW_NODATA_UNLOCK`. Only the write that has this logic as the issue doesn't exist for an SSL_Read. --- src/mqtt_client.c | 80 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 68 insertions(+), 12 deletions(-) diff --git a/src/mqtt_client.c b/src/mqtt_client.c index f284b0207..badcfdea4 100644 --- a/src/mqtt_client.c +++ b/src/mqtt_client.c @@ -26,6 +26,32 @@ #include "wolfmqtt/mqtt_client.h" +/* DOCUMENTED BUILD OPTIONS: + * + * WOLFMQTT_MULTITHREAD: Enables multi-thread support with mutex protection on + * client struct, write and read. When a pending response is needed its added + * to a linked list and if another thread reads the expected response it is + * flagged, so the other thread knows it completed. + * + * WOLFMQTT_NONBLOCK: Enabled transport support for returning WANT READ/WRITE, + * which becomes WOLFMQTT_CODE_CONTINUE. This prevents blocking if the + * transport (socket) has no data. + * + * WOLFMQTT_V5: Enables MQTT v5.0 support + * + * WOLFMQTT_ALLOW_NODATA_UNLOCK: Used with multi-threading and non-blocking to + * allow unlock if no data was sent/received. Note the TLS stack typically + * requires an attempt to write to continue with same write, not different. + * By default if we attempt a write we keep the mutex locked and return + * MQTT_CODE_CONTINUE + * + * WOLFMQTT_USER_THREADING: Allows custom mutex functions to be defined by the + * user. Example: wm_SemInit + * + * WOLFMQTT_DEBUG_CLIENT: Enables verbose PRINTF for the client code. + */ + + /* Private functions */ /* forward declarations */ @@ -221,7 +247,7 @@ static int MqttReadStart(MqttClient* client, MqttMsgStat* stat) MQTT_TRACE_MSG("Warning, recv already locked!"); rc = MQTT_CODE_ERROR_SYSTEM; } - /* detect if a write is already in progress */ + /* detect if a read is already in progress */ if (wm_SemLock(&client->lockClient) == 0) { if (client->read.total > 0) { MQTT_TRACE_MSG("Partial read in progress!"); @@ -692,7 +718,8 @@ static int MqttClient_DecodePacket(MqttClient* client, byte* rx_buf, #ifdef WOLFMQTT_DISCONNECT_CB /* Call disconnect callback with reason code */ if ((packet_obj != NULL) && client->disconnect_cb) { - client->disconnect_cb(client, p_disc->reason_code, client->disconnect_ctx); + client->disconnect_cb(client, p_disc->reason_code, + client->disconnect_ctx); } #endif #else @@ -1087,8 +1114,9 @@ static int MqttClient_WaitType(MqttClient *client, void *packet_obj, if (rc <= 0) { #ifdef WOLFMQTT_NONBLOCK if (rc == MQTT_CODE_CONTINUE && - (client->packet.stat > MQTT_PK_BEGIN || - client->read.total > 0)) { + (client->packet.stat > MQTT_PK_BEGIN || + client->read.total > 0) + ) { /* advance state, since we received some data */ mms_stat->read = MQTT_MSG_HEADER; } @@ -1580,7 +1608,11 @@ int MqttClient_Connect(MqttClient *client, MqttConnect *mc_connect) /* Send connect packet */ rc = MqttPacket_Write(client, client->tx_buf, xfer); #ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE && client->write.total > 0) { + if (rc == MQTT_CODE_CONTINUE + #ifdef WOLFMQTT_ALLOW_NODATA_UNLOCK + && client->write.total > 0 + #endif + ) { /* keep send locked and return early */ return rc; } @@ -1986,9 +2018,14 @@ static int MqttPublishMsg(MqttClient *client, MqttPublish *publish, /* Send publish packet */ rc = MqttPacket_Write(client, client->tx_buf, xfer); #ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE && client->write.total > 0) + if (rc == MQTT_CODE_CONTINUE + #ifdef WOLFMQTT_ALLOW_NODATA_UNLOCK + && client->write.total > 0 + #endif + ) { /* keep send locked and return early */ return rc; + } #endif client->write.len = 0; /* reset len, so publish chunk resets */ @@ -2175,7 +2212,11 @@ int MqttClient_Subscribe(MqttClient *client, MqttSubscribe *subscribe) /* Send subscribe packet */ rc = MqttPacket_Write(client, client->tx_buf, xfer); #ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE && client->write.total > 0) { + if (rc == MQTT_CODE_CONTINUE + #ifdef WOLFMQTT_ALLOW_NODATA_UNLOCK + && client->write.total > 0 + #endif + ) { /* keep send locked and return early */ return rc; } @@ -2276,7 +2317,11 @@ int MqttClient_Unsubscribe(MqttClient *client, MqttUnsubscribe *unsubscribe) /* Send unsubscribe packet */ rc = MqttPacket_Write(client, client->tx_buf, xfer); #ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE && client->write.total > 0) { + if (rc == MQTT_CODE_CONTINUE + #ifdef WOLFMQTT_ALLOW_NODATA_UNLOCK + && client->write.total > 0 + #endif + ) { /* keep send locked and return early */ return rc; } @@ -2369,7 +2414,11 @@ int MqttClient_Ping_ex(MqttClient *client, MqttPing* ping) /* Send ping req packet */ rc = MqttPacket_Write(client, client->tx_buf, xfer); #ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE && client->write.total > 0) { + if (rc == MQTT_CODE_CONTINUE + #ifdef WOLFMQTT_ALLOW_NODATA_UNLOCK + && client->write.total > 0 + #endif + ) { /* keep send locked and return early */ return rc; } @@ -2461,8 +2510,11 @@ int MqttClient_Disconnect_ex(MqttClient *client, MqttDisconnect *p_disconnect) rc = MqttPacket_Write(client, client->tx_buf, xfer); #ifdef WOLFMQTT_NONBLOCK /* if disconnect context avail allow partial write in non-blocking mode */ - if (p_disconnect != NULL && - rc == MQTT_CODE_CONTINUE && client->write.total > 0) { + if (p_disconnect != NULL && rc == MQTT_CODE_CONTINUE + #ifdef WOLFMQTT_ALLOW_NODATA_UNLOCK + && client->write.total > 0 + #endif + ) { /* keep send locked and return early */ return rc; } @@ -2544,7 +2596,11 @@ int MqttClient_Auth(MqttClient *client, MqttAuth* auth) /* Send authentication packet */ rc = MqttPacket_Write(client, client->tx_buf, xfer); #ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE && client->write.total > 0) { + if (rc == MQTT_CODE_CONTINUE + #ifdef WOLFMQTT_ALLOW_NODATA_UNLOCK + && client->write.total > 0 + #endif + ) { /* keep send locked and return early */ return rc; } From 6fdcee6ab0859b4d6cc63c52bfff031a53f7e418 Mon Sep 17 00:00:00 2001 From: David Garske Date: Fri, 24 Nov 2023 09:09:26 -0800 Subject: [PATCH 43/62] Add back logic to return MQTT_CODE_CONTINUE if a write lock happens while data has partially been written. --- src/mqtt_client.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/mqtt_client.c b/src/mqtt_client.c index badcfdea4..25f3b838a 100644 --- a/src/mqtt_client.c +++ b/src/mqtt_client.c @@ -195,15 +195,27 @@ static int MqttWriteStart(MqttClient* client, MqttMsgStat* stat) int rc = MQTT_CODE_SUCCESS; #ifdef WOLFMQTT_MULTITHREAD + #if defined(WOLFMQTT_DEBUG_CLIENT) || !defined(WOLFMQTT_ALLOW_NODATA_UNLOCK) #ifdef WOLFMQTT_DEBUG_CLIENT if (stat->isWriteActive) { MQTT_TRACE_MSG("Warning, send already locked!"); rc = MQTT_CODE_ERROR_SYSTEM; } + #endif /* WOLFMQTT_DEBUG_CLIENT */ + #ifndef WOLFMQTT_ALLOW_NODATA_UNLOCK + /* detect if a write is already in progress */ + if (wm_SemLock(&client->lockClient) == 0) { + if (client->write.total > 0) { + MQTT_TRACE_MSG("Partial write in progress!"); + rc = MQTT_CODE_CONTINUE; /* can't write yet */ + } + wm_SemUnlock(&client->lockClient); + } + #endif /* WOLFMQTT_ALLOW_NODATA_UNLOCK */ if (rc != 0) { return rc; } - #endif /* WOLFMQTT_DEBUG_CLIENT */ + #endif rc = wm_SemLock(&client->lockSend); #endif /* WOLFMQTT_MULTITHREAD */ From 033634773fd9ff10d0b2b44d1cda1a49b3caf5b2 Mon Sep 17 00:00:00 2001 From: David Garske Date: Mon, 27 Nov 2023 13:05:57 -0800 Subject: [PATCH 44/62] Improve logic for detection of active read or write. Add test case for trying to send multiple publishes in the same thread. --- examples/multithread/multithread.c | 72 ++++++++++++-------- src/mqtt_client.c | 106 ++++++++++++++++++++--------- wolfmqtt/mqtt_client.h | 3 + 3 files changed, 122 insertions(+), 59 deletions(-) diff --git a/examples/multithread/multithread.c b/examples/multithread/multithread.c index da4939802..0dc13bf79 100644 --- a/examples/multithread/multithread.c +++ b/examples/multithread/multithread.c @@ -38,7 +38,8 @@ /* Configuration */ /* Number of publish tasks. Each will send a unique message to the broker. */ -#define NUM_PUB_TASKS 10 +#define NUM_PUB_TASKS 5 +#define NUM_PUB_PER_TASK 2 /* Maximum size for network read/write callbacks. There is also a v5 define that describes the max MQTT control packet size, DEFAULT_MAX_PKT_SZ. */ @@ -470,7 +471,8 @@ static int TestIsDone(int rc, MQTTCtx* mqttCtx) /* check if we are in test mode and done */ wm_SemLock(&mtLock); if ((rc == 0 || rc == MQTT_CODE_CONTINUE) && mqttCtx->test_mode && - mNumMsgsDone == NUM_PUB_TASKS && mNumMsgsRecvd == NUM_PUB_TASKS + mNumMsgsDone == (NUM_PUB_TASKS * NUM_PUB_PER_TASK) && + mNumMsgsRecvd == (NUM_PUB_TASKS * NUM_PUB_PER_TASK) #ifdef WOLFMQTT_NONBLOCK && !MqttClient_IsMessageActive(&mqttCtx->client, NULL) #endif @@ -598,37 +600,51 @@ static DWORD WINAPI publish_task( LPVOID param ) static void *publish_task(void *param) #endif { - int rc; + int rc[NUM_PUB_PER_TASK], i; MQTTCtx *mqttCtx = (MQTTCtx*)param; - MqttPublish publish; - word32 startSec = 0; - - /* Publish Topic */ - XMEMSET(&publish, 0, sizeof(MqttPublish)); - publish.retain = 0; - publish.qos = mqttCtx->qos; - publish.duplicate = 0; - publish.topic_name = mqttCtx->topic_name; - publish.packet_id = mqtt_get_packetid_threadsafe(); - publish.buffer = (byte*)mTestMessage; - publish.total_len = sizeof(mTestMessage); + MqttPublish publish[NUM_PUB_PER_TASK]; + word32 startSec[NUM_PUB_PER_TASK]; + + /* Build publish */ + for (i=0; iqos; + publish[i].duplicate = 0; + publish[i].topic_name = mqttCtx->topic_name; + publish[i].packet_id = mqtt_get_packetid_threadsafe(); + publish[i].buffer = (byte*)mTestMessage; + publish[i].total_len = sizeof(mTestMessage); + + rc[i] = MQTT_CODE_CONTINUE; + startSec[i] = 0; + } - do { - rc = MqttClient_Publish_WriteOnly(&mqttCtx->client, &publish, NULL); - rc = check_response(mqttCtx, rc, &startSec, MQTT_PACKET_TYPE_PUBLISH, - mqttCtx->cmd_timeout_ms); - } while (rc == MQTT_CODE_CONTINUE); - if (rc != MQTT_CODE_SUCCESS) { - MqttClient_CancelMessage(&mqttCtx->client, (MqttObject*)&publish); + /* Send until != continue */ + for (i=0; iclient, &publish[i], + NULL); + rc[i] = check_response(mqttCtx, rc[i], &startSec[i], + MQTT_PACKET_TYPE_PUBLISH, mqttCtx->cmd_timeout_ms); + } } - PRINTF("MQTT Publish: Topic %s, %s (%d)", - publish.topic_name, - MqttClient_ReturnCodeToString(rc), rc); + /* Report result */ + for (i=0; iclient, (MqttObject*)&publish[i]); + } - wm_SemLock(&mtLock); - mNumMsgsDone++; - wm_SemUnlock(&mtLock); + PRINTF("MQTT Publish: Topic %s, %s (%d)", + publish[i].topic_name, + MqttClient_ReturnCodeToString(rc[i]), rc[i]); + + wm_SemLock(&mtLock); + mNumMsgsDone++; + wm_SemUnlock(&mtLock); + } THREAD_EXIT(0); } diff --git a/src/mqtt_client.c b/src/mqtt_client.c index 25f3b838a..4a4294dba 100644 --- a/src/mqtt_client.c +++ b/src/mqtt_client.c @@ -76,7 +76,7 @@ static int MqttClient_CancelMessage(MqttClient *client, MqttObject* msg); #elif defined(__MACH__) /* Apple style dispatch semaphore */ - int wm_SemInit(wm_Sem *s){ + int wm_SemInit(wm_Sem *s) { /* dispatch_release() fails hard, with Trace/BPT trap signal, if the * sem's internal count is less than the value passed in with * dispatch_semaphore_create(). work around this by initializing @@ -92,7 +92,7 @@ static int MqttClient_CancelMessage(MqttClient *client, MqttObject* msg); return 0; } - int wm_SemFree(wm_Sem *s){ + int wm_SemFree(wm_Sem *s) { if ((s == NULL) || (s->sem == NULL)) return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); @@ -194,38 +194,52 @@ static int MqttWriteStart(MqttClient* client, MqttMsgStat* stat) { int rc = MQTT_CODE_SUCCESS; -#ifdef WOLFMQTT_MULTITHREAD - #if defined(WOLFMQTT_DEBUG_CLIENT) || !defined(WOLFMQTT_ALLOW_NODATA_UNLOCK) - #ifdef WOLFMQTT_DEBUG_CLIENT +#if defined(WOLFMQTT_DEBUG_CLIENT) || !defined(WOLFMQTT_ALLOW_NODATA_UNLOCK) + #ifdef WOLFMQTT_DEBUG_CLIENT if (stat->isWriteActive) { MQTT_TRACE_MSG("Warning, send already locked!"); rc = MQTT_CODE_ERROR_SYSTEM; } - #endif /* WOLFMQTT_DEBUG_CLIENT */ - #ifndef WOLFMQTT_ALLOW_NODATA_UNLOCK + #endif + #ifndef WOLFMQTT_ALLOW_NODATA_UNLOCK /* detect if a write is already in progress */ - if (wm_SemLock(&client->lockClient) == 0) { - if (client->write.total > 0) { + #ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) + #endif + { + if (client->write.isActive) { MQTT_TRACE_MSG("Partial write in progress!"); rc = MQTT_CODE_CONTINUE; /* can't write yet */ } + #ifdef WOLFMQTT_MULTITHREAD wm_SemUnlock(&client->lockClient); + #endif } - #endif /* WOLFMQTT_ALLOW_NODATA_UNLOCK */ - if (rc != 0) { + #endif /* WOLFMQTT_ALLOW_NODATA_UNLOCK */ + if (rc != MQTT_CODE_SUCCESS) { return rc; } - #endif +#endif +#ifdef WOLFMQTT_MULTITHREAD rc = wm_SemLock(&client->lockSend); -#endif /* WOLFMQTT_MULTITHREAD */ - if (rc == 0) { +#endif + if (rc == MQTT_CODE_SUCCESS) { stat->isWriteActive = 1; + + #ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) + #endif + { + client->write.isActive = 1; + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockClient); + #endif + } + MQTT_TRACE_MSG("lockSend"); } - (void)client; - return rc; } static void MqttWriteStop(MqttClient* client, MqttMsgStat* stat) @@ -237,8 +251,16 @@ static void MqttWriteStop(MqttClient* client, MqttMsgStat* stat) } #endif - /* reset write */ - XMEMSET(&client->write, 0, sizeof(client->write)); +#ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) +#endif + { + /* reset write */ + XMEMSET(&client->write, 0, sizeof(client->write)); + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockClient); + #endif + } if (stat->isWriteActive) { MQTT_TRACE_MSG("unlockSend"); @@ -253,31 +275,47 @@ static int MqttReadStart(MqttClient* client, MqttMsgStat* stat) { int rc = MQTT_CODE_SUCCESS; -#ifdef WOLFMQTT_MULTITHREAD - #ifdef WOLFMQTT_DEBUG_CLIENT +#ifdef WOLFMQTT_DEBUG_CLIENT if (stat->isReadActive) { MQTT_TRACE_MSG("Warning, recv already locked!"); rc = MQTT_CODE_ERROR_SYSTEM; } /* detect if a read is already in progress */ - if (wm_SemLock(&client->lockClient) == 0) { - if (client->read.total > 0) { + #ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) + #endif + { + if (client->read.isActive) { MQTT_TRACE_MSG("Partial read in progress!"); rc = MQTT_CODE_CONTINUE; /* can't read yet */ } + #ifdef WOLFMQTT_MULTITHREAD wm_SemUnlock(&client->lockClient); + #endif } if (rc != 0) return rc; - #endif /* WOLFMQTT_DEBUG_CLIENT */ +#endif /* WOLFMQTT_DEBUG_CLIENT */ +#ifdef WOLFMQTT_MULTITHREAD rc = wm_SemLock(&client->lockRecv); -#endif /* WOLFMQTT_MULTITHREAD */ - if (rc == 0) { +#endif + if (rc == MQTT_CODE_SUCCESS) { stat->isReadActive = 1; + + #ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) + #endif + { + client->read.isActive = 1; + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockClient); + #endif + } + MQTT_TRACE_MSG("lockRecv"); } - (void)client; + return rc; } static void MqttReadStop(MqttClient* client, MqttMsgStat* stat) @@ -289,12 +327,20 @@ static void MqttReadStop(MqttClient* client, MqttMsgStat* stat) } #endif - /* reset read */ - XMEMSET(&client->read, 0, sizeof(client->read)); +#ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) +#endif + { + /* reset read */ + XMEMSET(&client->read, 0, sizeof(client->read)); + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockClient); + #endif + } if (stat->isReadActive) { - stat->isReadActive = 0; MQTT_TRACE_MSG("unlockRecv"); + stat->isReadActive = 0; #ifdef WOLFMQTT_MULTITHREAD wm_SemUnlock(&client->lockRecv); #endif @@ -2738,14 +2784,12 @@ int MqttClient_CancelMessage(MqttClient *client, MqttObject* msg) #ifdef WOLFMQTT_DEBUG_CLIENT PRINTF("Cancel Read Lock"); #endif - mms_stat->isReadActive = 0; MqttReadStop(client, mms_stat); } if (mms_stat->isWriteActive) { #ifdef WOLFMQTT_DEBUG_CLIENT PRINTF("Cancel Write Lock"); #endif - mms_stat->isWriteActive = 0; MqttWriteStop(client, mms_stat); } diff --git a/wolfmqtt/mqtt_client.h b/wolfmqtt/mqtt_client.h index e8ad0299c..a8bcd7d5f 100644 --- a/wolfmqtt/mqtt_client.h +++ b/wolfmqtt/mqtt_client.h @@ -131,6 +131,9 @@ typedef struct _MqttSk { int pos; /* position inside current buffer */ int len; /* length of current segment being sent */ int total; /* number bytes sent or received */ + + /* status bit for if client read or write is active */ + byte isActive:1; } MqttSk; #ifdef WOLFMQTT_DISCONNECT_CB From 8a0cafe48a23db2b210e2b8451e25a71d62911b8 Mon Sep 17 00:00:00 2001 From: David Garske Date: Mon, 27 Nov 2023 13:33:15 -0800 Subject: [PATCH 45/62] Add packet_id to the publish log message in examples. --- examples/aws/awsiot.c | 9 +++++---- examples/azure/azureiothub.c | 9 +++++---- examples/firmware/fwpush.c | 4 ++-- examples/mqttclient/mqttclient.c | 8 ++++---- examples/mqttsimple/mqttsimple.c | 5 +++-- examples/multithread/multithread.c | 8 ++++---- examples/nbclient/nbclient.c | 4 ++-- examples/pub-sub/mqtt-pub.c | 4 ++-- examples/wiot/wiot.c | 9 +++++---- 9 files changed, 32 insertions(+), 28 deletions(-) diff --git a/examples/aws/awsiot.c b/examples/aws/awsiot.c index 09c3a05ad..8ae5516c7 100644 --- a/examples/aws/awsiot.c +++ b/examples/aws/awsiot.c @@ -617,8 +617,9 @@ int awsiot_test(MQTTCtx *mqttCtx) if (rc == MQTT_CODE_CONTINUE) { return rc; } - PRINTF("MQTT Publish: Topic %s, %s (%d)", - mqttCtx->publish.topic_name, MqttClient_ReturnCodeToString(rc), rc); + PRINTF("MQTT Publish: Topic %s, ID %d, %s (%d)", + mqttCtx->publish.topic_name, mqttCtx->publish.packet_id, + MqttClient_ReturnCodeToString(rc), rc); if (rc != MQTT_CODE_SUCCESS) { goto disconn; } @@ -676,8 +677,8 @@ int awsiot_test(MQTTCtx *mqttCtx) mqttCtx->publish.buffer = (byte*)mqttCtx->app_ctx; mqttCtx->publish.total_len = (word32)XSTRLEN((char*)mqttCtx->app_ctx); rc = MqttClient_Publish(&mqttCtx->client, &mqttCtx->publish); - PRINTF("MQTT Publish: Topic %s, %s (%d)", - mqttCtx->publish.topic_name, + PRINTF("MQTT Publish: Topic %s, ID %d, %s (%d)", + mqttCtx->publish.topic_name, mqttCtx->publish.packet_id, MqttClient_ReturnCodeToString(rc), rc); } } diff --git a/examples/azure/azureiothub.c b/examples/azure/azureiothub.c index dc8205b65..80189cbc7 100644 --- a/examples/azure/azureiothub.c +++ b/examples/azure/azureiothub.c @@ -439,8 +439,9 @@ int azureiothub_test(MQTTCtx *mqttCtx) if (rc == MQTT_CODE_CONTINUE) { return rc; } - PRINTF("MQTT Publish: Topic %s, %s (%d)", - mqttCtx->publish.topic_name, MqttClient_ReturnCodeToString(rc), rc); + PRINTF("MQTT Publish: Topic %s, ID %d, %s (%d)", + mqttCtx->publish.topic_name, mqttCtx->publish.packet_id, + MqttClient_ReturnCodeToString(rc), rc); if (rc != MQTT_CODE_SUCCESS) { goto disconn; } @@ -496,8 +497,8 @@ int azureiothub_test(MQTTCtx *mqttCtx) mqttCtx->publish.total_len = (word16)rc; rc = MqttClient_Publish(&mqttCtx->client, &mqttCtx->publish); - PRINTF("MQTT Publish: Topic %s, %s (%d)", - mqttCtx->publish.topic_name, + PRINTF("MQTT Publish: Topic %s, ID %d, %s (%d)", + mqttCtx->publish.topic_name, mqttCtx->publish.packet_id, MqttClient_ReturnCodeToString(rc), rc); } } diff --git a/examples/firmware/fwpush.c b/examples/firmware/fwpush.c index 676ee95e7..fbefbdc90 100644 --- a/examples/firmware/fwpush.c +++ b/examples/firmware/fwpush.c @@ -455,8 +455,8 @@ int fwpush_test(MQTTCtx *mqttCtx) return rc; } - PRINTF("MQTT Publish: Topic %s, %s (%d)", - mqttCtx->publish.topic_name, + PRINTF("MQTT Publish: Topic %s, ID %d, %s (%d)", + mqttCtx->publish.topic_name, mqttCtx->publish.packet_id, MqttClient_ReturnCodeToString(rc), rc); if (rc != MQTT_CODE_SUCCESS) { goto disconn; diff --git a/examples/mqttclient/mqttclient.c b/examples/mqttclient/mqttclient.c index e390b94b7..7869a18ff 100644 --- a/examples/mqttclient/mqttclient.c +++ b/examples/mqttclient/mqttclient.c @@ -510,8 +510,8 @@ int mqttclient_test(MQTTCtx *mqttCtx) mqttCtx->pub_file = NULL; /* don't try and send file again */ } - PRINTF("MQTT Publish: Topic %s, %s (%d)", - mqttCtx->publish.topic_name, + PRINTF("MQTT Publish: Topic %s, ID %d, %s (%d)", + mqttCtx->publish.topic_name, mqttCtx->publish.packet_id, MqttClient_ReturnCodeToString(rc), rc); #ifdef WOLFMQTT_V5 if (mqttCtx->qos > 0) { @@ -576,8 +576,8 @@ int mqttclient_test(MQTTCtx *mqttCtx) mqttCtx->publish.total_len = (word16)rc; rc = MqttClient_Publish(&mqttCtx->client, &mqttCtx->publish); - PRINTF("MQTT Publish: Topic %s, %s (%d)", - mqttCtx->publish.topic_name, + PRINTF("MQTT Publish: Topic %s, ID %d, %s (%d)", + mqttCtx->publish.topic_name, mqttCtx->publish.packet_id, MqttClient_ReturnCodeToString(rc), rc); } } diff --git a/examples/mqttsimple/mqttsimple.c b/examples/mqttsimple/mqttsimple.c index 5fa115b53..814c8f370 100644 --- a/examples/mqttsimple/mqttsimple.c +++ b/examples/mqttsimple/mqttsimple.c @@ -424,8 +424,9 @@ int mqttsimple_test(void) if (rc != MQTT_CODE_SUCCESS) { goto exit; } - PRINTF("MQTT Publish: Topic %s, Qos %d, Message %s", - mqttObj.publish.topic_name, mqttObj.publish.qos, mqttObj.publish.buffer); + PRINTF("MQTT Publish: Topic %s, ID %d, Qos %d, Message %s", + mqttObj.publish.topic_name, mqttObj.publish.packet_id, + mqttObj.publish.qos, mqttObj.publish.buffer); /* Wait for messages */ while (1) { diff --git a/examples/multithread/multithread.c b/examples/multithread/multithread.c index 0dc13bf79..8a6ee52c9 100644 --- a/examples/multithread/multithread.c +++ b/examples/multithread/multithread.c @@ -559,8 +559,8 @@ static void *waitMessage_task(void *param) MqttClient_CancelMessage(&mqttCtx->client, (MqttObject*)&mqttCtx->publish); } - PRINTF("MQTT Publish: Topic %s, %s (%d)", - mqttCtx->publish.topic_name, + PRINTF("MQTT Publish: Topic %s, ID %d, %s (%d)", + mqttCtx->publish.topic_name, mqttCtx->publish.packet_id, MqttClient_ReturnCodeToString(rc), rc); } } @@ -637,8 +637,8 @@ static void *publish_task(void *param) MqttClient_CancelMessage(&mqttCtx->client, (MqttObject*)&publish[i]); } - PRINTF("MQTT Publish: Topic %s, %s (%d)", - publish[i].topic_name, + PRINTF("MQTT Publish: Topic %s, ID %d, %s (%d)", + publish[i].topic_name, publish[i].packet_id, MqttClient_ReturnCodeToString(rc[i]), rc[i]); wm_SemLock(&mtLock); diff --git a/examples/nbclient/nbclient.c b/examples/nbclient/nbclient.c index ed438aa89..786c12e70 100644 --- a/examples/nbclient/nbclient.c +++ b/examples/nbclient/nbclient.c @@ -450,8 +450,8 @@ int mqttclient_test(MQTTCtx *mqttCtx) mqttCtx->pub_file = NULL; /* don't try and send file again */ } - PRINTF("MQTT Publish: Topic %s, %s (%d)", - mqttCtx->publish.topic_name, + PRINTF("MQTT Publish: Topic %s, ID %d, %s (%d)", + mqttCtx->publish.topic_name, mqttCtx->publish.packet_id, MqttClient_ReturnCodeToString(rc), rc); if (rc != MQTT_CODE_SUCCESS) { goto disconn; diff --git a/examples/pub-sub/mqtt-pub.c b/examples/pub-sub/mqtt-pub.c index b6d220d39..40c1fc0a3 100644 --- a/examples/pub-sub/mqtt-pub.c +++ b/examples/pub-sub/mqtt-pub.c @@ -387,8 +387,8 @@ int pub_client(MQTTCtx *mqttCtx) mqttCtx->pub_file = NULL; /* don't try and send file again */ } if (mqttCtx->debug_on) { - PRINTF("MQTT Publish: Topic %s, %s (%d)", - mqttCtx->publish.topic_name, + PRINTF("MQTT Publish: Topic %s, ID %d, %s (%d)", + mqttCtx->publish.topic_name, mqttCtx->publish.packet_id, MqttClient_ReturnCodeToString(rc), rc); } if (rc != MQTT_CODE_SUCCESS) { diff --git a/examples/wiot/wiot.c b/examples/wiot/wiot.c index 96b6348cc..28a06aa41 100644 --- a/examples/wiot/wiot.c +++ b/examples/wiot/wiot.c @@ -265,8 +265,9 @@ int wiot_test(MQTTCtx *mqttCtx) rc = MqttClient_Publish(&mqttCtx->client, &mqttCtx->publish); - PRINTF("MQTT Publish: Topic %s, %s (%d)", - mqttCtx->publish.topic_name, MqttClient_ReturnCodeToString(rc), rc); + PRINTF("MQTT Publish: Topic %s, ID %d, %s (%d)", + mqttCtx->publish.topic_name, mqttCtx->publish.packet_id, + MqttClient_ReturnCodeToString(rc), rc); if (rc != MQTT_CODE_SUCCESS) { goto disconn; } @@ -316,8 +317,8 @@ int wiot_test(MQTTCtx *mqttCtx) mqttCtx->publish.buffer = mqttCtx->rx_buf; mqttCtx->publish.total_len = (word16)rc; rc = MqttClient_Publish(&mqttCtx->client, &mqttCtx->publish); - PRINTF("MQTT Publish: Topic %s, %s (%d)", - mqttCtx->publish.topic_name, + PRINTF("MQTT Publish: Topic %s, ID %d, %s (%d)", + mqttCtx->publish.topic_name, mqttCtx->publish.packet_id, MqttClient_ReturnCodeToString(rc), rc); } } From 4bdde13392b4c8fb8f5de3efa380c18d6dfe0b88 Mon Sep 17 00:00:00 2001 From: Eric Blankenhorn Date: Mon, 27 Nov 2023 12:28:16 -0600 Subject: [PATCH 46/62] Close socket on error in NetCOnnect --- examples/mqttnet.c | 67 +++++++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/examples/mqttnet.c b/examples/mqttnet.c index acd9d3340..312a5ff62 100644 --- a/examples/mqttnet.c +++ b/examples/mqttnet.c @@ -219,6 +219,20 @@ static int NetDisconnect(void *context) /* -------------------------------------------------------------------------- */ #elif defined(MICROCHIP_MPLAB_HARMONY) +static int NetDisconnect(void *context) +{ + SocketContext *sock = (SocketContext*)context; + if (sock) { + if (sock->fd != SOCKET_INVALID) { + closesocket(sock->fd); + sock->fd = SOCKET_INVALID; + } + + sock->stat = SOCK_BEGIN; + } + return 0; +} + static int NetConnect(void *context, const char* host, word16 port, int timeout_ms) { @@ -287,6 +301,7 @@ static int NetConnect(void *context, const char* host, word16 port, if (errno == EINPROGRESS || errno == EWOULDBLOCK) { return MQTT_CODE_CONTINUE; } + NetDisconnect(context); /* Show error */ PRINTF("NetConnect: Rc=%d, ErrNo=%d", rc, errno); @@ -358,20 +373,6 @@ static int NetRead(void *context, byte* buf, int buf_len, return rc; } -static int NetDisconnect(void *context) -{ - SocketContext *sock = (SocketContext*)context; - if (sock) { - if (sock->fd != SOCKET_INVALID) { - closesocket(sock->fd); - sock->fd = SOCKET_INVALID; - } - - sock->stat = SOCK_BEGIN; - } - return 0; -} - /* -------------------------------------------------------------------------- */ /* GENERIC BSD SOCKET TCP NETWORK CALLBACK EXAMPLE */ /* -------------------------------------------------------------------------- */ @@ -410,6 +411,20 @@ static void tcp_set_nonblocking(SOCKET_T* sockfd) #endif /* WOLFMQTT_NONBLOCK */ #endif /* !WOLFMQTT_NO_TIMEOUT */ +static int NetDisconnect(void *context) +{ + SocketContext *sock = (SocketContext*)context; + if (sock) { + if (sock->fd != SOCKET_INVALID) { + SOCK_CLOSE(sock->fd); + sock->fd = SOCKET_INVALID; + } + + sock->stat = SOCK_BEGIN; + } + return 0; +} + static int NetConnect(void *context, const char* host, word16 port, int timeout_ms) { @@ -539,9 +554,9 @@ static int NetConnect(void *context, const char* host, word16 port, (void)timeout_ms; exit: - /* Show error */ - if (rc != 0) { - PRINTF("NetConnect: Rc=%d, SoErr=%d", rc, so_error); + if ((rc != 0) && (rc != MQTT_CODE_CONTINUE)) { + NetDisconnect(context); + PRINTF("NetConnect: Rc=%d, SoErr=%d", rc, so_error); /* Show error */ } return rc; @@ -630,8 +645,8 @@ static int SN_NetConnect(void *context, const char* host, word16 port, exit: /* Show error */ - if (rc != 0) { - SOCK_CLOSE(sock->fd); + if ((rc != 0) && (rc != MQTT_CODE_CONTINUE)) { + NetDisconnect(context); PRINTF("NetConnect: Rc=%d, SoErr=%d", rc, so_error); } @@ -873,20 +888,6 @@ static int NetPeek(void *context, byte* buf, int buf_len, int timeout_ms) } #endif -static int NetDisconnect(void *context) -{ - SocketContext *sock = (SocketContext*)context; - if (sock) { - if (sock->fd != SOCKET_INVALID) { - SOCK_CLOSE(sock->fd); - sock->fd = -1; - } - - sock->stat = SOCK_BEGIN; - } - return 0; -} - #endif From bc7ae0af80ce2053e1e0992eb5e1f51bd3344896 Mon Sep 17 00:00:00 2001 From: David Garske Date: Mon, 27 Nov 2023 14:08:44 -0800 Subject: [PATCH 47/62] Fixed issue with QoS2 on received publish ACK getting skipped if write is already locked. --- src/mqtt_client.c | 61 ++++++++++++++++++++---------------------- wolfmqtt/mqtt_client.h | 2 +- 2 files changed, 30 insertions(+), 33 deletions(-) diff --git a/src/mqtt_client.c b/src/mqtt_client.c index 4a4294dba..4f1aa7fdd 100644 --- a/src/mqtt_client.c +++ b/src/mqtt_client.c @@ -307,7 +307,12 @@ static int MqttReadStart(MqttClient* client, MqttMsgStat* stat) if (wm_SemLock(&client->lockClient) == 0) #endif { + /* mark read active */ client->read.isActive = 1; + + /* reset the packet state used by MqttPacket_Read */ + client->packet.stat = MQTT_PK_BEGIN; + #ifdef WOLFMQTT_MULTITHREAD wm_SemUnlock(&client->lockClient); #endif @@ -1155,9 +1160,6 @@ static int MqttClient_WaitType(MqttClient *client, void *packet_obj, return rc; } - /* reset the packet state used by MqttPacket_Read */ - client->packet.stat = MQTT_PK_BEGIN; - mms_stat->read = MQTT_MSG_WAIT; } FALL_THROUGH; @@ -1311,6 +1313,10 @@ static int MqttClient_WaitType(MqttClient *client, void *packet_obj, if (rc >= 0) { rc = MQTT_CODE_SUCCESS; } + else { + /* error, break */ + break; + } #ifdef WOLFMQTT_MULTITHREAD if (pendResp) { @@ -1327,39 +1333,21 @@ static int MqttClient_WaitType(MqttClient *client, void *packet_obj, } #endif /* WOLFMQTT_MULTITHREAD */ - /* are we sending ACK or done with message? */ + /* Determine if we are sending ACK or done */ if (MqttIsPubRespPacket(resp.packet_type)) { + /* if we get here, then we are sending an ACK */ mms_stat->read = MQTT_MSG_ACK; - } - else { - mms_stat->read = MQTT_MSG_BEGIN; + mms_stat->ack = MQTT_MSG_WAIT; + + /* setup ACK in shared context */ + XMEMCPY(&client->packetAck, &resp, sizeof(MqttPublishResp)); + #ifdef WOLFMQTT_V5 + client->packetAck.protocol_level = client->protocol_level; + #endif } /* done reading */ MqttReadStop(client, mms_stat); - - /* if error, leave */ - if (rc != MQTT_CODE_SUCCESS) { - break; - } - - /* if not sending an ACK, we are done */ - if (!MqttIsPubRespPacket(resp.packet_type)) { - break; - } - - /* Flag write active / lock mutex */ - if ((rc = MqttWriteStart(client, mms_stat)) != 0) { - break; - } - - /* setup ACK in shared context */ - XMEMCPY(&client->packetAck, &resp, sizeof(MqttPublishResp)); - #ifdef WOLFMQTT_V5 - client->packetAck.protocol_level = client->protocol_level; - #endif - - mms_stat->ack = MQTT_MSG_ACK; break; } @@ -1382,10 +1370,19 @@ static int MqttClient_WaitType(MqttClient *client, void *packet_obj, switch (mms_stat->ack) { case MQTT_MSG_BEGIN: - case MQTT_MSG_WAIT: /* wait for read to set ack */ break; + case MQTT_MSG_WAIT: + { + /* Flag write active / lock mutex */ + if ((rc = MqttWriteStart(client, mms_stat)) != 0) { + break; + } + mms_stat->ack = MQTT_MSG_ACK; + } + FALL_THROUGH; + case MQTT_MSG_ACK: { /* send ack */ @@ -1453,7 +1450,7 @@ static int MqttClient_WaitType(MqttClient *client, void *packet_obj, #endif /* no data read or ack done, then reset state */ - if (mms_stat->read == MQTT_MSG_WAIT || mms_stat->read == MQTT_MSG_ACK) { + if (mms_stat->read == MQTT_MSG_WAIT) { mms_stat->read = MQTT_MSG_BEGIN; } diff --git a/wolfmqtt/mqtt_client.h b/wolfmqtt/mqtt_client.h index a8bcd7d5f..ddb70afe1 100644 --- a/wolfmqtt/mqtt_client.h +++ b/wolfmqtt/mqtt_client.h @@ -115,7 +115,7 @@ enum MqttClientFlags { WOLFMQTT_API word32 MqttClient_Flags(struct _MqttClient *client, word32 mask, word32 flags); typedef enum _MqttPkStat { - MQTT_PK_BEGIN, + MQTT_PK_BEGIN = 0, MQTT_PK_READ_HEAD, MQTT_PK_READ } MqttPkStat; From b4badfd9da191061d651750864b7ad2df7b717c8 Mon Sep 17 00:00:00 2001 From: Eric Blankenhorn Date: Tue, 31 Oct 2023 15:25:49 -0500 Subject: [PATCH 48/62] Add MQTT-SN CI tests --- .github/workflows/mqtt-sn-check.yml | 103 ++++++++++++++++++++++++++++ examples/sn-client/sn-client.c | 15 ++-- 2 files changed, 111 insertions(+), 7 deletions(-) create mode 100644 .github/workflows/mqtt-sn-check.yml diff --git a/.github/workflows/mqtt-sn-check.yml b/.github/workflows/mqtt-sn-check.yml new file mode 100644 index 000000000..4d00dd2b0 --- /dev/null +++ b/.github/workflows/mqtt-sn-check.yml @@ -0,0 +1,103 @@ +name: MQTT-SN Build Test + +on: + push: + branches: [ 'master', 'main', 'release/**' ] + pull_request: + branches: [ '*' ] + +jobs: + build: + + runs-on: ubuntu-latest + timeout-minutes: 5 + + steps: + - name: Install dependencies + run: | + # Don't prompt for anything + export DEBIAN_FRONTEND=noninteractive + sudo apt-get update + # Install mosquitto + sudo apt-get install -y mosquitto bubblewrap + + - name: Setup mosquitto broker + run: | + # Disable default broker daemon + sudo service mosquitto stop + sleep 1 + mosquitto -v &> ~/broker.log & + sleep 1 + + - uses: actions/checkout@master + with: + repository: eclipse/paho.mqtt-sn.embedded-c + path: gateway + - name: Build gateway + working-directory: ./gateway/MQTTSNGateway + run: ./build.sh udp -DDEBUG -DDEBUG_NW + - name: Write config to change broker + working-directory: ./gateway/MQTTSNGateway/bin + run: | + printf "GatewayID=1\nGatewayName=PahoGateway-01\nMaxNumberOfClients=30\nKeepAlive=60\nBrokerName=localhost\nBrokerPortNo=1883\nAggregatingGateway=NO\nQoS-1=NO\nForwarder=NO\nPredefinedTopic=NO\nClientAuthentication=NO\nGatewayPortNo=10000\nMulticastPortNo=1883\nMulticastIP=225.1.1.1\nMulticastTTL=1\n" > gateway.conf + - name: Display gateway config + working-directory: ./gateway/MQTTSNGateway/bin + run: more gateway.conf + - name: Run gateway + working-directory: ./gateway/MQTTSNGateway/bin + run: sudo ./MQTT-SNGateway &> ~/gateway.log & + sleep 1 + + # This is some debug info useful if something goes wrong + - name: Show network status + run: | + sudo ifconfig + sudo route + sudo netstat -tulpan + + - uses: actions/checkout@master + with: + repository: wolfssl/wolfssl + path: wolfssl + - name: wolfssl autogen + working-directory: ./wolfssl + run: ./autogen.sh + - name: wolfssl configure + working-directory: ./wolfssl + run: ./configure --enable-enckeys + - name: wolfssl make + working-directory: ./wolfssl + run: make + - name: wolfssl make install + working-directory: ./wolfssl + run: sudo make install + + - uses: actions/checkout@master + - name: wolfmqtt autogen + run: ./autogen.sh + + - name: wolfmqtt configure with SN Enabled + run: | + export WOLFMQTT_NO_EXTERNAL_BROKER_TESTS=1 + ./configure --enable-sn + - name: wolfmqtt make + run: make + + - name: test SN Client + run: ./examples/sn-client/sn-client -T + + # Cleanup + - name: Stop gateway + if: failure() || cancelled() + run: | + sudo kill -2 $(pgrep -f "MQTT-SNGateway") + sleep 3 + sudo kill -2 $(pgrep -f "mosquitto") + sleep 1 + + # capture logs on failure + - name: Show logs on failure + if: failure() || cancelled() + run: | + sudo cat ~/gateway.log + sudo cat ~/broker.log diff --git a/examples/sn-client/sn-client.c b/examples/sn-client/sn-client.c index 15b5a9bf1..31a909603 100644 --- a/examples/sn-client/sn-client.c +++ b/examples/sn-client/sn-client.c @@ -509,10 +509,6 @@ int sn_test(MQTTCtx *mqttCtx) PRINTF("MQTT Waiting for message..."); do { - /* Try and read packet */ - rc = SN_Client_WaitMessage(&mqttCtx->client, - mqttCtx->cmd_timeout_ms); - /* check for test mode */ if (mStopRead) { rc = MQTT_CODE_SUCCESS; @@ -520,9 +516,13 @@ int sn_test(MQTTCtx *mqttCtx) break; } + /* Try and read packet */ + rc = SN_Client_WaitMessage(&mqttCtx->client, + mqttCtx->cmd_timeout_ms); + /* check return code */ #ifdef WOLFMQTT_ENABLE_STDIN_CAP - else if (rc == MQTT_CODE_STDIN_WAKE) { + if (rc == MQTT_CODE_STDIN_WAKE) { XMEMSET(mqttCtx->rx_buf, 0, MAX_BUFFER_SIZE); if (XFGETS((char*)mqttCtx->rx_buf, MAX_BUFFER_SIZE - 1, stdin) != NULL) @@ -556,8 +556,9 @@ int sn_test(MQTTCtx *mqttCtx) } } } + else #endif - else if (rc == MQTT_CODE_ERROR_TIMEOUT) { + if (rc == MQTT_CODE_ERROR_TIMEOUT) { /* Keep Alive */ PRINTF("Keep-alive timeout, sending ping"); @@ -743,6 +744,6 @@ int main(int argc, char** argv) #endif - return (rc == 0) ? 0 : EXIT_FAILURE; + return (rc == MQTT_CODE_SUCCESS) ? 0 : EXIT_FAILURE; } From ebe65ad7fd87769eb3431a7dcef77813be9e4bd8 Mon Sep 17 00:00:00 2001 From: Eric Blankenhorn Date: Wed, 29 Nov 2023 08:52:08 -0600 Subject: [PATCH 49/62] wolfMQTT Release v1.17.1 preparation --- CMakeLists.txt | 2 +- ChangeLog.md | 2 ++ configure.ac | 4 ++-- wolfmqtt/version.h | 4 ++-- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index aab5eff0e..cb73c91b5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,7 +21,7 @@ cmake_minimum_required(VERSION 3.16) -project(wolfMQTT VERSION 1.17.0 LANGUAGES C) +project(wolfMQTT VERSION 1.17.1 LANGUAGES C) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set(MQTT_SOURCES diff --git a/ChangeLog.md b/ChangeLog.md index 9c26a858e..41ab307e0 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,4 +1,6 @@ ## Release Notes +### v1.17.1 (11/29/2023) + ### v1.17.0 (11/2/2023) Release 1.17.0 has been developed according to wolfSSL's development and QA process (see link below) and successfully passed the quality criteria. diff --git a/configure.ac b/configure.ac index 1e0ff92f7..cd34c2361 100644 --- a/configure.ac +++ b/configure.ac @@ -3,7 +3,7 @@ # All right reserved. AC_COPYRIGHT([Copyright (C) 2014-2023 wolfSSL Inc.]) -AC_INIT([wolfmqtt],[1.17.0],[https://github.com/wolfssl/wolfMQTT/issues],[wolfmqtt],[http://www.wolfssl.com]) +AC_INIT([wolfmqtt],[1.17.1],[https://github.com/wolfssl/wolfMQTT/issues],[wolfmqtt],[http://www.wolfssl.com]) AC_PREREQ([2.63]) AC_CONFIG_AUX_DIR([build-aux]) @@ -24,7 +24,7 @@ AC_ARG_PROGRAM AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_HEADERS([src/config.h]) -WOLFMQTT_LIBRARY_VERSION=16:0:0 +WOLFMQTT_LIBRARY_VERSION=16:1:0 # | | | # +------+ | +---+ # | | | diff --git a/wolfmqtt/version.h b/wolfmqtt/version.h index 8b501a23d..f8eedb3ed 100644 --- a/wolfmqtt/version.h +++ b/wolfmqtt/version.h @@ -34,8 +34,8 @@ extern "C" { #endif -#define LIBWOLFMQTT_VERSION_STRING "1.17.0" -#define LIBWOLFMQTT_VERSION_HEX 0x01017000 +#define LIBWOLFMQTT_VERSION_STRING "1.17.1" +#define LIBWOLFMQTT_VERSION_HEX 0x01017001 #ifdef __cplusplus } From 96c69d875b91fba9ac501f1376144bf662644252 Mon Sep 17 00:00:00 2001 From: Eric Blankenhorn Date: Wed, 29 Nov 2023 10:02:15 -0600 Subject: [PATCH 50/62] Fix Wild read in MqttProps_Free --- src/mqtt_packet.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/mqtt_packet.c b/src/mqtt_packet.c index 5f105ab3d..dae2ab1bf 100644 --- a/src/mqtt_packet.c +++ b/src/mqtt_packet.c @@ -368,7 +368,6 @@ int MqttEncode_Props(MqttPacketType packet, MqttProp* props, byte* buf) { int rc = 0, tmp; MqttProp* cur_prop = props; - int num_props = 0; /* TODO: Check against max size. Sometimes all properties are not expected to be added */ @@ -472,8 +471,6 @@ int MqttEncode_Props(MqttPacketType packet, MqttProp* props, byte* buf) } } - num_props++; - cur_prop = cur_prop->next; } @@ -491,7 +488,6 @@ int MqttDecode_Props(MqttPacketType packet, MqttProp** props, byte* pbuf, MqttProp* cur_prop; byte* buf = pbuf; - *props = NULL; total = 0; while (((int)prop_len > 0) && (rc >= 0)) @@ -827,6 +823,7 @@ int MqttDecode_ConnectAck(byte *rx_buf, int rx_buf_len, connect_ack->return_code = *rx_payload++; #ifdef WOLFMQTT_V5 + connect_ack->props = 0; if (connect_ack->protocol_level >= MQTT_CONNECT_PROTOCOL_LEVEL_5) { word32 props_len = 0; int tmp; @@ -1007,6 +1004,7 @@ int MqttDecode_Publish(byte *rx_buf, int rx_buf_len, MqttPublish *publish) } #ifdef WOLFMQTT_V5 + publish->props = 0; if (publish->protocol_level >= MQTT_CONNECT_PROTOCOL_LEVEL_5) { word32 props_len = 0; int tmp; @@ -1155,6 +1153,7 @@ int MqttDecode_PublishResp(byte* rx_buf, int rx_buf_len, byte type, rx_payload += MqttDecode_Num(rx_payload, &publish_resp->packet_id); #ifdef WOLFMQTT_V5 + publish_resp->props = 0; if (publish_resp->protocol_level >= MQTT_CONNECT_PROTOCOL_LEVEL_5) { if (remain_len > MQTT_DATA_LEN_SIZE) { /* Decode the Reason Code */ @@ -1306,6 +1305,7 @@ int MqttDecode_SubscribeAck(byte* rx_buf, int rx_buf_len, rx_payload += MqttDecode_Num(rx_payload, &subscribe_ack->packet_id); #ifdef WOLFMQTT_V5 + subscribe_ack->props = 0; if ((subscribe_ack->protocol_level >= MQTT_CONNECT_PROTOCOL_LEVEL_5) && (remain_len > MQTT_DATA_LEN_SIZE)) { word32 props_len = 0; @@ -1444,6 +1444,7 @@ int MqttDecode_UnsubscribeAck(byte *rx_buf, int rx_buf_len, if (unsubscribe_ack) { rx_payload += MqttDecode_Num(rx_payload, &unsubscribe_ack->packet_id); #ifdef WOLFMQTT_V5 + unsubscribe_ack->props = 0; if (unsubscribe_ack->protocol_level >= MQTT_CONNECT_PROTOCOL_LEVEL_5) { if (remain_len > MQTT_DATA_LEN_SIZE) { word32 props_len = 0; @@ -1626,6 +1627,7 @@ int MqttDecode_Disconnect(byte *rx_buf, int rx_buf_len, MqttDisconnect *disc) } rx_payload = &rx_buf[header_len]; + disc->props = 0; if (remain_len > 0) { /* Decode variable header */ disc->reason_code = *rx_payload++; @@ -1744,6 +1746,8 @@ int MqttDecode_Auth(byte *rx_buf, int rx_buf_len, MqttAuth *auth) if ((auth->reason_code == MQTT_REASON_SUCCESS) || (auth->reason_code == MQTT_REASON_CONT_AUTH)) { + auth->props = 0; + /* Decode Length of Properties */ tmp = MqttDecode_Vbi(rx_payload, &props_len, (word32)(rx_buf_len - (rx_payload - rx_buf))); From eb616e747441c1c5557a80b1416e74fe42f0fa80 Mon Sep 17 00:00:00 2001 From: Eric Blankenhorn Date: Wed, 29 Nov 2023 10:19:16 -0600 Subject: [PATCH 51/62] Prepend change log --- ChangeLog.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ChangeLog.md b/ChangeLog.md index 41ab307e0..54fb3a936 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,6 +1,15 @@ ## Release Notes ### v1.17.1 (11/29/2023) +Release 1.17.1 has been developed according to wolfSSL's development and QA process (see link below) and successfully passed the quality criteria. +https://www.wolfssl.com/about/wolfssl-software-development-process-quality-assurance +* Include stdint.h in userio_template.h by @lealem47 in #371 +* Improvements to multithread locking and tests. by @dgarske in #369 +* Cleanup executable status on src files. by @philljj in #372 +* Close socket on error in NetConnect by @embhorn in #375 +* Fixes for non-blocking with larger payload and improvements to the test and examples by @dgarske in #373 +* Add MQTT-SN CI tests by @embhorn in #376 +* Fix Wild read in MqttProps_Free by @embhorn in #377 ### v1.17.0 (11/2/2023) Release 1.17.0 has been developed according to wolfSSL's development and QA process (see link below) and successfully passed the quality criteria. From 98a722e060a4f6b618eab00cbce32ee5f05ceb94 Mon Sep 17 00:00:00 2001 From: Eric Blankenhorn Date: Wed, 29 Nov 2023 11:59:05 -0600 Subject: [PATCH 52/62] Fix fuzzer issues --- src/mqtt_packet.c | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/src/mqtt_packet.c b/src/mqtt_packet.c index dae2ab1bf..16e6d45d7 100644 --- a/src/mqtt_packet.c +++ b/src/mqtt_packet.c @@ -605,15 +605,20 @@ int MqttDecode_Props(MqttPacketType packet, MqttProp** props, byte* pbuf, buf += tmp; total += tmp; prop_len -= (word32)tmp; - - tmp = MqttDecode_String(buf, - (const char**)&cur_prop->data_str2.str, - &cur_prop->data_str2.len); - if (cur_prop->data_str2.len <= - (buf_len - (buf - pbuf))) { - buf += tmp; - total += tmp; - prop_len -= (word32)tmp; + if ((buf_len - (buf - pbuf)) > 0) { + tmp = MqttDecode_String(buf, + (const char**)&cur_prop->data_str2.str, + &cur_prop->data_str2.len); + if (cur_prop->data_str2.len <= + (buf_len - (buf - pbuf))) { + buf += tmp; + total += tmp; + prop_len -= (word32)tmp; + } + else { + /* Invalid length */ + rc = MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PROPERTY); + } } else { /* Invalid length */ @@ -627,11 +632,8 @@ int MqttDecode_Props(MqttPacketType packet, MqttProp** props, byte* pbuf, break; } case MQTT_DATA_TYPE_NONE: - PRINTF("DATA TYPE NONE"); - break; default: { - PRINTF("INVALID DATA TYPE"); /* Invalid property data type */ rc = MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PROPERTY); break; @@ -823,7 +825,7 @@ int MqttDecode_ConnectAck(byte *rx_buf, int rx_buf_len, connect_ack->return_code = *rx_payload++; #ifdef WOLFMQTT_V5 - connect_ack->props = 0; + connect_ack->props = NULL; if (connect_ack->protocol_level >= MQTT_CONNECT_PROTOCOL_LEVEL_5) { word32 props_len = 0; int tmp; @@ -1004,7 +1006,7 @@ int MqttDecode_Publish(byte *rx_buf, int rx_buf_len, MqttPublish *publish) } #ifdef WOLFMQTT_V5 - publish->props = 0; + publish->props = NULL; if (publish->protocol_level >= MQTT_CONNECT_PROTOCOL_LEVEL_5) { word32 props_len = 0; int tmp; @@ -1153,7 +1155,7 @@ int MqttDecode_PublishResp(byte* rx_buf, int rx_buf_len, byte type, rx_payload += MqttDecode_Num(rx_payload, &publish_resp->packet_id); #ifdef WOLFMQTT_V5 - publish_resp->props = 0; + publish_resp->props = NULL; if (publish_resp->protocol_level >= MQTT_CONNECT_PROTOCOL_LEVEL_5) { if (remain_len > MQTT_DATA_LEN_SIZE) { /* Decode the Reason Code */ @@ -1305,7 +1307,7 @@ int MqttDecode_SubscribeAck(byte* rx_buf, int rx_buf_len, rx_payload += MqttDecode_Num(rx_payload, &subscribe_ack->packet_id); #ifdef WOLFMQTT_V5 - subscribe_ack->props = 0; + subscribe_ack->props = NULL; if ((subscribe_ack->protocol_level >= MQTT_CONNECT_PROTOCOL_LEVEL_5) && (remain_len > MQTT_DATA_LEN_SIZE)) { word32 props_len = 0; @@ -1444,7 +1446,7 @@ int MqttDecode_UnsubscribeAck(byte *rx_buf, int rx_buf_len, if (unsubscribe_ack) { rx_payload += MqttDecode_Num(rx_payload, &unsubscribe_ack->packet_id); #ifdef WOLFMQTT_V5 - unsubscribe_ack->props = 0; + unsubscribe_ack->props = NULL; if (unsubscribe_ack->protocol_level >= MQTT_CONNECT_PROTOCOL_LEVEL_5) { if (remain_len > MQTT_DATA_LEN_SIZE) { word32 props_len = 0; @@ -1627,7 +1629,7 @@ int MqttDecode_Disconnect(byte *rx_buf, int rx_buf_len, MqttDisconnect *disc) } rx_payload = &rx_buf[header_len]; - disc->props = 0; + disc->props = NULL; if (remain_len > 0) { /* Decode variable header */ disc->reason_code = *rx_payload++; @@ -1746,7 +1748,7 @@ int MqttDecode_Auth(byte *rx_buf, int rx_buf_len, MqttAuth *auth) if ((auth->reason_code == MQTT_REASON_SUCCESS) || (auth->reason_code == MQTT_REASON_CONT_AUTH)) { - auth->props = 0; + auth->props = NULL; /* Decode Length of Properties */ tmp = MqttDecode_Vbi(rx_payload, &props_len, From 964af4f6c71b674cb4de5025da2a5efe9af9fba8 Mon Sep 17 00:00:00 2001 From: Eric Blankenhorn Date: Wed, 29 Nov 2023 13:41:57 -0600 Subject: [PATCH 53/62] Add to changelog --- ChangeLog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ChangeLog.md b/ChangeLog.md index 54fb3a936..9d257bd24 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -10,6 +10,8 @@ https://www.wolfssl.com/about/wolfssl-software-development-process-quality-assur * Fixes for non-blocking with larger payload and improvements to the test and examples by @dgarske in #373 * Add MQTT-SN CI tests by @embhorn in #376 * Fix Wild read in MqttProps_Free by @embhorn in #377 +* Fix fuzzer issues in MqttDecode_Props by @embhorn in #378 + ### v1.17.0 (11/2/2023) Release 1.17.0 has been developed according to wolfSSL's development and QA process (see link below) and successfully passed the quality criteria. From 2b267f6162d430a8f3f4b0aa5edb8f9b29f0d800 Mon Sep 17 00:00:00 2001 From: philljj Date: Wed, 6 Dec 2023 11:37:41 -0600 Subject: [PATCH 54/62] Add curl easy socket backend. --- configure.ac | 19 ++ examples/include.am | 9 + examples/mqttcurl.c | 415 +++++++++++++++++++++++++++++++++++++++++ examples/mqttcurl.h | 64 +++++++ examples/mqttexample.c | 21 ++- examples/mqttexample.h | 3 + src/include.am | 7 + src/mqtt_client.c | 4 + src/mqtt_curl.c | 239 ++++++++++++++++++++++++ wolfmqtt/include.am | 3 + wolfmqtt/mqtt_client.h | 6 +- wolfmqtt/mqtt_curl.h | 87 +++++++++ wolfmqtt/mqtt_packet.h | 6 +- wolfmqtt/mqtt_types.h | 4 + 14 files changed, 883 insertions(+), 4 deletions(-) create mode 100644 examples/mqttcurl.c create mode 100644 examples/mqttcurl.h create mode 100644 src/mqtt_curl.c create mode 100644 wolfmqtt/mqtt_curl.h diff --git a/configure.ac b/configure.ac index cd34c2361..f1008cd33 100644 --- a/configure.ac +++ b/configure.ac @@ -169,6 +169,24 @@ AC_CHECK_LIB([wolfssl],[wolfCrypt_Init],,[AC_MSG_ERROR([libwolfssl is required a fi +# libcurl support +AC_ARG_ENABLE([curl], + [AS_HELP_STRING([--enable-curl],[Enable curl easy socket backend (default: disabled)])], + [ ENABLED_CURL=$enableval ], + [ ENABLED_CURL=no ] + ) + +if test "x$ENABLED_CURL" = "xyes"; then + if test "x$ENABLED_TLS" = "xyes"; then + AC_MSG_ERROR([--enable-tls and --enable-curl are incompatible]) + fi + + AM_CFLAGS="$AM_CFLAGS -DENABLE_MQTT_CURL" + + AC_CHECK_LIB([curl],[curl_easy_init],,[AC_MSG_ERROR([libcurl is required and wasn't found on the system. It can be obtained from https://curl.se/download.html.])]) + +fi + # Non-Blocking support AC_ARG_ENABLE([nonblock], @@ -310,6 +328,7 @@ fi AM_CONDITIONAL([HAVE_LIBWOLFSSL], [test "x$ENABLED_TLS" = "xyes"]) +AM_CONDITIONAL([HAVE_LIBCURL], [test "x$ENABLED_CURL" = "xyes"]) AM_CONDITIONAL([BUILD_EXAMPLES], [test "x$ENABLED_EXAMPLES" = "xyes"]) AM_CONDITIONAL([BUILD_STDINCAP], [test "x$ENABLED_STDINCAP" = "xyes"]) AM_CONDITIONAL([BUILD_SN], [test "x$ENABLED_SN" = "xyes"]) diff --git a/examples/include.am b/examples/include.am index dd0548eae..09060b61e 100644 --- a/examples/include.am +++ b/examples/include.am @@ -36,11 +36,20 @@ noinst_HEADERS += examples/mqttclient/mqttclient.h \ if BUILD_SN noinst_HEADERS += examples/sn-client/sn-client.h endif +if HAVE_LIBCURL +noinst_HEADERS += examples/mqttcurl.h +endif # MQTT Client Example +if HAVE_LIBCURL +examples_mqttclient_mqttclient_SOURCES = examples/mqttclient/mqttclient.c \ + examples/mqttcurl.c \ + examples/mqttexample.c +else examples_mqttclient_mqttclient_SOURCES = examples/mqttclient/mqttclient.c \ examples/mqttnet.c \ examples/mqttexample.c +endif examples_mqttclient_mqttclient_LDADD = src/libwolfmqtt.la examples_mqttclient_mqttclient_DEPENDENCIES = src/libwolfmqtt.la examples_mqttclient_mqttclient_CPPFLAGS = -I$(top_srcdir)/examples $(AM_CPPFLAGS) diff --git a/examples/mqttcurl.c b/examples/mqttcurl.c new file mode 100644 index 000000000..aeee4292f --- /dev/null +++ b/examples/mqttcurl.c @@ -0,0 +1,415 @@ +/* mqttcurl.c + * + * Copyright (C) 2006-2023 wolfSSL Inc. + * + * This file is part of wolfMQTT. + * + * wolfMQTT 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; either version 2 of the License, or + * (at your option) any later version. + * + * wolfMQTT 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Include the autoconf generated config.h */ +#ifdef HAVE_CONFIG_H + #include +#endif + +#include "examples/mqttcurl.h" + +#if !defined(ENABLE_MQTT_CURL) + #error "This example requires ENABLE_MQTT_CURL" +#endif + +/* How many times to retry after a timeout. */ +#define MQTT_CURL_NUM_RETRY (2) + +/* Private functions */ + +/* -------------------------------------------------------------------------- */ +/* CURL EASY SOCKET BACKEND EXAMPLE */ +/* -------------------------------------------------------------------------- */ + +static int +wait_on_socket(curl_socket_t sockfd, int for_recv, int timeout_ms) +{ + struct timeval tv; + fd_set infd; + fd_set outfd; + fd_set errfd; + + tv.tv_sec = timeout_ms / 1000; + tv.tv_usec = (int)(timeout_ms % 1000) * 1000; + + FD_ZERO(&infd); + FD_ZERO(&outfd); + FD_ZERO(&errfd); + + FD_SET(sockfd, &errfd); + + if(for_recv) { + FD_SET(sockfd, &infd); + } + else { + FD_SET(sockfd, &outfd); + } + + return select((int)sockfd + 1, &infd, &outfd, &errfd, &tv); +} + +static int NetConnect(void *context, const char* host, word16 port, + int timeout_ms) +{ + CURLcode res = 0; + CurlContext * ctx = (CurlContext*)context; + int use_tls = 0; + + if (context == NULL || host == NULL || *host == '\0') { + return MQTT_CODE_ERROR_BAD_ARG; + } + + if (ctx->mqttCtx == NULL) { + return MQTT_CODE_ERROR_BAD_ARG; + } + + if (port == MQTT_SECURE_PORT) { use_tls = 1; } + +#if defined(WOLFMQTT_DEBUG_CURL_VERBOSE) + PRINTF("NetConnect: Host %s, Port %u, Timeout %d ms, Use TLS %d", + host, port, timeout_ms, 0); +#endif + + ctx->curl = curl_easy_init(); + + if (ctx->curl == NULL) { + PRINTF("error: curl_easy_init returned NULL"); + return MQTT_CODE_ERROR_MEMORY; + } + + res = curl_easy_setopt(ctx->curl, CURLOPT_VERBOSE, 1L); + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(VERBOSE, 1L) returned: %d, %s", + res, curl_easy_strerror(res)); + return MQTT_CODE_ERROR_CURL; + } + + if (timeout_ms != 0) { + res = curl_easy_setopt(ctx->curl, CURLOPT_CONNECTTIMEOUT_MS, + timeout_ms); + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(CONNECTTIMEOUT_MS, %d) " + "returned %d", timeout_ms, res); + return MQTT_CODE_ERROR_CURL; + } + + res = curl_easy_setopt(ctx->curl, CURLOPT_TIMEOUT_MS, + timeout_ms); + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(TIMEOUT_MS, %d) " + "returned %d", timeout_ms, res); + return MQTT_CODE_ERROR_CURL; + } + } + + res = curl_easy_setopt(ctx->curl, CURLOPT_URL, host); + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(URL, %s) returned: %d", + host, res); + return MQTT_CODE_ERROR_CURL; + } + + res = curl_easy_setopt(ctx->curl, CURLOPT_PORT, port); + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(PORT, %d) returned: %d", + port, res); + return MQTT_CODE_ERROR_CURL; + } + + if (use_tls) { + res = curl_easy_setopt(ctx->curl, CURLOPT_SSLVERSION, + CURL_SSLVERSION_TLSv1_2); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(SSLVERSION) returned: %d", + res); + return MQTT_CODE_ERROR_CURL; + } + + /* With CURLOPT_CONNECT_ONLY this means do TLS by default. */ + res = curl_easy_setopt(ctx->curl, CURLOPT_DEFAULT_PROTOCOL, + "https"); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(DEFAULT_PROTOCOL) returned: %d", + res); + return MQTT_CODE_ERROR_CURL; + } + + /* Set path to Certificate Authority (CA) file bundle. */ + if (ctx->mqttCtx->ca_file != NULL) { + res = curl_easy_setopt(ctx->curl, CURLOPT_CAINFO, + ctx->mqttCtx->ca_file); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(CAINFO) returned: %d", + res); + return MQTT_CODE_ERROR_CURL; + } + } + + /* Set path to dir holding CA files. + * Unused at the moment. */ + /* + if (ctx->mqttCtx->ca_path != NULL) { + res = curl_easy_setopt(ctx->curl, CURLOPT_CAPATH, + ctx->mqttCtx->ca_path); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(CAPATH) returned: %d", + res); + return MQTT_CODE_ERROR_CURL; + } + } + */ + + /* Require peer and host verification. */ + res = curl_easy_setopt(ctx->curl, CURLOPT_SSL_VERIFYPEER, 1); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(SSL_VERIFYPEER) returned: %d", + res); + return MQTT_CODE_ERROR_CURL; + } + + res = curl_easy_setopt(ctx->curl, CURLOPT_SSL_VERIFYHOST, 2); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(SSL_VERIFYHOST) returned: %d", + res); + return MQTT_CODE_ERROR_CURL; + } + } + + res = curl_easy_setopt(ctx->curl, CURLOPT_CONNECT_ONLY, 1); + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(CONNECT_ONLY, 1) returned: %d", + res); + return MQTT_CODE_ERROR_CURL; + } + + res = curl_easy_perform(ctx->curl); + if (res != CURLE_OK) { + PRINTF("error: curl_easy_perform returned: %d, %s", res, + curl_easy_strerror(res)); + return MQTT_CODE_ERROR_CURL; + } + + ctx->stat = SOCK_CONN; + return MQTT_CODE_SUCCESS; +} + +static int NetWrite(void *context, const byte* buf, int buf_len, + int timeout_ms) +{ + CURLcode res = 0; + CurlContext * ctx = (CurlContext*)context; + size_t sent = 0; + curl_socket_t sockfd = 0; + + if (context == NULL || buf == NULL || buf_len == 0) { + return MQTT_CODE_ERROR_BAD_ARG; + } + + /* get the active socket from libcurl */ + res = curl_easy_getinfo(ctx->curl, CURLINFO_ACTIVESOCKET, &sockfd); + if (res != CURLE_OK) { + PRINTF("error: curl_easy_getinfo(CURLINFO_ACTIVESOCKET) returned %d", + res); + return MQTT_CODE_ERROR_CURL; + } + + /* check it makes sense */ + if (sockfd <= 0) { + PRINTF("error: libcurl sockfd: %d", sockfd); + return MQTT_CODE_ERROR_CURL; + } + +#if defined(WOLFMQTT_DEBUG_CURL_VERBOSE) + PRINTF("ctx->curl = %lld, sockfd = %d", (long long) ctx->curl, sockfd); +#endif + + /* A very simple retry with timeout example. This assumes the entire + * payload will be transfered in a single shot without buffering. */ + for (size_t i = 0; i < MQTT_CURL_NUM_RETRY; ++i) { + res = curl_easy_send(ctx->curl, buf, buf_len, &sent); + + if (res == CURLE_OK) { +#if defined(WOLFMQTT_DEBUG_CURL_VERBOSE) + PRINTF("info: curl_easy_send(%d) returned: %d, %s", buf_len, res, + curl_easy_strerror(res)); +#endif + break; + } + + if (res == CURLE_AGAIN) { +#if defined(WOLFMQTT_DEBUG_CURL_VERBOSE) + PRINTF("info: curl_easy_send(%d) returned: %d, %s", buf_len, res, + curl_easy_strerror(res)); +#endif + + if (wait_on_socket(sockfd, 0, timeout_ms) >= 0) { + continue; + } + } + + PRINTF("error: curl_easy_send(%d) returned: %d, %s", buf_len, res, + curl_easy_strerror(res)); + return MQTT_CODE_ERROR_CURL; + } + + if ((int) sent != buf_len) { + PRINTF("error: sent %d bytes, expected %d", (int)sent, buf_len); + return MQTT_CODE_ERROR_CURL; + } + + return buf_len; +} + +static int NetRead(void *context, byte* buf, int buf_len, + int timeout_ms) +{ + CURLcode res = 0; + CurlContext * ctx = (CurlContext*)context; + size_t recvd = 0; + curl_socket_t sockfd = 0; + + if (context == NULL || buf == NULL || buf_len == 0) { + return MQTT_CODE_ERROR_BAD_ARG; + } + + /* get the active socket from libcurl */ + res = curl_easy_getinfo(ctx->curl, CURLINFO_ACTIVESOCKET, &sockfd); + if (res != CURLE_OK) { + PRINTF("error: curl_easy_getinfo(CURLINFO_ACTIVESOCKET) returned %d", + res); + return MQTT_CODE_ERROR_CURL; + } + + /* check it makes sense */ + if (sockfd <= 0) { + PRINTF("error: libcurl sockfd: %d", sockfd); + return MQTT_CODE_ERROR_CURL; + } + +#if defined(WOLFMQTT_DEBUG_CURL_VERBOSE) + PRINTF("ctx->curl = %lld, sockfd = %d", (long long) ctx->curl, sockfd); +#endif + + /* A very simple retry with timeout example. This assumes the entire + * payload will be transfered in a single shot without buffering. */ + for (size_t i = 0; i < MQTT_CURL_NUM_RETRY; ++i) { + res = curl_easy_recv(ctx->curl, buf, buf_len, &recvd); + + if (res == CURLE_OK) { +#if defined(WOLFMQTT_DEBUG_CURL_VERBOSE) + PRINTF("info: curl_easy_recv(%d) returned: %d, %s", buf_len, res, + curl_easy_strerror(res)); +#endif + break; + } + + if (res == CURLE_AGAIN) { +#if defined(WOLFMQTT_DEBUG_CURL_VERBOSE) + PRINTF("info: curl_easy_recv(%d) returned: %d, %s", buf_len, res, + curl_easy_strerror(res)); +#endif + + if (wait_on_socket(sockfd, 1, timeout_ms) >= 0) { + continue; + } + } + + PRINTF("error: curl_easy_recv(%d) returned: %d, %s", buf_len, res, + curl_easy_strerror(res)); + return MQTT_CODE_ERROR_CURL; + } + + if ((int) recvd != buf_len) { + PRINTF("error: recvd %d bytes, expected %d", (int)recvd, buf_len); + return MQTT_CODE_ERROR_CURL; + } + + return buf_len; +} + +static int NetDisconnect(void *context) +{ + CurlContext * ctx = (CurlContext*)context; + + if (ctx == NULL) { + return MQTT_CODE_ERROR_BAD_ARG; + } + + if (ctx->curl != NULL) { +#if defined(WOLFMQTT_DEBUG_CURL_VERBOSE) + PRINTF("info: curl_easy_cleanup"); +#endif + curl_easy_cleanup(ctx->curl); + ctx->curl = NULL; + } + + return 0; +} + +/* Public Functions */ +int MqttClientNet_Init(MqttNet* net, MQTTCtx* mqttCtx) +{ + if (net) { + CurlContext* curlCtx; + + XMEMSET(net, 0, sizeof(MqttNet)); + net->connect = NetConnect; + net->read = NetRead; + net->write = NetWrite; + net->disconnect = NetDisconnect; + + curlCtx = (CurlContext*)WOLFMQTT_MALLOC(sizeof(CurlContext)); + if (curlCtx == NULL) { + return MQTT_CODE_ERROR_MEMORY; + } + net->context = curlCtx; + XMEMSET(curlCtx, 0, sizeof(CurlContext)); + curlCtx->curl = NULL; + curlCtx->fd = SOCKET_INVALID; + curlCtx->stat = SOCK_BEGIN; + curlCtx->mqttCtx = mqttCtx; + } + + return MQTT_CODE_SUCCESS; +} + +int MqttClientNet_DeInit(MqttNet* net) +{ + if (net) { + if (net->context) { + WOLFMQTT_FREE(net->context); + } + XMEMSET(net, 0, sizeof(MqttNet)); + } + return 0; +} + +int MqttClientNet_Wake(MqttNet* net) +{ + (void)net; + return 0; +} diff --git a/examples/mqttcurl.h b/examples/mqttcurl.h new file mode 100644 index 000000000..a97de6ab2 --- /dev/null +++ b/examples/mqttcurl.h @@ -0,0 +1,64 @@ +/* mqttcurl.h + * + * Copyright (C) 2006-2023 wolfSSL Inc. + * + * This file is part of wolfMQTT. + * + * wolfMQTT 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; either version 2 of the License, or + * (at your option) any later version. + * + * wolfMQTT 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#ifndef WOLFMQTT_EXAMPLE_CURL_H +#define WOLFMQTT_EXAMPLE_CURL_H + +#ifdef __cplusplus + extern "C" { +#endif + +#include "examples/mqttexample.h" +#include "examples/mqttport.h" + +/* Local context for Net callbacks */ +typedef enum { + SOCK_BEGIN = 0, + SOCK_CONN +} NB_Stat; + +/* Structure for Network Security */ +#ifdef ENABLE_MQTT_CURL +typedef struct _CurlContext { + CURL * curl; + SOCKET_T fd; + NB_Stat stat; + int sockRcRead; + int sockRcWrite; + int timeout_ms; + MQTTCtx* mqttCtx; +} CurlContext; +#endif + +/* Functions used to handle the MqttNet structure creation / destruction */ +int MqttClientNet_Init(MqttNet* net, MQTTCtx* mqttCtx); +int MqttClientNet_DeInit(MqttNet* net); +#ifdef WOLFMQTT_SN +int SN_ClientNet_Init(MqttNet* net, MQTTCtx* mqttCtx); +#endif + +int MqttClientNet_Wake(MqttNet* net); + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif /* WOLFMQTT_EXAMPLE_CURL_H */ diff --git a/examples/mqttexample.c b/examples/mqttexample.c index 07d6bf374..3ff69b255 100644 --- a/examples/mqttexample.c +++ b/examples/mqttexample.c @@ -235,6 +235,10 @@ void mqtt_show_usage(MQTTCtx* mqttCtx) #ifdef HAVE_PQC PRINTF("-Q Use Key Share with post-quantum algorithm"); #endif +#elif defined (ENABLE_MQTT_CURL) + PRINTF("-p Port to connect on, default: Normal %d, TLS %d", + MQTT_DEFAULT_PORT, MQTT_SECURE_PORT); + PRINTF("-A Load CA (validate peer)"); #else PRINTF("-p Port to connect on, default: %d", MQTT_DEFAULT_PORT); @@ -286,6 +290,9 @@ void mqtt_init_ctx(MQTTCtx* mqttCtx) #endif #ifdef WOLFMQTT_DEFAULT_TLS mqttCtx->use_tls = WOLFMQTT_DEFAULT_TLS; +#endif +#ifdef ENABLE_MQTT_CURL + mqttCtx->ca_file = NULL; #endif mqttCtx->app_name = "mqttclient"; mqttCtx->message = DEFAULT_MESSAGE; @@ -295,6 +302,11 @@ int mqtt_parse_args(MQTTCtx* mqttCtx, int argc, char** argv) { int rc; + #ifdef ENABLE_MQTT_CURL + #define MQTT_CURL_ARGS "A:" + #else + #define MQTT_CURL_ARGS "" + #endif #ifdef ENABLE_MQTT_TLS #define MQTT_TLS_ARGS "c:A:K:S;Q:" #else @@ -307,7 +319,7 @@ int mqtt_parse_args(MQTTCtx* mqttCtx, int argc, char** argv) #endif while ((rc = mygetopt(argc, argv, "?h:p:q:sk:i:lu:w:m:n:C:Tf:rtd" \ - MQTT_TLS_ARGS MQTT_V5_ARGS)) != -1) { + MQTT_CURL_ARGS MQTT_TLS_ARGS MQTT_V5_ARGS)) != -1) { switch ((char)rc) { case '?' : mqtt_show_usage(mqttCtx); @@ -387,6 +399,11 @@ int mqtt_parse_args(MQTTCtx* mqttCtx, int argc, char** argv) mqttCtx->debug_on = 1; break; + #if defined (ENABLE_MQTT_CURL) + case 'A': + mqttCtx->ca_file = myoptarg; + break; + #endif /* ENABLE_MQTT_CURL */ #ifdef ENABLE_MQTT_TLS case 'A': mTlsCaFile = myoptarg; @@ -412,7 +429,7 @@ int mqtt_parse_args(MQTTCtx* mqttCtx, int argc, char** argv) PRINTF("To use '-Q', build wolfSSL with --with-liboqs"); #endif break; - #endif + #endif /* ENABLE_MQTT_TLS */ #ifdef WOLFMQTT_V5 case 'P': diff --git a/examples/mqttexample.h b/examples/mqttexample.h index 315f7e186..f18ce3e8d 100644 --- a/examples/mqttexample.h +++ b/examples/mqttexample.h @@ -148,6 +148,9 @@ typedef struct _MQTTCtx { const char* message; const char* pub_file; const char* client_id; +#if defined (ENABLE_MQTT_CURL) + const char* ca_file; +#endif byte *tx_buf, *rx_buf; int return_code; int use_tls; diff --git a/src/include.am b/src/include.am index 08f9cd162..5647eed8f 100644 --- a/src/include.am +++ b/src/include.am @@ -4,9 +4,16 @@ lib_LTLIBRARIES+= src/libwolfmqtt.la + +if HAVE_LIBCURL +src_libwolfmqtt_la_SOURCES = src/mqtt_client.c \ + src/mqtt_packet.c \ + src/mqtt_curl.c +else src_libwolfmqtt_la_SOURCES = src/mqtt_client.c \ src/mqtt_packet.c \ src/mqtt_socket.c +endif if BUILD_SN src_libwolfmqtt_la_SOURCES += src/mqtt_sn_client.c \ diff --git a/src/mqtt_client.c b/src/mqtt_client.c index 4f1aa7fdd..57ee94628 100644 --- a/src/mqtt_client.c +++ b/src/mqtt_client.c @@ -2944,6 +2944,10 @@ const char* MqttClient_ReturnCodeToString(int return_code) return "Error (System resource failed)"; case MQTT_CODE_ERROR_NOT_FOUND: return "Error (Not found)"; +#if defined(ENABLE_MQTT_CURL) + case MQTT_CODE_ERROR_CURL: + return "Error (libcurl)"; +#endif #ifdef WOLFMQTT_V5 /* MQTT v5 Reason code strings */ diff --git a/src/mqtt_curl.c b/src/mqtt_curl.c new file mode 100644 index 000000000..b47b1c08f --- /dev/null +++ b/src/mqtt_curl.c @@ -0,0 +1,239 @@ +/* mqtt_curl.c + * + * Copyright (C) 2006-2023 wolfSSL Inc. + * + * This file is part of wolfMQTT. + * + * wolfMQTT 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; either version 2 of the License, or + * (at your option) any later version. + * + * wolfMQTT 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Include the autoconf generated config.h */ +#ifdef HAVE_CONFIG_H + #include +#endif + +#ifdef WOLFMQTT_NONBLOCK + /* need EWOULDBLOCK and EAGAIN */ + #if defined(MICROCHIP_MPLAB_HARMONY) && \ + ((__XC32_VERSION < 4000) || (__XC32_VERSION == 243739000)) + /* xc32 versions >= v4.0 no longer have sys/errno.h */ + #include + #endif + #include +#endif + +#include "wolfmqtt/mqtt_client.h" +#include "wolfmqtt/mqtt_curl.h" + +/* Options */ +#ifdef WOLFMQTT_NO_STDIO + #undef WOLFMQTT_DEBUG_SOCKET +#endif + +/* #define WOLFMQTT_TEST_NONBLOCK */ +#ifdef WOLFMQTT_TEST_NONBLOCK + #define WOLFMQTT_TEST_NONBLOCK_TIMES 1 +#endif + +/* Public Functions */ + +int MqttSocket_Init(MqttClient *client, MqttNet *net) +{ + int rc = MQTT_CODE_ERROR_BAD_ARG; + if (client) { + curl_global_init(CURL_GLOBAL_DEFAULT); + + client->net = net; + MqttClient_Flags(client, (MQTT_CLIENT_FLAG_IS_CONNECTED | + MQTT_CLIENT_FLAG_IS_TLS), 0);; + + /* Validate callbacks are not null! */ + if (net && net->connect && net->read && net->write && net->disconnect) { + rc = MQTT_CODE_SUCCESS; + } + } + return rc; +} + +int MqttSocket_Write(MqttClient *client, const byte* buf, int buf_len, + int timeout_ms) +{ + int rc = 0; + + /* Validate arguments */ + if (client == NULL || client->net == NULL || client->net->write == NULL || + buf == NULL || buf_len <= 0) { + return MQTT_CODE_ERROR_BAD_ARG; + } + + /* check for buffer position overflow */ + if (client->write.pos >= buf_len) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + do { + rc = client->net->write(client->net->context, &buf[client->write.pos], + buf_len - client->write.pos, timeout_ms); + if (rc <= 0) { + break; + } + client->write.pos += rc; + client->write.total += rc; + } while (client->write.pos < buf_len); + + /* handle return code */ + if (rc > 0) { + /* return length write and reset position */ + rc = client->write.pos; + client->write.pos = 0; + } + +#ifdef WOLFMQTT_DEBUG_SOCKET + if (rc != 0 && rc != MQTT_CODE_CONTINUE) { /* hide in non-blocking case */ + PRINTF("MqttSocket_Write: Len=%d, Rc=%d", buf_len, rc); + } +#endif + + return rc; +} + +int MqttSocket_Read(MqttClient *client, byte* buf, int buf_len, int timeout_ms) +{ + int rc = 0; + + /* Validate arguments */ + if (client == NULL || client->net == NULL || client->net->read == NULL || + buf == NULL || buf_len <= 0) { + return MQTT_CODE_ERROR_BAD_ARG; + } + + /* check for buffer position overflow */ + if (client->read.pos >= buf_len) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + do { + rc = client->net->read(client->net->context, &buf[client->read.pos], + buf_len - client->read.pos, timeout_ms); + if (rc <= 0) { + break; + } + client->read.pos += rc; + client->read.total += rc; + } while (client->read.pos < buf_len); + + /* handle return code */ + if (rc > 0) { + /* return length read and reset position */ + rc = client->read.pos; + client->read.pos = 0; + } + +#ifdef WOLFMQTT_DEBUG_SOCKET + if (rc != 0 && rc != MQTT_CODE_CONTINUE) { /* hide in non-blocking case */ + PRINTF("MqttSocket_Read: Len=%d, Rc=%d", buf_len, rc); + } +#endif + + return rc; +} + +#ifdef WOLFMQTT_SN +int MqttSocket_Peek(MqttClient *client, byte* buf, int buf_len, int timeout_ms) +{ + int rc; + + /* Validate arguments */ + if (client == NULL || client->net == NULL || client->net->peek == NULL || + buf == NULL || buf_len <= 0) { + return MQTT_CODE_ERROR_BAD_ARG; + } + + /* check for buffer position overflow */ + if (client->read.pos >= buf_len) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + rc = client->net->peek(client->net->context, buf, buf_len, timeout_ms); + if (rc > 0) { + #ifdef WOLFMQTT_DEBUG_SOCKET + PRINTF("MqttSocket_Peek: Len=%d, Rc=%d", buf_len, rc); + #endif + + /* return length read and reset position */ + client->read.pos = 0; + } + + return rc; +} +#endif /* WOLFMQTT_SN */ + +int MqttSocket_Connect(MqttClient *client, const char* host, word16 port, + int timeout_ms, int use_tls, MqttTlsCb cb) +{ + int rc = MQTT_CODE_SUCCESS; + + /* Validate arguments */ + if (client == NULL || client->net == NULL || + client->net->connect == NULL) { + return MQTT_CODE_ERROR_BAD_ARG; + } + + if ((MqttClient_Flags(client, 0, 0) & MQTT_CLIENT_FLAG_IS_CONNECTED) == 0) { + /* Validate port */ + if (port == 0) { + port = (use_tls) ? MQTT_SECURE_PORT : MQTT_DEFAULT_PORT; + } + + /* Connect to host */ + rc = client->net->connect(client->net->context, host, port, timeout_ms); + if (rc != MQTT_CODE_SUCCESS) { + return rc; + } + MqttClient_Flags(client, 0, MQTT_CLIENT_FLAG_IS_CONNECTED); + } + + (void)cb; + +#ifdef WOLFMQTT_DEBUG_SOCKET + PRINTF("MqttSocket_Connect: Rc=%d", rc); +#endif + + return rc; +} + +int MqttSocket_Disconnect(MqttClient *client) +{ + int rc = MQTT_CODE_SUCCESS; + if (client) { + /* Make sure socket is closed */ + if (client->net && client->net->disconnect) { + rc = client->net->disconnect(client->net->context); + } + MqttClient_Flags(client, MQTT_CLIENT_FLAG_IS_CONNECTED, 0); + + curl_global_cleanup(); + } +#ifdef WOLFMQTT_DEBUG_SOCKET + PRINTF("MqttSocket_Disconnect: Rc=%d", rc); +#endif + + /* Check for error */ + if (rc < 0) { + rc = MQTT_CODE_ERROR_NETWORK; + } + + return rc; +} diff --git a/wolfmqtt/include.am b/wolfmqtt/include.am index 77dfcc9da..d48ec4a3b 100644 --- a/wolfmqtt/include.am +++ b/wolfmqtt/include.am @@ -11,6 +11,9 @@ nobase_include_HEADERS+= \ wolfmqtt/visibility.h \ wolfmqtt/options.h \ wolfmqtt/vs_settings.h +if HAVE_LIBCURL +nobase_include_HEADERS+= wolfmqtt/mqtt_curl.h +endif if BUILD_SN nobase_include_HEADERS+= wolfmqtt/mqtt_sn_client.h \ diff --git a/wolfmqtt/mqtt_client.h b/wolfmqtt/mqtt_client.h index ddb70afe1..c65f3adbd 100644 --- a/wolfmqtt/mqtt_client.h +++ b/wolfmqtt/mqtt_client.h @@ -40,7 +40,11 @@ #endif #include "wolfmqtt/mqtt_types.h" #include "wolfmqtt/mqtt_packet.h" -#include "wolfmqtt/mqtt_socket.h" +#ifdef ENABLE_MQTT_CURL + #include "wolfmqtt/mqtt_curl.h" +#else + #include "wolfmqtt/mqtt_socket.h" +#endif #ifdef WOLFMQTT_SN #include "wolfmqtt/mqtt_sn_packet.h" diff --git a/wolfmqtt/mqtt_curl.h b/wolfmqtt/mqtt_curl.h new file mode 100644 index 000000000..d51f5d3da --- /dev/null +++ b/wolfmqtt/mqtt_curl.h @@ -0,0 +1,87 @@ +/* mqtt_curl.h + * + * Copyright (C) 2006-2023 wolfSSL Inc. + * + * This file is part of wolfMQTT. + * + * wolfMQTT 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; either version 2 of the License, or + * (at your option) any later version. + * + * wolfMQTT 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#ifndef WOLFMQTT_CURL_H +#define WOLFMQTT_CURL_H + +#include + +#ifdef __cplusplus + extern "C" { +#endif + +#include "wolfmqtt/mqtt_types.h" + +/* Default Port Numbers */ +#define MQTT_DEFAULT_PORT 1883 +#define MQTT_SECURE_PORT 8883 + + +struct _MqttClient; + +/* Function callbacks */ +typedef int (*MqttTlsCb)(struct _MqttClient* client); + +typedef int (*MqttNetConnectCb)(void *context, + const char* host, word16 port, int timeout_ms); +typedef int (*MqttNetWriteCb)(void *context, + const byte* buf, int buf_len, int timeout_ms); +typedef int (*MqttNetReadCb)(void *context, + byte* buf, int buf_len, int timeout_ms); +#ifdef WOLFMQTT_SN +typedef int (*MqttNetPeekCb)(void *context, + byte* buf, int buf_len, int timeout_ms); +#endif +typedef int (*MqttNetDisconnectCb)(void *context); + +/* Structure for Network callbacks */ +typedef struct _MqttNet { + void *context; + MqttNetConnectCb connect; + MqttNetReadCb read; + MqttNetWriteCb write; + MqttNetDisconnectCb disconnect; +#ifdef WOLFMQTT_SN + MqttNetPeekCb peek; + void *multi_ctx; +#endif +} MqttNet; + +/* MQTT SOCKET APPLICATION INTERFACE */ +WOLFMQTT_LOCAL int MqttSocket_Init(struct _MqttClient *client, MqttNet* net); +WOLFMQTT_LOCAL int MqttSocket_Write(struct _MqttClient *client, const byte* buf, + int buf_len, int timeout_ms); +WOLFMQTT_LOCAL int MqttSocket_Read(struct _MqttClient *client, byte* buf, + int buf_len, int timeout_ms); +#ifdef WOLFMQTT_SN +WOLFMQTT_LOCAL int MqttSocket_Peek(struct _MqttClient *client, byte* buf, + int buf_len, int timeout_ms); +#endif /* WOLFMQTT_SN */ +WOLFMQTT_LOCAL int MqttSocket_Connect(struct _MqttClient *client, + const char* host, word16 port, int timeout_ms, int use_tls, + MqttTlsCb cb); +WOLFMQTT_LOCAL int MqttSocket_Disconnect(struct _MqttClient *client); + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif /* WOLFMQTT_CURL_H */ diff --git a/wolfmqtt/mqtt_packet.h b/wolfmqtt/mqtt_packet.h index efa2d46b5..239c374a5 100644 --- a/wolfmqtt/mqtt_packet.h +++ b/wolfmqtt/mqtt_packet.h @@ -32,7 +32,11 @@ #endif #include "wolfmqtt/mqtt_types.h" -#include "wolfmqtt/mqtt_socket.h" +#ifdef ENABLE_MQTT_CURL + #include "wolfmqtt/mqtt_curl.h" +#else + #include "wolfmqtt/mqtt_socket.h" +#endif /* Size of a data length elements in protocol */ diff --git a/wolfmqtt/mqtt_types.h b/wolfmqtt/mqtt_types.h index cc47234d1..ae32e70dc 100644 --- a/wolfmqtt/mqtt_types.h +++ b/wolfmqtt/mqtt_types.h @@ -194,6 +194,10 @@ enum MqttPacketResponseCodes { MQTT_CODE_ERROR_CALLBACK = -13, MQTT_CODE_ERROR_SYSTEM = -14, MQTT_CODE_ERROR_NOT_FOUND = -15, +#if defined(ENABLE_MQTT_CURL) + MQTT_CODE_ERROR_CURL = -16, /* An error in libcurl that is not clearly + * a network, memory, TLS, or system error. */ +#endif MQTT_CODE_CONTINUE = -101, MQTT_CODE_STDIN_WAKE = -102, From e25d2a6d712c2acf9ab241dc45a2c727e484c426 Mon Sep 17 00:00:00 2001 From: philljj Date: Wed, 6 Dec 2023 22:59:06 -0600 Subject: [PATCH 55/62] Add curl easy socket backend: consolidate mqttcurl into mqttnet. --- examples/include.am | 9 - examples/mqttcurl.c | 415 -------------------------------------------- examples/mqttcurl.h | 64 ------- examples/mqttnet.c | 388 +++++++++++++++++++++++++++++++++++++++++ examples/mqttnet.h | 3 + src/mqtt_curl.c | 4 + 6 files changed, 395 insertions(+), 488 deletions(-) delete mode 100644 examples/mqttcurl.c delete mode 100644 examples/mqttcurl.h diff --git a/examples/include.am b/examples/include.am index 09060b61e..dd0548eae 100644 --- a/examples/include.am +++ b/examples/include.am @@ -36,20 +36,11 @@ noinst_HEADERS += examples/mqttclient/mqttclient.h \ if BUILD_SN noinst_HEADERS += examples/sn-client/sn-client.h endif -if HAVE_LIBCURL -noinst_HEADERS += examples/mqttcurl.h -endif # MQTT Client Example -if HAVE_LIBCURL -examples_mqttclient_mqttclient_SOURCES = examples/mqttclient/mqttclient.c \ - examples/mqttcurl.c \ - examples/mqttexample.c -else examples_mqttclient_mqttclient_SOURCES = examples/mqttclient/mqttclient.c \ examples/mqttnet.c \ examples/mqttexample.c -endif examples_mqttclient_mqttclient_LDADD = src/libwolfmqtt.la examples_mqttclient_mqttclient_DEPENDENCIES = src/libwolfmqtt.la examples_mqttclient_mqttclient_CPPFLAGS = -I$(top_srcdir)/examples $(AM_CPPFLAGS) diff --git a/examples/mqttcurl.c b/examples/mqttcurl.c deleted file mode 100644 index aeee4292f..000000000 --- a/examples/mqttcurl.c +++ /dev/null @@ -1,415 +0,0 @@ -/* mqttcurl.c - * - * Copyright (C) 2006-2023 wolfSSL Inc. - * - * This file is part of wolfMQTT. - * - * wolfMQTT 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; either version 2 of the License, or - * (at your option) any later version. - * - * wolfMQTT 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, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA - */ - -/* Include the autoconf generated config.h */ -#ifdef HAVE_CONFIG_H - #include -#endif - -#include "examples/mqttcurl.h" - -#if !defined(ENABLE_MQTT_CURL) - #error "This example requires ENABLE_MQTT_CURL" -#endif - -/* How many times to retry after a timeout. */ -#define MQTT_CURL_NUM_RETRY (2) - -/* Private functions */ - -/* -------------------------------------------------------------------------- */ -/* CURL EASY SOCKET BACKEND EXAMPLE */ -/* -------------------------------------------------------------------------- */ - -static int -wait_on_socket(curl_socket_t sockfd, int for_recv, int timeout_ms) -{ - struct timeval tv; - fd_set infd; - fd_set outfd; - fd_set errfd; - - tv.tv_sec = timeout_ms / 1000; - tv.tv_usec = (int)(timeout_ms % 1000) * 1000; - - FD_ZERO(&infd); - FD_ZERO(&outfd); - FD_ZERO(&errfd); - - FD_SET(sockfd, &errfd); - - if(for_recv) { - FD_SET(sockfd, &infd); - } - else { - FD_SET(sockfd, &outfd); - } - - return select((int)sockfd + 1, &infd, &outfd, &errfd, &tv); -} - -static int NetConnect(void *context, const char* host, word16 port, - int timeout_ms) -{ - CURLcode res = 0; - CurlContext * ctx = (CurlContext*)context; - int use_tls = 0; - - if (context == NULL || host == NULL || *host == '\0') { - return MQTT_CODE_ERROR_BAD_ARG; - } - - if (ctx->mqttCtx == NULL) { - return MQTT_CODE_ERROR_BAD_ARG; - } - - if (port == MQTT_SECURE_PORT) { use_tls = 1; } - -#if defined(WOLFMQTT_DEBUG_CURL_VERBOSE) - PRINTF("NetConnect: Host %s, Port %u, Timeout %d ms, Use TLS %d", - host, port, timeout_ms, 0); -#endif - - ctx->curl = curl_easy_init(); - - if (ctx->curl == NULL) { - PRINTF("error: curl_easy_init returned NULL"); - return MQTT_CODE_ERROR_MEMORY; - } - - res = curl_easy_setopt(ctx->curl, CURLOPT_VERBOSE, 1L); - if (res != CURLE_OK) { - PRINTF("error: curl_easy_setopt(VERBOSE, 1L) returned: %d, %s", - res, curl_easy_strerror(res)); - return MQTT_CODE_ERROR_CURL; - } - - if (timeout_ms != 0) { - res = curl_easy_setopt(ctx->curl, CURLOPT_CONNECTTIMEOUT_MS, - timeout_ms); - if (res != CURLE_OK) { - PRINTF("error: curl_easy_setopt(CONNECTTIMEOUT_MS, %d) " - "returned %d", timeout_ms, res); - return MQTT_CODE_ERROR_CURL; - } - - res = curl_easy_setopt(ctx->curl, CURLOPT_TIMEOUT_MS, - timeout_ms); - if (res != CURLE_OK) { - PRINTF("error: curl_easy_setopt(TIMEOUT_MS, %d) " - "returned %d", timeout_ms, res); - return MQTT_CODE_ERROR_CURL; - } - } - - res = curl_easy_setopt(ctx->curl, CURLOPT_URL, host); - if (res != CURLE_OK) { - PRINTF("error: curl_easy_setopt(URL, %s) returned: %d", - host, res); - return MQTT_CODE_ERROR_CURL; - } - - res = curl_easy_setopt(ctx->curl, CURLOPT_PORT, port); - if (res != CURLE_OK) { - PRINTF("error: curl_easy_setopt(PORT, %d) returned: %d", - port, res); - return MQTT_CODE_ERROR_CURL; - } - - if (use_tls) { - res = curl_easy_setopt(ctx->curl, CURLOPT_SSLVERSION, - CURL_SSLVERSION_TLSv1_2); - - if (res != CURLE_OK) { - PRINTF("error: curl_easy_setopt(SSLVERSION) returned: %d", - res); - return MQTT_CODE_ERROR_CURL; - } - - /* With CURLOPT_CONNECT_ONLY this means do TLS by default. */ - res = curl_easy_setopt(ctx->curl, CURLOPT_DEFAULT_PROTOCOL, - "https"); - - if (res != CURLE_OK) { - PRINTF("error: curl_easy_setopt(DEFAULT_PROTOCOL) returned: %d", - res); - return MQTT_CODE_ERROR_CURL; - } - - /* Set path to Certificate Authority (CA) file bundle. */ - if (ctx->mqttCtx->ca_file != NULL) { - res = curl_easy_setopt(ctx->curl, CURLOPT_CAINFO, - ctx->mqttCtx->ca_file); - - if (res != CURLE_OK) { - PRINTF("error: curl_easy_setopt(CAINFO) returned: %d", - res); - return MQTT_CODE_ERROR_CURL; - } - } - - /* Set path to dir holding CA files. - * Unused at the moment. */ - /* - if (ctx->mqttCtx->ca_path != NULL) { - res = curl_easy_setopt(ctx->curl, CURLOPT_CAPATH, - ctx->mqttCtx->ca_path); - - if (res != CURLE_OK) { - PRINTF("error: curl_easy_setopt(CAPATH) returned: %d", - res); - return MQTT_CODE_ERROR_CURL; - } - } - */ - - /* Require peer and host verification. */ - res = curl_easy_setopt(ctx->curl, CURLOPT_SSL_VERIFYPEER, 1); - - if (res != CURLE_OK) { - PRINTF("error: curl_easy_setopt(SSL_VERIFYPEER) returned: %d", - res); - return MQTT_CODE_ERROR_CURL; - } - - res = curl_easy_setopt(ctx->curl, CURLOPT_SSL_VERIFYHOST, 2); - - if (res != CURLE_OK) { - PRINTF("error: curl_easy_setopt(SSL_VERIFYHOST) returned: %d", - res); - return MQTT_CODE_ERROR_CURL; - } - } - - res = curl_easy_setopt(ctx->curl, CURLOPT_CONNECT_ONLY, 1); - if (res != CURLE_OK) { - PRINTF("error: curl_easy_setopt(CONNECT_ONLY, 1) returned: %d", - res); - return MQTT_CODE_ERROR_CURL; - } - - res = curl_easy_perform(ctx->curl); - if (res != CURLE_OK) { - PRINTF("error: curl_easy_perform returned: %d, %s", res, - curl_easy_strerror(res)); - return MQTT_CODE_ERROR_CURL; - } - - ctx->stat = SOCK_CONN; - return MQTT_CODE_SUCCESS; -} - -static int NetWrite(void *context, const byte* buf, int buf_len, - int timeout_ms) -{ - CURLcode res = 0; - CurlContext * ctx = (CurlContext*)context; - size_t sent = 0; - curl_socket_t sockfd = 0; - - if (context == NULL || buf == NULL || buf_len == 0) { - return MQTT_CODE_ERROR_BAD_ARG; - } - - /* get the active socket from libcurl */ - res = curl_easy_getinfo(ctx->curl, CURLINFO_ACTIVESOCKET, &sockfd); - if (res != CURLE_OK) { - PRINTF("error: curl_easy_getinfo(CURLINFO_ACTIVESOCKET) returned %d", - res); - return MQTT_CODE_ERROR_CURL; - } - - /* check it makes sense */ - if (sockfd <= 0) { - PRINTF("error: libcurl sockfd: %d", sockfd); - return MQTT_CODE_ERROR_CURL; - } - -#if defined(WOLFMQTT_DEBUG_CURL_VERBOSE) - PRINTF("ctx->curl = %lld, sockfd = %d", (long long) ctx->curl, sockfd); -#endif - - /* A very simple retry with timeout example. This assumes the entire - * payload will be transfered in a single shot without buffering. */ - for (size_t i = 0; i < MQTT_CURL_NUM_RETRY; ++i) { - res = curl_easy_send(ctx->curl, buf, buf_len, &sent); - - if (res == CURLE_OK) { -#if defined(WOLFMQTT_DEBUG_CURL_VERBOSE) - PRINTF("info: curl_easy_send(%d) returned: %d, %s", buf_len, res, - curl_easy_strerror(res)); -#endif - break; - } - - if (res == CURLE_AGAIN) { -#if defined(WOLFMQTT_DEBUG_CURL_VERBOSE) - PRINTF("info: curl_easy_send(%d) returned: %d, %s", buf_len, res, - curl_easy_strerror(res)); -#endif - - if (wait_on_socket(sockfd, 0, timeout_ms) >= 0) { - continue; - } - } - - PRINTF("error: curl_easy_send(%d) returned: %d, %s", buf_len, res, - curl_easy_strerror(res)); - return MQTT_CODE_ERROR_CURL; - } - - if ((int) sent != buf_len) { - PRINTF("error: sent %d bytes, expected %d", (int)sent, buf_len); - return MQTT_CODE_ERROR_CURL; - } - - return buf_len; -} - -static int NetRead(void *context, byte* buf, int buf_len, - int timeout_ms) -{ - CURLcode res = 0; - CurlContext * ctx = (CurlContext*)context; - size_t recvd = 0; - curl_socket_t sockfd = 0; - - if (context == NULL || buf == NULL || buf_len == 0) { - return MQTT_CODE_ERROR_BAD_ARG; - } - - /* get the active socket from libcurl */ - res = curl_easy_getinfo(ctx->curl, CURLINFO_ACTIVESOCKET, &sockfd); - if (res != CURLE_OK) { - PRINTF("error: curl_easy_getinfo(CURLINFO_ACTIVESOCKET) returned %d", - res); - return MQTT_CODE_ERROR_CURL; - } - - /* check it makes sense */ - if (sockfd <= 0) { - PRINTF("error: libcurl sockfd: %d", sockfd); - return MQTT_CODE_ERROR_CURL; - } - -#if defined(WOLFMQTT_DEBUG_CURL_VERBOSE) - PRINTF("ctx->curl = %lld, sockfd = %d", (long long) ctx->curl, sockfd); -#endif - - /* A very simple retry with timeout example. This assumes the entire - * payload will be transfered in a single shot without buffering. */ - for (size_t i = 0; i < MQTT_CURL_NUM_RETRY; ++i) { - res = curl_easy_recv(ctx->curl, buf, buf_len, &recvd); - - if (res == CURLE_OK) { -#if defined(WOLFMQTT_DEBUG_CURL_VERBOSE) - PRINTF("info: curl_easy_recv(%d) returned: %d, %s", buf_len, res, - curl_easy_strerror(res)); -#endif - break; - } - - if (res == CURLE_AGAIN) { -#if defined(WOLFMQTT_DEBUG_CURL_VERBOSE) - PRINTF("info: curl_easy_recv(%d) returned: %d, %s", buf_len, res, - curl_easy_strerror(res)); -#endif - - if (wait_on_socket(sockfd, 1, timeout_ms) >= 0) { - continue; - } - } - - PRINTF("error: curl_easy_recv(%d) returned: %d, %s", buf_len, res, - curl_easy_strerror(res)); - return MQTT_CODE_ERROR_CURL; - } - - if ((int) recvd != buf_len) { - PRINTF("error: recvd %d bytes, expected %d", (int)recvd, buf_len); - return MQTT_CODE_ERROR_CURL; - } - - return buf_len; -} - -static int NetDisconnect(void *context) -{ - CurlContext * ctx = (CurlContext*)context; - - if (ctx == NULL) { - return MQTT_CODE_ERROR_BAD_ARG; - } - - if (ctx->curl != NULL) { -#if defined(WOLFMQTT_DEBUG_CURL_VERBOSE) - PRINTF("info: curl_easy_cleanup"); -#endif - curl_easy_cleanup(ctx->curl); - ctx->curl = NULL; - } - - return 0; -} - -/* Public Functions */ -int MqttClientNet_Init(MqttNet* net, MQTTCtx* mqttCtx) -{ - if (net) { - CurlContext* curlCtx; - - XMEMSET(net, 0, sizeof(MqttNet)); - net->connect = NetConnect; - net->read = NetRead; - net->write = NetWrite; - net->disconnect = NetDisconnect; - - curlCtx = (CurlContext*)WOLFMQTT_MALLOC(sizeof(CurlContext)); - if (curlCtx == NULL) { - return MQTT_CODE_ERROR_MEMORY; - } - net->context = curlCtx; - XMEMSET(curlCtx, 0, sizeof(CurlContext)); - curlCtx->curl = NULL; - curlCtx->fd = SOCKET_INVALID; - curlCtx->stat = SOCK_BEGIN; - curlCtx->mqttCtx = mqttCtx; - } - - return MQTT_CODE_SUCCESS; -} - -int MqttClientNet_DeInit(MqttNet* net) -{ - if (net) { - if (net->context) { - WOLFMQTT_FREE(net->context); - } - XMEMSET(net, 0, sizeof(MqttNet)); - } - return 0; -} - -int MqttClientNet_Wake(MqttNet* net) -{ - (void)net; - return 0; -} diff --git a/examples/mqttcurl.h b/examples/mqttcurl.h deleted file mode 100644 index a97de6ab2..000000000 --- a/examples/mqttcurl.h +++ /dev/null @@ -1,64 +0,0 @@ -/* mqttcurl.h - * - * Copyright (C) 2006-2023 wolfSSL Inc. - * - * This file is part of wolfMQTT. - * - * wolfMQTT 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; either version 2 of the License, or - * (at your option) any later version. - * - * wolfMQTT 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, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA - */ - -#ifndef WOLFMQTT_EXAMPLE_CURL_H -#define WOLFMQTT_EXAMPLE_CURL_H - -#ifdef __cplusplus - extern "C" { -#endif - -#include "examples/mqttexample.h" -#include "examples/mqttport.h" - -/* Local context for Net callbacks */ -typedef enum { - SOCK_BEGIN = 0, - SOCK_CONN -} NB_Stat; - -/* Structure for Network Security */ -#ifdef ENABLE_MQTT_CURL -typedef struct _CurlContext { - CURL * curl; - SOCKET_T fd; - NB_Stat stat; - int sockRcRead; - int sockRcWrite; - int timeout_ms; - MQTTCtx* mqttCtx; -} CurlContext; -#endif - -/* Functions used to handle the MqttNet structure creation / destruction */ -int MqttClientNet_Init(MqttNet* net, MQTTCtx* mqttCtx); -int MqttClientNet_DeInit(MqttNet* net); -#ifdef WOLFMQTT_SN -int SN_ClientNet_Init(MqttNet* net, MQTTCtx* mqttCtx); -#endif - -int MqttClientNet_Wake(MqttNet* net); - -#ifdef __cplusplus - } /* extern "C" */ -#endif - -#endif /* WOLFMQTT_EXAMPLE_CURL_H */ diff --git a/examples/mqttnet.c b/examples/mqttnet.c index 3f811b74f..98167f4af 100644 --- a/examples/mqttnet.c +++ b/examples/mqttnet.c @@ -377,6 +377,391 @@ static int NetRead(void *context, byte* buf, int buf_len, return rc; } + +/* -------------------------------------------------------------------------- */ +/* CURL EASY SOCKET BACKEND EXAMPLE */ +/* -------------------------------------------------------------------------- */ +#elif defined(ENABLE_MQTT_CURL) + +/* How many times to retry after a timeout. */ +#define MQTT_CURL_NUM_RETRY (2) + +static int +mqttcurl_wait(curl_socket_t sockfd, int for_recv, int timeout_ms) +{ + struct timeval tv; + fd_set infd; + fd_set outfd; + fd_set errfd; + int rc = 0; + + tv.tv_sec = timeout_ms / 1000; + tv.tv_usec = (int)(timeout_ms % 1000) * 1000; + + FD_ZERO(&infd); + FD_ZERO(&outfd); + FD_ZERO(&errfd); + + FD_SET(sockfd, &errfd); + + if(for_recv) { + FD_SET(sockfd, &infd); + } + else { + FD_SET(sockfd, &outfd); + } + + rc = select((int)sockfd + 1, &infd, &outfd, &errfd, &tv); + + if (rc > 0) { + if (for_recv && FD_ISSET(sockfd, &infd)) { + rc = 1; + } + else if (!for_recv && FD_ISSET(sockfd, &outfd)) { + rc = 1; + } + else if (FD_ISSET(sockfd, &errfd)) { + rc = -1; + } + } + + return rc; +} + +static int +mqttcurl_connect(SocketContext * ctx, const char* host, word16 port, + int timeout_ms) +{ + CURLcode res = 0; + int use_tls = 0; + + if (ctx == NULL) { + return MQTT_CODE_ERROR_BAD_ARG; + } + + if (port == MQTT_SECURE_PORT) { use_tls = 1; } + + res = curl_easy_setopt(ctx->curl, CURLOPT_VERBOSE, 1L); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(VERBOSE, 1L) returned: %d, %s", + res, curl_easy_strerror(res)); + return MQTT_CODE_ERROR_CURL; + } + + if (timeout_ms != 0) { + res = curl_easy_setopt(ctx->curl, CURLOPT_CONNECTTIMEOUT_MS, + timeout_ms); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(CONNECTTIMEOUT_MS, %d) " + "returned %d", timeout_ms, res); + return MQTT_CODE_ERROR_CURL; + } + + res = curl_easy_setopt(ctx->curl, CURLOPT_TIMEOUT_MS, + timeout_ms); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(TIMEOUT_MS, %d) " + "returned %d", timeout_ms, res); + return MQTT_CODE_ERROR_CURL; + } + } + + res = curl_easy_setopt(ctx->curl, CURLOPT_URL, host); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(URL, %s) returned: %d", + host, res); + return MQTT_CODE_ERROR_CURL; + } + + res = curl_easy_setopt(ctx->curl, CURLOPT_PORT, port); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(PORT, %d) returned: %d", + port, res); + return MQTT_CODE_ERROR_CURL; + } + + if (use_tls) { + /* Set TLS specific options. */ + res = curl_easy_setopt(ctx->curl, CURLOPT_SSLVERSION, + CURL_SSLVERSION_TLSv1_2); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(SSLVERSION) returned: %d", + res); + return MQTT_CODE_ERROR_CURL; + } + + /* With CURLOPT_CONNECT_ONLY this means do TLS by default. */ + res = curl_easy_setopt(ctx->curl, CURLOPT_DEFAULT_PROTOCOL, + "https"); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(DEFAULT_PROTOCOL) returned: %d", + res); + return MQTT_CODE_ERROR_CURL; + } + + /* Set path to Certificate Authority (CA) file bundle. */ + if (ctx->mqttCtx->ca_file != NULL) { + res = curl_easy_setopt(ctx->curl, CURLOPT_CAINFO, + ctx->mqttCtx->ca_file); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(CAINFO) returned: %d", + res); + return MQTT_CODE_ERROR_CURL; + } + } + + /* Set path to dir holding CA files. + * Unused at the moment. */ + /* + if (ctx->mqttCtx->ca_path != NULL) { + res = curl_easy_setopt(ctx->curl, CURLOPT_CAPATH, + ctx->mqttCtx->ca_path); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(CAPATH) returned: %d", + res); + return MQTT_CODE_ERROR_CURL; + } + } + */ + + /* Require peer and host verification. */ + res = curl_easy_setopt(ctx->curl, CURLOPT_SSL_VERIFYPEER, 1); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(SSL_VERIFYPEER) returned: %d", + res); + return MQTT_CODE_ERROR_CURL; + } + + res = curl_easy_setopt(ctx->curl, CURLOPT_SSL_VERIFYHOST, 2); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(SSL_VERIFYHOST) returned: %d", + res); + return MQTT_CODE_ERROR_CURL; + } + } + + res = curl_easy_setopt(ctx->curl, CURLOPT_CONNECT_ONLY, 1); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(CONNECT_ONLY, 1) returned: %d", + res); + return MQTT_CODE_ERROR_CURL; + } + + /* Finally do the connection. */ + res = curl_easy_perform(ctx->curl); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_perform returned: %d, %s", res, + curl_easy_strerror(res)); + return MQTT_CODE_ERROR_CURL; + } + + return MQTT_CODE_SUCCESS; +} + +static int NetConnect(void *context, const char* host, word16 port, + int timeout_ms) +{ + SocketContext * ctx = (SocketContext*)context; + int rc = 0; + + if (context == NULL || host == NULL || *host == '\0') { + return MQTT_CODE_ERROR_BAD_ARG; + } + + if (ctx->mqttCtx == NULL) { + return MQTT_CODE_ERROR_BAD_ARG; + } + +#if defined(WOLFMQTT_DEBUG_SOCKET) + PRINTF("NetConnect: Host %s, Port %u, Timeout %d ms, Use TLS %d", + host, port, timeout_ms, port == MQTT_SECURE_PORT); +#endif + + ctx->curl = curl_easy_init(); + + if (ctx->curl == NULL) { + PRINTF("error: curl_easy_init returned NULL"); + return MQTT_CODE_ERROR_MEMORY; + } + + rc = mqttcurl_connect(ctx, host, port, timeout_ms); + + if (rc != MQTT_CODE_SUCCESS) { + curl_easy_cleanup(ctx->curl); + ctx->curl = NULL; + return rc; + } + + ctx->stat = SOCK_CONN; + return MQTT_CODE_SUCCESS; +} + +static int NetWrite(void *context, const byte* buf, int buf_len, + int timeout_ms) +{ + CURLcode res = 0; + SocketContext * ctx = (SocketContext*)context; + size_t sent = 0; + curl_socket_t sockfd = 0; + + if (context == NULL || buf == NULL || buf_len == 0) { + return MQTT_CODE_ERROR_BAD_ARG; + } + + /* get the active socket from libcurl */ + res = curl_easy_getinfo(ctx->curl, CURLINFO_ACTIVESOCKET, &sockfd); + if (res != CURLE_OK) { + PRINTF("error: curl_easy_getinfo(CURLINFO_ACTIVESOCKET) returned %d", + res); + return MQTT_CODE_ERROR_CURL; + } + + /* check it makes sense */ + if (sockfd <= 0) { + PRINTF("error: libcurl sockfd: %d", sockfd); + return MQTT_CODE_ERROR_CURL; + } + +#if defined(WOLFMQTT_DEBUG_SOCKET) + PRINTF("ctx->curl = %lld, sockfd = %d", (long long) ctx->curl, sockfd); +#endif + + /* A very simple retry with timeout example. This assumes the entire + * payload will be transfered in a single shot without buffering. */ + for (size_t i = 0; i < MQTT_CURL_NUM_RETRY; ++i) { + res = curl_easy_send(ctx->curl, buf, buf_len, &sent); + + if (res == CURLE_OK) { +#if defined(WOLFMQTT_DEBUG_SOCKET) + PRINTF("info: curl_easy_send(%d) returned: %d, %s", buf_len, res, + curl_easy_strerror(res)); +#endif + break; + } + + if (res == CURLE_AGAIN) { +#if defined(WOLFMQTT_DEBUG_SOCKET) + PRINTF("info: curl_easy_send(%d) returned: %d, %s", buf_len, res, + curl_easy_strerror(res)); +#endif + + if (mqttcurl_wait(sockfd, 0, timeout_ms) >= 0) { + continue; + } + } + + PRINTF("error: curl_easy_send(%d) returned: %d, %s", buf_len, res, + curl_easy_strerror(res)); + return MQTT_CODE_ERROR_CURL; + } + + if ((int) sent != buf_len) { + PRINTF("error: sent %d bytes, expected %d", (int)sent, buf_len); + return MQTT_CODE_ERROR_CURL; + } + + return buf_len; +} + +static int NetRead(void *context, byte* buf, int buf_len, + int timeout_ms) +{ + CURLcode res = 0; + SocketContext * ctx = (SocketContext*)context; + size_t recvd = 0; + curl_socket_t sockfd = 0; + + if (context == NULL || buf == NULL || buf_len == 0) { + return MQTT_CODE_ERROR_BAD_ARG; + } + + /* get the active socket from libcurl */ + res = curl_easy_getinfo(ctx->curl, CURLINFO_ACTIVESOCKET, &sockfd); + if (res != CURLE_OK) { + PRINTF("error: curl_easy_getinfo(CURLINFO_ACTIVESOCKET) returned %d", + res); + return MQTT_CODE_ERROR_CURL; + } + + /* check it makes sense */ + if (sockfd <= 0) { + PRINTF("error: libcurl sockfd: %d", sockfd); + return MQTT_CODE_ERROR_CURL; + } + +#if defined(WOLFMQTT_DEBUG_SOCKET) + PRINTF("ctx->curl = %lld, sockfd = %d", (long long) ctx->curl, sockfd); +#endif + + /* A very simple retry with timeout example. This assumes the entire + * payload will be transfered in a single shot without buffering. */ + for (size_t i = 0; i < MQTT_CURL_NUM_RETRY; ++i) { + res = curl_easy_recv(ctx->curl, buf, buf_len, &recvd); + + if (res == CURLE_OK) { +#if defined(WOLFMQTT_DEBUG_SOCKET) + PRINTF("info: curl_easy_recv(%d) returned: %d, %s", buf_len, res, + curl_easy_strerror(res)); +#endif + break; + } + + if (res == CURLE_AGAIN) { +#if defined(WOLFMQTT_DEBUG_SOCKET) + PRINTF("info: curl_easy_recv(%d) returned: %d, %s", buf_len, res, + curl_easy_strerror(res)); +#endif + + if (mqttcurl_wait(sockfd, 1, timeout_ms) >= 0) { + continue; + } + } + + PRINTF("error: curl_easy_recv(%d) returned: %d, %s", buf_len, res, + curl_easy_strerror(res)); + return MQTT_CODE_ERROR_CURL; + } + + if ((int) recvd != buf_len) { + PRINTF("error: recvd %d bytes, expected %d", (int)recvd, buf_len); + return MQTT_CODE_ERROR_CURL; + } + + return buf_len; +} + +static int NetDisconnect(void *context) +{ + SocketContext * ctx = (SocketContext*)context; + + if (ctx == NULL) { + return MQTT_CODE_ERROR_BAD_ARG; + } + + if (ctx->curl != NULL) { +#if defined(WOLFMQTT_DEBUG_SOCKET) + PRINTF("info: curl_easy_cleanup"); +#endif + curl_easy_cleanup(ctx->curl); + ctx->curl = NULL; + } + + return 0; +} + /* -------------------------------------------------------------------------- */ /* GENERIC BSD SOCKET TCP NETWORK CALLBACK EXAMPLE */ /* -------------------------------------------------------------------------- */ @@ -996,6 +1381,9 @@ int MqttClientNet_Init(MqttNet* net, MQTTCtx* mqttCtx) } net->context = sockCtx; XMEMSET(sockCtx, 0, sizeof(SocketContext)); +#if defined(ENABLE_MQTT_CURL) + sockCtx->curl = NULL; +#endif sockCtx->fd = SOCKET_INVALID; sockCtx->stat = SOCK_BEGIN; sockCtx->mqttCtx = mqttCtx; diff --git a/examples/mqttnet.h b/examples/mqttnet.h index 56ad8c780..761574912 100644 --- a/examples/mqttnet.h +++ b/examples/mqttnet.h @@ -45,6 +45,9 @@ typedef struct _SocketContext { #if defined(WOLFMQTT_MULTITHREAD) && defined(WOLFMQTT_ENABLE_STDIN_CAP) /* "self pipe" -> signal wake sleep() */ SOCKET_T pfd[2]; +#endif +#ifdef ENABLE_MQTT_CURL + CURL * curl; #endif MQTTCtx* mqttCtx; } SocketContext; diff --git a/src/mqtt_curl.c b/src/mqtt_curl.c index b47b1c08f..d38431485 100644 --- a/src/mqtt_curl.c +++ b/src/mqtt_curl.c @@ -47,6 +47,8 @@ #define WOLFMQTT_TEST_NONBLOCK_TIMES 1 #endif +#ifdef ENABLE_MQTT_CURL + /* Public Functions */ int MqttSocket_Init(MqttClient *client, MqttNet *net) @@ -237,3 +239,5 @@ int MqttSocket_Disconnect(MqttClient *client) return rc; } + +#endif /* defined ENABLE_MQTT_CURL */ From da9432797410ae6aef490636fb95ee3c39e6b8d0 Mon Sep 17 00:00:00 2001 From: philljj Date: Wed, 6 Dec 2023 23:51:54 -0600 Subject: [PATCH 56/62] Add curl easy socket backend: consolidate mqtt_curl into mqtt_socket. --- examples/mqttnet.c | 5 +- examples/mqttnet.h | 4 + src/include.am | 6 - src/mqtt_curl.c | 243 ----------------------------------------- src/mqtt_socket.c | 12 ++ wolfmqtt/include.am | 3 - wolfmqtt/mqtt_client.h | 6 +- wolfmqtt/mqtt_curl.h | 87 --------------- wolfmqtt/mqtt_packet.h | 6 +- 9 files changed, 21 insertions(+), 351 deletions(-) delete mode 100644 src/mqtt_curl.c delete mode 100644 wolfmqtt/mqtt_curl.h diff --git a/examples/mqttnet.c b/examples/mqttnet.c index 98167f4af..e12d83a20 100644 --- a/examples/mqttnet.c +++ b/examples/mqttnet.c @@ -441,6 +441,7 @@ mqttcurl_connect(SocketContext * ctx, const char* host, word16 port, if (port == MQTT_SECURE_PORT) { use_tls = 1; } + /* Toggle with option, or put behind debug define? */ res = curl_easy_setopt(ctx->curl, CURLOPT_VERBOSE, 1L); if (res != CURLE_OK) { @@ -636,7 +637,7 @@ static int NetWrite(void *context, const byte* buf, int buf_len, } #if defined(WOLFMQTT_DEBUG_SOCKET) - PRINTF("ctx->curl = %lld, sockfd = %d", (long long) ctx->curl, sockfd); + PRINTF("ctx->curl = %p, sockfd = %d", (void *)ctx->curl, sockfd); #endif /* A very simple retry with timeout example. This assumes the entire @@ -703,7 +704,7 @@ static int NetRead(void *context, byte* buf, int buf_len, } #if defined(WOLFMQTT_DEBUG_SOCKET) - PRINTF("ctx->curl = %lld, sockfd = %d", (long long) ctx->curl, sockfd); + PRINTF("ctx->curl = %p, sockfd = %d", (void *)ctx->curl, sockfd); #endif /* A very simple retry with timeout example. This assumes the entire diff --git a/examples/mqttnet.h b/examples/mqttnet.h index 761574912..2a6b01b88 100644 --- a/examples/mqttnet.h +++ b/examples/mqttnet.h @@ -26,6 +26,10 @@ extern "C" { #endif +#ifdef ENABLE_MQTT_CURL + #include +#endif + #include "examples/mqttexample.h" #include "examples/mqttport.h" diff --git a/src/include.am b/src/include.am index 5647eed8f..05afb8dbd 100644 --- a/src/include.am +++ b/src/include.am @@ -5,15 +5,9 @@ lib_LTLIBRARIES+= src/libwolfmqtt.la -if HAVE_LIBCURL -src_libwolfmqtt_la_SOURCES = src/mqtt_client.c \ - src/mqtt_packet.c \ - src/mqtt_curl.c -else src_libwolfmqtt_la_SOURCES = src/mqtt_client.c \ src/mqtt_packet.c \ src/mqtt_socket.c -endif if BUILD_SN src_libwolfmqtt_la_SOURCES += src/mqtt_sn_client.c \ diff --git a/src/mqtt_curl.c b/src/mqtt_curl.c deleted file mode 100644 index d38431485..000000000 --- a/src/mqtt_curl.c +++ /dev/null @@ -1,243 +0,0 @@ -/* mqtt_curl.c - * - * Copyright (C) 2006-2023 wolfSSL Inc. - * - * This file is part of wolfMQTT. - * - * wolfMQTT 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; either version 2 of the License, or - * (at your option) any later version. - * - * wolfMQTT 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, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA - */ - -/* Include the autoconf generated config.h */ -#ifdef HAVE_CONFIG_H - #include -#endif - -#ifdef WOLFMQTT_NONBLOCK - /* need EWOULDBLOCK and EAGAIN */ - #if defined(MICROCHIP_MPLAB_HARMONY) && \ - ((__XC32_VERSION < 4000) || (__XC32_VERSION == 243739000)) - /* xc32 versions >= v4.0 no longer have sys/errno.h */ - #include - #endif - #include -#endif - -#include "wolfmqtt/mqtt_client.h" -#include "wolfmqtt/mqtt_curl.h" - -/* Options */ -#ifdef WOLFMQTT_NO_STDIO - #undef WOLFMQTT_DEBUG_SOCKET -#endif - -/* #define WOLFMQTT_TEST_NONBLOCK */ -#ifdef WOLFMQTT_TEST_NONBLOCK - #define WOLFMQTT_TEST_NONBLOCK_TIMES 1 -#endif - -#ifdef ENABLE_MQTT_CURL - -/* Public Functions */ - -int MqttSocket_Init(MqttClient *client, MqttNet *net) -{ - int rc = MQTT_CODE_ERROR_BAD_ARG; - if (client) { - curl_global_init(CURL_GLOBAL_DEFAULT); - - client->net = net; - MqttClient_Flags(client, (MQTT_CLIENT_FLAG_IS_CONNECTED | - MQTT_CLIENT_FLAG_IS_TLS), 0);; - - /* Validate callbacks are not null! */ - if (net && net->connect && net->read && net->write && net->disconnect) { - rc = MQTT_CODE_SUCCESS; - } - } - return rc; -} - -int MqttSocket_Write(MqttClient *client, const byte* buf, int buf_len, - int timeout_ms) -{ - int rc = 0; - - /* Validate arguments */ - if (client == NULL || client->net == NULL || client->net->write == NULL || - buf == NULL || buf_len <= 0) { - return MQTT_CODE_ERROR_BAD_ARG; - } - - /* check for buffer position overflow */ - if (client->write.pos >= buf_len) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - do { - rc = client->net->write(client->net->context, &buf[client->write.pos], - buf_len - client->write.pos, timeout_ms); - if (rc <= 0) { - break; - } - client->write.pos += rc; - client->write.total += rc; - } while (client->write.pos < buf_len); - - /* handle return code */ - if (rc > 0) { - /* return length write and reset position */ - rc = client->write.pos; - client->write.pos = 0; - } - -#ifdef WOLFMQTT_DEBUG_SOCKET - if (rc != 0 && rc != MQTT_CODE_CONTINUE) { /* hide in non-blocking case */ - PRINTF("MqttSocket_Write: Len=%d, Rc=%d", buf_len, rc); - } -#endif - - return rc; -} - -int MqttSocket_Read(MqttClient *client, byte* buf, int buf_len, int timeout_ms) -{ - int rc = 0; - - /* Validate arguments */ - if (client == NULL || client->net == NULL || client->net->read == NULL || - buf == NULL || buf_len <= 0) { - return MQTT_CODE_ERROR_BAD_ARG; - } - - /* check for buffer position overflow */ - if (client->read.pos >= buf_len) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - do { - rc = client->net->read(client->net->context, &buf[client->read.pos], - buf_len - client->read.pos, timeout_ms); - if (rc <= 0) { - break; - } - client->read.pos += rc; - client->read.total += rc; - } while (client->read.pos < buf_len); - - /* handle return code */ - if (rc > 0) { - /* return length read and reset position */ - rc = client->read.pos; - client->read.pos = 0; - } - -#ifdef WOLFMQTT_DEBUG_SOCKET - if (rc != 0 && rc != MQTT_CODE_CONTINUE) { /* hide in non-blocking case */ - PRINTF("MqttSocket_Read: Len=%d, Rc=%d", buf_len, rc); - } -#endif - - return rc; -} - -#ifdef WOLFMQTT_SN -int MqttSocket_Peek(MqttClient *client, byte* buf, int buf_len, int timeout_ms) -{ - int rc; - - /* Validate arguments */ - if (client == NULL || client->net == NULL || client->net->peek == NULL || - buf == NULL || buf_len <= 0) { - return MQTT_CODE_ERROR_BAD_ARG; - } - - /* check for buffer position overflow */ - if (client->read.pos >= buf_len) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - rc = client->net->peek(client->net->context, buf, buf_len, timeout_ms); - if (rc > 0) { - #ifdef WOLFMQTT_DEBUG_SOCKET - PRINTF("MqttSocket_Peek: Len=%d, Rc=%d", buf_len, rc); - #endif - - /* return length read and reset position */ - client->read.pos = 0; - } - - return rc; -} -#endif /* WOLFMQTT_SN */ - -int MqttSocket_Connect(MqttClient *client, const char* host, word16 port, - int timeout_ms, int use_tls, MqttTlsCb cb) -{ - int rc = MQTT_CODE_SUCCESS; - - /* Validate arguments */ - if (client == NULL || client->net == NULL || - client->net->connect == NULL) { - return MQTT_CODE_ERROR_BAD_ARG; - } - - if ((MqttClient_Flags(client, 0, 0) & MQTT_CLIENT_FLAG_IS_CONNECTED) == 0) { - /* Validate port */ - if (port == 0) { - port = (use_tls) ? MQTT_SECURE_PORT : MQTT_DEFAULT_PORT; - } - - /* Connect to host */ - rc = client->net->connect(client->net->context, host, port, timeout_ms); - if (rc != MQTT_CODE_SUCCESS) { - return rc; - } - MqttClient_Flags(client, 0, MQTT_CLIENT_FLAG_IS_CONNECTED); - } - - (void)cb; - -#ifdef WOLFMQTT_DEBUG_SOCKET - PRINTF("MqttSocket_Connect: Rc=%d", rc); -#endif - - return rc; -} - -int MqttSocket_Disconnect(MqttClient *client) -{ - int rc = MQTT_CODE_SUCCESS; - if (client) { - /* Make sure socket is closed */ - if (client->net && client->net->disconnect) { - rc = client->net->disconnect(client->net->context); - } - MqttClient_Flags(client, MQTT_CLIENT_FLAG_IS_CONNECTED, 0); - - curl_global_cleanup(); - } -#ifdef WOLFMQTT_DEBUG_SOCKET - PRINTF("MqttSocket_Disconnect: Rc=%d", rc); -#endif - - /* Check for error */ - if (rc < 0) { - rc = MQTT_CODE_ERROR_NETWORK; - } - - return rc; -} - -#endif /* defined ENABLE_MQTT_CURL */ diff --git a/src/mqtt_socket.c b/src/mqtt_socket.c index 7fa70caf9..0663ba5e0 100644 --- a/src/mqtt_socket.c +++ b/src/mqtt_socket.c @@ -34,6 +34,10 @@ #include #endif +#ifdef ENABLE_MQTT_CURL + #include +#endif + #include "wolfmqtt/mqtt_client.h" #include "wolfmqtt/mqtt_socket.h" @@ -102,6 +106,10 @@ int MqttSocket_Init(MqttClient *client, MqttNet *net) { int rc = MQTT_CODE_ERROR_BAD_ARG; if (client) { + #ifdef ENABLE_MQTT_CURL + curl_global_init(CURL_GLOBAL_DEFAULT); + #endif + client->net = net; MqttClient_Flags(client, (MQTT_CLIENT_FLAG_IS_CONNECTED | MQTT_CLIENT_FLAG_IS_TLS), 0);; @@ -534,6 +542,10 @@ int MqttSocket_Disconnect(MqttClient *client) rc = client->net->disconnect(client->net->context); } MqttClient_Flags(client, MQTT_CLIENT_FLAG_IS_CONNECTED, 0); + + #ifdef ENABLE_MQTT_CURL + curl_global_cleanup(); + #endif } #ifdef WOLFMQTT_DEBUG_SOCKET PRINTF("MqttSocket_Disconnect: Rc=%d", rc); diff --git a/wolfmqtt/include.am b/wolfmqtt/include.am index d48ec4a3b..77dfcc9da 100644 --- a/wolfmqtt/include.am +++ b/wolfmqtt/include.am @@ -11,9 +11,6 @@ nobase_include_HEADERS+= \ wolfmqtt/visibility.h \ wolfmqtt/options.h \ wolfmqtt/vs_settings.h -if HAVE_LIBCURL -nobase_include_HEADERS+= wolfmqtt/mqtt_curl.h -endif if BUILD_SN nobase_include_HEADERS+= wolfmqtt/mqtt_sn_client.h \ diff --git a/wolfmqtt/mqtt_client.h b/wolfmqtt/mqtt_client.h index c65f3adbd..ddb70afe1 100644 --- a/wolfmqtt/mqtt_client.h +++ b/wolfmqtt/mqtt_client.h @@ -40,11 +40,7 @@ #endif #include "wolfmqtt/mqtt_types.h" #include "wolfmqtt/mqtt_packet.h" -#ifdef ENABLE_MQTT_CURL - #include "wolfmqtt/mqtt_curl.h" -#else - #include "wolfmqtt/mqtt_socket.h" -#endif +#include "wolfmqtt/mqtt_socket.h" #ifdef WOLFMQTT_SN #include "wolfmqtt/mqtt_sn_packet.h" diff --git a/wolfmqtt/mqtt_curl.h b/wolfmqtt/mqtt_curl.h deleted file mode 100644 index d51f5d3da..000000000 --- a/wolfmqtt/mqtt_curl.h +++ /dev/null @@ -1,87 +0,0 @@ -/* mqtt_curl.h - * - * Copyright (C) 2006-2023 wolfSSL Inc. - * - * This file is part of wolfMQTT. - * - * wolfMQTT 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; either version 2 of the License, or - * (at your option) any later version. - * - * wolfMQTT 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, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA - */ - -#ifndef WOLFMQTT_CURL_H -#define WOLFMQTT_CURL_H - -#include - -#ifdef __cplusplus - extern "C" { -#endif - -#include "wolfmqtt/mqtt_types.h" - -/* Default Port Numbers */ -#define MQTT_DEFAULT_PORT 1883 -#define MQTT_SECURE_PORT 8883 - - -struct _MqttClient; - -/* Function callbacks */ -typedef int (*MqttTlsCb)(struct _MqttClient* client); - -typedef int (*MqttNetConnectCb)(void *context, - const char* host, word16 port, int timeout_ms); -typedef int (*MqttNetWriteCb)(void *context, - const byte* buf, int buf_len, int timeout_ms); -typedef int (*MqttNetReadCb)(void *context, - byte* buf, int buf_len, int timeout_ms); -#ifdef WOLFMQTT_SN -typedef int (*MqttNetPeekCb)(void *context, - byte* buf, int buf_len, int timeout_ms); -#endif -typedef int (*MqttNetDisconnectCb)(void *context); - -/* Structure for Network callbacks */ -typedef struct _MqttNet { - void *context; - MqttNetConnectCb connect; - MqttNetReadCb read; - MqttNetWriteCb write; - MqttNetDisconnectCb disconnect; -#ifdef WOLFMQTT_SN - MqttNetPeekCb peek; - void *multi_ctx; -#endif -} MqttNet; - -/* MQTT SOCKET APPLICATION INTERFACE */ -WOLFMQTT_LOCAL int MqttSocket_Init(struct _MqttClient *client, MqttNet* net); -WOLFMQTT_LOCAL int MqttSocket_Write(struct _MqttClient *client, const byte* buf, - int buf_len, int timeout_ms); -WOLFMQTT_LOCAL int MqttSocket_Read(struct _MqttClient *client, byte* buf, - int buf_len, int timeout_ms); -#ifdef WOLFMQTT_SN -WOLFMQTT_LOCAL int MqttSocket_Peek(struct _MqttClient *client, byte* buf, - int buf_len, int timeout_ms); -#endif /* WOLFMQTT_SN */ -WOLFMQTT_LOCAL int MqttSocket_Connect(struct _MqttClient *client, - const char* host, word16 port, int timeout_ms, int use_tls, - MqttTlsCb cb); -WOLFMQTT_LOCAL int MqttSocket_Disconnect(struct _MqttClient *client); - -#ifdef __cplusplus - } /* extern "C" */ -#endif - -#endif /* WOLFMQTT_CURL_H */ diff --git a/wolfmqtt/mqtt_packet.h b/wolfmqtt/mqtt_packet.h index 239c374a5..efa2d46b5 100644 --- a/wolfmqtt/mqtt_packet.h +++ b/wolfmqtt/mqtt_packet.h @@ -32,11 +32,7 @@ #endif #include "wolfmqtt/mqtt_types.h" -#ifdef ENABLE_MQTT_CURL - #include "wolfmqtt/mqtt_curl.h" -#else - #include "wolfmqtt/mqtt_socket.h" -#endif +#include "wolfmqtt/mqtt_socket.h" /* Size of a data length elements in protocol */ From 23b50a0f36094e08c166a404e1ceee73109551b1 Mon Sep 17 00:00:00 2001 From: philljj Date: Thu, 7 Dec 2023 14:11:12 -0600 Subject: [PATCH 57/62] Add curl easy socket backend: allow enable-curl with enable-tls. --- configure.ac | 5 -- examples/aws/awsiot.c | 5 +- examples/azure/azureiothub.c | 5 +- examples/firmware/fwclient.c | 4 +- examples/mqttexample.c | 51 +++++++++++---------- examples/mqttexample.h | 2 +- examples/mqttnet.c | 89 ++++++++++++++++++------------------ src/include.am | 1 - src/mqtt_socket.c | 22 +++++---- wolfmqtt/mqtt_socket.h | 4 +- 10 files changed, 95 insertions(+), 93 deletions(-) diff --git a/configure.ac b/configure.ac index f1008cd33..11b7c2c5d 100644 --- a/configure.ac +++ b/configure.ac @@ -177,14 +177,9 @@ AC_ARG_ENABLE([curl], ) if test "x$ENABLED_CURL" = "xyes"; then - if test "x$ENABLED_TLS" = "xyes"; then - AC_MSG_ERROR([--enable-tls and --enable-curl are incompatible]) - fi - AM_CFLAGS="$AM_CFLAGS -DENABLE_MQTT_CURL" AC_CHECK_LIB([curl],[curl_easy_init],,[AC_MSG_ERROR([libcurl is required and wasn't found on the system. It can be obtained from https://curl.se/download.html.])]) - fi diff --git a/examples/aws/awsiot.c b/examples/aws/awsiot.c index 8ae5516c7..a72c4579b 100644 --- a/examples/aws/awsiot.c +++ b/examples/aws/awsiot.c @@ -27,8 +27,9 @@ #include "wolfmqtt/mqtt_client.h" -/* This example only works with ENABLE_MQTT_TLS (wolfSSL library) */ -#if defined(ENABLE_MQTT_TLS) +/* This example only works with ENABLE_MQTT_TLS (wolfSSL library), + * and without ENABLE_MQTT_CURL. */ +#if defined(ENABLE_MQTT_TLS) && !defined(ENABLE_MQTT_CURL) #if !defined(WOLFSSL_USER_SETTINGS) && !defined(USE_WINDOWS_API) #include #endif diff --git a/examples/azure/azureiothub.c b/examples/azure/azureiothub.c index 80189cbc7..6c6eef7aa 100644 --- a/examples/azure/azureiothub.c +++ b/examples/azure/azureiothub.c @@ -27,7 +27,8 @@ #include "wolfmqtt/mqtt_client.h" -/* This example only works with ENABLE_MQTT_TLS (wolfSSL library) */ +/* This example only works with ENABLE_MQTT_TLS (wolfSSL library) + * and without ENABLE_MQTT_CURL. */ /* Notes: * The wolfSSL library must be built with * #define WOLFSSL_BASE64_ENCODE @@ -39,7 +40,7 @@ */ /* This example requires features in wolfSSL 3.9.1 or later */ -#if defined(ENABLE_MQTT_TLS) +#if defined(ENABLE_MQTT_TLS) && !defined(ENABLE_MQTT_CURL) #if !defined(WOLFSSL_USER_SETTINGS) && !defined(USE_WINDOWS_API) #include #endif diff --git a/examples/firmware/fwclient.c b/examples/firmware/fwclient.c index 22259fd8d..d2c9ac0af 100644 --- a/examples/firmware/fwclient.c +++ b/examples/firmware/fwclient.c @@ -26,7 +26,9 @@ #include "wolfmqtt/mqtt_client.h" -#if defined(ENABLE_MQTT_TLS) +/* This example only works with ENABLE_MQTT_TLS (wolfSSL library), + * and without ENABLE_MQTT_CURL. */ +#if defined(ENABLE_MQTT_TLS) && !defined(ENABLE_MQTT_CURL) #if !defined(WOLFSSL_USER_SETTINGS) && !defined(USE_WINDOWS_API) #include #endif diff --git a/examples/mqttexample.c b/examples/mqttexample.c index 3ff69b255..32d74980d 100644 --- a/examples/mqttexample.c +++ b/examples/mqttexample.c @@ -40,15 +40,14 @@ static int myoptind = 0; static char* myoptarg = NULL; #ifdef ENABLE_MQTT_TLS -static const char* mTlsCaFile; -static const char* mTlsCertFile; -static const char* mTlsKeyFile; +static const char* mTlsCertFile = NULL; +static const char* mTlsKeyFile = NULL; #ifdef HAVE_SNI static int useSNI; -static const char* mTlsSniHostName; +static const char* mTlsSniHostName = NULL; #endif #ifdef HAVE_PQC -static const char* mTlsPQAlg; +static const char* mTlsPQAlg = NULL; #endif #endif /* ENABLE_MQTT_TLS */ @@ -291,7 +290,7 @@ void mqtt_init_ctx(MQTTCtx* mqttCtx) #ifdef WOLFMQTT_DEFAULT_TLS mqttCtx->use_tls = WOLFMQTT_DEFAULT_TLS; #endif -#ifdef ENABLE_MQTT_CURL +#ifdef ENABLE_MQTT_TLS mqttCtx->ca_file = NULL; #endif mqttCtx->app_name = "mqttclient"; @@ -302,13 +301,12 @@ int mqtt_parse_args(MQTTCtx* mqttCtx, int argc, char** argv) { int rc; - #ifdef ENABLE_MQTT_CURL - #define MQTT_CURL_ARGS "A:" - #else - #define MQTT_CURL_ARGS "" - #endif #ifdef ENABLE_MQTT_TLS + #ifdef ENABLE_MQTT_CURL + #define MQTT_TLS_ARGS "A:" + #else #define MQTT_TLS_ARGS "c:A:K:S;Q:" + #endif #else #define MQTT_TLS_ARGS "" #endif @@ -319,7 +317,7 @@ int mqtt_parse_args(MQTTCtx* mqttCtx, int argc, char** argv) #endif while ((rc = mygetopt(argc, argv, "?h:p:q:sk:i:lu:w:m:n:C:Tf:rtd" \ - MQTT_CURL_ARGS MQTT_TLS_ARGS MQTT_V5_ARGS)) != -1) { + MQTT_TLS_ARGS MQTT_V5_ARGS)) != -1) { switch ((char)rc) { case '?' : mqtt_show_usage(mqttCtx); @@ -399,15 +397,11 @@ int mqtt_parse_args(MQTTCtx* mqttCtx, int argc, char** argv) mqttCtx->debug_on = 1; break; - #if defined (ENABLE_MQTT_CURL) - case 'A': - mqttCtx->ca_file = myoptarg; - break; - #endif /* ENABLE_MQTT_CURL */ #ifdef ENABLE_MQTT_TLS case 'A': - mTlsCaFile = myoptarg; + mqttCtx->ca_file = myoptarg; break; + #ifndef ENABLE_MQTT_CURL case 'c': mTlsCertFile = myoptarg; break; @@ -429,6 +423,7 @@ int mqtt_parse_args(MQTTCtx* mqttCtx, int argc, char** argv) PRINTF("To use '-Q', build wolfSSL with --with-liboqs"); #endif break; + #endif /* ENABLE_MQTT_CURL */ #endif /* ENABLE_MQTT_TLS */ #ifdef WOLFMQTT_V5 @@ -637,6 +632,7 @@ static int mqtt_tls_verify_cb(int preverify, WOLFSSL_X509_STORE_CTX* store) int mqtt_tls_cb(MqttClient* client) { int rc = WOLFSSL_FAILURE; + SocketContext * sock = (SocketContext *)client->net->context; /* Use highest available and allow downgrade. If wolfSSL is built with * old TLS support, it is possible for a server to force a downgrade to @@ -651,12 +647,12 @@ int mqtt_tls_cb(MqttClient* client) #if !defined(NO_CERT) #if !defined(NO_FILESYSTEM) - if (mTlsCaFile) { + if (sock->mqttCtx->ca_file) { /* Load CA certificate file */ rc = wolfSSL_CTX_load_verify_locations(client->tls.ctx, - mTlsCaFile, NULL); + sock->mqttCtx->ca_file, NULL); if (rc != WOLFSSL_SUCCESS) { - PRINTF("Error loading CA %s: %d (%s)", mTlsCaFile, + PRINTF("Error loading CA %s: %d (%s)", sock->mqttCtx->ca_file, rc, wolfSSL_ERR_reason_error_string(rc)); return rc; } @@ -750,6 +746,10 @@ int mqtt_tls_cb(MqttClient* client) #endif /* HAVE_PQC */ } +#if defined(NO_CERT) || defined(NO_FILESYSTEM) + (void)sock; +#endif + PRINTF("MQTT TLS Setup (%d)", rc); return rc; @@ -759,6 +759,7 @@ int mqtt_tls_cb(MqttClient* client) int mqtt_dtls_cb(MqttClient* client) { #ifdef WOLFSSL_DTLS int rc = WOLFSSL_FAILURE; + SocketContext * sock = (SocketContext *)client->net->context; client->tls.ctx = wolfSSL_CTX_new(wolfDTLSv1_2_client_method()); if (client->tls.ctx) { @@ -769,12 +770,12 @@ int mqtt_dtls_cb(MqttClient* client) { rc = WOLFSSL_SUCCESS; #if !defined(NO_CERT) && !defined(NO_FILESYSTEM) - if (mTlsCaFile) { + if (sock->mqttCtx->ca_file) { /* Load CA certificate file */ rc = wolfSSL_CTX_load_verify_locations(client->tls.ctx, - mTlsCaFile, NULL); + sock->mqttCtx->ca_file, NULL); if (rc != WOLFSSL_SUCCESS) { - PRINTF("Error loading CA %s: %d (%s)", mTlsCaFile, + PRINTF("Error loading CA %s: %d (%s)", sock->mqttCtx->ca_file, rc, wolfSSL_ERR_reason_error_string(rc)); return rc; } @@ -797,6 +798,8 @@ int mqtt_dtls_cb(MqttClient* client) { return rc; } } +#else + (void)sock; #endif client->tls.ssl = wolfSSL_new(client->tls.ctx); diff --git a/examples/mqttexample.h b/examples/mqttexample.h index f18ce3e8d..468724ca0 100644 --- a/examples/mqttexample.h +++ b/examples/mqttexample.h @@ -148,7 +148,7 @@ typedef struct _MQTTCtx { const char* message; const char* pub_file; const char* client_id; -#if defined (ENABLE_MQTT_CURL) +#if defined (ENABLE_MQTT_TLS) const char* ca_file; #endif byte *tx_buf, *rx_buf; diff --git a/examples/mqttnet.c b/examples/mqttnet.c index e12d83a20..b2f4baa65 100644 --- a/examples/mqttnet.c +++ b/examples/mqttnet.c @@ -429,20 +429,17 @@ mqttcurl_wait(curl_socket_t sockfd, int for_recv, int timeout_ms) } static int -mqttcurl_connect(SocketContext * ctx, const char* host, word16 port, +mqttcurl_connect(SocketContext * sock, const char* host, word16 port, int timeout_ms) { CURLcode res = 0; - int use_tls = 0; - if (ctx == NULL) { + if (sock == NULL) { return MQTT_CODE_ERROR_BAD_ARG; } - if (port == MQTT_SECURE_PORT) { use_tls = 1; } - /* Toggle with option, or put behind debug define? */ - res = curl_easy_setopt(ctx->curl, CURLOPT_VERBOSE, 1L); + res = curl_easy_setopt(sock->curl, CURLOPT_VERBOSE, 1L); if (res != CURLE_OK) { PRINTF("error: curl_easy_setopt(VERBOSE, 1L) returned: %d, %s", @@ -451,7 +448,7 @@ mqttcurl_connect(SocketContext * ctx, const char* host, word16 port, } if (timeout_ms != 0) { - res = curl_easy_setopt(ctx->curl, CURLOPT_CONNECTTIMEOUT_MS, + res = curl_easy_setopt(sock->curl, CURLOPT_CONNECTTIMEOUT_MS, timeout_ms); if (res != CURLE_OK) { @@ -460,7 +457,7 @@ mqttcurl_connect(SocketContext * ctx, const char* host, word16 port, return MQTT_CODE_ERROR_CURL; } - res = curl_easy_setopt(ctx->curl, CURLOPT_TIMEOUT_MS, + res = curl_easy_setopt(sock->curl, CURLOPT_TIMEOUT_MS, timeout_ms); if (res != CURLE_OK) { @@ -470,7 +467,7 @@ mqttcurl_connect(SocketContext * ctx, const char* host, word16 port, } } - res = curl_easy_setopt(ctx->curl, CURLOPT_URL, host); + res = curl_easy_setopt(sock->curl, CURLOPT_URL, host); if (res != CURLE_OK) { PRINTF("error: curl_easy_setopt(URL, %s) returned: %d", @@ -478,7 +475,7 @@ mqttcurl_connect(SocketContext * ctx, const char* host, word16 port, return MQTT_CODE_ERROR_CURL; } - res = curl_easy_setopt(ctx->curl, CURLOPT_PORT, port); + res = curl_easy_setopt(sock->curl, CURLOPT_PORT, port); if (res != CURLE_OK) { PRINTF("error: curl_easy_setopt(PORT, %d) returned: %d", @@ -486,9 +483,10 @@ mqttcurl_connect(SocketContext * ctx, const char* host, word16 port, return MQTT_CODE_ERROR_CURL; } - if (use_tls) { + #ifdef ENABLE_MQTT_TLS + if (sock->mqttCtx->use_tls) { /* Set TLS specific options. */ - res = curl_easy_setopt(ctx->curl, CURLOPT_SSLVERSION, + res = curl_easy_setopt(sock->curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); if (res != CURLE_OK) { @@ -498,7 +496,7 @@ mqttcurl_connect(SocketContext * ctx, const char* host, word16 port, } /* With CURLOPT_CONNECT_ONLY this means do TLS by default. */ - res = curl_easy_setopt(ctx->curl, CURLOPT_DEFAULT_PROTOCOL, + res = curl_easy_setopt(sock->curl, CURLOPT_DEFAULT_PROTOCOL, "https"); if (res != CURLE_OK) { @@ -508,9 +506,9 @@ mqttcurl_connect(SocketContext * ctx, const char* host, word16 port, } /* Set path to Certificate Authority (CA) file bundle. */ - if (ctx->mqttCtx->ca_file != NULL) { - res = curl_easy_setopt(ctx->curl, CURLOPT_CAINFO, - ctx->mqttCtx->ca_file); + if (sock->mqttCtx->ca_file != NULL) { + res = curl_easy_setopt(sock->curl, CURLOPT_CAINFO, + sock->mqttCtx->ca_file); if (res != CURLE_OK) { PRINTF("error: curl_easy_setopt(CAINFO) returned: %d", @@ -522,9 +520,9 @@ mqttcurl_connect(SocketContext * ctx, const char* host, word16 port, /* Set path to dir holding CA files. * Unused at the moment. */ /* - if (ctx->mqttCtx->ca_path != NULL) { - res = curl_easy_setopt(ctx->curl, CURLOPT_CAPATH, - ctx->mqttCtx->ca_path); + if (sock->mqttCtx->ca_path != NULL) { + res = curl_easy_setopt(sock->curl, CURLOPT_CAPATH, + sock->mqttCtx->ca_path); if (res != CURLE_OK) { PRINTF("error: curl_easy_setopt(CAPATH) returned: %d", @@ -535,7 +533,7 @@ mqttcurl_connect(SocketContext * ctx, const char* host, word16 port, */ /* Require peer and host verification. */ - res = curl_easy_setopt(ctx->curl, CURLOPT_SSL_VERIFYPEER, 1); + res = curl_easy_setopt(sock->curl, CURLOPT_SSL_VERIFYPEER, 1); if (res != CURLE_OK) { PRINTF("error: curl_easy_setopt(SSL_VERIFYPEER) returned: %d", @@ -543,7 +541,7 @@ mqttcurl_connect(SocketContext * ctx, const char* host, word16 port, return MQTT_CODE_ERROR_CURL; } - res = curl_easy_setopt(ctx->curl, CURLOPT_SSL_VERIFYHOST, 2); + res = curl_easy_setopt(sock->curl, CURLOPT_SSL_VERIFYHOST, 2); if (res != CURLE_OK) { PRINTF("error: curl_easy_setopt(SSL_VERIFYHOST) returned: %d", @@ -551,8 +549,9 @@ mqttcurl_connect(SocketContext * ctx, const char* host, word16 port, return MQTT_CODE_ERROR_CURL; } } + #endif /* ENABLE_MQTT_TLS */ - res = curl_easy_setopt(ctx->curl, CURLOPT_CONNECT_ONLY, 1); + res = curl_easy_setopt(sock->curl, CURLOPT_CONNECT_ONLY, 1); if (res != CURLE_OK) { PRINTF("error: curl_easy_setopt(CONNECT_ONLY, 1) returned: %d", @@ -561,7 +560,7 @@ mqttcurl_connect(SocketContext * ctx, const char* host, word16 port, } /* Finally do the connection. */ - res = curl_easy_perform(ctx->curl); + res = curl_easy_perform(sock->curl); if (res != CURLE_OK) { PRINTF("error: curl_easy_perform returned: %d, %s", res, @@ -575,38 +574,38 @@ mqttcurl_connect(SocketContext * ctx, const char* host, word16 port, static int NetConnect(void *context, const char* host, word16 port, int timeout_ms) { - SocketContext * ctx = (SocketContext*)context; + SocketContext * sock = (SocketContext*)context; int rc = 0; if (context == NULL || host == NULL || *host == '\0') { return MQTT_CODE_ERROR_BAD_ARG; } - if (ctx->mqttCtx == NULL) { + if (sock->mqttCtx == NULL) { return MQTT_CODE_ERROR_BAD_ARG; } #if defined(WOLFMQTT_DEBUG_SOCKET) PRINTF("NetConnect: Host %s, Port %u, Timeout %d ms, Use TLS %d", - host, port, timeout_ms, port == MQTT_SECURE_PORT); + host, port, timeout_ms, sock->mqttCtx->use_tls); #endif - ctx->curl = curl_easy_init(); + sock->curl = curl_easy_init(); - if (ctx->curl == NULL) { + if (sock->curl == NULL) { PRINTF("error: curl_easy_init returned NULL"); return MQTT_CODE_ERROR_MEMORY; } - rc = mqttcurl_connect(ctx, host, port, timeout_ms); + rc = mqttcurl_connect(sock, host, port, timeout_ms); if (rc != MQTT_CODE_SUCCESS) { - curl_easy_cleanup(ctx->curl); - ctx->curl = NULL; + curl_easy_cleanup(sock->curl); + sock->curl = NULL; return rc; } - ctx->stat = SOCK_CONN; + sock->stat = SOCK_CONN; return MQTT_CODE_SUCCESS; } @@ -614,7 +613,7 @@ static int NetWrite(void *context, const byte* buf, int buf_len, int timeout_ms) { CURLcode res = 0; - SocketContext * ctx = (SocketContext*)context; + SocketContext * sock = (SocketContext*)context; size_t sent = 0; curl_socket_t sockfd = 0; @@ -623,7 +622,7 @@ static int NetWrite(void *context, const byte* buf, int buf_len, } /* get the active socket from libcurl */ - res = curl_easy_getinfo(ctx->curl, CURLINFO_ACTIVESOCKET, &sockfd); + res = curl_easy_getinfo(sock->curl, CURLINFO_ACTIVESOCKET, &sockfd); if (res != CURLE_OK) { PRINTF("error: curl_easy_getinfo(CURLINFO_ACTIVESOCKET) returned %d", res); @@ -637,13 +636,13 @@ static int NetWrite(void *context, const byte* buf, int buf_len, } #if defined(WOLFMQTT_DEBUG_SOCKET) - PRINTF("ctx->curl = %p, sockfd = %d", (void *)ctx->curl, sockfd); + PRINTF("sock->curl = %p, sockfd = %d", (void *)sock->curl, sockfd); #endif /* A very simple retry with timeout example. This assumes the entire * payload will be transfered in a single shot without buffering. */ for (size_t i = 0; i < MQTT_CURL_NUM_RETRY; ++i) { - res = curl_easy_send(ctx->curl, buf, buf_len, &sent); + res = curl_easy_send(sock->curl, buf, buf_len, &sent); if (res == CURLE_OK) { #if defined(WOLFMQTT_DEBUG_SOCKET) @@ -681,7 +680,7 @@ static int NetRead(void *context, byte* buf, int buf_len, int timeout_ms) { CURLcode res = 0; - SocketContext * ctx = (SocketContext*)context; + SocketContext * sock = (SocketContext*)context; size_t recvd = 0; curl_socket_t sockfd = 0; @@ -690,7 +689,7 @@ static int NetRead(void *context, byte* buf, int buf_len, } /* get the active socket from libcurl */ - res = curl_easy_getinfo(ctx->curl, CURLINFO_ACTIVESOCKET, &sockfd); + res = curl_easy_getinfo(sock->curl, CURLINFO_ACTIVESOCKET, &sockfd); if (res != CURLE_OK) { PRINTF("error: curl_easy_getinfo(CURLINFO_ACTIVESOCKET) returned %d", res); @@ -704,13 +703,13 @@ static int NetRead(void *context, byte* buf, int buf_len, } #if defined(WOLFMQTT_DEBUG_SOCKET) - PRINTF("ctx->curl = %p, sockfd = %d", (void *)ctx->curl, sockfd); + PRINTF("sock->curl = %p, sockfd = %d", (void *)sock->curl, sockfd); #endif /* A very simple retry with timeout example. This assumes the entire * payload will be transfered in a single shot without buffering. */ for (size_t i = 0; i < MQTT_CURL_NUM_RETRY; ++i) { - res = curl_easy_recv(ctx->curl, buf, buf_len, &recvd); + res = curl_easy_recv(sock->curl, buf, buf_len, &recvd); if (res == CURLE_OK) { #if defined(WOLFMQTT_DEBUG_SOCKET) @@ -746,18 +745,18 @@ static int NetRead(void *context, byte* buf, int buf_len, static int NetDisconnect(void *context) { - SocketContext * ctx = (SocketContext*)context; + SocketContext * sock = (SocketContext*)context; - if (ctx == NULL) { + if (sock == NULL) { return MQTT_CODE_ERROR_BAD_ARG; } - if (ctx->curl != NULL) { + if (sock->curl != NULL) { #if defined(WOLFMQTT_DEBUG_SOCKET) PRINTF("info: curl_easy_cleanup"); #endif - curl_easy_cleanup(ctx->curl); - ctx->curl = NULL; + curl_easy_cleanup(sock->curl); + sock->curl = NULL; } return 0; diff --git a/src/include.am b/src/include.am index 05afb8dbd..08f9cd162 100644 --- a/src/include.am +++ b/src/include.am @@ -4,7 +4,6 @@ lib_LTLIBRARIES+= src/libwolfmqtt.la - src_libwolfmqtt_la_SOURCES = src/mqtt_client.c \ src/mqtt_packet.c \ src/mqtt_socket.c diff --git a/src/mqtt_socket.c b/src/mqtt_socket.c index 0663ba5e0..03e062bb3 100644 --- a/src/mqtt_socket.c +++ b/src/mqtt_socket.c @@ -55,7 +55,7 @@ /* Public Functions */ -#ifdef ENABLE_MQTT_TLS +#if defined(ENABLE_MQTT_TLS) && !defined(ENABLE_MQTT_CURL) int MqttSocket_TlsSocketReceive(WOLFSSL* ssl, char *buf, int sz, void *ptr) { @@ -100,7 +100,7 @@ int MqttSocket_TlsSocketSend(WOLFSSL* ssl, char *buf, int sz, } return rc; } -#endif +#endif /* ENABLE_MQTT_TLS && !ENABLE_MQTT_CURL*/ int MqttSocket_Init(MqttClient *client, MqttNet *net) { @@ -113,7 +113,7 @@ int MqttSocket_Init(MqttClient *client, MqttNet *net) client->net = net; MqttClient_Flags(client, (MQTT_CLIENT_FLAG_IS_CONNECTED | MQTT_CLIENT_FLAG_IS_TLS), 0);; - #ifdef ENABLE_MQTT_TLS + #if defined(ENABLE_MQTT_TLS) && !defined(ENABLE_MQTT_CURL) client->tls.ctx = NULL; client->tls.ssl = NULL; client->tls.timeout_ms = client->cmd_timeout_ms; @@ -132,7 +132,7 @@ static int MqttSocket_WriteDo(MqttClient *client, const byte* buf, int buf_len, { int rc; -#ifdef ENABLE_MQTT_TLS +#if defined(ENABLE_MQTT_TLS) && !defined(ENABLE_MQTT_CURL) if (MqttClient_Flags(client,0,0) & MQTT_CLIENT_FLAG_IS_TLS) { client->tls.timeout_ms = timeout_ms; client->tls.sockRcWrite = 0; /* init value */ @@ -162,7 +162,7 @@ static int MqttSocket_WriteDo(MqttClient *client, const byte* buf, int buf_len, } } else -#endif /* ENABLE_MQTT_TLS */ +#endif /* ENABLE_MQTT_TLS && !ENABLE_MQTT_CURL*/ { rc = client->net->write(client->net->context, buf, buf_len, timeout_ms); @@ -234,7 +234,7 @@ static int MqttSocket_ReadDo(MqttClient *client, byte* buf, int buf_len, { int rc; -#ifdef ENABLE_MQTT_TLS +#if defined(ENABLE_MQTT_TLS) && !defined(ENABLE_MQTT_CURL) if (MqttClient_Flags(client,0,0) & MQTT_CLIENT_FLAG_IS_TLS) { client->tls.timeout_ms = timeout_ms; client->tls.sockRcRead = 0; /* init value */ @@ -267,7 +267,7 @@ static int MqttSocket_ReadDo(MqttClient *client, byte* buf, int buf_len, } } else -#endif /* ENABLE_MQTT_TLS */ +#endif /* ENABLE_MQTT_TLS && !ENABLE_MQTT_CURL*/ { rc = client->net->read(client->net->context, buf, buf_len, timeout_ms); } @@ -394,7 +394,7 @@ int MqttSocket_Connect(MqttClient *client, const char* host, word16 port, MqttClient_Flags(client, 0, MQTT_CLIENT_FLAG_IS_CONNECTED); } -#ifdef ENABLE_MQTT_TLS +#if defined(ENABLE_MQTT_TLS) && !defined(ENABLE_MQTT_CURL) if (use_tls) { if (client->tls.ctx == NULL) { #ifdef DEBUG_WOLFSSL @@ -510,7 +510,7 @@ int MqttSocket_Connect(MqttClient *client, const char* host, word16 port, #else (void)cb; -#endif /* ENABLE_MQTT_TLS */ +#endif /* ENABLE_MQTT_TLS && !ENABLE_MQTT_CURL*/ #ifdef WOLFMQTT_DEBUG_SOCKET PRINTF("MqttSocket_Connect: Rc=%d", rc); @@ -523,7 +523,8 @@ int MqttSocket_Disconnect(MqttClient *client) { int rc = MQTT_CODE_SUCCESS; if (client) { - #ifdef ENABLE_MQTT_TLS + #if defined(ENABLE_MQTT_TLS) + #if !defined(ENABLE_MQTT_CURL) if (client->tls.ssl) { wolfSSL_free(client->tls.ssl); client->tls.ssl = NULL; @@ -533,6 +534,7 @@ int MqttSocket_Disconnect(MqttClient *client) client->tls.ctx = NULL; } wolfSSL_Cleanup(); + #endif MqttClient_Flags(client, (MQTT_CLIENT_FLAG_IS_TLS | MQTT_CLIENT_FLAG_IS_DTLS), 0); #endif diff --git a/wolfmqtt/mqtt_socket.h b/wolfmqtt/mqtt_socket.h index cedc2ed6c..cb1465e60 100644 --- a/wolfmqtt/mqtt_socket.h +++ b/wolfmqtt/mqtt_socket.h @@ -104,12 +104,12 @@ WOLFMQTT_LOCAL int MqttSocket_Connect(struct _MqttClient *client, MqttTlsCb cb); WOLFMQTT_LOCAL int MqttSocket_Disconnect(struct _MqttClient *client); -#ifdef ENABLE_MQTT_TLS +#if defined(ENABLE_MQTT_TLS) && !defined(ENABLE_MQTT_CURL) /* make these public for cases where user needs to create * WOLFSSL_CTX context and WOLFSSL object in the TLS callback */ WOLFMQTT_API int MqttSocket_TlsSocketReceive(WOLFSSL* ssl, char *buf, int sz, void *ptr); WOLFMQTT_API int MqttSocket_TlsSocketSend(WOLFSSL* ssl, char *buf, int sz, void *ptr); -#endif +#endif /* ENABLE_MQTT_TLS && !ENABLE_MQTT_CURL*/ #ifdef __cplusplus } /* extern "C" */ From 6cdc37610358135f1b1dbb27f734660dd47cb4f9 Mon Sep 17 00:00:00 2001 From: philljj Date: Fri, 8 Dec 2023 10:08:52 -0600 Subject: [PATCH 58/62] Add curl easy socket backend: fix tests, add curl readme. --- README.md | 17 ++++++++++++ configure.ac | 1 + examples/firmware/fwclient.c | 5 ++-- examples/mqttexample.c | 51 +++++++++++++++++------------------- examples/mqttexample.h | 2 ++ examples/mqttnet.c | 35 +++++++++++++++++++++++-- scripts/client.test | 11 ++++---- scripts/firmware.test | 2 +- 8 files changed, 86 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 7398f0cad..9df1684a2 100644 --- a/README.md +++ b/README.md @@ -351,3 +351,20 @@ Since the broker and subscriber are still running, you can use `mqttclient` to p Congratulations! You have just published an MQTT message using TLS 1.3 with the `KYBER_LEVEL1` KEM and `FALCON_LEVEL1` signature scheme. To use the hybrid group, replace `KYBER_LEVEL1` with `P256_KYBER_LEVEL1`. + +## Curl Easy Socket Support + +wolfMQTT now supports using libcurl's easy socket interface as a backend. +When enabled, wolfMQTT will use the libcurl API for the socket backend, +and libcurl will use wolfSSL to negotiate TLS. +This can be enabled with `--enable-curl`. + +At this time wolfMQTT's libcurl option supports both TLS and mTLS, but not Post-Quantum TLS. + +### How to use libcurl with wolfMQTT + +To use wolfMQTT with libcurl and wolfSSL: +- build wolfssl with `--enable-curl` and install to `/usr/local`. +- build libcurl with `--with-wolfssl` and install to `/usr/local`. + +Finally, build wolfMQTT with `--enable-curl`. diff --git a/configure.ac b/configure.ac index 11b7c2c5d..f9827a0ff 100644 --- a/configure.ac +++ b/configure.ac @@ -454,4 +454,5 @@ echo " * Examples: $ENABLED_EXAMPLES" echo " * Non-Blocking: $ENABLED_NONBLOCK" echo " * STDIN Capture: $ENABLED_STDINCAP" echo " * TLS: $ENABLED_TLS" +echo " * CURL: $ENABLED_CURL" echo " * Multi-thread: $ENABLED_MULTITHREAD" diff --git a/examples/firmware/fwclient.c b/examples/firmware/fwclient.c index d2c9ac0af..fc4f9c489 100644 --- a/examples/firmware/fwclient.c +++ b/examples/firmware/fwclient.c @@ -26,9 +26,8 @@ #include "wolfmqtt/mqtt_client.h" -/* This example only works with ENABLE_MQTT_TLS (wolfSSL library), - * and without ENABLE_MQTT_CURL. */ -#if defined(ENABLE_MQTT_TLS) && !defined(ENABLE_MQTT_CURL) +/* This example only works with ENABLE_MQTT_TLS (wolfSSL library). */ +#if defined(ENABLE_MQTT_TLS) #if !defined(WOLFSSL_USER_SETTINGS) && !defined(USE_WINDOWS_API) #include #endif diff --git a/examples/mqttexample.c b/examples/mqttexample.c index 32d74980d..38b2f0184 100644 --- a/examples/mqttexample.c +++ b/examples/mqttexample.c @@ -40,8 +40,6 @@ static int myoptind = 0; static char* myoptarg = NULL; #ifdef ENABLE_MQTT_TLS -static const char* mTlsCertFile = NULL; -static const char* mTlsKeyFile = NULL; #ifdef HAVE_SNI static int useSNI; static const char* mTlsSniHostName = NULL; @@ -225,20 +223,17 @@ void mqtt_show_usage(MQTTCtx* mqttCtx) PRINTF("-A Load CA (validate peer)"); PRINTF("-K Use private key (for TLS mutual auth)"); PRINTF("-c Use certificate (for TLS mutual auth)"); - #ifdef HAVE_SNI + #ifndef ENABLE_MQTT_CURL + #ifdef HAVE_SNI /* Remove SNI args for sn-client */ if(XSTRNCMP(mqttCtx->app_name, "sn-client", 10)){ PRINTF("-S Use Host Name Indication, blank defaults to host"); } - #endif - #ifdef HAVE_PQC + #endif /* HAVE_SNI */ + #ifdef HAVE_PQC PRINTF("-Q Use Key Share with post-quantum algorithm"); - #endif -#elif defined (ENABLE_MQTT_CURL) - PRINTF("-p Port to connect on, default: Normal %d, TLS %d", - MQTT_DEFAULT_PORT, MQTT_SECURE_PORT); - PRINTF("-A Load CA (validate peer)"); -#else + #endif /* HAVE_PQC */ + #endif /* !ENABLE_MQTT_CURL */ PRINTF("-p Port to connect on, default: %d", MQTT_DEFAULT_PORT); #endif @@ -292,6 +287,8 @@ void mqtt_init_ctx(MQTTCtx* mqttCtx) #endif #ifdef ENABLE_MQTT_TLS mqttCtx->ca_file = NULL; + mqttCtx->mtls_keyfile = NULL; + mqttCtx->mtls_certfile = NULL; #endif mqttCtx->app_name = "mqttclient"; mqttCtx->message = DEFAULT_MESSAGE; @@ -303,9 +300,9 @@ int mqtt_parse_args(MQTTCtx* mqttCtx, int argc, char** argv) #ifdef ENABLE_MQTT_TLS #ifdef ENABLE_MQTT_CURL - #define MQTT_TLS_ARGS "A:" + #define MQTT_TLS_ARGS "c:A:K:" #else - #define MQTT_TLS_ARGS "c:A:K:S;Q:" + #define MQTT_TLS_ARGS "c:A:K:S;Q:" #endif #else #define MQTT_TLS_ARGS "" @@ -401,13 +398,13 @@ int mqtt_parse_args(MQTTCtx* mqttCtx, int argc, char** argv) case 'A': mqttCtx->ca_file = myoptarg; break; - #ifndef ENABLE_MQTT_CURL case 'c': - mTlsCertFile = myoptarg; + mqttCtx->mtls_certfile = myoptarg; break; case 'K': - mTlsKeyFile = myoptarg; + mqttCtx->mtls_keyfile = myoptarg; break; + #ifndef ENABLE_MQTT_CURL case 'S': #ifdef HAVE_SNI useSNI = 1; @@ -423,7 +420,7 @@ int mqtt_parse_args(MQTTCtx* mqttCtx, int argc, char** argv) PRINTF("To use '-Q', build wolfSSL with --with-liboqs"); #endif break; - #endif /* ENABLE_MQTT_CURL */ + #endif /* !ENABLE_MQTT_CURL */ #endif /* ENABLE_MQTT_TLS */ #ifdef WOLFMQTT_V5 @@ -657,12 +654,12 @@ int mqtt_tls_cb(MqttClient* client) return rc; } } - if (mTlsCertFile && mTlsKeyFile) { + if (sock->mqttCtx->mtls_certfile && sock->mqttCtx->mtls_keyfile) { /* Load If using a mutual authentication */ rc = wolfSSL_CTX_use_certificate_file(client->tls.ctx, - mTlsCertFile, WOLFSSL_FILETYPE_PEM); + sock->mqttCtx->mtls_certfile, WOLFSSL_FILETYPE_PEM); if (rc != WOLFSSL_SUCCESS) { - PRINTF("Error loading certificate %s: %d (%s)", mTlsCertFile, + PRINTF("Error loading certificate %s: %d (%s)", sock->mqttCtx->mtls_certfile, rc, wolfSSL_ERR_reason_error_string(rc)); return rc; } @@ -674,9 +671,9 @@ int mqtt_tls_cb(MqttClient* client) #endif rc = wolfSSL_CTX_use_PrivateKey_file(client->tls.ctx, - mTlsKeyFile, WOLFSSL_FILETYPE_PEM); + sock->mqttCtx->mtls_keyfile, WOLFSSL_FILETYPE_PEM); if (rc != WOLFSSL_SUCCESS) { - PRINTF("Error loading key %s: %d (%s)", mTlsKeyFile, + PRINTF("Error loading key %s: %d (%s)", sock->mqttCtx->mtls_keyfile, rc, wolfSSL_ERR_reason_error_string(rc)); return rc; } @@ -780,20 +777,20 @@ int mqtt_dtls_cb(MqttClient* client) { return rc; } } - if (mTlsCertFile && mTlsKeyFile) { + if (sock->mqttCtx->mtls_certfile && sock->mqttCtx->mtls_keyfile) { /* Load If using a mutual authentication */ rc = wolfSSL_CTX_use_certificate_file(client->tls.ctx, - mTlsCertFile, WOLFSSL_FILETYPE_PEM); + sock->mqttCtx->mtls_certfile, WOLFSSL_FILETYPE_PEM); if (rc != WOLFSSL_SUCCESS) { - PRINTF("Error loading certificate %s: %d (%s)", mTlsCertFile, + PRINTF("Error loading certificate %s: %d (%s)", sock->mqttCtx->mtls_certfile, rc, wolfSSL_ERR_reason_error_string(rc)); return rc; } rc = wolfSSL_CTX_use_PrivateKey_file(client->tls.ctx, - mTlsKeyFile, WOLFSSL_FILETYPE_PEM); + sock->mqttCtx->mtls_keyfile, WOLFSSL_FILETYPE_PEM); if (rc != WOLFSSL_SUCCESS) { - PRINTF("Error loading key %s: %d (%s)", mTlsKeyFile, + PRINTF("Error loading key %s: %d (%s)", sock->mqttCtx->mtls_keyfile, rc, wolfSSL_ERR_reason_error_string(rc)); return rc; } diff --git a/examples/mqttexample.h b/examples/mqttexample.h index 468724ca0..6bdb427b8 100644 --- a/examples/mqttexample.h +++ b/examples/mqttexample.h @@ -150,6 +150,8 @@ typedef struct _MQTTCtx { const char* client_id; #if defined (ENABLE_MQTT_TLS) const char* ca_file; + const char* mtls_keyfile; + const char* mtls_certfile; #endif byte *tx_buf, *rx_buf; int return_code; diff --git a/examples/mqttnet.c b/examples/mqttnet.c index b2f4baa65..d50bd2c33 100644 --- a/examples/mqttnet.c +++ b/examples/mqttnet.c @@ -517,6 +517,30 @@ mqttcurl_connect(SocketContext * sock, const char* host, word16 port, } } + /* Set path to mutual TLS keyfile. */ + if (sock->mqttCtx->mtls_keyfile != NULL) { + res = curl_easy_setopt(sock->curl, CURLOPT_SSLKEY, + sock->mqttCtx->mtls_keyfile); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(CURLOPT_SSLKEY) returned: %d", + res); + return MQTT_CODE_ERROR_CURL; + } + } + + /* Set path to mutual TLS certfile. */ + if (sock->mqttCtx->mtls_certfile != NULL) { + res = curl_easy_setopt(sock->curl, CURLOPT_SSLCERT, + sock->mqttCtx->mtls_certfile); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(CURLOPT_SSLCERT) returned: %d", + res); + return MQTT_CODE_ERROR_CURL; + } + } + /* Set path to dir holding CA files. * Unused at the moment. */ /* @@ -532,7 +556,7 @@ mqttcurl_connect(SocketContext * sock, const char* host, word16 port, } */ - /* Require peer and host verification. */ + /* Set peer and host verification. */ res = curl_easy_setopt(sock->curl, CURLOPT_SSL_VERIFYPEER, 1); if (res != CURLE_OK) { @@ -541,7 +565,14 @@ mqttcurl_connect(SocketContext * sock, const char* host, word16 port, return MQTT_CODE_ERROR_CURL; } - res = curl_easy_setopt(sock->curl, CURLOPT_SSL_VERIFYHOST, 2); + /* Only do server host verification when not running against + * localhost broker. */ + if (XSTRCMP(host, "localhost") == 0) { + res = curl_easy_setopt(sock->curl, CURLOPT_SSL_VERIFYHOST, 0); + } + else { + res = curl_easy_setopt(sock->curl, CURLOPT_SSL_VERIFYHOST, 2); + } if (res != CURLE_OK) { PRINTF("error: curl_easy_setopt(SSL_VERIFYHOST) returned: %d", diff --git a/scripts/client.test b/scripts/client.test index e86752c26..086f19f69 100755 --- a/scripts/client.test +++ b/scripts/client.test @@ -73,6 +73,7 @@ then def_args="${def_args} -h localhost" tls_port_args="-p 18883" port_args="-p ${port}" + cacert_args="-A scripts/broker_test/ca-cert.pem" mutual_auth_args="-c certs/client-cert.pem -K certs/client-key.pem" ecc_mutual_auth_args="-c certs/client-ecc-cert.pem -K certs/ecc-client-key.pem" fi @@ -95,23 +96,23 @@ RESULT=$? if test $has_tls == yes then # Run with TLS and QoS 0-2 - ./examples/mqttclient/mqttclient $def_args $tls_port_args -t -q 0 $1 + ./examples/mqttclient/mqttclient $def_args $cacert_args $tls_port_args -t -q 0 $1 RESULT=$? [ $RESULT -ne 0 ] && echo -e "\n\nMQTT Client failed! TLS=On, QoS=0" && do_cleanup "-1" - ./examples/mqttclient/mqttclient $def_args $tls_port_args -t -q 1 $1 + ./examples/mqttclient/mqttclient $def_args $cacert_args $tls_port_args -t -q 1 $1 RESULT=$? [ $RESULT -ne 0 ] && echo -e "\n\nMQTT Client failed! TLS=On, QoS=1" && do_cleanup "-1" - ./examples/mqttclient/mqttclient $def_args $tls_port_args -t -q 2 $1 + ./examples/mqttclient/mqttclient $def_args $cacert_args $tls_port_args -t -q 2 $1 RESULT=$? [ $RESULT -ne 0 ] && echo -e "\n\nMQTT Client failed! TLS=On, QoS=2" && do_cleanup "-1" - ./examples/mqttclient/mqttclient $def_args $mutual_auth_args $tls_port_args -t -q 0 $1 + ./examples/mqttclient/mqttclient $def_args $cacert_args $mutual_auth_args $tls_port_args -t -q 0 $1 RESULT=$? [ $RESULT -ne 0 ] && echo -e "\n\nMQTT Client failed! TLS=On, QoS=0, RSA mutual auth" && do_cleanup "-1" - ./examples/mqttclient/mqttclient $def_args $ecc_mutual_auth_args $tls_port_args -t -q 0 $1 + ./examples/mqttclient/mqttclient $def_args $cacert_args $ecc_mutual_auth_args $tls_port_args -t -q 0 $1 RESULT=$? [ $RESULT -ne 0 ] && echo -e "\n\nMQTT Client failed! TLS=On, QoS=0, ECC mutual auth" && do_cleanup "-1" fi diff --git a/scripts/firmware.test b/scripts/firmware.test index 13d743b9e..706729141 100755 --- a/scripts/firmware.test +++ b/scripts/firmware.test @@ -87,7 +87,7 @@ fi if test $has_tls == yes then - def_args="${def_args} -t" + def_args="${def_args} -A scripts/broker_test/ca-cert.pem -t" fi echo -e "Base args: $def_args" From d98607e37dd0f4d2fc190564742296dbd1c389ec Mon Sep 17 00:00:00 2001 From: philljj Date: Fri, 8 Dec 2023 11:27:48 -0600 Subject: [PATCH 59/62] Add curl easy socket backend: cleanup, add curl CI test. --- .github/workflows/ubuntu-check-curl.yml | 106 ++++++++++++++++++++++++ examples/mqttexample.c | 12 ++- examples/mqttnet.c | 9 +- 3 files changed, 119 insertions(+), 8 deletions(-) create mode 100644 .github/workflows/ubuntu-check-curl.yml diff --git a/.github/workflows/ubuntu-check-curl.yml b/.github/workflows/ubuntu-check-curl.yml new file mode 100644 index 000000000..c5e0e0cc6 --- /dev/null +++ b/.github/workflows/ubuntu-check-curl.yml @@ -0,0 +1,106 @@ +name: Ubuntu Build Test with Curl Support + +on: + push: + branches: [ 'master', 'main', 'release/**' ] + pull_request: + branches: [ '*' ] + +jobs: + build: + + runs-on: ubuntu-latest + timeout-minutes: 5 + + steps: + - name: Install dependencies + run: | + # Don't prompt for anything + export DEBIAN_FRONTEND=noninteractive + sudo apt-get update + # Install mosquitto + sudo apt-get install -y mosquitto bubblewrap + + - name: Setup mosquitto broker + run: | + # Disable default broker daemon + sudo service mosquitto stop + sleep 1 + + # This is some debug info useful if something goes wrong + - name: Show network status + run: | + sudo ifconfig + sudo route + sudo netstat -tulpan + + - uses: actions/checkout@master + with: + repository: wolfssl/wolfssl + path: wolfssl + - name: wolfssl autogen + working-directory: ./wolfssl + run: ./autogen.sh + - name: wolfssl configure + working-directory: ./wolfssl + run: ./configure --enable-curl + - name: wolfssl make + working-directory: ./wolfssl + run: make + - name: wolfssl make install + working-directory: ./wolfssl + run: sudo make install + + - uses: actions/checkout@master + with: + repository: curl/curl + path: curl + - name: curl autoreconf + working-directory: ./curl + run: autoreconf -fi + - name: curl configure + working-directory: ./curl + run: ./configure --with-wolfssl + - name: curl make + working-directory: ./curl + run: make + - name: curl make install + working-directory: ./curl + run: sudo make install + + - uses: actions/checkout@master + - name: wolfmqtt autogen + run: ./autogen.sh + + - name: wolfmqtt configure + run: ./configure + - name: wolfmqtt make + run: make + # Note: this will run the external tests for this CI only + - name: wolfmqtt make check + run: make check + + - name: wolfmqtt configure with libCurl Enabled + env: + WOLFMQTT_NO_EXTERNAL_BROKER_TESTS: 1 + run: ./configure --enable-curl + - name: wolfmqtt make + run: make + - name: wolfmqtt make check + run: make check + + - name: wolfmqtt configure with libCurl Enabled without TLS + env: + WOLFMQTT_NO_EXTERNAL_BROKER_TESTS: 1 + run: ./configure --enable-curl --disable-tls + - name: wolfmqtt make + run: make + - name: wolfmqtt make check + run: make check + + # capture logs on failure + - name: Show logs on failure + if: failure() || cancelled() + run: | + cat test-suite.log + cat scripts/*.log diff --git a/examples/mqttexample.c b/examples/mqttexample.c index 38b2f0184..405190d7e 100644 --- a/examples/mqttexample.c +++ b/examples/mqttexample.c @@ -659,7 +659,8 @@ int mqtt_tls_cb(MqttClient* client) rc = wolfSSL_CTX_use_certificate_file(client->tls.ctx, sock->mqttCtx->mtls_certfile, WOLFSSL_FILETYPE_PEM); if (rc != WOLFSSL_SUCCESS) { - PRINTF("Error loading certificate %s: %d (%s)", sock->mqttCtx->mtls_certfile, + PRINTF("Error loading certificate %s: %d (%s)", + sock->mqttCtx->mtls_certfile, rc, wolfSSL_ERR_reason_error_string(rc)); return rc; } @@ -673,7 +674,8 @@ int mqtt_tls_cb(MqttClient* client) rc = wolfSSL_CTX_use_PrivateKey_file(client->tls.ctx, sock->mqttCtx->mtls_keyfile, WOLFSSL_FILETYPE_PEM); if (rc != WOLFSSL_SUCCESS) { - PRINTF("Error loading key %s: %d (%s)", sock->mqttCtx->mtls_keyfile, + PRINTF("Error loading key %s: %d (%s)", + sock->mqttCtx->mtls_keyfile, rc, wolfSSL_ERR_reason_error_string(rc)); return rc; } @@ -782,7 +784,8 @@ int mqtt_dtls_cb(MqttClient* client) { rc = wolfSSL_CTX_use_certificate_file(client->tls.ctx, sock->mqttCtx->mtls_certfile, WOLFSSL_FILETYPE_PEM); if (rc != WOLFSSL_SUCCESS) { - PRINTF("Error loading certificate %s: %d (%s)", sock->mqttCtx->mtls_certfile, + PRINTF("Error loading certificate %s: %d (%s)", + sock->mqttCtx->mtls_certfile, rc, wolfSSL_ERR_reason_error_string(rc)); return rc; } @@ -790,7 +793,8 @@ int mqtt_dtls_cb(MqttClient* client) { rc = wolfSSL_CTX_use_PrivateKey_file(client->tls.ctx, sock->mqttCtx->mtls_keyfile, WOLFSSL_FILETYPE_PEM); if (rc != WOLFSSL_SUCCESS) { - PRINTF("Error loading key %s: %d (%s)", sock->mqttCtx->mtls_keyfile, + PRINTF("Error loading key %s: %d (%s)", + sock->mqttCtx->mtls_keyfile, rc, wolfSSL_ERR_reason_error_string(rc)); return rc; } diff --git a/examples/mqttnet.c b/examples/mqttnet.c index d50bd2c33..57028455d 100644 --- a/examples/mqttnet.c +++ b/examples/mqttnet.c @@ -404,7 +404,7 @@ mqttcurl_wait(curl_socket_t sockfd, int for_recv, int timeout_ms) FD_SET(sockfd, &errfd); - if(for_recv) { + if (for_recv) { FD_SET(sockfd, &infd); } else { @@ -438,7 +438,7 @@ mqttcurl_connect(SocketContext * sock, const char* host, word16 port, return MQTT_CODE_ERROR_BAD_ARG; } - /* Toggle with option, or put behind debug define? */ +#ifdef DEBUG_WOLFMQTT res = curl_easy_setopt(sock->curl, CURLOPT_VERBOSE, 1L); if (res != CURLE_OK) { @@ -446,6 +446,7 @@ mqttcurl_connect(SocketContext * sock, const char* host, word16 port, res, curl_easy_strerror(res)); return MQTT_CODE_ERROR_CURL; } +#endif if (timeout_ms != 0) { res = curl_easy_setopt(sock->curl, CURLOPT_CONNECTTIMEOUT_MS, @@ -671,7 +672,7 @@ static int NetWrite(void *context, const byte* buf, int buf_len, #endif /* A very simple retry with timeout example. This assumes the entire - * payload will be transfered in a single shot without buffering. */ + * payload will be transferred in a single shot without buffering. */ for (size_t i = 0; i < MQTT_CURL_NUM_RETRY; ++i) { res = curl_easy_send(sock->curl, buf, buf_len, &sent); @@ -738,7 +739,7 @@ static int NetRead(void *context, byte* buf, int buf_len, #endif /* A very simple retry with timeout example. This assumes the entire - * payload will be transfered in a single shot without buffering. */ + * payload will be transferred in a single shot without buffering. */ for (size_t i = 0; i < MQTT_CURL_NUM_RETRY; ++i) { res = curl_easy_recv(sock->curl, buf, buf_len, &recvd); From c329ef77b3e7b3972bfd9217061a70110e291a5d Mon Sep 17 00:00:00 2001 From: philljj Date: Fri, 8 Dec 2023 15:07:33 -0600 Subject: [PATCH 60/62] Add curl easy socket backend: fix warning, fix multithread test. --- examples/mqttnet.c | 6 +++--- scripts/multithread.test | 7 ++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/examples/mqttnet.c b/examples/mqttnet.c index 57028455d..7646d98a6 100644 --- a/examples/mqttnet.c +++ b/examples/mqttnet.c @@ -432,7 +432,7 @@ static int mqttcurl_connect(SocketContext * sock, const char* host, word16 port, int timeout_ms) { - CURLcode res = 0; + CURLcode res = CURLE_OK; if (sock == NULL) { return MQTT_CODE_ERROR_BAD_ARG; @@ -644,7 +644,7 @@ static int NetConnect(void *context, const char* host, word16 port, static int NetWrite(void *context, const byte* buf, int buf_len, int timeout_ms) { - CURLcode res = 0; + CURLcode res = CURLE_OK; SocketContext * sock = (SocketContext*)context; size_t sent = 0; curl_socket_t sockfd = 0; @@ -711,7 +711,7 @@ static int NetWrite(void *context, const byte* buf, int buf_len, static int NetRead(void *context, byte* buf, int buf_len, int timeout_ms) { - CURLcode res = 0; + CURLcode res = CURLE_OK; SocketContext * sock = (SocketContext*)context; size_t recvd = 0; curl_socket_t sockfd = 0; diff --git a/scripts/multithread.test b/scripts/multithread.test index 0f382e717..e7d13b7eb 100755 --- a/scripts/multithread.test +++ b/scripts/multithread.test @@ -72,6 +72,7 @@ then def_args="${def_args} -h localhost" tls_port_args="-p 18883" port_args="-p ${port}" + cacert_args="-A scripts/broker_test/ca-cert.pem" fi echo -e "Base args: $def_args $port_args" @@ -92,15 +93,15 @@ RESULT=$? if test $has_tls == yes then # Run with TLS and QoS 0-2 - ./examples/multithread/multithread $def_args $tls_port_args -t -q 0 $1 + ./examples/multithread/multithread $def_args $cacert_args $tls_port_args -t -q 0 $1 RESULT=$? [ $RESULT -ne 0 ] && echo -e "\n\nMultithread Client failed! TLS=On, QoS=0" && do_cleanup "-1" - ./examples/multithread/multithread $def_args $tls_port_args -t -q 1 $1 + ./examples/multithread/multithread $def_args $cacert_args $tls_port_args -t -q 1 $1 RESULT=$? [ $RESULT -ne 0 ] && echo -e "\n\nMultithread Client failed! TLS=On, QoS=1" && do_cleanup "-1" - ./examples/multithread/multithread $def_args $tls_port_args -t -q 2 $1 + ./examples/multithread/multithread $def_args $cacert_args $tls_port_args -t -q 2 $1 RESULT=$? [ $RESULT -ne 0 ] && echo -e "\n\nMultithread Client failed! TLS=On, QoS=2" && do_cleanup "-1" fi From ce679c97e93b241438ac0c7c160f2c17588b8a49 Mon Sep 17 00:00:00 2001 From: jordan Date: Sun, 10 Dec 2023 14:41:43 -0600 Subject: [PATCH 61/62] Add curl easy socket backend: configure checks, handle STDIN_WAKE and TIMEOUT, fix tests. --- configure.ac | 35 ++++++++++------ examples/mqttnet.c | 94 ++++++++++++++++++++++++++++++++++--------- scripts/nbclient.test | 9 +++-- 3 files changed, 103 insertions(+), 35 deletions(-) diff --git a/configure.ac b/configure.ac index f9827a0ff..032735fe2 100644 --- a/configure.ac +++ b/configure.ac @@ -169,19 +169,6 @@ AC_CHECK_LIB([wolfssl],[wolfCrypt_Init],,[AC_MSG_ERROR([libwolfssl is required a fi -# libcurl support -AC_ARG_ENABLE([curl], - [AS_HELP_STRING([--enable-curl],[Enable curl easy socket backend (default: disabled)])], - [ ENABLED_CURL=$enableval ], - [ ENABLED_CURL=no ] - ) - -if test "x$ENABLED_CURL" = "xyes"; then - AM_CFLAGS="$AM_CFLAGS -DENABLE_MQTT_CURL" - - AC_CHECK_LIB([curl],[curl_easy_init],,[AC_MSG_ERROR([libcurl is required and wasn't found on the system. It can be obtained from https://curl.se/download.html.])]) -fi - # Non-Blocking support AC_ARG_ENABLE([nonblock], @@ -255,6 +242,28 @@ then fi +# libcurl support +AC_ARG_ENABLE([curl], + [AS_HELP_STRING([--enable-curl],[Enable curl easy socket backend (default: disabled)])], + [ ENABLED_CURL=$enableval ], + [ ENABLED_CURL=no ] + ) + +if test "x$ENABLED_CURL" = "xyes"; then + if test "x$ENABLED_ALL" = "xyes"; then + AC_MSG_ERROR([--enable-all and --enable-curl are incompatible]) + fi + + if test "x$ENABLED_SN" = "xyes"; then + AC_MSG_ERROR([--enable-sn and --enable-curl are incompatible]) + fi + + AM_CFLAGS="$AM_CFLAGS -DENABLE_MQTT_CURL" + + AC_CHECK_LIB([curl],[curl_easy_init],,[AC_MSG_ERROR([libcurl is required and wasn't found on the system. It can be obtained from https://curl.se/download.html.])]) +fi + + # MQTT v5.0 AC_ARG_ENABLE([v5], [AS_HELP_STRING([--enable-v5],[Enable MQTT v5.0 support (default: disabled)])], diff --git a/examples/mqttnet.c b/examples/mqttnet.c index 7646d98a6..e070e8480 100644 --- a/examples/mqttnet.c +++ b/examples/mqttnet.c @@ -387,7 +387,8 @@ static int NetRead(void *context, byte* buf, int buf_len, #define MQTT_CURL_NUM_RETRY (2) static int -mqttcurl_wait(curl_socket_t sockfd, int for_recv, int timeout_ms) +mqttcurl_wait(curl_socket_t sockfd, int for_recv, int timeout_ms, + int test_mode) { struct timeval tv; fd_set infd; @@ -406,6 +407,11 @@ mqttcurl_wait(curl_socket_t sockfd, int for_recv, int timeout_ms) if (for_recv) { FD_SET(sockfd, &infd); + #ifdef WOLFMQTT_ENABLE_STDIN_CAP + if (!test_mode) { + FD_SET(STDIN, &infd); + } + #endif /* WOLFMQTT_ENABLE_STDIN_CAP */ } else { FD_SET(sockfd, &outfd); @@ -415,17 +421,25 @@ mqttcurl_wait(curl_socket_t sockfd, int for_recv, int timeout_ms) if (rc > 0) { if (for_recv && FD_ISSET(sockfd, &infd)) { - rc = 1; + return MQTT_CODE_CONTINUE; } else if (!for_recv && FD_ISSET(sockfd, &outfd)) { - rc = 1; + return MQTT_CODE_CONTINUE; + } + #ifdef WOLFMQTT_ENABLE_STDIN_CAP + else if (for_recv && !test_mode && FD_ISSET(STDIN, &infd)) { + return MQTT_CODE_STDIN_WAKE; } + #endif /* WOLFMQTT_ENABLE_STDIN_CAP */ else if (FD_ISSET(sockfd, &errfd)) { - rc = -1; + return MQTT_CODE_ERROR_NETWORK; } } + else if (rc == 0) { + return MQTT_CODE_ERROR_TIMEOUT; + } - return rc; + return MQTT_CODE_ERROR_NETWORK; } static int @@ -434,7 +448,7 @@ mqttcurl_connect(SocketContext * sock, const char* host, word16 port, { CURLcode res = CURLE_OK; - if (sock == NULL) { + if (sock == NULL || sock->curl == NULL) { return MQTT_CODE_ERROR_BAD_ARG; } @@ -583,6 +597,34 @@ mqttcurl_connect(SocketContext * sock, const char* host, word16 port, } #endif /* ENABLE_MQTT_TLS */ + #if 0 + /* Set proxy options. + * Unused at the moment. */ + if (sock->mqttCtx->use_proxy != NULL) { + /* Set the proxy hostname or ip address string. Append + * ":[port num]" to the string to specify a port. */ + res = curl_easy_setopt(sock->curl, CURLOPT_PROXY, + sock->mqttCtx->proxy_str); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(CURLOPT_PROXY, %s) returned: %d", + res, sock->mqttCtx->proxy_str); + return MQTT_CODE_ERROR_CURL; + } + + /* Set the proxy type. E.g. CURLPROXY_HTTP, CURLPROXY_HTTPS, + * CURLPROXY_HTTPS2, etc. */ + res = curl_easy_setopt(sock->curl, CURLOPT_PROXYTYPE, + CURLPROXY_HTTP); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(CURLOPT_PROXYTYPE) returned: %d", + res); + return MQTT_CODE_ERROR_CURL; + } + } + #endif + res = curl_easy_setopt(sock->curl, CURLOPT_CONNECT_ONLY, 1); if (res != CURLE_OK) { @@ -648,6 +690,7 @@ static int NetWrite(void *context, const byte* buf, int buf_len, SocketContext * sock = (SocketContext*)context; size_t sent = 0; curl_socket_t sockfd = 0; + int wait_rc = 0; if (context == NULL || buf == NULL || buf_len == 0) { return MQTT_CODE_ERROR_BAD_ARG; @@ -672,27 +715,34 @@ static int NetWrite(void *context, const byte* buf, int buf_len, #endif /* A very simple retry with timeout example. This assumes the entire - * payload will be transferred in a single shot without buffering. */ + * payload will be transferred in a single shot without buffering. + * todo: add buffering? */ for (size_t i = 0; i < MQTT_CURL_NUM_RETRY; ++i) { res = curl_easy_send(sock->curl, buf, buf_len, &sent); if (res == CURLE_OK) { -#if defined(WOLFMQTT_DEBUG_SOCKET) + #if defined(WOLFMQTT_DEBUG_SOCKET) PRINTF("info: curl_easy_send(%d) returned: %d, %s", buf_len, res, curl_easy_strerror(res)); -#endif + #endif break; } if (res == CURLE_AGAIN) { -#if defined(WOLFMQTT_DEBUG_SOCKET) + #if defined(WOLFMQTT_DEBUG_SOCKET) PRINTF("info: curl_easy_send(%d) returned: %d, %s", buf_len, res, curl_easy_strerror(res)); -#endif + #endif - if (mqttcurl_wait(sockfd, 0, timeout_ms) >= 0) { + wait_rc = mqttcurl_wait(sockfd, 0, timeout_ms, + sock->mqttCtx->test_mode); + + if (wait_rc == MQTT_CODE_CONTINUE) { continue; } + else { + return wait_rc; + } } PRINTF("error: curl_easy_send(%d) returned: %d, %s", buf_len, res, @@ -715,6 +765,7 @@ static int NetRead(void *context, byte* buf, int buf_len, SocketContext * sock = (SocketContext*)context; size_t recvd = 0; curl_socket_t sockfd = 0; + int wait_rc = 0; if (context == NULL || buf == NULL || buf_len == 0) { return MQTT_CODE_ERROR_BAD_ARG; @@ -739,27 +790,34 @@ static int NetRead(void *context, byte* buf, int buf_len, #endif /* A very simple retry with timeout example. This assumes the entire - * payload will be transferred in a single shot without buffering. */ + * payload will be transferred in a single shot without buffering. + * todo: add buffering? */ for (size_t i = 0; i < MQTT_CURL_NUM_RETRY; ++i) { res = curl_easy_recv(sock->curl, buf, buf_len, &recvd); if (res == CURLE_OK) { -#if defined(WOLFMQTT_DEBUG_SOCKET) + #if defined(WOLFMQTT_DEBUG_SOCKET) PRINTF("info: curl_easy_recv(%d) returned: %d, %s", buf_len, res, curl_easy_strerror(res)); -#endif + #endif break; } if (res == CURLE_AGAIN) { -#if defined(WOLFMQTT_DEBUG_SOCKET) + #if defined(WOLFMQTT_DEBUG_SOCKET) PRINTF("info: curl_easy_recv(%d) returned: %d, %s", buf_len, res, curl_easy_strerror(res)); -#endif + #endif + + wait_rc = mqttcurl_wait(sockfd, 1, timeout_ms, + sock->mqttCtx->test_mode); - if (mqttcurl_wait(sockfd, 1, timeout_ms) >= 0) { + if (wait_rc == MQTT_CODE_CONTINUE) { continue; } + else { + return wait_rc; + } } PRINTF("error: curl_easy_recv(%d) returned: %d, %s", buf_len, res, diff --git a/scripts/nbclient.test b/scripts/nbclient.test index 9c15f786b..91624a920 100755 --- a/scripts/nbclient.test +++ b/scripts/nbclient.test @@ -36,7 +36,7 @@ generate_port() { # function to produce a random port number # Check for TLS support has_tls=no -./examples/multithread/multithread -? 2>&1 | grep -- 'Enable TLS' +./examples/nbclient/nbclient -? 2>&1 | grep -- 'Enable TLS' if [ $? -eq 0 ]; then has_tls=yes fi @@ -74,6 +74,7 @@ then def_args="${def_args} -h localhost" tls_port_args="-p 18883" port_args="-p ${port}" + cacert_args="-A scripts/broker_test/ca-cert.pem" fi echo -e "Base args: $def_args $port_args" @@ -94,15 +95,15 @@ RESULT=$? if test $has_tls == yes then # Run with TLS and QoS 0-2 - ./examples/nbclient/nbclient $def_args $tls_port_args -t -q 0 $1 + ./examples/nbclient/nbclient $def_args $cacert_args $tls_port_args -t -q 0 $1 RESULT=$? [ $RESULT -ne 0 ] && echo -e "\n\nNon-blocking Client failed! TLS=On, QoS=0" && do_cleanup "-1" - ./examples/nbclient/nbclient $def_args $tls_port_args -t -q 1 $1 + ./examples/nbclient/nbclient $def_args $cacert_args $tls_port_args -t -q 1 $1 RESULT=$? [ $RESULT -ne 0 ] && echo -e "\n\nNon-blocking Client failed! TLS=On, QoS=1" && do_cleanup "-1" - ./examples/nbclient/nbclient $def_args $tls_port_args -t -q 2 $1 + ./examples/nbclient/nbclient $def_args $cacert_args $tls_port_args -t -q 2 $1 RESULT=$? [ $RESULT -ne 0 ] && echo -e "\n\nNon-blocking Client failed! TLS=On, QoS=2" && do_cleanup "-1" fi From 2945e1e9a62b13a5658cbe3e79ae5f4095f3fbff Mon Sep 17 00:00:00 2001 From: jordan Date: Sun, 10 Dec 2023 15:32:02 -0600 Subject: [PATCH 62/62] Add curl easy socket backend: update readme. --- README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/README.md b/README.md index 9df1684a2..b9c9adf0f 100644 --- a/README.md +++ b/README.md @@ -368,3 +368,15 @@ To use wolfMQTT with libcurl and wolfSSL: - build libcurl with `--with-wolfssl` and install to `/usr/local`. Finally, build wolfMQTT with `--enable-curl`. + +### Supported Build Options + +The `--enable-curl` option works with these combinations: +- `--enable-mt` +- `--enable-nonblock` +- `--enable-tls` (default enabled) +- `--enable-timeout` (default enabled) + +However `--enable-curl` is incompatible and not supported with these options: +- `--enable-all` +- `--enable-sn`