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/README.md b/README.md index 7398f0cad..b9c9adf0f 100644 --- a/README.md +++ b/README.md @@ -351,3 +351,32 @@ 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`. + +### 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` diff --git a/configure.ac b/configure.ac index cd34c2361..032735fe2 100644 --- a/configure.ac +++ b/configure.ac @@ -242,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)])], @@ -310,6 +332,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"]) @@ -440,4 +463,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/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..fc4f9c489 100644 --- a/examples/firmware/fwclient.c +++ b/examples/firmware/fwclient.c @@ -26,6 +26,7 @@ #include "wolfmqtt/mqtt_client.h" +/* 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 diff --git a/examples/mqttexample.c b/examples/mqttexample.c index 07d6bf374..405190d7e 100644 --- a/examples/mqttexample.c +++ b/examples/mqttexample.c @@ -40,15 +40,12 @@ static int myoptind = 0; static char* myoptarg = NULL; #ifdef ENABLE_MQTT_TLS -static const char* mTlsCaFile; -static const char* mTlsCertFile; -static const char* mTlsKeyFile; #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 */ @@ -226,16 +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 -#else + #endif /* HAVE_PQC */ + #endif /* !ENABLE_MQTT_CURL */ PRINTF("-p Port to connect on, default: %d", MQTT_DEFAULT_PORT); #endif @@ -286,6 +284,11 @@ void mqtt_init_ctx(MQTTCtx* mqttCtx) #endif #ifdef WOLFMQTT_DEFAULT_TLS mqttCtx->use_tls = WOLFMQTT_DEFAULT_TLS; +#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; @@ -296,7 +299,11 @@ int mqtt_parse_args(MQTTCtx* mqttCtx, int argc, char** argv) int rc; #ifdef ENABLE_MQTT_TLS - #define MQTT_TLS_ARGS "c:A:K:S;Q:" + #ifdef ENABLE_MQTT_CURL + #define MQTT_TLS_ARGS "c:A:K:" + #else + #define MQTT_TLS_ARGS "c:A:K:S;Q:" + #endif #else #define MQTT_TLS_ARGS "" #endif @@ -389,14 +396,15 @@ int mqtt_parse_args(MQTTCtx* mqttCtx, int argc, char** argv) #ifdef ENABLE_MQTT_TLS case 'A': - mTlsCaFile = myoptarg; + mqttCtx->ca_file = myoptarg; break; 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; @@ -412,7 +420,8 @@ 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_CURL */ + #endif /* ENABLE_MQTT_TLS */ #ifdef WOLFMQTT_V5 case 'P': @@ -620,6 +629,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 @@ -634,22 +644,23 @@ 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; } } - 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; } @@ -661,9 +672,10 @@ 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; } @@ -733,6 +745,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; @@ -742,6 +758,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) { @@ -752,34 +769,38 @@ 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; } } - 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; } } +#else + (void)sock; #endif client->tls.ssl = wolfSSL_new(client->tls.ctx); diff --git a/examples/mqttexample.h b/examples/mqttexample.h index 315f7e186..6bdb427b8 100644 --- a/examples/mqttexample.h +++ b/examples/mqttexample.h @@ -148,6 +148,11 @@ typedef struct _MQTTCtx { const char* message; const char* pub_file; 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; int use_tls; diff --git a/examples/mqttnet.c b/examples/mqttnet.c index 3f811b74f..e070e8480 100644 --- a/examples/mqttnet.c +++ b/examples/mqttnet.c @@ -377,6 +377,481 @@ 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, + int test_mode) +{ + 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); + #ifdef WOLFMQTT_ENABLE_STDIN_CAP + if (!test_mode) { + FD_SET(STDIN, &infd); + } + #endif /* WOLFMQTT_ENABLE_STDIN_CAP */ + } + else { + FD_SET(sockfd, &outfd); + } + + rc = select((int)sockfd + 1, &infd, &outfd, &errfd, &tv); + + if (rc > 0) { + if (for_recv && FD_ISSET(sockfd, &infd)) { + return MQTT_CODE_CONTINUE; + } + else if (!for_recv && FD_ISSET(sockfd, &outfd)) { + 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)) { + return MQTT_CODE_ERROR_NETWORK; + } + } + else if (rc == 0) { + return MQTT_CODE_ERROR_TIMEOUT; + } + + return MQTT_CODE_ERROR_NETWORK; +} + +static int +mqttcurl_connect(SocketContext * sock, const char* host, word16 port, + int timeout_ms) +{ + CURLcode res = CURLE_OK; + + if (sock == NULL || sock->curl == NULL) { + return MQTT_CODE_ERROR_BAD_ARG; + } + +#ifdef DEBUG_WOLFMQTT + res = curl_easy_setopt(sock->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; + } +#endif + + if (timeout_ms != 0) { + res = curl_easy_setopt(sock->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(sock->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(sock->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(sock->curl, CURLOPT_PORT, port); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(PORT, %d) returned: %d", + port, res); + return MQTT_CODE_ERROR_CURL; + } + + #ifdef ENABLE_MQTT_TLS + if (sock->mqttCtx->use_tls) { + /* Set TLS specific options. */ + res = curl_easy_setopt(sock->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(sock->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 (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", + res); + return MQTT_CODE_ERROR_CURL; + } + } + + /* 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. */ + /* + 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", + res); + return MQTT_CODE_ERROR_CURL; + } + } + */ + + /* Set peer and host verification. */ + res = curl_easy_setopt(sock->curl, CURLOPT_SSL_VERIFYPEER, 1); + + if (res != CURLE_OK) { + PRINTF("error: curl_easy_setopt(SSL_VERIFYPEER) returned: %d", + res); + return MQTT_CODE_ERROR_CURL; + } + + /* 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", + res); + return MQTT_CODE_ERROR_CURL; + } + } + #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) { + PRINTF("error: curl_easy_setopt(CONNECT_ONLY, 1) returned: %d", + res); + return MQTT_CODE_ERROR_CURL; + } + + /* Finally do the connection. */ + res = curl_easy_perform(sock->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 * sock = (SocketContext*)context; + int rc = 0; + + if (context == NULL || host == NULL || *host == '\0') { + return MQTT_CODE_ERROR_BAD_ARG; + } + + 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, sock->mqttCtx->use_tls); +#endif + + sock->curl = curl_easy_init(); + + if (sock->curl == NULL) { + PRINTF("error: curl_easy_init returned NULL"); + return MQTT_CODE_ERROR_MEMORY; + } + + rc = mqttcurl_connect(sock, host, port, timeout_ms); + + if (rc != MQTT_CODE_SUCCESS) { + curl_easy_cleanup(sock->curl); + sock->curl = NULL; + return rc; + } + + sock->stat = SOCK_CONN; + return MQTT_CODE_SUCCESS; +} + +static int NetWrite(void *context, const byte* buf, int buf_len, + int timeout_ms) +{ + CURLcode res = CURLE_OK; + 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; + } + + /* get the active socket from libcurl */ + res = curl_easy_getinfo(sock->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("sock->curl = %p, sockfd = %d", (void *)sock->curl, sockfd); +#endif + + /* A very simple retry with timeout example. This assumes the entire + * 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) + 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 + + 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, + 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 = CURLE_OK; + 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; + } + + /* get the active socket from libcurl */ + res = curl_easy_getinfo(sock->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("sock->curl = %p, sockfd = %d", (void *)sock->curl, sockfd); +#endif + + /* A very simple retry with timeout example. This assumes the entire + * 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) + 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 + + wait_rc = mqttcurl_wait(sockfd, 1, timeout_ms, + sock->mqttCtx->test_mode); + + if (wait_rc == MQTT_CODE_CONTINUE) { + continue; + } + else { + return wait_rc; + } + } + + 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 * sock = (SocketContext*)context; + + if (sock == NULL) { + return MQTT_CODE_ERROR_BAD_ARG; + } + + if (sock->curl != NULL) { +#if defined(WOLFMQTT_DEBUG_SOCKET) + PRINTF("info: curl_easy_cleanup"); +#endif + curl_easy_cleanup(sock->curl); + sock->curl = NULL; + } + + return 0; +} + /* -------------------------------------------------------------------------- */ /* GENERIC BSD SOCKET TCP NETWORK CALLBACK EXAMPLE */ /* -------------------------------------------------------------------------- */ @@ -996,6 +1471,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..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" @@ -45,6 +49,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/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" 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 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 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_socket.c b/src/mqtt_socket.c index 7fa70caf9..03e062bb3 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" @@ -51,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) { @@ -96,16 +100,20 @@ 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) { 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);; - #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; @@ -124,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 */ @@ -154,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); @@ -226,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 */ @@ -259,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); } @@ -386,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 @@ -502,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); @@ -515,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; @@ -525,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 @@ -534,6 +544,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/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" */ 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,