From 2b267f6162d430a8f3f4b0aa5edb8f9b29f0d800 Mon Sep 17 00:00:00 2001 From: philljj Date: Wed, 6 Dec 2023 11:37:41 -0600 Subject: [PATCH 1/9] 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 2/9] 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 3/9] 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 4/9] 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 5/9] 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 6/9] 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 7/9] 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 8/9] 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 9/9] 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`