diff --git a/.github/workflows/fsanitize-check.yml b/.github/workflows/fsanitize-check.yml index 7ec5483bf..fdba323c9 100644 --- a/.github/workflows/fsanitize-check.yml +++ b/.github/workflows/fsanitize-check.yml @@ -2,7 +2,7 @@ name: fsanitize check Test on: push: - branches: [ '*' ] + branches: [ 'master', 'main', 'release/**' ] pull_request: branches: [ '*' ] @@ -11,17 +11,40 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 5 + env: + WOLFMQTT_NO_EXTERNAL_BROKER_TESTS: 1 steps: + - name: Install dependencies + run: | + # Don't prompt for anything + export DEBIAN_FRONTEND=noninteractive + sudo apt-get update + # Install mosquitto + sudo apt-get install -y mosquitto bubblewrap + + - name: Setup mosquitto broker + run: | + # Disable default broker daemon + sudo service mosquitto stop + sleep 1 + + # This is some debug info useful if something goes wrong + - name: Show network status + run: | + sudo ifconfig + sudo route + sudo netstat -tulpan + - uses: actions/checkout@master with: repository: wolfssl/wolfssl path: wolfssl - name: wolfssl autogen - working-directory: ./wolfssl + working-directory: ./wolfssl run: ./autogen.sh - name: wolfssl configure - working-directory: ./wolfssl + working-directory: ./wolfssl run: ./configure --enable-enckeys - name: wolfssl make working-directory: ./wolfssl @@ -32,27 +55,45 @@ jobs: - uses: actions/checkout@master - name: autogen run: ./autogen.sh + - name: configure run: ./configure CC="gcc -fsanitize=address" - name: make run: make - name: make check run: make check + + - name: configure without TLS + run: ./configure CC="gcc -fsanitize=address" --enable-all --disable-tls + - name: make + run: make + - name: make check + run: make check + - name: configure with SN Enabled - run: ./configure --enable-sn CC="gcc -fsanitize=address" + run: ./configure CC="gcc -fsanitize=address" --enable-sn - name: make run: make - name: make check run: make check + - name: configure with Non-Block - run: ./configure --enable-nonblock CFLAGS="-DWOLFMQTT_TEST_NONBLOCK" CC="gcc -fsanitize=address" + run: ./configure CC="gcc -fsanitize=address" --enable-nonblock CFLAGS="-DWOLFMQTT_TEST_NONBLOCK" - name: make run: make - name: make check run: make check + - name: configure with Non-Block and Multi-threading - run: ./configure --enable-mt --enable-nonblock CFLAGS="-DWOLFMQTT_TEST_NONBLOCK" CC="gcc -fsanitize=address" + run: ./configure CC="gcc -fsanitize=address" --enable-mt --enable-nonblock CFLAGS="-DWOLFMQTT_TEST_NONBLOCK" - name: make run: make - name: make check 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/.github/workflows/macos-check.yml b/.github/workflows/macos-check.yml index eea2f48a8..18c724a22 100644 --- a/.github/workflows/macos-check.yml +++ b/.github/workflows/macos-check.yml @@ -2,10 +2,13 @@ name: macOS Build Test on: push: - branches: [ '*' ] + branches: [ 'master', 'main', 'release/**' ] pull_request: branches: [ '*' ] +env: + WOLFMQTT_NO_EXTERNAL_BROKER_TESTS: 1 + jobs: build: @@ -17,12 +20,16 @@ jobs: repository: wolfssl/wolfssl path: wolfssl - name: brew - run: brew install automake libtool + run: | + brew install automake libtool md5sha1sum mosquitto + echo "/usr/local/sbin/" >> $GITHUB_PATH + echo "/usr/local/opt/mosquitto/sbin/" >> $GITHUB_PATH + - name: wolfssl autogen - working-directory: ./wolfssl + working-directory: ./wolfssl run: ./autogen.sh - name: wolfssl configure - working-directory: ./wolfssl + working-directory: ./wolfssl run: ./configure --enable-enckeys - name: wolfssl make working-directory: ./wolfssl @@ -30,30 +37,49 @@ jobs: - name: wolfssl make install working-directory: ./wolfssl run: make install + - uses: actions/checkout@master - name: autogen run: ./autogen.sh + - name: configure run: ./configure - name: make run: make - name: make check run: make check + + - name: configure without TLS + run: ./configure --enable-all --disable-tls + - name: make + run: make + - name: make check + run: make check + - name: configure with SN Enabled run: ./configure --enable-sn - name: make run: make - name: make check run: make check + - name: configure with Non-Block run: ./configure --enable-nonblock CFLAGS="-DWOLFMQTT_TEST_NONBLOCK" - name: make run: make - name: make check run: make check + - name: configure with Non-Block and Multi-threading run: ./configure --enable-mt --enable-nonblock CFLAGS="-DWOLFMQTT_TEST_NONBLOCK" - name: make run: make - name: make check run: make check + + # capture logs on failure + - name: Show logs on failure + if: failure() || cancelled() + run: | + cat test-suite.log + cat scripts/*.log diff --git a/.github/workflows/mqtt-sn-check.yml b/.github/workflows/mqtt-sn-check.yml new file mode 100644 index 000000000..4d00dd2b0 --- /dev/null +++ b/.github/workflows/mqtt-sn-check.yml @@ -0,0 +1,103 @@ +name: MQTT-SN Build Test + +on: + push: + branches: [ 'master', 'main', 'release/**' ] + pull_request: + branches: [ '*' ] + +jobs: + build: + + runs-on: ubuntu-latest + timeout-minutes: 5 + + steps: + - name: Install dependencies + run: | + # Don't prompt for anything + export DEBIAN_FRONTEND=noninteractive + sudo apt-get update + # Install mosquitto + sudo apt-get install -y mosquitto bubblewrap + + - name: Setup mosquitto broker + run: | + # Disable default broker daemon + sudo service mosquitto stop + sleep 1 + mosquitto -v &> ~/broker.log & + sleep 1 + + - uses: actions/checkout@master + with: + repository: eclipse/paho.mqtt-sn.embedded-c + path: gateway + - name: Build gateway + working-directory: ./gateway/MQTTSNGateway + run: ./build.sh udp -DDEBUG -DDEBUG_NW + - name: Write config to change broker + working-directory: ./gateway/MQTTSNGateway/bin + run: | + printf "GatewayID=1\nGatewayName=PahoGateway-01\nMaxNumberOfClients=30\nKeepAlive=60\nBrokerName=localhost\nBrokerPortNo=1883\nAggregatingGateway=NO\nQoS-1=NO\nForwarder=NO\nPredefinedTopic=NO\nClientAuthentication=NO\nGatewayPortNo=10000\nMulticastPortNo=1883\nMulticastIP=225.1.1.1\nMulticastTTL=1\n" > gateway.conf + - name: Display gateway config + working-directory: ./gateway/MQTTSNGateway/bin + run: more gateway.conf + - name: Run gateway + working-directory: ./gateway/MQTTSNGateway/bin + run: sudo ./MQTT-SNGateway &> ~/gateway.log & + sleep 1 + + # This is some debug info useful if something goes wrong + - name: Show network status + run: | + sudo ifconfig + sudo route + sudo netstat -tulpan + + - uses: actions/checkout@master + with: + repository: wolfssl/wolfssl + path: wolfssl + - name: wolfssl autogen + working-directory: ./wolfssl + run: ./autogen.sh + - name: wolfssl configure + working-directory: ./wolfssl + run: ./configure --enable-enckeys + - name: wolfssl make + working-directory: ./wolfssl + run: make + - name: wolfssl make install + working-directory: ./wolfssl + run: sudo make install + + - uses: actions/checkout@master + - name: wolfmqtt autogen + run: ./autogen.sh + + - name: wolfmqtt configure with SN Enabled + run: | + export WOLFMQTT_NO_EXTERNAL_BROKER_TESTS=1 + ./configure --enable-sn + - name: wolfmqtt make + run: make + + - name: test SN Client + run: ./examples/sn-client/sn-client -T + + # Cleanup + - name: Stop gateway + if: failure() || cancelled() + run: | + sudo kill -2 $(pgrep -f "MQTT-SNGateway") + sleep 3 + sudo kill -2 $(pgrep -f "mosquitto") + sleep 1 + + # capture logs on failure + - name: Show logs on failure + if: failure() || cancelled() + run: | + sudo cat ~/gateway.log + sudo cat ~/broker.log diff --git a/.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/.github/workflows/ubuntu-check.yml b/.github/workflows/ubuntu-check.yml index c0a65c04a..5f6bef603 100644 --- a/.github/workflows/ubuntu-check.yml +++ b/.github/workflows/ubuntu-check.yml @@ -2,7 +2,7 @@ name: Ubuntu Build Test on: push: - branches: [ '*' ] + branches: [ 'master', 'main', 'release/**' ] pull_request: branches: [ '*' ] @@ -13,15 +13,36 @@ jobs: timeout-minutes: 5 steps: + - name: Install dependencies + run: | + # Don't prompt for anything + export DEBIAN_FRONTEND=noninteractive + sudo apt-get update + # Install mosquitto + sudo apt-get install -y mosquitto bubblewrap + + - name: Setup mosquitto broker + run: | + # Disable default broker daemon + sudo service mosquitto stop + sleep 1 + + # This is some debug info useful if something goes wrong + - name: Show network status + run: | + sudo ifconfig + sudo route + sudo netstat -tulpan + - uses: actions/checkout@master with: repository: wolfssl/wolfssl path: wolfssl - name: wolfssl autogen - working-directory: ./wolfssl + working-directory: ./wolfssl run: ./autogen.sh - name: wolfssl configure - working-directory: ./wolfssl + working-directory: ./wolfssl run: ./configure --enable-enckeys - name: wolfssl make working-directory: ./wolfssl @@ -29,50 +50,67 @@ jobs: - name: wolfssl make install working-directory: ./wolfssl run: sudo make install + - uses: actions/checkout@master - - name: autogen + - name: wolfmqtt autogen run: ./autogen.sh - - name: configure + + - name: wolfmqtt configure run: ./configure - - name: make + - name: wolfmqtt make run: make - - name: make check - id: make-check + # Note: this will run the external tests for this CI only + - name: wolfmqtt make check run: make check - - name: Show logs on failure - if: ${{ failure() && steps.make-check.outcome == 'failure' }} - run: | - more test-suite.log - - name: configure with SN Enabled + + - name: wolfmqtt configure without TLS + env: + WOLFMQTT_NO_EXTERNAL_BROKER_TESTS: 1 + run: ./configure --enable-all --disable-tls + - name: wolfmqtt make + run: make + - name: wolfmqtt make check + run: make check + + - name: wolfmqtt configure with SN Enabled + env: + WOLFMQTT_NO_EXTERNAL_BROKER_TESTS: 1 run: ./configure --enable-sn - - name: make + - name: wolfmqtt make run: make - - name: make check - id: make-check-sn + - name: wolfmqtt make check run: make check - - name: Show logs on failure - if: ${{ failure() && steps.make-check-sn.outcome == 'failure' }} - run: | - more test-suite.log - - name: configure with Non-Block + + - name: wolfmqtt configure with Non-Block + env: + WOLFMQTT_NO_EXTERNAL_BROKER_TESTS: 1 run: ./configure --enable-nonblock CFLAGS="-DWOLFMQTT_TEST_NONBLOCK" - - name: make + - name: wolfmqtt make run: make - - name: make check - id: make-check-nonblock + - name: wolfmqtt make check run: make check - - name: Show logs on failure - if: ${{ failure() && steps.make-check-nonblock.outcome == 'failure' }} - run: | - more test-suite.log - - name: configure with Non-Block and Multi-threading + + - name: wolfmqtt configure with Non-Block and Multi-threading + env: + WOLFMQTT_NO_EXTERNAL_BROKER_TESTS: 1 run: ./configure --enable-mt --enable-nonblock CFLAGS="-DWOLFMQTT_TEST_NONBLOCK" + - name: wolfmqtt make + run: make + - name: wolfmqtt make check + run: make check + + - name: configure with Multi-threading and WOLFMQTT_DYN_PROP + env: + WOLFMQTT_NO_EXTERNAL_BROKER_TESTS: 1 + run: ./configure --enable-mt CFLAGS="-DWOLFMQTT_DYN_PROP" - name: make run: make - name: make check - id: make-check-nonblock-mt run: make check + + # capture logs on failure - name: Show logs on failure - if: ${{ failure() && steps.make-check-nonblock-mt.outcome == 'failure' }} + if: failure() || cancelled() run: | - more test-suite.log + cat test-suite.log + cat scripts/*.log diff --git a/.github/workflows/windows-check.yml b/.github/workflows/windows-check.yml index 287d7c8f8..5b9bed0e5 100644 --- a/.github/workflows/windows-check.yml +++ b/.github/workflows/windows-check.yml @@ -2,7 +2,7 @@ name: Windows Build Test on: push: - branches: [ '*' ] + branches: [ 'master', 'main', 'release/**' ] pull_request: branches: [ '*' ] diff --git a/.github/workflows/zephyr.yml b/.github/workflows/zephyr.yml index eb81835c0..8501ea4f2 100644 --- a/.github/workflows/zephyr.yml +++ b/.github/workflows/zephyr.yml @@ -1,4 +1,6 @@ name: Zephyr tests +env: + WOLFMQTT_NO_EXTERNAL_BROKER_TESTS: 1 on: push: @@ -17,6 +19,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 20 steps: + - name: Install dependencies run: | # Don't prompt for anything @@ -86,7 +89,7 @@ jobs: sudo service mosquitto stop mosquitto -c scripts/broker_test/mosquitto.conf &> broker.log & sleep 1 # let the broker set everything up - mosquitto_sub -t sensors &> sub.log & + mosquitto_sub -p 11883 -t sensors &> sub.log & # This is some debug info useful if something goes wrong - name: Show network status diff --git a/.gitignore b/.gitignore index ca4c36036..96e4168cb 100644 --- a/.gitignore +++ b/.gitignore @@ -78,6 +78,8 @@ examples/sn-client/sn-client_qos-1 examples/sn-client/sn-multithread examples/multithread/multithread examples/wiot/wiot +examples/pub-sub/mqtt-pub +examples/pub-sub/mqtt-sub # eclipse .cproject diff --git a/CMakeLists.txt b/CMakeLists.txt index d81318f9c..cb73c91b5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,7 +21,7 @@ cmake_minimum_required(VERSION 3.16) -project(wolfMQTT VERSION 1.16.0 LANGUAGES C) +project(wolfMQTT VERSION 1.17.1 LANGUAGES C) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set(MQTT_SOURCES @@ -147,6 +147,8 @@ if (WOLFMQTT_EXAMPLES) add_mqtt_example(azureiothub azure/azureiothub.c) add_mqtt_example(fwpush firmware/fwpush.c) add_mqtt_example(fwclient firmware/fwclient.c) + add_mqtt_example(mqtt-pub pub-sub/mqtt-pub.c) + add_mqtt_example(mqtt-sub pub-sub/mqtt-sub.c) endif() #################################################### diff --git a/ChangeLog.md b/ChangeLog.md index 482331ae9..9d257bd24 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,4 +1,39 @@ ## Release Notes +### v1.17.1 (11/29/2023) +Release 1.17.1 has been developed according to wolfSSL's development and QA process (see link below) and successfully passed the quality criteria. +https://www.wolfssl.com/about/wolfssl-software-development-process-quality-assurance + +* Include stdint.h in userio_template.h by @lealem47 in #371 +* Improvements to multithread locking and tests. by @dgarske in #369 +* Cleanup executable status on src files. by @philljj in #372 +* Close socket on error in NetConnect by @embhorn in #375 +* Fixes for non-blocking with larger payload and improvements to the test and examples by @dgarske in #373 +* Add MQTT-SN CI tests by @embhorn in #376 +* Fix Wild read in MqttProps_Free by @embhorn in #377 +* Fix fuzzer issues in MqttDecode_Props by @embhorn in #378 + + +### v1.17.0 (11/2/2023) +Release 1.17.0 has been developed according to wolfSSL's development and QA process (see link below) and successfully passed the quality criteria. +https://www.wolfssl.com/about/wolfssl-software-development-process-quality-assurance + +* Fix for declaration after executable block by @lealem47 in #341 +* Add QNX IDE, Makefile, and remove source code exec bit by @JacobBarthelmeh + in #317 +* update for cmake after wolfssl added NAMESPACE by @JacobBarthelmeh in #343 +* Add mutex protection to MqttClient_NetDisconnect by @embhorn in #342 +* Add DTLS support to MQTT-SN client by @embhorn in #348 +* Tie zephyr tests to a release by @julek-wolfssl in #350 +* add documentation link to README by @gojimmypi in #355 +* Possible patch for POSIX conditional wait issue by @dgarske in #356 +* Fix publish with topic ID >=127 by @embhorn in #351 +* Adding publish and subscribe atomic client examples by @embhorn in #347 +* Allow disabling the posix conditional signal by @dgarske in #360 +* Exclude CI tests with external brokers by @embhorn in #362 +* Improvements for client property stack by @dgarske in #361 +* Add mosquitto to CI tests by @embhorn in #365 +* Fixes for non-blocking edge cases by @dgarske in #363 +* Refactor MQTT-SN code by @embhorn in #366 ### v1.16.0 (6/29/2023) * Add testing for TLS mutual auth, and fsanitize gh test by @lealem47 in #321 diff --git a/IDE/STM32CUBE/userio_template.h b/IDE/STM32CUBE/userio_template.h index e61720c7c..d902850c2 100644 --- a/IDE/STM32CUBE/userio_template.h +++ b/IDE/STM32CUBE/userio_template.h @@ -24,6 +24,8 @@ #ifdef WOLFMQTT_USER_IO +#include + #define SOCK_STREAM 1 #define SOCK_DGRAM 2 #define SOCK_RAW 3 diff --git a/README.md b/README.md index adeb2877c..b9c9adf0f 100644 --- a/README.md +++ b/README.md @@ -195,6 +195,17 @@ More about MQTT-SN examples in [examples/sn-client/README.md](examples/sn-client ### Multithread Example This example exercises the multithreading capabilities of the client library. The client implements two tasks: one that publishes to the broker; and another that waits for messages from the broker. The publish thread is created `NUM_PUB_TASKS` times (10 by default) and sends unique messages to the broker. This feature is enabled using the `--enable-mt` configuration option. The example is located in `/examples/multithread/`. +The multi-threading feature can also be used with the non-blocking socket (--enable-nb). + +If you are having issues with thread synchronization on Linux consider using not the conditional signal (`WOLFMQTT_NO_COND_SIGNAL`). + +### Atomic publish and subscribe examples +In the `examples/pub-sub` folder, there are two simple client examples: +* mqtt-pub - publishes to a topic +* mqtt-sub - subscribes to a topic and waits for messages + +These examples are useful for quickly testing or scripting. + ## Example Options The command line examples can be executed with optional parameters. To see a list of the available parameters, add the `-?` @@ -340,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 abdbd0b74..032735fe2 100644 --- a/configure.ac +++ b/configure.ac @@ -3,7 +3,7 @@ # All right reserved. AC_COPYRIGHT([Copyright (C) 2014-2023 wolfSSL Inc.]) -AC_INIT([wolfmqtt],[1.16.0],[https://github.com/wolfssl/wolfMQTT/issues],[wolfmqtt],[http://www.wolfssl.com]) +AC_INIT([wolfmqtt],[1.17.1],[https://github.com/wolfssl/wolfMQTT/issues],[wolfmqtt],[http://www.wolfssl.com]) AC_PREREQ([2.63]) AC_CONFIG_AUX_DIR([build-aux]) @@ -24,7 +24,7 @@ AC_ARG_PROGRAM AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_HEADERS([src/config.h]) -WOLFMQTT_LIBRARY_VERSION=15:0:0 +WOLFMQTT_LIBRARY_VERSION=16:1:0 # | | | # +------+ | +---+ # | | | @@ -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 283c20e87..398ecb147 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 @@ -54,20 +55,24 @@ /* Locals */ static int mStopRead = 0; +static int mTestDone = 0; /* Configuration */ #define APP_HARDWARE "wolf_aws_iot_demo" #define APP_FIRMWARE_VERSION LIBWOLFMQTT_VERSION_STRING +#ifndef MAX_BUFFER_SIZE #define MAX_BUFFER_SIZE 512 /* Maximum size for network read/write callbacks */ +#endif #define AWSIOT_HOST "a2dujmi05ideo2-ats.iot.us-west-2.amazonaws.com" #define AWSIOT_DEVICE_ID "demoDevice" #define AWSIOT_QOS MQTT_QOS_1 #define AWSIOT_KEEP_ALIVE_SEC DEFAULT_KEEP_ALIVE_SEC #define AWSIOT_CMD_TIMEOUT_MS DEFAULT_CMD_TIMEOUT_MS -#define AWSIOT_SUBSCRIBE_TOPIC "$aws/things/" AWSIOT_DEVICE_ID "/shadow/update/delta" #define AWSIOT_PUBLISH_TOPIC "$aws/things/" AWSIOT_DEVICE_ID "/shadow/update" +#define AWSIOT_SUBSCRIBE_TOPIC AWSIOT_PUBLISH_TOPIC + #define AWSIOT_PUBLISH_MSG_SZ 400 @@ -293,6 +298,9 @@ static int mqtt_message_cb(MqttClient *client, MqttMessage *msg, if (msg_done) { PRINTF("MQTT Message: Done"); + if (mqttCtx->test_mode) { + mTestDone = 1; + } } /* Return negative to terminate publish processing */ @@ -610,8 +618,9 @@ int awsiot_test(MQTTCtx *mqttCtx) if (rc == MQTT_CODE_CONTINUE) { return rc; } - PRINTF("MQTT Publish: Topic %s, %s (%d)", - mqttCtx->publish.topic_name, MqttClient_ReturnCodeToString(rc), rc); + PRINTF("MQTT Publish: Topic %s, ID %d, %s (%d)", + mqttCtx->publish.topic_name, mqttCtx->publish.packet_id, + MqttClient_ReturnCodeToString(rc), rc); if (rc != MQTT_CODE_SUCCESS) { goto disconn; } @@ -626,13 +635,6 @@ int awsiot_test(MQTTCtx *mqttCtx) mqttCtx->stat = WMQ_WAIT_MSG; do { - /* check for test mode or stop */ - if (mStopRead || mqttCtx->test_mode) { - rc = MQTT_CODE_SUCCESS; - PRINTF("MQTT Exiting..."); - break; - } - /* Try and read packet */ rc = MqttClient_WaitMessage(&mqttCtx->client, mqttCtx->cmd_timeout_ms); @@ -646,8 +648,17 @@ int awsiot_test(MQTTCtx *mqttCtx) if (rc == MQTT_CODE_CONTINUE) { return rc; } + + /* check for test mode or stop */ + if (mStopRead || mTestDone) { + rc = MQTT_CODE_SUCCESS; + mqttCtx->stat = WMQ_DISCONNECT; + PRINTF("MQTT Exiting..."); + break; + } + #ifdef WOLFMQTT_ENABLE_STDIN_CAP - else if (rc == MQTT_CODE_STDIN_WAKE) { + if (rc == MQTT_CODE_STDIN_WAKE) { /* Get data from STDIO */ XMEMSET(mqttCtx->rx_buf, 0, MAX_BUFFER_SIZE); if (XFGETS((char*)mqttCtx->rx_buf, MAX_BUFFER_SIZE - 1, stdin) != NULL) { @@ -667,13 +678,14 @@ int awsiot_test(MQTTCtx *mqttCtx) mqttCtx->publish.buffer = (byte*)mqttCtx->app_ctx; mqttCtx->publish.total_len = (word32)XSTRLEN((char*)mqttCtx->app_ctx); rc = MqttClient_Publish(&mqttCtx->client, &mqttCtx->publish); - PRINTF("MQTT Publish: Topic %s, %s (%d)", - mqttCtx->publish.topic_name, + PRINTF("MQTT Publish: Topic %s, ID %d, %s (%d)", + mqttCtx->publish.topic_name, mqttCtx->publish.packet_id, MqttClient_ReturnCodeToString(rc), rc); } } + else #endif - else if (rc == MQTT_CODE_ERROR_TIMEOUT) { + if (rc == MQTT_CODE_ERROR_TIMEOUT) { /* Keep Alive */ PRINTF("Keep-alive timeout, sending ping"); @@ -838,7 +850,7 @@ int awsiot_test(MQTTCtx *mqttCtx) #ifdef ENABLE_AWSIOT_EXAMPLE do { rc = awsiot_test(&mqttCtx); - } while (rc == MQTT_CODE_CONTINUE); + } while (!mStopRead && rc == MQTT_CODE_CONTINUE); mqtt_free_ctx(&mqttCtx); #else diff --git a/examples/azure/azureiothub.c b/examples/azure/azureiothub.c index 5b6fbbbef..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 @@ -67,6 +68,7 @@ /* Locals */ static int mStopRead = 0; +static int mTestDone = 0; /* Configuration */ /* Reference: @@ -75,7 +77,9 @@ static int mStopRead = 0; * https://azure.microsoft.com/en-us/documentation/articles/iot-hub-sas-tokens/#using-sas-tokens-as-a-device * https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-mqtt-support */ +#ifndef MAX_BUFFER_SIZE #define MAX_BUFFER_SIZE 1024 /* Maximum size for network read/write callbacks */ +#endif #define AZURE_API_VERSION "?api-version=2018-06-30" #define AZURE_HOST "wolfMQTT.azure-devices.net" #define AZURE_DEVICE_ID "demoDevice" @@ -160,6 +164,9 @@ static int mqtt_message_cb(MqttClient *client, MqttMessage *msg, if (msg_done) { PRINTF("MQTT Message: Done"); + if (mqttCtx->test_mode) { + mTestDone = 1; + } } /* Return negative to terminate publish processing */ @@ -433,8 +440,9 @@ int azureiothub_test(MQTTCtx *mqttCtx) if (rc == MQTT_CODE_CONTINUE) { return rc; } - PRINTF("MQTT Publish: Topic %s, %s (%d)", - mqttCtx->publish.topic_name, MqttClient_ReturnCodeToString(rc), rc); + PRINTF("MQTT Publish: Topic %s, ID %d, %s (%d)", + mqttCtx->publish.topic_name, mqttCtx->publish.packet_id, + MqttClient_ReturnCodeToString(rc), rc); if (rc != MQTT_CODE_SUCCESS) { goto disconn; } @@ -449,13 +457,6 @@ int azureiothub_test(MQTTCtx *mqttCtx) mqttCtx->stat = WMQ_WAIT_MSG; do { - /* check for test mode or stop */ - if (mStopRead || mqttCtx->test_mode) { - rc = MQTT_CODE_SUCCESS; - PRINTF("MQTT Exiting..."); - break; - } - /* Try and read packet */ rc = MqttClient_WaitMessage(&mqttCtx->client, mqttCtx->cmd_timeout_ms); @@ -469,8 +470,17 @@ int azureiothub_test(MQTTCtx *mqttCtx) if (rc == MQTT_CODE_CONTINUE) { return rc; } + + /* check for test mode or stop */ + if (mStopRead || mTestDone) { + rc = MQTT_CODE_SUCCESS; + mqttCtx->stat = WMQ_DISCONNECT; + PRINTF("MQTT Exiting..."); + goto disconn; + } + #ifdef WOLFMQTT_ENABLE_STDIN_CAP - else if (rc == MQTT_CODE_STDIN_WAKE) { + if (rc == MQTT_CODE_STDIN_WAKE) { /* Get data from STDIO */ XMEMSET(mqttCtx->rx_buf, 0, MAX_BUFFER_SIZE); if (XFGETS((char*)mqttCtx->rx_buf, MAX_BUFFER_SIZE - 1, stdin) != NULL) { @@ -488,43 +498,58 @@ int azureiothub_test(MQTTCtx *mqttCtx) mqttCtx->publish.total_len = (word16)rc; rc = MqttClient_Publish(&mqttCtx->client, &mqttCtx->publish); - PRINTF("MQTT Publish: Topic %s, %s (%d)", - mqttCtx->publish.topic_name, + PRINTF("MQTT Publish: Topic %s, ID %d, %s (%d)", + mqttCtx->publish.topic_name, mqttCtx->publish.packet_id, MqttClient_ReturnCodeToString(rc), rc); } } + else #endif - else if (rc == MQTT_CODE_ERROR_TIMEOUT) { + if (rc == MQTT_CODE_ERROR_TIMEOUT) { /* Keep Alive */ PRINTF("Keep-alive timeout, sending ping"); - - rc = MqttClient_Ping_ex(&mqttCtx->client, &mqttCtx->ping); - if (rc == MQTT_CODE_CONTINUE) { - return rc; - } - else if (rc != MQTT_CODE_SUCCESS) { - PRINTF("MQTT Ping Keep Alive Error: %s (%d)", - MqttClient_ReturnCodeToString(rc), rc); - break; - } + mqttCtx->stat = WMQ_PING; + break; } else if (rc != MQTT_CODE_SUCCESS) { /* There was an error */ PRINTF("MQTT Message Wait: %s (%d)", MqttClient_ReturnCodeToString(rc), rc); - break; + goto disconn; } } while (1); + } + FALL_THROUGH; - /* Check for error */ - if (rc != MQTT_CODE_SUCCESS) { - goto disconn; + case WMQ_PING: + { + mqttCtx->stat = WMQ_PING; + + rc = MqttClient_Ping_ex(&mqttCtx->client, &mqttCtx->ping); + if (rc == MQTT_CODE_CONTINUE) { + return rc; + } + else if (rc != MQTT_CODE_SUCCESS) { + PRINTF("MQTT Ping Keep Alive Error: %s (%d)", + MqttClient_ReturnCodeToString(rc), rc); + break; + } + + if (mqttCtx->test_mode) { + PRINTF("MQTT Ping Done, exiting for test mode"); + mTestDone = 1; + } + else { + mqttCtx->stat = WMQ_WAIT_MSG; + break; } } FALL_THROUGH; case WMQ_DISCONNECT: { + mqttCtx->stat = WMQ_DISCONNECT; + /* Disconnect */ rc = MqttClient_Disconnect(&mqttCtx->client); if (rc == MQTT_CODE_CONTINUE) { @@ -559,7 +584,6 @@ int azureiothub_test(MQTTCtx *mqttCtx) } case WMQ_UNSUB: /* not used */ - case WMQ_PING: default: rc = MQTT_CODE_ERROR_STAT; goto exit; @@ -659,7 +683,7 @@ int azureiothub_test(MQTTCtx *mqttCtx) #ifdef ENABLE_AZUREIOTHUB_EXAMPLE do { rc = azureiothub_test(&mqttCtx); - } while (rc == MQTT_CODE_CONTINUE); + } while (!mStopRead && rc == MQTT_CODE_CONTINUE); mqtt_free_ctx(&mqttCtx); #else diff --git a/examples/firmware/fwclient.c b/examples/firmware/fwclient.c index 126105e93..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 @@ -36,18 +37,18 @@ /* The signature wrapper for this example was added in wolfSSL after 3.7.1 */ #if defined(LIBWOLFSSL_VERSION_HEX) && LIBWOLFSSL_VERSION_HEX > 0x03007001 \ && defined(HAVE_ECC) && !defined(NO_SIG_WRAPPER) - #undef ENABLE_FIRMWARE_EXAMPLE - #define ENABLE_FIRMWARE_EXAMPLE + #undef ENABLE_FIRMWARE_SIG + #define ENABLE_FIRMWARE_SIG #endif #endif -#if defined(ENABLE_FIRMWARE_EXAMPLE) - +#if defined(ENABLE_FIRMWARE_SIG) #include #include #include #include +#endif #include "fwclient.h" #include "firmware.h" @@ -55,10 +56,13 @@ #include "examples/mqttnet.h" /* Configuration */ +#ifndef MAX_BUFFER_SIZE #define MAX_BUFFER_SIZE FIRMWARE_MAX_PACKET +#endif /* Locals */ static int mStopRead = 0; +static int mTestDone = 0; static byte* mFwBuf; @@ -108,10 +112,12 @@ static int fwfile_save(const char* filePath, byte* fileBuf, int fileLen) static int fw_message_process(MQTTCtx *mqttCtx, byte* buffer, word32 len) { - int rc; + int rc = 0; FirmwareHeader* header = (FirmwareHeader*)buffer; byte *sigBuf, *pubKeyBuf, *fwBuf; +#ifdef ENABLE_FIRMWARE_SIG ecc_key eccKey; +#endif word32 check_len = sizeof(FirmwareHeader) + header->sigLen + header->pubKeyLen + header->fwLen; @@ -128,6 +134,7 @@ static int fw_message_process(MQTTCtx *mqttCtx, byte* buffer, word32 len) fwBuf = (buffer + sizeof(FirmwareHeader) + header->sigLen + header->pubKeyLen); +#ifdef ENABLE_FIRMWARE_SIG /* Import the public key */ wc_ecc_init(&eccKey); rc = wc_ecc_import_x963(pubKeyBuf, header->pubKeyLen, &eccKey); @@ -140,17 +147,22 @@ static int fw_message_process(MQTTCtx *mqttCtx, byte* buffer, word32 len) &eccKey, sizeof(eccKey)); PRINTF("Firmware Signature Verification: %s (%d)", (rc == 0) ? "Pass" : "Fail", rc); - +#else + (void)pubKeyBuf; + (void)sigBuf; +#endif if (rc == 0) { /* TODO: Process firmware image */ /* For example, save to disk using topic name */ fwfile_save(mqttCtx->pub_file, fwBuf, header->fwLen); } +#ifdef ENABLE_FIRMWARE_SIG } else { PRINTF("ECC public key import failed! %d", rc); } wc_ecc_free(&eccKey); +#endif return rc; } @@ -162,13 +174,13 @@ static int mqtt_message_cb(MqttClient *client, MqttMessage *msg, /* Verify this message is for the firmware topic */ if (msg_new && - XMEMCMP(msg->topic_name, FIRMWARE_TOPIC_NAME, + XSTRNCMP(msg->topic_name, mqttCtx->topic_name, msg->topic_name_len) == 0 && !mFwBuf) { /* Allocate buffer for entire message */ /* Note: On an embedded system this could just be a write to flash. - If writting to flash change FIRMWARE_MAX_BUFFER to match + If writing to flash change FIRMWARE_MAX_BUFFER to match block size */ mFwBuf = (byte*)WOLFMQTT_MALLOC(msg->total_len); if (mFwBuf == NULL) { @@ -193,7 +205,7 @@ static int mqtt_message_cb(MqttClient *client, MqttMessage *msg, /* for test mode stop client */ if (mqttCtx->test_mode) { - mStopRead = 1; + mTestDone = 1; } } } @@ -325,8 +337,6 @@ int fwclient_test(MQTTCtx *mqttCtx) mqttCtx->subscribe.packet_id = mqtt_get_packetid(); mqttCtx->subscribe.topic_count = 1; mqttCtx->subscribe.topics = mqttCtx->topics; - mqttCtx->topics[0].topic_filter = FIRMWARE_TOPIC_NAME; - mqttCtx->topics[0].qos = mqttCtx->qos; } FALL_THROUGH; @@ -365,13 +375,6 @@ int fwclient_test(MQTTCtx *mqttCtx) rc = MqttClient_WaitMessage(&mqttCtx->client, mqttCtx->cmd_timeout_ms); - /* check for test mode */ - if (mStopRead) { - rc = MQTT_CODE_SUCCESS; - PRINTF("MQTT Exiting..."); - break; - } - #ifdef WOLFMQTT_NONBLOCK /* Track elapsed time with no activity and trigger timeout */ rc = mqtt_check_timeout(rc, &mqttCtx->start_sec, @@ -382,7 +385,20 @@ int fwclient_test(MQTTCtx *mqttCtx) if (rc == MQTT_CODE_CONTINUE) { return rc; } - else if (rc == MQTT_CODE_ERROR_TIMEOUT) { + + /* check for test mode */ + if (mStopRead || mTestDone) { + rc = MQTT_CODE_SUCCESS; + mqttCtx->stat = WMQ_DISCONNECT; + PRINTF("MQTT Exiting..."); + break; + } + + if (rc == MQTT_CODE_ERROR_TIMEOUT) { + if (mqttCtx->test_mode) { + PRINTF("Timeout in test mode, exit early!"); + mTestDone = 1; + } /* Keep Alive */ PRINTF("Keep-alive timeout, sending ping"); @@ -479,87 +495,76 @@ int fwclient_test(MQTTCtx *mqttCtx) return rc; } -#endif /* ENABLE_FIRMWARE_EXAMPLE */ /* so overall tests can pull in test function */ - #ifdef USE_WINDOWS_API - #include /* for ctrl handler */ +#ifdef USE_WINDOWS_API + #include /* for ctrl handler */ - static BOOL CtrlHandler(DWORD fdwCtrlType) - { - if (fdwCtrlType == CTRL_C_EVENT) { - #if defined(ENABLE_FIRMWARE_EXAMPLE) - mStopRead = 1; - #endif - PRINTF("Received Ctrl+c"); - return TRUE; - } - return FALSE; + static BOOL CtrlHandler(DWORD fdwCtrlType) + { + if (fdwCtrlType == CTRL_C_EVENT) { + #if defined(ENABLE_FIRMWARE_SIG) + mStopRead = 1; + #endif + PRINTF("Received Ctrl+c"); + return TRUE; } - #elif HAVE_SIGNAL - #include - static void sig_handler(int signo) - { - if (signo == SIGINT) { - #if defined(ENABLE_FIRMWARE_EXAMPLE) - mStopRead = 1; - #endif - PRINTF("Received SIGINT"); - } + return FALSE; + } +#elif HAVE_SIGNAL + #include + static void sig_handler(int signo) + { + if (signo == SIGINT) { + #if defined(ENABLE_FIRMWARE_SIG) + mStopRead = 1; + #endif + PRINTF("Received SIGINT"); } - #endif + } +#endif #if defined(NO_MAIN_DRIVER) - int fwclient_main(int argc, char** argv) +int fwclient_main(int argc, char** argv) #else - int main(int argc, char** argv) +int main(int argc, char** argv) #endif - { - int rc; - #ifdef ENABLE_FIRMWARE_EXAMPLE - MQTTCtx mqttCtx; - - /* init defaults */ - mqtt_init_ctx(&mqttCtx); - mqttCtx.app_name = "fwclient"; - mqttCtx.client_id = FIRMWARE_CLIIENT_ID; - mqttCtx.topic_name = FIRMWARE_TOPIC_NAME; - mqttCtx.qos = FIRMWARE_MQTT_QOS; - mqttCtx.pub_file = FIRMWARE_DEF_SAVE_AS; - - /* parse arguments */ - rc = mqtt_parse_args(&mqttCtx, argc, argv); - if (rc != 0) { - return rc; - } - #endif - - #ifdef USE_WINDOWS_API - if (SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE) == FALSE) { - PRINTF("Error setting Ctrl Handler! Error %d", (int)GetLastError()); - } - #elif HAVE_SIGNAL - if (signal(SIGINT, sig_handler) == SIG_ERR) { - PRINTF("Can't catch SIGINT"); - } - #endif - - #ifdef ENABLE_FIRMWARE_EXAMPLE - do { - rc = fwclient_test(&mqttCtx); - } while (rc == MQTT_CODE_CONTINUE); +{ + int rc; + MQTTCtx mqttCtx; + + /* init defaults */ + mqtt_init_ctx(&mqttCtx); + mqttCtx.app_name = "fwclient"; + mqttCtx.client_id = mqtt_append_random(FIRMWARE_CLIIENT_ID, + (word32)XSTRLEN(FIRMWARE_CLIIENT_ID)); + mqttCtx.dynamicClientId = 1; + mqttCtx.topic_name = FIRMWARE_TOPIC_NAME; + mqttCtx.qos = FIRMWARE_MQTT_QOS; + mqttCtx.pub_file = FIRMWARE_DEF_SAVE_AS; + + /* parse arguments */ + rc = mqtt_parse_args(&mqttCtx, argc, argv); + if (rc != 0) { + return rc; + } - mqtt_free_ctx(&mqttCtx); - #else - (void)argc; - (void)argv; +#ifdef USE_WINDOWS_API + if (SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE) == FALSE) { + PRINTF("Error setting Ctrl Handler! Error %d", (int)GetLastError()); + } +#elif HAVE_SIGNAL + if (signal(SIGINT, sig_handler) == SIG_ERR) { + PRINTF("Can't catch SIGINT"); + } +#endif - /* This example requires wolfSSL after 3.7.1 for signature wrapper */ - PRINTF("Example not compiled in!"); - rc = 0; /* return success, so make check passes with TLS disabled */ - #endif + do { + rc = fwclient_test(&mqttCtx); + } while (!mStopRead && rc == MQTT_CODE_CONTINUE); - return (rc == 0) ? 0 : EXIT_FAILURE; - } + mqtt_free_ctx(&mqttCtx); + return (rc == 0) ? 0 : EXIT_FAILURE; +} diff --git a/examples/firmware/fwpush.c b/examples/firmware/fwpush.c index ae1672d6f..fbefbdc90 100644 --- a/examples/firmware/fwpush.c +++ b/examples/firmware/fwpush.c @@ -36,18 +36,19 @@ /* The signature wrapper for this example was added in wolfSSL after 3.7.1 */ #if defined(LIBWOLFSSL_VERSION_HEX) && LIBWOLFSSL_VERSION_HEX > 0x03007001 \ && defined(HAVE_ECC) && !defined(NO_SIG_WRAPPER) - #undef ENABLE_FIRMWARE_EXAMPLE - #define ENABLE_FIRMWARE_EXAMPLE + #undef ENABLE_FIRMWARE_SIG + #define ENABLE_FIRMWARE_SIG #endif #endif -#if defined(ENABLE_FIRMWARE_EXAMPLE) +#ifdef ENABLE_FIRMWARE_SIG #include #include #include #include +#endif #include "fwpush.h" #include "firmware.h" @@ -55,7 +56,9 @@ #include "examples/mqttnet.h" /* Configuration */ +#ifndef MAX_BUFFER_SIZE #define MAX_BUFFER_SIZE FIRMWARE_MAX_PACKET +#endif /* Locals */ static int mStopRead = 0; @@ -121,12 +124,11 @@ static int mqtt_publish_cb(MqttPublish *publish) { /* read a buffer of data from the file */ bytes_read = fread(publish->buffer, 1, publish->buffer_len, cbData->fp); - if (bytes_read != 0) { - ret = (int)bytes_read; - } + ret = (int)bytes_read; } if (cbData->fp && feof(cbData->fp)) { fclose(cbData->fp); + cbData->fp = NULL; } } } @@ -144,10 +146,12 @@ static int fw_message_build(MQTTCtx *mqttCtx, const char* fwFile, int msgLen = 0, fwLen = 0; word32 keyLen = 0, sigLen = 0; FirmwareHeader *header; +#ifdef ENABLE_FIRMWARE_SIG ecc_key eccKey; WC_RNG rng; wc_InitRng(&rng); +#endif /* Verify file can be loaded */ rc = mqtt_file_load(fwFile, &fwBuf, &fwLen); @@ -158,6 +162,7 @@ static int fw_message_build(MQTTCtx *mqttCtx, const char* fwFile, } PRINTF("Firmware File %s is %d bytes", fwFile, fwLen); +#ifdef ENABLE_FIRMWARE_SIG /* Generate Key */ /* Note: Real implementation would use previously exchanged/signed key */ wc_ecc_init(&eccKey); @@ -193,11 +198,13 @@ static int fw_message_build(MQTTCtx *mqttCtx, const char* fwFile, rc = EXIT_FAILURE; goto exit; } +#endif /* Display lengths */ PRINTF("Firmware Message: Sig %d bytes, Key %d bytes, File %d bytes", sigLen, keyLen, fwLen); +#ifdef ENABLE_FIRMWARE_SIG /* Generate Signature */ rc = wc_SignatureGenerate( FIRMWARE_HASH_TYPE, FIRMWARE_SIG_TYPE, @@ -210,6 +217,7 @@ static int fw_message_build(MQTTCtx *mqttCtx, const char* fwFile, rc = EXIT_FAILURE; goto exit; } +#endif /* Assemble message */ msgLen = sizeof(FirmwareHeader) + sigLen + keyLen + fwLen; @@ -226,8 +234,10 @@ static int fw_message_build(MQTTCtx *mqttCtx, const char* fwFile, header->sigLen = sigLen; header->pubKeyLen = keyLen; header->fwLen = fwLen; - XMEMCPY(&msgBuf[sizeof(FirmwareHeader)], sigBuf, sigLen); - XMEMCPY(&msgBuf[sizeof(FirmwareHeader) + sigLen], keyBuf, keyLen); + if (sigLen > 0) + XMEMCPY(&msgBuf[sizeof(FirmwareHeader)], sigBuf, sigLen); + if (keyLen > 0) + XMEMCPY(&msgBuf[sizeof(FirmwareHeader) + sigLen], keyBuf, keyLen); rc = 0; @@ -247,8 +257,10 @@ static int fw_message_build(MQTTCtx *mqttCtx, const char* fwFile, if (sigBuf) WOLFMQTT_FREE(sigBuf); if (fwBuf) WOLFMQTT_FREE(fwBuf); +#ifdef ENABLE_FIRMWARE_SIG wc_ecc_free(&eccKey); wc_FreeRng(&rng); +#endif return rc; } @@ -273,7 +285,7 @@ int fwpush_test(MQTTCtx *mqttCtx) goto disconn; } - switch(mqttCtx->stat) + switch (mqttCtx->stat) { case WMQ_BEGIN: { @@ -404,8 +416,8 @@ int fwpush_test(MQTTCtx *mqttCtx) } /* Calculate the total payload length and store the FirmwareHeader, - signature, and key in fwpushCBdata structure to be used by the - callback. */ + * signature, and key in FwpushCBdata structure to be used by the + * callback. */ cbData = (FwpushCBdata*)WOLFMQTT_MALLOC(sizeof(FwpushCBdata)); if (cbData == NULL) { rc = MQTT_CODE_ERROR_OUT_OF_BUFFER; @@ -418,7 +430,7 @@ int fwpush_test(MQTTCtx *mqttCtx) (int*)&mqttCtx->publish.total_len); /* The publish->ctx is available for use by the application to pass - data to the callback routine. */ + * data to the callback routine. */ mqttCtx->publish.ctx = cbData; if (rc != 0) { @@ -443,8 +455,8 @@ int fwpush_test(MQTTCtx *mqttCtx) return rc; } - PRINTF("MQTT Publish: Topic %s, %s (%d)", - mqttCtx->publish.topic_name, + PRINTF("MQTT Publish: Topic %s, ID %d, %s (%d)", + mqttCtx->publish.topic_name, mqttCtx->publish.packet_id, MqttClient_ReturnCodeToString(rc), rc); if (rc != MQTT_CODE_SUCCESS) { goto disconn; @@ -454,6 +466,8 @@ int fwpush_test(MQTTCtx *mqttCtx) case WMQ_DISCONNECT: { + mqttCtx->stat = WMQ_DISCONNECT; + /* Disconnect */ rc = MqttClient_Disconnect(&mqttCtx->client); if (rc == MQTT_CODE_CONTINUE) { @@ -505,6 +519,7 @@ int fwpush_test(MQTTCtx *mqttCtx) if (rc != MQTT_CODE_CONTINUE) { if (cbData) { + if (cbData->fp) fclose(cbData->fp); if (cbData->data) WOLFMQTT_FREE(cbData->data); WOLFMQTT_FREE(cbData); } @@ -520,85 +535,76 @@ int fwpush_test(MQTTCtx *mqttCtx) return rc; } -#endif /* ENABLE_FIRMWARE_EXAMPLE */ /* so overall tests can pull in test function */ -#ifndef NO_MAIN_DRIVER - #ifdef USE_WINDOWS_API - #include /* for ctrl handler */ - - static BOOL CtrlHandler(DWORD fdwCtrlType) - { - if (fdwCtrlType == CTRL_C_EVENT) { - #if defined(ENABLE_FIRMWARE_EXAMPLE) - mStopRead = 1; - #endif - PRINTF("Received Ctrl+c"); - return TRUE; - } - return FALSE; - } - #elif HAVE_SIGNAL - #include - static void sig_handler(int signo) - { - if (signo == SIGINT) { - #if defined(ENABLE_FIRMWARE_EXAMPLE) - mStopRead = 1; - #endif - PRINTF("Received SIGINT"); - } - } - #endif +#ifdef USE_WINDOWS_API + #include /* for ctrl handler */ - int main(int argc, char** argv) + static BOOL CtrlHandler(DWORD fdwCtrlType) { - int rc; - #ifdef ENABLE_FIRMWARE_EXAMPLE - MQTTCtx mqttCtx; - - /* init defaults */ - mqtt_init_ctx(&mqttCtx); - mqttCtx.app_name = "fwpush"; - mqttCtx.client_id = FIRMWARE_PUSH_CLIENT_ID; - mqttCtx.topic_name = FIRMWARE_TOPIC_NAME; - mqttCtx.qos = FIRMWARE_MQTT_QOS; - mqttCtx.pub_file = FIRMWARE_PUSH_DEF_FILE; - - /* parse arguments */ - rc = mqtt_parse_args(&mqttCtx, argc, argv); - if (rc != 0) { - return rc; - } - #endif - - #ifdef USE_WINDOWS_API - if (SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE) == FALSE) { - PRINTF("Error setting Ctrl Handler! Error %d", (int)GetLastError()); + if (fdwCtrlType == CTRL_C_EVENT) { + #if defined(ENABLE_FIRMWARE_SIG) + mStopRead = 1; + #endif + PRINTF("Received Ctrl+c"); + return TRUE; } - #elif HAVE_SIGNAL - if (signal(SIGINT, sig_handler) == SIG_ERR) { - PRINTF("Can't catch SIGINT"); + return FALSE; + } +#elif HAVE_SIGNAL + #include + static void sig_handler(int signo) + { + if (signo == SIGINT) { + #if defined(ENABLE_FIRMWARE_SIG) + mStopRead = 1; + #endif + PRINTF("Received SIGINT"); } - #endif + } +#endif - #ifdef ENABLE_FIRMWARE_EXAMPLE - do { - rc = fwpush_test(&mqttCtx); - } while (rc == MQTT_CODE_CONTINUE); +#if defined(NO_MAIN_DRIVER) +int fwpush_main(int argc, char** argv) +#else +int main(int argc, char** argv) +#endif +{ + int rc; + MQTTCtx mqttCtx; + + /* init defaults */ + mqtt_init_ctx(&mqttCtx); + mqttCtx.app_name = "fwpush"; + mqttCtx.client_id = mqtt_append_random(FIRMWARE_PUSH_CLIENT_ID, + (word32)XSTRLEN(FIRMWARE_PUSH_CLIENT_ID)); + mqttCtx.dynamicClientId = 1; + mqttCtx.topic_name = FIRMWARE_TOPIC_NAME; + mqttCtx.qos = FIRMWARE_MQTT_QOS; + mqttCtx.pub_file = FIRMWARE_PUSH_DEF_FILE; + + /* parse arguments */ + rc = mqtt_parse_args(&mqttCtx, argc, argv); + if (rc != 0) { + return rc; + } - mqtt_free_ctx(&mqttCtx); - #else - (void)argc; - (void)argv; +#ifdef USE_WINDOWS_API + if (SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE) == FALSE) { + PRINTF("Error setting Ctrl Handler! Error %d", (int)GetLastError()); + } +#elif HAVE_SIGNAL + if (signal(SIGINT, sig_handler) == SIG_ERR) { + PRINTF("Can't catch SIGINT"); + } +#endif - /* This example requires wolfSSL after 3.7.1 for signature wrapper */ - PRINTF("Example not compiled in!"); - rc = 0; /* return success, so make check passes with TLS disabled */ - #endif + do { + rc = fwpush_test(&mqttCtx); + } while (!mStopRead && rc == MQTT_CODE_CONTINUE); - return (rc == 0) ? 0 : EXIT_FAILURE; - } + mqtt_free_ctx(&mqttCtx); -#endif /* NO_MAIN_DRIVER */ + return (rc == 0) ? 0 : EXIT_FAILURE; +} diff --git a/examples/firmware/fwpush.h b/examples/firmware/fwpush.h index 1f590a67c..3230652de 100644 --- a/examples/firmware/fwpush.h +++ b/examples/firmware/fwpush.h @@ -33,9 +33,13 @@ typedef struct FwpushCBdata_s { const char *filename; byte *data; FILE *fp; -}FwpushCBdata; +} FwpushCBdata; /* Exposed functions */ int fwpush_test(MQTTCtx *mqttCtx); +#if defined(NO_MAIN_DRIVER) +int fwpush_main(int argc, char** argv); +#endif + #endif /* WOLFMQTT_FWPUSH_H */ diff --git a/examples/include.am b/examples/include.am index b6b44c967..dd0548eae 100644 --- a/examples/include.am +++ b/examples/include.am @@ -11,9 +11,13 @@ noinst_PROGRAMS += examples/mqttclient/mqttclient \ examples/wiot/wiot \ examples/nbclient/nbclient \ examples/multithread/multithread \ - examples/sn-client/sn-client \ + examples/pub-sub/mqtt-pub \ + examples/pub-sub/mqtt-sub +if BUILD_SN +noinst_PROGRAMS += examples/sn-client/sn-client \ examples/sn-client/sn-client_qos-1 \ examples/sn-client/sn-multithread +endif noinst_HEADERS += examples/mqttclient/mqttclient.h \ examples/mqttsimple/mqttsimple.h \ @@ -28,7 +32,10 @@ noinst_HEADERS += examples/mqttclient/mqttclient.h \ examples/mqttport.h \ examples/nbclient/nbclient.h \ examples/multithread/multithread.h \ - examples/sn-client/sn-client.h + examples/pub-sub/mqtt-pub-sub.h +if BUILD_SN +noinst_HEADERS += examples/sn-client/sn-client.h +endif # MQTT Client Example examples_mqttclient_mqttclient_SOURCES = examples/mqttclient/mqttclient.c \ @@ -105,7 +112,7 @@ examples_wiot_wiot_LDADD = src/libwolfmqtt.la examples_wiot_wiot_DEPENDENCIES = src/libwolfmqtt.la examples_wiot_wiot_CPPFLAGS = -I$(top_srcdir)/examples $(AM_CPPFLAGS) - +if BUILD_SN # MQTT-SN Examples examples_sn_client_sn_client_SOURCES = examples/sn-client/sn-client.c \ examples/mqttnet.c \ @@ -129,6 +136,22 @@ examples_sn_client_sn_multithread_DEPENDENCIES = src/libwolfmqtt.la examples_sn_client_sn_multithread_CPPFLAGS = -I$(top_srcdir)/examples $(AM_CPPFLAGS) endif +# MQTT pub and sub clients +examples_pub_sub_mqtt_pub_SOURCES = examples/pub-sub/mqtt-pub.c \ + examples/mqttnet.c \ + examples/mqttexample.c +examples_pub_sub_mqtt_pub_LDADD = src/libwolfmqtt.la +examples_pub_sub_mqtt_pub_DEPENDENCIES = src/libwolfmqtt.la +examples_pub_sub_mqtt_pub_CPPFLAGS = -I$(top_srcdir)/examples $(AM_CPPFLAGS) + +examples_pub_sub_mqtt_sub_SOURCES = examples/pub-sub/mqtt-sub.c \ + examples/mqttnet.c \ + examples/mqttexample.c +examples_pub_sub_mqtt_sub_LDADD = src/libwolfmqtt.la +examples_pub_sub_mqtt_sub_DEPENDENCIES = src/libwolfmqtt.la +examples_pub_sub_mqtt_sub_CPPFLAGS = -I$(top_srcdir)/examples $(AM_CPPFLAGS) +endif + dist_example_DATA+= examples/mqttnet.c \ examples/mqttexample.c \ @@ -142,9 +165,13 @@ dist_example_DATA+= examples/mqttnet.c \ examples/wiot/wiot.c dist_example_DATA+= examples/nbclient/nbclient.c dist_example_DATA+= examples/multithread/multithread.c +if BUILD_SN dist_example_DATA+= examples/sn-client/sn-client.c dist_example_DATA+= examples/sn-client/sn-client_qos-1.c dist_example_DATA+= examples/sn-client/sn-multithread.c +endif +dist_example_DATA+= examples/pub-sub/mqtt-pub.c +dist_example_DATA+= examples/pub-sub/mqtt-sub.c DISTCLEANFILES+= examples/mqttclient/.libs/mqttclient \ examples/firmware/.libs/fwpush \ @@ -154,9 +181,13 @@ DISTCLEANFILES+= examples/mqttclient/.libs/mqttclient \ examples/wiot/.libs/wiot \ examples/nbclient/.libs/nbclient \ examples/multithread/.libs/multithread \ - examples/sn-client/.libs/sn-client \ + examples/pub-sub/mqtt-pub \ + examples/pub-sub/mqtt-sub +if BUILD_SN +DISTCLEANFILES+= examples/sn-client/.libs/sn-client \ examples/sn-client/.libs/sn-client_qos-1 \ examples/sn-client/.libs/sn-multithread +endif EXTRA_DIST+= examples/mqttuart.c \ examples/publish.dat \ diff --git a/examples/mqttclient/mqttclient.c b/examples/mqttclient/mqttclient.c index ef4721ca3..7869a18ff 100644 --- a/examples/mqttclient/mqttclient.c +++ b/examples/mqttclient/mqttclient.c @@ -34,7 +34,9 @@ static int mStopRead = 0; /* Maximum size for network read/write callbacks. There is also a v5 define that describes the max MQTT control packet size, DEFAULT_MAX_PKT_SZ. */ +#ifndef MAX_BUFFER_SIZE #define MAX_BUFFER_SIZE 1024 +#endif #ifdef WOLFMQTT_PROPERTY_CB #define MAX_CLIENT_ID_LEN 64 @@ -497,18 +499,19 @@ int mqttclient_test(MQTTCtx *mqttCtx) #endif /* This loop allows payloads larger than the buffer to be sent by - repeatedly calling publish. - */ + * repeatedly calling publish. */ do { rc = MqttClient_Publish(&mqttCtx->client, &mqttCtx->publish); - } while(rc == MQTT_CODE_PUB_CONTINUE); + } while (rc == MQTT_CODE_PUB_CONTINUE || rc == MQTT_CODE_CONTINUE); if ((mqttCtx->pub_file) && (mqttCtx->publish.buffer)) { WOLFMQTT_FREE(mqttCtx->publish.buffer); + mqttCtx->publish.buffer = NULL; + mqttCtx->pub_file = NULL; /* don't try and send file again */ } - PRINTF("MQTT Publish: Topic %s, %s (%d)", - mqttCtx->publish.topic_name, + PRINTF("MQTT Publish: Topic %s, ID %d, %s (%d)", + mqttCtx->publish.topic_name, mqttCtx->publish.packet_id, MqttClient_ReturnCodeToString(rc), rc); #ifdef WOLFMQTT_V5 if (mqttCtx->qos > 0) { @@ -573,8 +576,8 @@ int mqttclient_test(MQTTCtx *mqttCtx) mqttCtx->publish.total_len = (word16)rc; rc = MqttClient_Publish(&mqttCtx->client, &mqttCtx->publish); - PRINTF("MQTT Publish: Topic %s, %s (%d)", - mqttCtx->publish.topic_name, + PRINTF("MQTT Publish: Topic %s, ID %d, %s (%d)", + mqttCtx->publish.topic_name, mqttCtx->publish.packet_id, MqttClient_ReturnCodeToString(rc), rc); } } @@ -648,9 +651,6 @@ int mqttclient_test(MQTTCtx *mqttCtx) PRINTF("MQTT Disconnect: %s (%d)", MqttClient_ReturnCodeToString(rc), rc); - if (rc != MQTT_CODE_SUCCESS) { - goto disconn; - } rc = MqttClient_NetDisconnect(&mqttCtx->client); diff --git a/examples/mqttexample.c b/examples/mqttexample.c index 8e41e80c8..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 */ @@ -150,44 +147,64 @@ static int mqtt_get_rand(byte* data, word32 len) for (i = 0; i> 4]; - rndHexStr[(i*2)+1] = kHexChar[in & 0xf]; + byte rndBytes[32]; /* fill up to x bytes at a time */ + + while (rc == 0 && pos < bufLen) { + sz = bufLen - pos; + if (sz > (int)sizeof(rndBytes)) + sz = (int)sizeof(rndBytes); + sz /= 2; /* 1 byte expands to 2 bytes */ + + rc = mqtt_get_rand(rndBytes, sz); + if (rc == 0) { + /* Convert random to hex string */ + for (i=0; i> 4]; + buf[pos + (i*2)+1] = kHexChar[in & 0xf]; + } + pos += sz*2; } - } - if (rc == 0) { - /* Allocate topic name and client id */ - tmp = (char*)WOLFMQTT_MALLOC(inLen + 1 + sizeof(rndHexStr) + 1); - if (tmp == NULL) { - rc = MQTT_CODE_ERROR_MEMORY; + else { + PRINTF("MQTT Fill Random Failed! %d", rc); } } + return rc; +} + +#ifndef TEST_RAND_SZ +#define TEST_RAND_SZ 4 +#endif +char* mqtt_append_random(const char* inStr, word32 inLen) +{ + int rc = 0; + char *tmp; + + tmp = (char*)WOLFMQTT_MALLOC(inLen + 1 + (TEST_RAND_SZ*2) + 1); + if (tmp == NULL) { + rc = MQTT_CODE_ERROR_MEMORY; + } if (rc == 0) { /* Format: inStr + `_` randhex + null term */ XMEMCPY(tmp, inStr, inLen); tmp[inLen] = '_'; - XMEMCPY(tmp + inLen + 1, rndHexStr, sizeof(rndHexStr)); - tmp[inLen + 1 + sizeof(rndHexStr)] = '\0'; + rc = mqtt_fill_random_hexstr(tmp + inLen + 1, (TEST_RAND_SZ*2)); + tmp[inLen + 1 + (TEST_RAND_SZ*2)] = '\0'; /* null term */ + } + if (rc != 0) { + WOLFMQTT_FREE(tmp); + tmp = NULL; } return tmp; } @@ -201,20 +218,22 @@ void mqtt_show_usage(MQTTCtx* mqttCtx) #ifdef ENABLE_MQTT_TLS PRINTF("-p Port to connect on, default: Normal %d, TLS %d", MQTT_DEFAULT_PORT, MQTT_SECURE_PORT); - PRINTF("-t Enable TLS"); + PRINTF("-t Enable TLS"); /* Note: this string is used in test + * scripts to detect TLS feature */ PRINTF("-A Load CA (validate peer)"); PRINTF("-K Use private key (for TLS mutual auth)"); PRINTF("-c Use certificate (for TLS mutual auth)"); - #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 @@ -242,6 +261,9 @@ void mqtt_show_usage(MQTTCtx* mqttCtx) #endif PRINTF("-T Test mode"); PRINTF("-f Use file contents for publish"); + if (!mqttCtx->debug_on) { + PRINTF("-d Enable example debug messages"); + } } void mqtt_init_ctx(MQTTCtx* mqttCtx) @@ -254,6 +276,7 @@ void mqtt_init_ctx(MQTTCtx* mqttCtx) mqttCtx->client_id = kDefClientId; mqttCtx->topic_name = kDefTopicName; mqttCtx->cmd_timeout_ms = DEFAULT_CMD_TIMEOUT_MS; + mqttCtx->debug_on = 1; #ifdef WOLFMQTT_V5 mqttCtx->max_packet_size = DEFAULT_MAX_PKT_SZ; mqttCtx->topic_alias = 1; @@ -261,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; @@ -271,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 @@ -281,7 +313,7 @@ int mqtt_parse_args(MQTTCtx* mqttCtx, int argc, char** argv) #define MQTT_V5_ARGS "" #endif - while ((rc = mygetopt(argc, argv, "?h:p:q:sk:i:lu:w:m:n:C:Tf:rt" \ + while ((rc = mygetopt(argc, argv, "?h:p:q:sk:i:lu:w:m:n:C:Tf:rtd" \ MQTT_TLS_ARGS MQTT_V5_ARGS)) != -1) { switch ((char)rc) { case '?' : @@ -343,7 +375,6 @@ int mqtt_parse_args(MQTTCtx* mqttCtx, int argc, char** argv) break; case 'T': - mqttCtx->test_mode = 1; break; @@ -359,16 +390,21 @@ int mqtt_parse_args(MQTTCtx* mqttCtx, int argc, char** argv) mqttCtx->use_tls = 1; break; + case 'd': + mqttCtx->debug_on = 1; + break; + #ifdef ENABLE_MQTT_TLS case 'A': - mTlsCaFile = myoptarg; + 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; @@ -384,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': @@ -399,7 +436,7 @@ int mqtt_parse_args(MQTTCtx* mqttCtx, int argc, char** argv) /* Remove SNI functionality for sn-client */ if(!XSTRNCMP(mqttCtx->app_name, "sn-client", 10)){ - #ifdef HAVE_SNI + #ifdef HAVE_SNI useSNI=0; #endif } @@ -592,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 @@ -606,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; } @@ -633,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; } @@ -705,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; @@ -714,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) { @@ -724,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 5e196b37b..581225182 100644 --- a/examples/mqttexample.h +++ b/examples/mqttexample.h @@ -160,6 +160,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; @@ -180,6 +185,7 @@ typedef struct _MQTTCtx { #endif byte clean_session; byte test_mode; + byte debug_on:1; /* enable debug messages in example */ #ifdef WOLFMQTT_V5 byte subId_not_avail; /* Server property */ byte enable_eauth; /* Enhanced authentication */ @@ -211,6 +217,9 @@ word16 mqtt_get_packetid(void); int mqtt_check_timeout(int rc, word32* start_sec, word32 timeout_sec); #endif +int mqtt_fill_random_hexstr(char* buf, word32 bufLen); +char* mqtt_append_random(const char* inStr, word32 inLen); + int mqtt_file_load(const char* filePath, byte** fileBuf, int *fileLen); #ifdef WOLFSSL_ENCRYPTED_KEYS diff --git a/examples/mqttnet.c b/examples/mqttnet.c index c397cd2ff..e070e8480 100644 --- a/examples/mqttnet.c +++ b/examples/mqttnet.c @@ -32,6 +32,10 @@ typedef struct MulticastCtx { } MulticastCtx; #endif +#ifndef WOLFMQTT_TEST_NONBLOCK_TIMES +#define WOLFMQTT_TEST_NONBLOCK_TIMES 1 +#endif + /* Private functions */ /* -------------------------------------------------------------------------- */ @@ -52,8 +56,10 @@ static int NetConnect(void *context, const char* host, word16 port, switch (sock->stat) { case SOCK_BEGIN: - PRINTF("NetConnect: Host %s, Port %u, Timeout %d ms, Use TLS %d", - host, port, timeout_ms, mqttCtx->use_tls); + if (mqttCtx->debug_on) { + PRINTF("NetConnect: Host %s, Port %u, Timeout %d ms, Use TLS %d", + host, port, timeout_ms, mqttCtx->use_tls); + } hostIp = FreeRTOS_gethostbyname_a(host, NULL, 0, 0); if (hostIp == 0) @@ -217,6 +223,20 @@ static int NetDisconnect(void *context) /* -------------------------------------------------------------------------- */ #elif defined(MICROCHIP_MPLAB_HARMONY) +static int NetDisconnect(void *context) +{ + SocketContext *sock = (SocketContext*)context; + if (sock) { + if (sock->fd != SOCKET_INVALID) { + closesocket(sock->fd); + sock->fd = SOCKET_INVALID; + } + + sock->stat = SOCK_BEGIN; + } + return 0; +} + static int NetConnect(void *context, const char* host, word16 port, int timeout_ms) { @@ -231,9 +251,10 @@ static int NetConnect(void *context, const char* host, word16 port, switch(sock->stat) { case SOCK_BEGIN: { - PRINTF("NetConnect: Host %s, Port %u, Timeout %d ms, Use TLS %d", - host, port, timeout_ms, mqttCtx->use_tls); - + if (mqttCtx->debug_on) { + PRINTF("NetConnect: Host %s, Port %u, Timeout %d ms, " + "Use TLS %d", host, port, timeout_ms, mqttCtx->use_tls); + } XMEMSET(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; @@ -284,6 +305,7 @@ static int NetConnect(void *context, const char* host, word16 port, if (errno == EINPROGRESS || errno == EWOULDBLOCK) { return MQTT_CODE_CONTINUE; } + NetDisconnect(context); /* Show error */ PRINTF("NetConnect: Rc=%d, ErrNo=%d", rc, errno); @@ -355,17 +377,478 @@ static int NetRead(void *context, byte* buf, int buf_len, return rc; } -static int NetDisconnect(void *context) + +/* -------------------------------------------------------------------------- */ +/* 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) { - SocketContext *sock = (SocketContext*)context; - if (sock) { - if (sock->fd != SOCKET_INVALID) { - closesocket(sock->fd); - sock->fd = SOCKET_INVALID; + 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); + } - sock->stat = SOCK_BEGIN; + 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; } @@ -407,6 +890,20 @@ static void tcp_set_nonblocking(SOCKET_T* sockfd) #endif /* WOLFMQTT_NONBLOCK */ #endif /* !WOLFMQTT_NO_TIMEOUT */ +static int NetDisconnect(void *context) +{ + SocketContext *sock = (SocketContext*)context; + if (sock) { + if (sock->fd != SOCKET_INVALID) { + SOCK_CLOSE(sock->fd); + sock->fd = SOCKET_INVALID; + } + + sock->stat = SOCK_BEGIN; + } + return 0; +} + static int NetConnect(void *context, const char* host, word16 port, int timeout_ms) { @@ -422,8 +919,10 @@ static int NetConnect(void *context, const char* host, word16 port, switch(sock->stat) { case SOCK_BEGIN: { - PRINTF("NetConnect: Host %s, Port %u, Timeout %d ms, Use TLS %d", - host, port, timeout_ms, mqttCtx->use_tls); + if (mqttCtx->debug_on) { + PRINTF("NetConnect: Host %s, Port %u, Timeout %d ms, " + "Use TLS %d", host, port, timeout_ms, mqttCtx->use_tls); + } XMEMSET(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; @@ -534,9 +1033,9 @@ static int NetConnect(void *context, const char* host, word16 port, (void)timeout_ms; exit: - /* Show error */ - if (rc != 0) { - PRINTF("NetConnect: Rc=%d, SoErr=%d", rc, so_error); + if ((rc != 0) && (rc != MQTT_CODE_CONTINUE)) { + NetDisconnect(context); + PRINTF("NetConnect: Rc=%d, SoErr=%d", rc, so_error); /* Show error */ } return rc; @@ -625,8 +1124,8 @@ static int SN_NetConnect(void *context, const char* host, word16 port, exit: /* Show error */ - if (rc != 0) { - SOCK_CLOSE(sock->fd); + if ((rc != 0) && (rc != MQTT_CODE_CONTINUE)) { + NetDisconnect(context); PRINTF("NetConnect: Rc=%d, SoErr=%d", rc, so_error); } @@ -638,11 +1137,16 @@ static int NetWrite(void *context, const byte* buf, int buf_len, int timeout_ms) { SocketContext *sock = (SocketContext*)context; + MQTTCtx* mqttCtx; int rc; SOERROR_T so_error = 0; #ifndef WOLFMQTT_NO_TIMEOUT struct timeval tv; #endif +#if defined(WOLFMQTT_NONBLOCK) && defined(WOLFMQTT_TEST_NONBLOCK) + static int testNbWriteAlt = 0; + static int testSmallerWrite = 0; +#endif if (context == NULL || buf == NULL || buf_len <= 0) { return MQTT_CODE_ERROR_BAD_ARG; @@ -651,6 +1155,27 @@ static int NetWrite(void *context, const byte* buf, int buf_len, if (sock->fd == SOCKET_INVALID) return MQTT_CODE_ERROR_BAD_ARG; + mqttCtx = sock->mqttCtx; + (void)mqttCtx; + +#if defined(WOLFMQTT_NONBLOCK) && defined(WOLFMQTT_TEST_NONBLOCK) + if (mqttCtx->useNonBlockMode) { + if (testNbWriteAlt < WOLFMQTT_TEST_NONBLOCK_TIMES) { + testNbWriteAlt++; + return MQTT_CODE_CONTINUE; + } + testNbWriteAlt = 0; + if (!testSmallerWrite) { + if (buf_len > 2) + buf_len /= 2; + testSmallerWrite = 1; + } + else { + testSmallerWrite = 0; + } + } +#endif + #ifndef WOLFMQTT_NO_TIMEOUT /* Setup timeout */ setup_timeout(&tv, timeout_ms); @@ -701,6 +1226,10 @@ static int NetRead_ex(void *context, byte* buf, int buf_len, fd_set errfds; struct timeval tv; #endif +#if defined(WOLFMQTT_NONBLOCK) && defined(WOLFMQTT_TEST_NONBLOCK) + static int testNbReadAlt = 0; + static int testSmallerRead = 0; +#endif if (context == NULL || buf == NULL || buf_len <= 0) { return MQTT_CODE_ERROR_BAD_ARG; @@ -716,6 +1245,24 @@ static int NetRead_ex(void *context, byte* buf, int buf_len, mqttCtx = sock->mqttCtx; (void)mqttCtx; +#if defined(WOLFMQTT_NONBLOCK) && defined(WOLFMQTT_TEST_NONBLOCK) + if (mqttCtx->useNonBlockMode) { + if (testNbReadAlt < WOLFMQTT_TEST_NONBLOCK_TIMES) { + testNbReadAlt++; + return MQTT_CODE_CONTINUE; + } + testNbReadAlt = 0; + if (!testSmallerRead) { + if (buf_len > 2) + buf_len /= 2; + testSmallerRead = 1; + } + else { + testSmallerRead = 0; + } + } +#endif + #ifndef WOLFMQTT_NO_TIMEOUT /* Setup timeout */ setup_timeout(&tv, timeout_ms); @@ -795,7 +1342,7 @@ static int NetRead_ex(void *context, byte* buf, int buf_len, if (do_read) { /* Try and read number of buf_len provided, - minus what's already been read */ + * minus what's already been read */ rc = (int)SOCK_RECV(sock->fd, &buf[bytes], buf_len - bytes, @@ -868,20 +1415,6 @@ static int NetPeek(void *context, byte* buf, int buf_len, int timeout_ms) } #endif -static int NetDisconnect(void *context) -{ - SocketContext *sock = (SocketContext*)context; - if (sock) { - if (sock->fd != SOCKET_INVALID) { - SOCK_CLOSE(sock->fd); - sock->fd = -1; - } - - sock->stat = SOCK_BEGIN; - } - return 0; -} - #endif @@ -938,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/examples/mqttsimple/mqttsimple.c b/examples/mqttsimple/mqttsimple.c index 5fa115b53..814c8f370 100644 --- a/examples/mqttsimple/mqttsimple.c +++ b/examples/mqttsimple/mqttsimple.c @@ -424,8 +424,9 @@ int mqttsimple_test(void) if (rc != MQTT_CODE_SUCCESS) { goto exit; } - PRINTF("MQTT Publish: Topic %s, Qos %d, Message %s", - mqttObj.publish.topic_name, mqttObj.publish.qos, mqttObj.publish.buffer); + PRINTF("MQTT Publish: Topic %s, ID %d, Qos %d, Message %s", + mqttObj.publish.topic_name, mqttObj.publish.packet_id, + mqttObj.publish.qos, mqttObj.publish.buffer); /* Wait for messages */ while (1) { diff --git a/examples/multithread/multithread.c b/examples/multithread/multithread.c old mode 100755 new mode 100644 index 92ce00692..8a6ee52c9 --- a/examples/multithread/multithread.c +++ b/examples/multithread/multithread.c @@ -33,19 +33,25 @@ #include +#ifdef WOLFMQTT_MULTITHREAD + /* Configuration */ +/* Number of publish tasks. Each will send a unique message to the broker. */ +#define NUM_PUB_TASKS 5 +#define NUM_PUB_PER_TASK 2 + /* Maximum size for network read/write callbacks. There is also a v5 define that describes the max MQTT control packet size, DEFAULT_MAX_PKT_SZ. */ +#ifndef MAX_BUFFER_SIZE #define MAX_BUFFER_SIZE 1024 -#define TEST_MESSAGE "test00" -/* Number of publish tasks. Each will send a unique message to the broker. */ -#define NUM_PUB_TASKS 10 - +#endif -#ifdef WOLFMQTT_MULTITHREAD +/* Total size of test message to build */ +#define TEST_MESSAGE_SIZE 1048 /* span more than one max packet */ /* Locals */ +static char mTestMessage[TEST_MESSAGE_SIZE]; static int mStopRead = 0; static int mNumMsgsRecvd; static int mNumMsgsDone; @@ -100,12 +106,14 @@ static int mqtt_stop_get(void) return rc; } -static int check_response(MQTTCtx* mqttCtx, int rc, word32* startSec, int packet_type) +#define MQTT_CODE_TEST_EXIT -200 +static int check_response(MQTTCtx* mqttCtx, int rc, word32* startSec, + int packet_type, word32 timeoutMs) { /* check for test mode */ - if (mqtt_stop_get()) { + if (mqtt_stop_get() && packet_type != MQTT_PACKET_TYPE_UNSUBSCRIBE) { PRINTF("MQTT Exiting Thread..."); - return MQTT_CODE_SUCCESS; + return MQTT_CODE_TEST_EXIT; } #ifdef WOLFMQTT_NONBLOCK @@ -114,12 +122,9 @@ static int check_response(MQTTCtx* mqttCtx, int rc, word32* startSec, int packet PRINTF("Test cancel by setting early timeout"); return MQTT_CODE_ERROR_TIMEOUT; } - else -#else - (void)packet_type; #endif /* Track elapsed time with no activity and trigger timeout */ - rc = mqtt_check_timeout(rc, startSec, mqttCtx->cmd_timeout_ms/1000); + rc = mqtt_check_timeout(rc, startSec, timeoutMs/1000); /* check return code */ if (rc == MQTT_CODE_CONTINUE) { @@ -128,11 +133,13 @@ static int check_response(MQTTCtx* mqttCtx, int rc, word32* startSec, int packet usleep(100*1000); #endif } -#else - (void)packet_type; - (void)startSec; +#endif /* WOLFMQTT_NONBLOCK */ + (void)mqttCtx; -#endif + (void)startSec; + (void)packet_type; + (void)timeoutMs; + return rc; } @@ -170,17 +177,6 @@ static int mqtt_message_cb(MqttClient *client, MqttMessage *msg, /* Print incoming message */ PRINTF("MQTT Message: Topic %s, Qos %d, Id %d, Len %u, %u, %u", buf, msg->qos, msg->packet_id, msg->total_len, msg->buffer_len, msg->buffer_pos); - - /* for test mode: count the number of TEST_MESSAGE matches received */ - if (mqttCtx->test_mode) { - if (XSTRLEN(TEST_MESSAGE) == msg->buffer_len && - /* Only compare the "test" part */ - XSTRNCMP(TEST_MESSAGE, (char*)msg->buffer, - msg->buffer_len-2) == 0) - { - mNumMsgsRecvd++; - } - } } /* Print message payload */ @@ -194,6 +190,17 @@ static int mqtt_message_cb(MqttClient *client, MqttMessage *msg, msg->buffer_pos, msg->buffer_pos + msg->buffer_len, len, buf); if (msg_done) { + /* for test mode: count the number of messages received */ + if (mqttCtx->test_mode) { + if (msg->buffer_pos + msg->buffer_len == + (word32)sizeof(mTestMessage) && + XMEMCMP(&mTestMessage[msg->buffer_pos], msg->buffer, + msg->buffer_len) == 0) + { + mNumMsgsRecvd++; + } + } + PRINTF("MQTT Message: Done"); } wm_SemUnlock(&mtLock); @@ -311,7 +318,8 @@ static int multithread_test_init(MQTTCtx *mqttCtx) do { rc = MqttClient_NetConnect(&mqttCtx->client, mqttCtx->host, mqttCtx->port, DEFAULT_CON_TIMEOUT_MS, mqttCtx->use_tls, mqtt_tls_cb); - rc = check_response(mqttCtx, rc, &startSec, MQTT_PACKET_TYPE_CONNECT); + rc = check_response(mqttCtx, rc, &startSec, MQTT_PACKET_TYPE_CONNECT, + DEFAULT_CON_TIMEOUT_MS); } while (rc == MQTT_CODE_CONTINUE); PRINTF("MQTT Socket Connect: %s (%d)", @@ -348,7 +356,8 @@ static int multithread_test_init(MQTTCtx *mqttCtx) startSec = 0; do { rc = MqttClient_Connect(&mqttCtx->client, &mqttCtx->connect); - rc = check_response(mqttCtx, rc, &startSec, MQTT_PACKET_TYPE_CONNECT); + rc = check_response(mqttCtx, rc, &startSec, MQTT_PACKET_TYPE_CONNECT, + DEFAULT_CON_TIMEOUT_MS); } while (rc == MQTT_CODE_CONTINUE); if (rc != MQTT_CODE_SUCCESS) { MqttClient_CancelMessage(&mqttCtx->client, @@ -382,6 +391,11 @@ static int multithread_test_finish(MQTTCtx *mqttCtx) PRINTF("MQTT Client Done: %d", mqttCtx->return_code); + if (mStopRead && mqttCtx->return_code == MQTT_CODE_TEST_EXIT) { + /* this is okay, we requested termination */ + mqttCtx->return_code = MQTT_CODE_SUCCESS; + } + return mqttCtx->return_code; } @@ -421,7 +435,8 @@ static void *subscribe_task(void *param) do { rc = MqttClient_Subscribe(&mqttCtx->client, &mqttCtx->subscribe); - rc = check_response(mqttCtx, rc, &startSec, MQTT_PACKET_TYPE_SUBSCRIBE); + rc = check_response(mqttCtx, rc, &startSec, MQTT_PACKET_TYPE_SUBSCRIBE, + mqttCtx->cmd_timeout_ms); } while (rc == MQTT_CODE_CONTINUE); if (rc != MQTT_CODE_SUCCESS) { MqttClient_CancelMessage(&mqttCtx->client, @@ -456,7 +471,8 @@ static int TestIsDone(int rc, MQTTCtx* mqttCtx) /* check if we are in test mode and done */ wm_SemLock(&mtLock); if ((rc == 0 || rc == MQTT_CODE_CONTINUE) && mqttCtx->test_mode && - mNumMsgsDone == NUM_PUB_TASKS && mNumMsgsRecvd == NUM_PUB_TASKS + mNumMsgsDone == (NUM_PUB_TASKS * NUM_PUB_PER_TASK) && + mNumMsgsRecvd == (NUM_PUB_TASKS * NUM_PUB_PER_TASK) #ifdef WOLFMQTT_NONBLOCK && !MqttClient_IsMessageActive(&mqttCtx->client, NULL) #endif @@ -499,12 +515,19 @@ static void *waitMessage_task(void *param) ){ cmd_timeout_ms = 1000; /* short timeout */ } + /* Try and read packet */ - rc = MqttClient_WaitMessage(&mqttCtx->client, cmd_timeout_ms); + rc = MqttClient_WaitMessage_ex(&mqttCtx->client, &mqttCtx->client.msg, + cmd_timeout_ms); if (mqttCtx->test_mode && rc == MQTT_CODE_ERROR_TIMEOUT) { rc = 0; } - rc = check_response(mqttCtx, rc, &startSec, MQTT_PACKET_TYPE_ANY); + rc = check_response(mqttCtx, rc, &startSec, MQTT_PACKET_TYPE_ANY, + cmd_timeout_ms); + if (rc != MQTT_CODE_SUCCESS && rc != MQTT_CODE_CONTINUE) { + MqttClient_CancelMessage(&mqttCtx->client, + (MqttObject*)&mqttCtx->client.msg); + } /* check return code */ if (rc == MQTT_CODE_CONTINUE) { @@ -528,10 +551,16 @@ static void *waitMessage_task(void *param) mqttCtx->publish.packet_id = mqtt_get_packetid_threadsafe(); mqttCtx->publish.buffer = mqttCtx->rx_buf; mqttCtx->publish.total_len = (word16)rc; - rc = MqttClient_Publish(&mqttCtx->client, - &mqttCtx->publish); - PRINTF("MQTT Publish: Topic %s, %s (%d)", - mqttCtx->publish.topic_name, + do { + rc = MqttClient_Publish(&mqttCtx->client, + &mqttCtx->publish); + } while (rc == MQTT_CODE_CONTINUE); + if (rc != MQTT_CODE_SUCCESS) { + MqttClient_CancelMessage(&mqttCtx->client, + (MqttObject*)&mqttCtx->publish); + } + PRINTF("MQTT Publish: Topic %s, ID %d, %s (%d)", + mqttCtx->publish.topic_name, mqttCtx->publish.packet_id, MqttClient_ReturnCodeToString(rc), rc); } } @@ -539,6 +568,7 @@ static void *waitMessage_task(void *param) else if (rc == MQTT_CODE_ERROR_TIMEOUT) { if (mqttCtx->test_mode) { /* timeout in test mode should exit */ + mqtt_stop_set(); PRINTF("MQTT Exiting timeout..."); break; } @@ -570,40 +600,51 @@ static DWORD WINAPI publish_task( LPVOID param ) static void *publish_task(void *param) #endif { - int rc; - char buf[7]; + int rc[NUM_PUB_PER_TASK], i; MQTTCtx *mqttCtx = (MQTTCtx*)param; - MqttPublish publish; - word32 startSec = 0; - - /* Publish Topic */ - XMEMSET(&publish, 0, sizeof(MqttPublish)); - publish.retain = 0; - publish.qos = mqttCtx->qos; - publish.duplicate = 0; - publish.topic_name = mqttCtx->topic_name; - publish.packet_id = mqtt_get_packetid_threadsafe(); - XSTRNCPY(buf, TEST_MESSAGE, sizeof(buf)); - buf[4] = '0' + ((publish.packet_id / 10) % 10); - buf[5] = '0' + (publish.packet_id % 10); - publish.buffer = (byte*)buf; - publish.total_len = (word16)XSTRLEN(buf); + MqttPublish publish[NUM_PUB_PER_TASK]; + word32 startSec[NUM_PUB_PER_TASK]; + + /* Build publish */ + for (i=0; iqos; + publish[i].duplicate = 0; + publish[i].topic_name = mqttCtx->topic_name; + publish[i].packet_id = mqtt_get_packetid_threadsafe(); + publish[i].buffer = (byte*)mTestMessage; + publish[i].total_len = sizeof(mTestMessage); + + rc[i] = MQTT_CODE_CONTINUE; + startSec[i] = 0; + } - do { - rc = MqttClient_Publish_WriteOnly(&mqttCtx->client, &publish, NULL); - rc = check_response(mqttCtx, rc, &startSec, MQTT_PACKET_TYPE_PUBLISH); - } while (rc == MQTT_CODE_CONTINUE); - if (rc != MQTT_CODE_SUCCESS) { - MqttClient_CancelMessage(&mqttCtx->client, (MqttObject*)&publish); + /* Send until != continue */ + for (i=0; iclient, &publish[i], + NULL); + rc[i] = check_response(mqttCtx, rc[i], &startSec[i], + MQTT_PACKET_TYPE_PUBLISH, mqttCtx->cmd_timeout_ms); + } } - wm_SemLock(&mtLock); - PRINTF("MQTT Publish: Topic %s, %s (%d)", - publish.topic_name, - MqttClient_ReturnCodeToString(rc), rc); + /* Report result */ + for (i=0; iclient, (MqttObject*)&publish[i]); + } - mNumMsgsDone++; - wm_SemUnlock(&mtLock); + PRINTF("MQTT Publish: Topic %s, ID %d, %s (%d)", + publish[i].topic_name, publish[i].packet_id, + MqttClient_ReturnCodeToString(rc[i]), rc[i]); + + wm_SemLock(&mtLock); + mNumMsgsDone++; + wm_SemUnlock(&mtLock); + } THREAD_EXIT(0); } @@ -630,9 +671,11 @@ static void *ping_task(void *param) startSec = 0; XMEMSET(&ping, 0, sizeof(ping)); + do { rc = MqttClient_Ping_ex(&mqttCtx->client, &ping); - rc = check_response(mqttCtx, rc, &startSec, MQTT_PACKET_TYPE_PING_REQ); + rc = check_response(mqttCtx, rc, &startSec, MQTT_PACKET_TYPE_PING_REQ, + mqttCtx->cmd_timeout_ms); } while (rc == MQTT_CODE_CONTINUE); if (rc != MQTT_CODE_SUCCESS) { MqttClient_CancelMessage(&mqttCtx->client, (MqttObject*)&ping); @@ -663,7 +706,8 @@ static int unsubscribe_do(MQTTCtx *mqttCtx) /* Unsubscribe Topics */ do { rc = MqttClient_Unsubscribe(&mqttCtx->client, &mqttCtx->unsubscribe); - rc = check_response(mqttCtx, rc, &startSec, MQTT_PACKET_TYPE_UNSUBSCRIBE); + rc = check_response(mqttCtx, rc, &startSec, MQTT_PACKET_TYPE_UNSUBSCRIBE, + mqttCtx->cmd_timeout_ms); } while (rc == MQTT_CODE_CONTINUE); if (rc != MQTT_CODE_SUCCESS) { MqttClient_CancelMessage(&mqttCtx->client, @@ -681,6 +725,12 @@ int multithread_test(MQTTCtx *mqttCtx) int rc = 0, i, threadCount = 0; THREAD_T threadList[NUM_PUB_TASKS+3]; + /* Build test message */ + rc = mqtt_fill_random_hexstr(mTestMessage, (word32)sizeof(mTestMessage)); + if (rc != 0) { + return rc; + } + rc = multithread_test_init(mqttCtx); if (rc == 0) { if (THREAD_CREATE(&threadList[threadCount++], subscribe_task, mqttCtx)) { @@ -705,6 +755,7 @@ int multithread_test(MQTTCtx *mqttCtx) PRINTF("THREAD_CREATE failed: %d", errno); return -1; } + /* Create threads that publish unique messages */ for (i = 0; i < NUM_PUB_TASKS; i++) { if (THREAD_CREATE(&threadList[threadCount++], publish_task, mqttCtx)) { @@ -716,11 +767,11 @@ int multithread_test(MQTTCtx *mqttCtx) /* Join threads - wait for completion */ if (THREAD_JOIN(threadList, threadCount)) { #ifdef __GLIBC__ - /* %m is specific to glibc/uclibc/musl, and recently (2018) - * added to FreeBSD */ + /* "%m" is specific to glibc/uclibc/musl, and FreeBSD (as of 2018). + * Uses errno and not argument required */ PRINTF("THREAD_JOIN failed: %m"); #else - PRINTF("THREAD_JOIN failed: %d",errno); + PRINTF("THREAD_JOIN failed: %d", errno); #endif } diff --git a/examples/nbclient/nbclient.c b/examples/nbclient/nbclient.c index 487cad893..786c12e70 100644 --- a/examples/nbclient/nbclient.c +++ b/examples/nbclient/nbclient.c @@ -33,11 +33,14 @@ /* Locals */ static int mStopRead = 0; +static int mTestDone = 0; /* Configuration */ /* Maximum size for network read/write callbacks. */ +#ifndef MAX_BUFFER_SIZE #define MAX_BUFFER_SIZE 1024 +#endif #ifdef WOLFMQTT_PROPERTY_CB #define MAX_CLIENT_ID_LEN 64 @@ -84,7 +87,7 @@ static int mqtt_message_cb(MqttClient *client, MqttMessage *msg, XSTRNCMP(DEFAULT_MESSAGE, (char*)msg->buffer, msg->buffer_len) == 0) { - mStopRead = 1; + mTestDone = 1; } } } @@ -443,10 +446,12 @@ int mqttclient_test(MQTTCtx *mqttCtx) if ((mqttCtx->pub_file) && (mqttCtx->publish.buffer)) { WOLFMQTT_FREE(mqttCtx->publish.buffer); + mqttCtx->publish.buffer = NULL; + mqttCtx->pub_file = NULL; /* don't try and send file again */ } - PRINTF("MQTT Publish: Topic %s, %s (%d)", - mqttCtx->publish.topic_name, + PRINTF("MQTT Publish: Topic %s, ID %d, %s (%d)", + mqttCtx->publish.topic_name, mqttCtx->publish.packet_id, MqttClient_ReturnCodeToString(rc), rc); if (rc != MQTT_CODE_SUCCESS) { goto disconn; @@ -467,13 +472,6 @@ int mqttclient_test(MQTTCtx *mqttCtx) rc = MqttClient_WaitMessage(&mqttCtx->client, mqttCtx->cmd_timeout_ms); - /* check for test mode */ - if (mStopRead) { - rc = MQTT_CODE_SUCCESS; - PRINTF("MQTT Exiting..."); - break; - } - /* Track elapsed time with no activity and trigger timeout */ rc = mqtt_check_timeout(rc, &mqttCtx->start_sec, mqttCtx->cmd_timeout_ms/1000); @@ -482,8 +480,17 @@ int mqttclient_test(MQTTCtx *mqttCtx) if (rc == MQTT_CODE_CONTINUE) { return rc; } + + /* check for test mode */ + if (mStopRead || mTestDone) { + rc = MQTT_CODE_SUCCESS; + mqttCtx->stat = WMQ_DISCONNECT; + PRINTF("MQTT Exiting..."); + break; + } + #ifdef WOLFMQTT_ENABLE_STDIN_CAP - else if (rc == MQTT_CODE_STDIN_WAKE) { + if (rc == MQTT_CODE_STDIN_WAKE) { XMEMSET(mqttCtx->rx_buf, 0, MAX_BUFFER_SIZE); if (XFGETS((char*)mqttCtx->rx_buf, MAX_BUFFER_SIZE - 1, stdin) != NULL) @@ -504,8 +511,9 @@ int mqttclient_test(MQTTCtx *mqttCtx) return rc; } } + else #endif - else if (rc == MQTT_CODE_ERROR_TIMEOUT) { + if (rc == MQTT_CODE_ERROR_TIMEOUT) { /* Need to send keep-alive ping */ PRINTF("Keep-alive timeout, sending ping"); rc = MQTT_CODE_CONTINUE; @@ -715,7 +723,7 @@ int mqttclient_test(MQTTCtx *mqttCtx) #ifdef WOLFSSL_ASYNC_CRYPT wolfSSL_AsyncPoll(mqttCtx.client.tls.ssl, WOLF_POLL_FLAG_CHECK_HW); #endif - } while (rc == MQTT_CODE_CONTINUE); + } while (!mStopRead && rc == MQTT_CODE_CONTINUE); mqtt_free_ctx(&mqttCtx); #else diff --git a/examples/pub-sub/mqtt-pub-sub.h b/examples/pub-sub/mqtt-pub-sub.h new file mode 100644 index 000000000..60d0ccbbb --- /dev/null +++ b/examples/pub-sub/mqtt-pub-sub.h @@ -0,0 +1,43 @@ +/* mqtt-pub-sub + * + * Copyright (C) 2006-2023 wolfSSL Inc. + * + * This file is part of wolfMQTT. + * + * wolfMQTT is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfMQTT is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#ifndef WOLFMQTT_PUB_SUB_H +#define WOLFMQTT_PUB_SUB_H + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Exposed functions */ +int pub_client(MQTTCtx *mqttCtx); +int sub_client(MQTTCtx *mqttCtx); + +#if defined(NO_MAIN_DRIVER) +int mqttPub_main(int argc, char** argv); +int mqttSub_main(int argc, char** argv); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* WOLFMQTT_PUB_SUB_H */ diff --git a/examples/pub-sub/mqtt-pub.c b/examples/pub-sub/mqtt-pub.c new file mode 100644 index 000000000..40c1fc0a3 --- /dev/null +++ b/examples/pub-sub/mqtt-pub.c @@ -0,0 +1,532 @@ +/* mqtt-pub.c + * + * Copyright (C) 2006-2023 wolfSSL Inc. + * + * This file is part of wolfMQTT. + * + * wolfMQTT is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfMQTT is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Include the autoconf generated config.h */ +#ifdef HAVE_CONFIG_H + #include +#endif + +#include "examples/mqttnet.h" +#include "examples/pub-sub/mqtt-pub-sub.h" + +/* Configuration */ + +/* Maximum size for network read/write callbacks. There is also a v5 define that + * describes the max MQTT control packet size, DEFAULT_MAX_PKT_SZ. */ +#ifndef MAX_BUFFER_SIZE +#define MAX_BUFFER_SIZE 1024 +#endif + +#ifdef WOLFMQTT_PROPERTY_CB +#define MAX_CLIENT_ID_LEN 64 +char gClientId[MAX_CLIENT_ID_LEN] = {0}; +#endif + +#ifdef WOLFMQTT_DISCONNECT_CB +/* callback indicates a network error occurred */ +static int mqtt_disconnect_cb(MqttClient* client, int error_code, void* ctx) +{ + (void)client; + (void)ctx; + PRINTF("Network Error Callback: %s (error %d)", + MqttClient_ReturnCodeToString(error_code), error_code); + return 0; +} +#endif + + +#ifdef WOLFMQTT_PROPERTY_CB +/* The property callback is called after decoding a packet that contains at + least one property. The property list is deallocated after returning from + the callback. */ +static int mqtt_property_cb(MqttClient *client, MqttProp *head, void *ctx) +{ + MqttProp *prop = head; + int rc = 0; + MQTTCtx* mqttCtx; + + if ((client == NULL) || (client->ctx == NULL)) { + return MQTT_CODE_ERROR_BAD_ARG; + } + mqttCtx = (MQTTCtx*)client->ctx; + + while (prop != NULL) { + PRINTF("Property CB: Type %d", prop->type); + switch (prop->type) { + case MQTT_PROP_ASSIGNED_CLIENT_ID: + /* Store client ID in global */ + mqttCtx->client_id = &gClientId[0]; + + /* Store assigned client ID from CONNACK*/ + XSTRNCPY((char*)mqttCtx->client_id, prop->data_str.str, + MAX_CLIENT_ID_LEN - 1); + /* should use strlcpy() semantics, but non-portable */ + ((char*)mqttCtx->client_id)[MAX_CLIENT_ID_LEN - 1] = '\0'; + break; + + case MQTT_PROP_SUBSCRIPTION_ID_AVAIL: + mqttCtx->subId_not_avail = + prop->data_byte == 0; + break; + + case MQTT_PROP_TOPIC_ALIAS_MAX: + mqttCtx->topic_alias_max = + (mqttCtx->topic_alias_max < prop->data_short) ? + mqttCtx->topic_alias_max : prop->data_short; + break; + + case MQTT_PROP_MAX_PACKET_SZ: + if ((prop->data_int > 0) && + (prop->data_int <= MQTT_PACKET_SZ_MAX)) + { + client->packet_sz_max = + (client->packet_sz_max < prop->data_int) ? + client->packet_sz_max : prop->data_int; + } + else { + /* Protocol error */ + rc = MQTT_CODE_ERROR_PROPERTY; + } + break; + + case MQTT_PROP_SERVER_KEEP_ALIVE: + mqttCtx->keep_alive_sec = prop->data_short; + break; + + case MQTT_PROP_MAX_QOS: + client->max_qos = prop->data_byte; + break; + + case MQTT_PROP_RETAIN_AVAIL: + client->retain_avail = prop->data_byte; + break; + + case MQTT_PROP_REASON_STR: + PRINTF("Reason String: %.*s", + prop->data_str.len, prop->data_str.str); + break; + + case MQTT_PROP_USER_PROP: + PRINTF("User property: key=\"%.*s\", value=\"%.*s\"", + prop->data_str.len, prop->data_str.str, + prop->data_str2.len, prop->data_str2.str); + break; + + case MQTT_PROP_PAYLOAD_FORMAT_IND: + case MQTT_PROP_MSG_EXPIRY_INTERVAL: + case MQTT_PROP_CONTENT_TYPE: + case MQTT_PROP_RESP_TOPIC: + case MQTT_PROP_CORRELATION_DATA: + case MQTT_PROP_SUBSCRIPTION_ID: + case MQTT_PROP_SESSION_EXPIRY_INTERVAL: + case MQTT_PROP_TOPIC_ALIAS: + case MQTT_PROP_TYPE_MAX: + case MQTT_PROP_RECEIVE_MAX: + case MQTT_PROP_WILDCARD_SUB_AVAIL: + case MQTT_PROP_SHARED_SUBSCRIPTION_AVAIL: + case MQTT_PROP_RESP_INFO: + case MQTT_PROP_SERVER_REF: + case MQTT_PROP_AUTH_METHOD: + case MQTT_PROP_AUTH_DATA: + case MQTT_PROP_NONE: + break; + case MQTT_PROP_REQ_PROB_INFO: + case MQTT_PROP_WILL_DELAY_INTERVAL: + case MQTT_PROP_REQ_RESP_INFO: + default: + /* Invalid */ + rc = MQTT_CODE_ERROR_PROPERTY; + break; + } + prop = prop->next; + } + + (void)ctx; + + return rc; +} +#endif /* WOLFMQTT_PROPERTY_CB */ + +int pub_client(MQTTCtx *mqttCtx) +{ + int rc = MQTT_CODE_SUCCESS; + + /* Initialize Network */ + rc = MqttClientNet_Init(&mqttCtx->net, mqttCtx); + if (mqttCtx->debug_on) { + PRINTF("MQTT Net Init: %s (%d)", + MqttClient_ReturnCodeToString(rc), rc); + } + if (rc != MQTT_CODE_SUCCESS) { + goto exit; + } + + /* setup tx/rx buffers */ + mqttCtx->tx_buf = (byte*)WOLFMQTT_MALLOC(MAX_BUFFER_SIZE); + mqttCtx->rx_buf = (byte*)WOLFMQTT_MALLOC(MAX_BUFFER_SIZE); + + /* Initialize MqttClient structure */ + rc = MqttClient_Init(&mqttCtx->client, &mqttCtx->net, + NULL, + mqttCtx->tx_buf, MAX_BUFFER_SIZE, + mqttCtx->rx_buf, MAX_BUFFER_SIZE, + mqttCtx->cmd_timeout_ms); + + if (mqttCtx->debug_on) { + PRINTF("MQTT Init: %s (%d)", + MqttClient_ReturnCodeToString(rc), rc); + } + if (rc != MQTT_CODE_SUCCESS) { + goto exit; + } + /* The client.ctx will be stored in the cert callback ctx during + MqttSocket_Connect for use by mqtt_tls_verify_cb */ + mqttCtx->client.ctx = mqttCtx; + +#ifdef WOLFMQTT_DISCONNECT_CB + /* setup disconnect callback */ + rc = MqttClient_SetDisconnectCallback(&mqttCtx->client, + mqtt_disconnect_cb, NULL); + if (rc != MQTT_CODE_SUCCESS) { + goto exit; + } +#endif +#ifdef WOLFMQTT_PROPERTY_CB + rc = MqttClient_SetPropertyCallback(&mqttCtx->client, + mqtt_property_cb, NULL); + if (rc != MQTT_CODE_SUCCESS) { + goto exit; + } +#endif + + /* Connect to broker */ + rc = MqttClient_NetConnect(&mqttCtx->client, mqttCtx->host, + mqttCtx->port, + DEFAULT_CON_TIMEOUT_MS, mqttCtx->use_tls, mqtt_tls_cb); + + if (mqttCtx->debug_on) { + PRINTF("MQTT Socket Connect: %s (%d)", + MqttClient_ReturnCodeToString(rc), rc); + } + + if (rc != MQTT_CODE_SUCCESS) { + goto exit; + } + + /* Build connect packet */ + XMEMSET(&mqttCtx->connect, 0, sizeof(MqttConnect)); + mqttCtx->connect.keep_alive_sec = mqttCtx->keep_alive_sec; + mqttCtx->connect.clean_session = mqttCtx->clean_session; + mqttCtx->connect.client_id = mqttCtx->client_id; + + /* Last will and testament sent by broker to subscribers + of topic when broker connection is lost */ + XMEMSET(&mqttCtx->lwt_msg, 0, sizeof(mqttCtx->lwt_msg)); + mqttCtx->connect.lwt_msg = &mqttCtx->lwt_msg; + mqttCtx->connect.enable_lwt = mqttCtx->enable_lwt; + if (mqttCtx->enable_lwt) { + /* Send client id in LWT payload */ + mqttCtx->lwt_msg.qos = mqttCtx->qos; + mqttCtx->lwt_msg.retain = 0; + mqttCtx->lwt_msg.topic_name = WOLFMQTT_TOPIC_NAME"lwttopic"; + mqttCtx->lwt_msg.buffer = (byte*)mqttCtx->client_id; + mqttCtx->lwt_msg.total_len = (word16)XSTRLEN(mqttCtx->client_id); + +#ifdef WOLFMQTT_V5 + { + /* Add a 5 second delay to sending the LWT */ + MqttProp* prop = MqttClient_PropsAdd(&mqttCtx->lwt_msg.props); + prop->type = MQTT_PROP_WILL_DELAY_INTERVAL; + prop->data_int = 5; + } +#endif + } + /* Optional authentication */ + mqttCtx->connect.username = mqttCtx->username; + mqttCtx->connect.password = mqttCtx->password; +#ifdef WOLFMQTT_V5 + mqttCtx->client.packet_sz_max = mqttCtx->max_packet_size; + mqttCtx->client.enable_eauth = mqttCtx->enable_eauth; + + if (mqttCtx->client.enable_eauth == 1) { + /* Enhanced authentication */ + /* Add property: Authentication Method */ + MqttProp* prop = MqttClient_PropsAdd(&mqttCtx->connect.props); + prop->type = MQTT_PROP_AUTH_METHOD; + prop->data_str.str = (char*)DEFAULT_AUTH_METHOD; + prop->data_str.len = (word16)XSTRLEN(prop->data_str.str); + } + { + /* Request Response Information */ + MqttProp* prop = MqttClient_PropsAdd(&mqttCtx->connect.props); + prop->type = MQTT_PROP_REQ_RESP_INFO; + prop->data_byte = 1; + } + { + /* Request Problem Information */ + MqttProp* prop = MqttClient_PropsAdd(&mqttCtx->connect.props); + prop->type = MQTT_PROP_REQ_PROB_INFO; + prop->data_byte = 1; + } + { + /* Maximum Packet Size */ + MqttProp* prop = MqttClient_PropsAdd(&mqttCtx->connect.props); + prop->type = MQTT_PROP_MAX_PACKET_SZ; + prop->data_int = (word32)mqttCtx->max_packet_size; + } + { + /* Topic Alias Maximum */ + MqttProp* prop = MqttClient_PropsAdd(&mqttCtx->connect.props); + prop->type = MQTT_PROP_TOPIC_ALIAS_MAX; + prop->data_short = mqttCtx->topic_alias_max; + } + if (mqttCtx->clean_session == 0) { + /* Session expiry interval */ + MqttProp* prop = MqttClient_PropsAdd(&mqttCtx->connect.props); + prop->type = MQTT_PROP_SESSION_EXPIRY_INTERVAL; + prop->data_int = DEFAULT_SESS_EXP_INT; /* Session does not expire */ + } +#endif + + /* Send Connect and wait for Connect Ack */ + rc = MqttClient_Connect(&mqttCtx->client, &mqttCtx->connect); + if (mqttCtx->debug_on) { + PRINTF("MQTT Connect: Proto (%s), %s (%d)", + MqttClient_GetProtocolVersionString(&mqttCtx->client), + MqttClient_ReturnCodeToString(rc), rc); + } + if (rc != MQTT_CODE_SUCCESS) { + goto disconn; + } + +#ifdef WOLFMQTT_V5 + if (mqttCtx->connect.props != NULL) { + /* Release the allocated properties */ + MqttClient_PropsFree(mqttCtx->connect.props); + } + if (mqttCtx->lwt_msg.props != NULL) { + /* Release the allocated properties */ + MqttClient_PropsFree(mqttCtx->lwt_msg.props); + } +#endif + + /* Publish Topic */ + XMEMSET(&mqttCtx->publish, 0, sizeof(MqttPublish)); + mqttCtx->publish.retain = 0; + mqttCtx->publish.qos = mqttCtx->qos; + mqttCtx->publish.duplicate = 0; + mqttCtx->publish.topic_name = mqttCtx->topic_name; + mqttCtx->publish.packet_id = mqtt_get_packetid(); + + if (mqttCtx->pub_file) { + /* If a file is specified, then read into the allocated buffer */ + rc = mqtt_file_load(mqttCtx->pub_file, &mqttCtx->publish.buffer, + (int*)&mqttCtx->publish.total_len); + if (rc != MQTT_CODE_SUCCESS) { + /* There was an error loading the file */ + PRINTF("MQTT Publish file error: %d", rc); + } + } + else { + mqttCtx->publish.buffer = (byte*)mqttCtx->message; + mqttCtx->publish.total_len = (word16)XSTRLEN(mqttCtx->message); + } + + if (rc == MQTT_CODE_SUCCESS) { + #ifdef WOLFMQTT_V5 + { + /* Payload Format Indicator */ + MqttProp* prop = MqttClient_PropsAdd(&mqttCtx->publish.props); + prop->type = MQTT_PROP_PAYLOAD_FORMAT_IND; + prop->data_byte = 1; + } + { + /* Content Type */ + MqttProp* prop = MqttClient_PropsAdd(&mqttCtx->publish.props); + prop->type = MQTT_PROP_CONTENT_TYPE; + prop->data_str.str = (char*)"wolf_type"; + prop->data_str.len = (word16)XSTRLEN(prop->data_str.str); + } + if ((mqttCtx->topic_alias_max > 0) && + (mqttCtx->topic_alias > 0) && + (mqttCtx->topic_alias < mqttCtx->topic_alias_max)) { + /* Topic Alias */ + MqttProp* prop = MqttClient_PropsAdd(&mqttCtx->publish.props); + prop->type = MQTT_PROP_TOPIC_ALIAS; + prop->data_short = mqttCtx->topic_alias; + } + #endif + + /* This loop allows payloads larger than the buffer to be sent by + * repeatedly calling publish. */ + do { + rc = MqttClient_Publish(&mqttCtx->client, &mqttCtx->publish); + } while (rc == MQTT_CODE_PUB_CONTINUE); + + if ((mqttCtx->pub_file) && (mqttCtx->publish.buffer)) { + WOLFMQTT_FREE(mqttCtx->publish.buffer); + mqttCtx->publish.buffer = NULL; + mqttCtx->pub_file = NULL; /* don't try and send file again */ + } + if (mqttCtx->debug_on) { + PRINTF("MQTT Publish: Topic %s, ID %d, %s (%d)", + mqttCtx->publish.topic_name, mqttCtx->publish.packet_id, + MqttClient_ReturnCodeToString(rc), rc); + } + if (rc != MQTT_CODE_SUCCESS) { + #ifdef WOLFMQTT_V5 + if (mqttCtx->qos > 0) { + PRINTF("\tResponse Reason Code %d", mqttCtx->publish.resp.reason_code); + } + #endif + goto disconn; + } + #ifdef WOLFMQTT_V5 + if (mqttCtx->publish.props != NULL) { + /* Release the allocated properties */ + MqttClient_PropsFree(mqttCtx->publish.props); + } + #endif + } + + +disconn: + /* Disconnect */ + XMEMSET(&mqttCtx->disconnect, 0, sizeof(mqttCtx->disconnect)); +#ifdef WOLFMQTT_V5 + { + /* Session expiry interval */ + MqttProp* prop = MqttClient_PropsAdd(&mqttCtx->disconnect.props); + prop->type = MQTT_PROP_SESSION_EXPIRY_INTERVAL; + prop->data_int = 0; + } + #if 0 /* enable to test sending a disconnect reason code */ + if (mqttCtx->enable_lwt) { + /* Disconnect with Will Message */ + mqttCtx->disconnect.reason_code = MQTT_REASON_DISCONNECT_W_WILL_MSG; + } + #endif +#endif + rc = MqttClient_Disconnect_ex(&mqttCtx->client, &mqttCtx->disconnect); +#ifdef WOLFMQTT_V5 + if (mqttCtx->disconnect.props != NULL) { + /* Release the allocated properties */ + MqttClient_PropsFree(mqttCtx->disconnect.props); + } +#endif + + if (mqttCtx->debug_on) { + PRINTF("MQTT Disconnect: %s (%d)", + MqttClient_ReturnCodeToString(rc), rc); + } + + rc = MqttClient_NetDisconnect(&mqttCtx->client); + if (mqttCtx->debug_on) { + PRINTF("MQTT Socket Disconnect: %s (%d)", + MqttClient_ReturnCodeToString(rc), rc); + } +exit: + + /* Free resources */ + if (mqttCtx->tx_buf) WOLFMQTT_FREE(mqttCtx->tx_buf); + if (mqttCtx->rx_buf) WOLFMQTT_FREE(mqttCtx->rx_buf); + + /* Cleanup network */ + MqttClientNet_DeInit(&mqttCtx->net); + + MqttClient_DeInit(&mqttCtx->client); + + return rc; +} + + +/* so overall tests can pull in test function */ + #ifdef USE_WINDOWS_API + #include /* for ctrl handler */ + + static BOOL CtrlHandler(DWORD fdwCtrlType) + { + if (fdwCtrlType == CTRL_C_EVENT) { + PRINTF("Received Ctrl+c"); + return TRUE; + } + return FALSE; + } + #elif HAVE_SIGNAL + #include + static void sig_handler(int signo) + { + if (signo == SIGINT) { + PRINTF("Received SIGINT"); + } + } + #endif + +#if defined(NO_MAIN_DRIVER) +int mqttPub_main(int argc, char** argv) +#else +int main(int argc, char** argv) +#endif +{ + int rc; + MQTTCtx mqttCtx; + + /* init defaults */ + mqtt_init_ctx(&mqttCtx); + + /* Set default host to localhost */ + mqttCtx.host = "localhost"; + + /* Set default client ID */ + mqttCtx.client_id = "wolfMQTT_pub"; + + /* Set example debug messages off (turn on with '-d') */ + mqttCtx.debug_on = 0; + + /* parse arguments */ + rc = mqtt_parse_args(&mqttCtx, argc, argv); + if (rc != 0) { + if (rc == MY_EX_USAGE) { + /* return success, so make check passes with TLS disabled */ + return 0; + } + return rc; + } + +#ifdef USE_WINDOWS_API + if (SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, + TRUE) == FALSE) + { + PRINTF("Error setting Ctrl Handler! Error %d", (int)GetLastError()); + } +#elif HAVE_SIGNAL + if (signal(SIGINT, sig_handler) == SIG_ERR) { + PRINTF("Can't catch SIGINT"); + } +#endif + + rc = pub_client(&mqttCtx); + + mqtt_free_ctx(&mqttCtx); + + return (rc == 0) ? 0 : EXIT_FAILURE; +} + diff --git a/examples/pub-sub/mqtt-sub.c b/examples/pub-sub/mqtt-sub.c new file mode 100644 index 000000000..84d791e10 --- /dev/null +++ b/examples/pub-sub/mqtt-sub.c @@ -0,0 +1,657 @@ +/* mqtt-sub.c + * + * Copyright (C) 2006-2023 wolfSSL Inc. + * + * This file is part of wolfMQTT. + * + * wolfMQTT is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfMQTT is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Include the autoconf generated config.h */ +#ifdef HAVE_CONFIG_H + #include +#endif + +#include "examples/mqttnet.h" +#include "examples/pub-sub/mqtt-pub-sub.h" + +/* Locals */ +static int mStopRead = 0; + +/* Configuration */ + +/* Maximum size for network read/write callbacks. There is also a v5 define that + * describes the max MQTT control packet size, DEFAULT_MAX_PKT_SZ. */ +#ifndef MAX_BUFFER_SIZE +#define MAX_BUFFER_SIZE 1024 +#endif + +#ifdef WOLFMQTT_PROPERTY_CB +#define MAX_CLIENT_ID_LEN 64 +char gClientId[MAX_CLIENT_ID_LEN] = {0}; +#endif + +#ifdef WOLFMQTT_DISCONNECT_CB +/* callback indicates a network error occurred */ +static int mqtt_disconnect_cb(MqttClient* client, int error_code, void* ctx) +{ + MQTTCtx* mqttCtx = (MQTTCtx*)client->ctx; + (void)ctx; + + if (mqttCtx->debug_on) { + PRINTF("Network Error Callback: %s (error %d)", + MqttClient_ReturnCodeToString(error_code), error_code); + } + return 0; +} +#endif + +static int mqtt_message_cb(MqttClient *client, MqttMessage *msg, + byte msg_new, byte msg_done) +{ + byte buf[PRINT_BUFFER_SIZE+1]; + word32 len; + MQTTCtx* mqttCtx = (MQTTCtx*)client->ctx; + + (void)mqttCtx; + + if (msg_new) { + /* Determine min size to dump */ + len = msg->topic_name_len; + if (len > PRINT_BUFFER_SIZE) { + len = PRINT_BUFFER_SIZE; + } + XMEMCPY(buf, msg->topic_name, len); + buf[len] = '\0'; /* Make sure its null terminated */ + + if (mqttCtx->debug_on) { + /* Print incoming message */ + PRINTF("MQTT Message: Topic %s, Qos %d, Len %u", + buf, msg->qos, msg->total_len); + } + } + + /* Print message payload */ + len = msg->buffer_len; + if (len > PRINT_BUFFER_SIZE) { + len = PRINT_BUFFER_SIZE; + } + XMEMCPY(buf, msg->buffer, len); + buf[len] = '\0'; /* Make sure its null terminated */ + if (mqttCtx->debug_on) { + PRINTF("Payload (%d - %d) printing %d bytes:" LINE_END "%s", + msg->buffer_pos, msg->buffer_pos + msg->buffer_len, len, buf); + } + else { + PRINTF("%s", buf); + } + + #ifdef WOLFMQTT_V5 + { + /* Properties can be checked in the message callback */ + MqttProp *prop = msg->props; + while (prop != NULL) + { + if (prop->type == MQTT_PROP_CONTENT_TYPE) { + PRINTF("Content type: %.*s", prop->data_str.len, + prop->data_str.str); + } + prop = prop->next; + } + } + #endif + + if (msg_done && mqttCtx->debug_on) { + PRINTF("MQTT Message: Done"); + } + + /* Return negative to terminate publish processing */ + return MQTT_CODE_SUCCESS; +} + +#ifdef WOLFMQTT_PROPERTY_CB +/* The property callback is called after decoding a packet that contains at + least one property. The property list is deallocated after returning from + the callback. */ +static int mqtt_property_cb(MqttClient *client, MqttProp *head, void *ctx) +{ + MqttProp *prop = head; + int rc = 0; + MQTTCtx* mqttCtx; + + if ((client == NULL) || (client->ctx == NULL)) { + return MQTT_CODE_ERROR_BAD_ARG; + } + mqttCtx = (MQTTCtx*)client->ctx; + + while (prop != NULL) { + PRINTF("Property CB: Type %d", prop->type); + switch (prop->type) { + case MQTT_PROP_ASSIGNED_CLIENT_ID: + /* Store client ID in global */ + mqttCtx->client_id = &gClientId[0]; + + /* Store assigned client ID from CONNACK*/ + XSTRNCPY((char*)mqttCtx->client_id, prop->data_str.str, + MAX_CLIENT_ID_LEN - 1); + /* should use strlcpy() semantics, but non-portable */ + ((char*)mqttCtx->client_id)[MAX_CLIENT_ID_LEN - 1] = '\0'; + break; + + case MQTT_PROP_SUBSCRIPTION_ID_AVAIL: + mqttCtx->subId_not_avail = + prop->data_byte == 0; + break; + + case MQTT_PROP_TOPIC_ALIAS_MAX: + mqttCtx->topic_alias_max = + (mqttCtx->topic_alias_max < prop->data_short) ? + mqttCtx->topic_alias_max : prop->data_short; + break; + + case MQTT_PROP_MAX_PACKET_SZ: + if ((prop->data_int > 0) && + (prop->data_int <= MQTT_PACKET_SZ_MAX)) + { + client->packet_sz_max = + (client->packet_sz_max < prop->data_int) ? + client->packet_sz_max : prop->data_int; + } + else { + /* Protocol error */ + rc = MQTT_CODE_ERROR_PROPERTY; + } + break; + + case MQTT_PROP_SERVER_KEEP_ALIVE: + mqttCtx->keep_alive_sec = prop->data_short; + break; + + case MQTT_PROP_MAX_QOS: + client->max_qos = prop->data_byte; + break; + + case MQTT_PROP_RETAIN_AVAIL: + client->retain_avail = prop->data_byte; + break; + + case MQTT_PROP_REASON_STR: + PRINTF("Reason String: %.*s", + prop->data_str.len, prop->data_str.str); + break; + + case MQTT_PROP_USER_PROP: + PRINTF("User property: key=\"%.*s\", value=\"%.*s\"", + prop->data_str.len, prop->data_str.str, + prop->data_str2.len, prop->data_str2.str); + break; + + case MQTT_PROP_PAYLOAD_FORMAT_IND: + case MQTT_PROP_MSG_EXPIRY_INTERVAL: + case MQTT_PROP_CONTENT_TYPE: + case MQTT_PROP_RESP_TOPIC: + case MQTT_PROP_CORRELATION_DATA: + case MQTT_PROP_SUBSCRIPTION_ID: + case MQTT_PROP_SESSION_EXPIRY_INTERVAL: + case MQTT_PROP_TOPIC_ALIAS: + case MQTT_PROP_TYPE_MAX: + case MQTT_PROP_RECEIVE_MAX: + case MQTT_PROP_WILDCARD_SUB_AVAIL: + case MQTT_PROP_SHARED_SUBSCRIPTION_AVAIL: + case MQTT_PROP_RESP_INFO: + case MQTT_PROP_SERVER_REF: + case MQTT_PROP_AUTH_METHOD: + case MQTT_PROP_AUTH_DATA: + case MQTT_PROP_NONE: + break; + case MQTT_PROP_REQ_PROB_INFO: + case MQTT_PROP_WILL_DELAY_INTERVAL: + case MQTT_PROP_REQ_RESP_INFO: + default: + /* Invalid */ + rc = MQTT_CODE_ERROR_PROPERTY; + break; + } + prop = prop->next; + } + + (void)ctx; + + return rc; +} +#endif /* WOLFMQTT_PROPERTY_CB */ + +int sub_client(MQTTCtx *mqttCtx) +{ + int rc = MQTT_CODE_SUCCESS, i; + + if (mqttCtx->debug_on) { + PRINTF("Subscribe Client: QoS %d, Use TLS %d", mqttCtx->qos, + mqttCtx->use_tls); + } + /* Initialize Network */ + rc = MqttClientNet_Init(&mqttCtx->net, mqttCtx); + if (mqttCtx->debug_on) { + PRINTF("MQTT Net Init: %s (%d)", + MqttClient_ReturnCodeToString(rc), rc); + } + if (rc != MQTT_CODE_SUCCESS) { + goto exit; + } + + /* setup tx/rx buffers */ + mqttCtx->tx_buf = (byte*)WOLFMQTT_MALLOC(MAX_BUFFER_SIZE); + mqttCtx->rx_buf = (byte*)WOLFMQTT_MALLOC(MAX_BUFFER_SIZE); + + /* Initialize MqttClient structure */ + rc = MqttClient_Init(&mqttCtx->client, &mqttCtx->net, + mqtt_message_cb, + mqttCtx->tx_buf, MAX_BUFFER_SIZE, + mqttCtx->rx_buf, MAX_BUFFER_SIZE, + mqttCtx->cmd_timeout_ms); + + if (mqttCtx->debug_on) { + PRINTF("MQTT Init: %s (%d)", + MqttClient_ReturnCodeToString(rc), rc); + } + if (rc != MQTT_CODE_SUCCESS) { + goto exit; + } + /* The client.ctx will be stored in the cert callback ctx during + MqttSocket_Connect for use by mqtt_tls_verify_cb */ + mqttCtx->client.ctx = mqttCtx; + +#ifdef WOLFMQTT_DISCONNECT_CB + /* setup disconnect callback */ + rc = MqttClient_SetDisconnectCallback(&mqttCtx->client, + mqtt_disconnect_cb, NULL); + if (rc != MQTT_CODE_SUCCESS) { + goto exit; + } +#endif +#ifdef WOLFMQTT_PROPERTY_CB + rc = MqttClient_SetPropertyCallback(&mqttCtx->client, + mqtt_property_cb, NULL); + if (rc != MQTT_CODE_SUCCESS) { + goto exit; + } +#endif + + /* Connect to broker */ + rc = MqttClient_NetConnect(&mqttCtx->client, mqttCtx->host, + mqttCtx->port, + DEFAULT_CON_TIMEOUT_MS, mqttCtx->use_tls, mqtt_tls_cb); + + if (mqttCtx->debug_on) { + PRINTF("MQTT Socket Connect: %s (%d)", + MqttClient_ReturnCodeToString(rc), rc); + } + if (rc != MQTT_CODE_SUCCESS) { + goto exit; + } + + /* Build connect packet */ + XMEMSET(&mqttCtx->connect, 0, sizeof(MqttConnect)); + mqttCtx->connect.keep_alive_sec = mqttCtx->keep_alive_sec; + mqttCtx->connect.clean_session = mqttCtx->clean_session; + mqttCtx->connect.client_id = mqttCtx->client_id; + + /* Last will and testament sent by broker to subscribers + of topic when broker connection is lost */ + XMEMSET(&mqttCtx->lwt_msg, 0, sizeof(mqttCtx->lwt_msg)); + mqttCtx->connect.lwt_msg = &mqttCtx->lwt_msg; + mqttCtx->connect.enable_lwt = mqttCtx->enable_lwt; + if (mqttCtx->enable_lwt) { + /* Send client id in LWT payload */ + mqttCtx->lwt_msg.qos = mqttCtx->qos; + mqttCtx->lwt_msg.retain = 0; + mqttCtx->lwt_msg.topic_name = WOLFMQTT_TOPIC_NAME"lwttopic"; + mqttCtx->lwt_msg.buffer = (byte*)mqttCtx->client_id; + mqttCtx->lwt_msg.total_len = (word16)XSTRLEN(mqttCtx->client_id); + +#ifdef WOLFMQTT_V5 + { + /* Add a 5 second delay to sending the LWT */ + MqttProp* prop = MqttClient_PropsAdd(&mqttCtx->lwt_msg.props); + prop->type = MQTT_PROP_WILL_DELAY_INTERVAL; + prop->data_int = 5; + } +#endif + } + /* Optional authentication */ + mqttCtx->connect.username = mqttCtx->username; + mqttCtx->connect.password = mqttCtx->password; +#ifdef WOLFMQTT_V5 + mqttCtx->client.packet_sz_max = mqttCtx->max_packet_size; + + { + /* Request Response Information */ + MqttProp* prop = MqttClient_PropsAdd(&mqttCtx->connect.props); + prop->type = MQTT_PROP_REQ_RESP_INFO; + prop->data_byte = 1; + } + { + /* Request Problem Information */ + MqttProp* prop = MqttClient_PropsAdd(&mqttCtx->connect.props); + prop->type = MQTT_PROP_REQ_PROB_INFO; + prop->data_byte = 1; + } + { + /* Maximum Packet Size */ + MqttProp* prop = MqttClient_PropsAdd(&mqttCtx->connect.props); + prop->type = MQTT_PROP_MAX_PACKET_SZ; + prop->data_int = (word32)mqttCtx->max_packet_size; + } + { + /* Topic Alias Maximum */ + MqttProp* prop = MqttClient_PropsAdd(&mqttCtx->connect.props); + prop->type = MQTT_PROP_TOPIC_ALIAS_MAX; + prop->data_short = mqttCtx->topic_alias_max; + } + if (mqttCtx->clean_session == 0) { + /* Session expiry interval */ + MqttProp* prop = MqttClient_PropsAdd(&mqttCtx->connect.props); + prop->type = MQTT_PROP_SESSION_EXPIRY_INTERVAL; + prop->data_int = DEFAULT_SESS_EXP_INT; /* Session does not expire */ + } +#endif + + /* Send Connect and wait for Connect Ack */ + rc = MqttClient_Connect(&mqttCtx->client, &mqttCtx->connect); + + if (mqttCtx->debug_on) { + PRINTF("MQTT Connect: Proto (%s), %s (%d)", + MqttClient_GetProtocolVersionString(&mqttCtx->client), + MqttClient_ReturnCodeToString(rc), rc); + } + if (rc != MQTT_CODE_SUCCESS) { + goto disconn; + } + +#ifdef WOLFMQTT_V5 + if (mqttCtx->connect.props != NULL) { + /* Release the allocated properties */ + MqttClient_PropsFree(mqttCtx->connect.props); + } + if (mqttCtx->lwt_msg.props != NULL) { + /* Release the allocated properties */ + MqttClient_PropsFree(mqttCtx->lwt_msg.props); + } +#endif + + if (mqttCtx->debug_on) { + /* Validate Connect Ack info */ + PRINTF("MQTT Connect Ack: Return Code %u, Session Present %d", + mqttCtx->connect.ack.return_code, + (mqttCtx->connect.ack.flags & + MQTT_CONNECT_ACK_FLAG_SESSION_PRESENT) ? + 1 : 0 + ); +#ifdef WOLFMQTT_PROPERTY_CB + /* Print the acquired client ID */ + PRINTF("MQTT Connect Ack: Assigned Client ID: %s", + mqttCtx->client_id); +#endif + } + + /* Build list of topics */ + XMEMSET(&mqttCtx->subscribe, 0, sizeof(MqttSubscribe)); + + i = 0; + mqttCtx->topics[i].topic_filter = mqttCtx->topic_name; + mqttCtx->topics[i].qos = mqttCtx->qos; + +#ifdef WOLFMQTT_V5 + if (mqttCtx->subId_not_avail != 1) { + /* Subscription Identifier */ + MqttProp* prop; + prop = MqttClient_PropsAdd(&mqttCtx->subscribe.props); + prop->type = MQTT_PROP_SUBSCRIPTION_ID; + prop->data_int = DEFAULT_SUB_ID; + } +#endif + + /* Subscribe Topic */ + mqttCtx->subscribe.packet_id = mqtt_get_packetid(); + mqttCtx->subscribe.topic_count = + sizeof(mqttCtx->topics) / sizeof(MqttTopic); + mqttCtx->subscribe.topics = mqttCtx->topics; + + rc = MqttClient_Subscribe(&mqttCtx->client, &mqttCtx->subscribe); + +#ifdef WOLFMQTT_V5 + if (mqttCtx->subscribe.props != NULL) { + /* Release the allocated properties */ + MqttClient_PropsFree(mqttCtx->subscribe.props); + } +#endif + + if (mqttCtx->debug_on) { + PRINTF("MQTT Subscribe: %s (%d)", + MqttClient_ReturnCodeToString(rc), rc); + } + if (rc != MQTT_CODE_SUCCESS) { + goto disconn; + } + + if (mqttCtx->debug_on) { + /* show subscribe results */ + for (i = 0; i < mqttCtx->subscribe.topic_count; i++) { + MqttTopic *topic = &mqttCtx->subscribe.topics[i]; + PRINTF(" Topic %s, Qos %u, Return Code %u", + topic->topic_filter, + topic->qos, topic->return_code); + } + } + /* Read Loop */ + if (mqttCtx->debug_on) { + PRINTF("MQTT Waiting for message..."); + } + + do { + /* Try and read packet */ + rc = MqttClient_WaitMessage(&mqttCtx->client, + mqttCtx->cmd_timeout_ms); + + #ifdef WOLFMQTT_NONBLOCK + /* Track elapsed time with no activity and trigger timeout */ + rc = mqtt_check_timeout(rc, &mqttCtx->start_sec, + mqttCtx->cmd_timeout_ms/1000); + #endif + + if (mStopRead) { + rc = MQTT_CODE_SUCCESS; + if (mqttCtx->debug_on) { + PRINTF("MQTT Exiting..."); + } + break; + } + + /* check return code */ + #ifdef WOLFMQTT_ENABLE_STDIN_CAP + else if (rc == MQTT_CODE_STDIN_WAKE) { + XMEMSET(mqttCtx->rx_buf, 0, MAX_BUFFER_SIZE); + if (XFGETS((char*)mqttCtx->rx_buf, MAX_BUFFER_SIZE - 1, + stdin) != NULL) { + } + } + #endif + else if (rc == MQTT_CODE_ERROR_TIMEOUT) { + /* Keep Alive */ + if (mqttCtx->debug_on) { + PRINTF("Keep-alive timeout, sending ping"); + } + rc = MqttClient_Ping_ex(&mqttCtx->client, &mqttCtx->ping); + if (rc != MQTT_CODE_SUCCESS) { + PRINTF("MQTT Ping Keep Alive Error: %s (%d)", + MqttClient_ReturnCodeToString(rc), rc); + break; + } + } + else if (rc != MQTT_CODE_SUCCESS) { + /* There was an error */ + PRINTF("MQTT Message Wait: %s (%d)", + MqttClient_ReturnCodeToString(rc), rc); + break; + } + } while (!mStopRead); + + /* Check for error */ + if (rc != MQTT_CODE_SUCCESS) { + goto disconn; + } + + /* Unsubscribe Topics */ + XMEMSET(&mqttCtx->unsubscribe, 0, sizeof(MqttUnsubscribe)); + mqttCtx->unsubscribe.packet_id = mqtt_get_packetid(); + mqttCtx->unsubscribe.topic_count = + sizeof(mqttCtx->topics) / sizeof(MqttTopic); + mqttCtx->unsubscribe.topics = mqttCtx->topics; + + /* Unsubscribe Topics */ + rc = MqttClient_Unsubscribe(&mqttCtx->client, + &mqttCtx->unsubscribe); + + if (mqttCtx->debug_on) { + PRINTF("MQTT Unsubscribe: %s (%d)", + MqttClient_ReturnCodeToString(rc), rc); + } + if (rc != MQTT_CODE_SUCCESS) { + goto disconn; + } + mqttCtx->return_code = rc; + +disconn: + /* Disconnect */ + XMEMSET(&mqttCtx->disconnect, 0, sizeof(mqttCtx->disconnect)); +#ifdef WOLFMQTT_V5 + { + /* Session expiry interval */ + MqttProp* prop = MqttClient_PropsAdd(&mqttCtx->disconnect.props); + prop->type = MQTT_PROP_SESSION_EXPIRY_INTERVAL; + prop->data_int = 0; + } + #if 0 /* enable to test sending a disconnect reason code */ + if (mqttCtx->enable_lwt) { + /* Disconnect with Will Message */ + mqttCtx->disconnect.reason_code = MQTT_REASON_DISCONNECT_W_WILL_MSG; + } + #endif +#endif + rc = MqttClient_Disconnect_ex(&mqttCtx->client, &mqttCtx->disconnect); +#ifdef WOLFMQTT_V5 + if (mqttCtx->disconnect.props != NULL) { + /* Release the allocated properties */ + MqttClient_PropsFree(mqttCtx->disconnect.props); + } +#endif + + if (mqttCtx->debug_on) { + PRINTF("MQTT Disconnect: %s (%d)", + MqttClient_ReturnCodeToString(rc), rc); + } + rc = MqttClient_NetDisconnect(&mqttCtx->client); + + if (mqttCtx->debug_on) { + PRINTF("MQTT Socket Disconnect: %s (%d)", + MqttClient_ReturnCodeToString(rc), rc); + } +exit: + + /* Free resources */ + if (mqttCtx->tx_buf) WOLFMQTT_FREE(mqttCtx->tx_buf); + if (mqttCtx->rx_buf) WOLFMQTT_FREE(mqttCtx->rx_buf); + + /* Cleanup network */ + MqttClientNet_DeInit(&mqttCtx->net); + + MqttClient_DeInit(&mqttCtx->client); + + return rc; +} + + +/* so overall tests can pull in test function */ + #ifdef USE_WINDOWS_API + #include /* for ctrl handler */ + + static BOOL CtrlHandler(DWORD fdwCtrlType) + { + if (fdwCtrlType == CTRL_C_EVENT) { + mStopRead = 1; + return TRUE; + } + return FALSE; + } + #elif HAVE_SIGNAL + #include + static void sig_handler(int signo) + { + if (signo == SIGINT) { + mStopRead = 1; + } + } + #endif + +#if defined(NO_MAIN_DRIVER) +int mqttSub_main(int argc, char** argv) +#else +int main(int argc, char** argv) +#endif +{ + int rc; + MQTTCtx mqttCtx; + + /* init defaults */ + mqtt_init_ctx(&mqttCtx); + + /* Set default client ID */ + mqttCtx.client_id = "wolfMQTT_sub"; + + /* Set default host to localhost */ + mqttCtx.host = "localhost"; + + /* Set example debug messages off (turn on with '-d') */ + mqttCtx.debug_on = 0; + + /* parse arguments */ + rc = mqtt_parse_args(&mqttCtx, argc, argv); + if (rc != 0) { + if (rc == MY_EX_USAGE) { + /* return success, so make check passes with TLS disabled */ + return 0; + } + return rc; + } + +#ifdef USE_WINDOWS_API + if (SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, + TRUE) == FALSE) + { + PRINTF("Error setting Ctrl Handler! Error %d", (int)GetLastError()); + } +#elif HAVE_SIGNAL + if (signal(SIGINT, sig_handler) == SIG_ERR) { + PRINTF("Can't catch SIGINT"); + } +#endif + + rc = sub_client(&mqttCtx); + + mqtt_free_ctx(&mqttCtx); + + return (rc == 0) ? 0 : EXIT_FAILURE; +} + diff --git a/examples/sn-client/sn-client.c b/examples/sn-client/sn-client.c index 9d7ab5ad7..31a909603 100644 --- a/examples/sn-client/sn-client.c +++ b/examples/sn-client/sn-client.c @@ -36,7 +36,9 @@ static int mStopRead = 0; /* Configuration */ /* Maximum size for network read/write callbacks. */ +#ifndef MAX_BUFFER_SIZE #define MAX_BUFFER_SIZE 1024 +#endif #define TEST_MESSAGE "test" #define SHORT_TOPIC_NAME "s1" @@ -49,13 +51,22 @@ static int sn_message_cb(MqttClient *client, MqttMessage *msg, MQTTCtx* mqttCtx = (MQTTCtx*)client->ctx; if (msg_new) { - /* Topic ID or short topic name */ - topicId = (word16)(msg->topic_name[0] << 8 | msg->topic_name[1]); - - /* Print incoming message */ - PRINTF("MQTT-SN Message: Topic ID %d, Qos %d, Id %d, Len %u", - topicId, msg->qos, msg->packet_id, msg->total_len); - + if (!(msg->topic_type & SN_TOPIC_ID_TYPE_SHORT)) { + /* Topic ID name */ + topicId = (word16)((byte)msg->topic_name[0] << 8 | + (byte)msg->topic_name[1]); + + /* Print incoming message */ + PRINTF("MQTT-SN Message: Topic ID %hu, Qos %d, Id %d, Len %u", + topicId, msg->qos, msg->packet_id, msg->total_len); + } + else { + /* Topic short name */ + /* Print incoming message */ + PRINTF("MQTT-SN Message: Topic ID %c%c, Qos %d, Id %d, Len %u", + msg->topic_name[0], msg->topic_name[1], + msg->qos, msg->packet_id, msg->total_len); + } /* for test mode: check if TEST_MESSAGE was received */ if (mqttCtx != NULL && mqttCtx->test_mode) { if (XSTRLEN(TEST_MESSAGE) == msg->buffer_len && @@ -89,7 +100,7 @@ static int sn_message_cb(MqttClient *client, MqttMessage *msg, assigns a new topic ID to a topic name. */ static int sn_reg_callback(word16 topicId, const char* topicName, void *ctx) { - PRINTF("MQTT-SN Register CB: New topic ID: %d : \"%s\"", topicId, topicName); + PRINTF("MQTT-SN Register CB: New topic ID: %hu : \"%s\"", topicId, topicName); (void)ctx; return(MQTT_CODE_SUCCESS); @@ -112,7 +123,9 @@ int sn_test(MQTTCtx *mqttCtx) int rc = MQTT_CODE_SUCCESS; word16 topicID; - PRINTF("MQTT-SN Client: QoS %d", mqttCtx->qos); + PRINTF("MQTT-SN Client: Client ID %s, QoS %d", + mqttCtx->client_id, + mqttCtx->qos); /* Initialize Network */ rc = SN_ClientNet_Init(&mqttCtx->net, mqttCtx); @@ -204,11 +217,11 @@ int sn_test(MQTTCtx *mqttCtx) /* Send Connect and wait for Connect Ack */ rc = SN_Client_Connect(&mqttCtx->client, connect); - if (rc != MQTT_CODE_SUCCESS) { - PRINTF("MQTT-SN Connect: %s (%d)", - MqttClient_ReturnCodeToString(rc), rc); - goto disconn; - } + if (rc != MQTT_CODE_SUCCESS) { + PRINTF("MQTT-SN Connect: %s (%d)", + MqttClient_ReturnCodeToString(rc), rc); + goto disconn; + } /* Validate Connect Ack info */ PRINTF("....MQTT-SN Connect Ack: Return Code %u", @@ -232,7 +245,7 @@ int sn_test(MQTTCtx *mqttCtx) /* Topic ID is returned in RegAck */ topicID = regist->regack.topicId; } - PRINTF("....MQTT-SN Register Ack: rc = %d, topic id = %d", + PRINTF("....MQTT-SN Register Ack: rc = %d, topic id = %hu", regist->regack.return_code, regist->regack.topicId); } @@ -251,7 +264,7 @@ int sn_test(MQTTCtx *mqttCtx) PRINTF("MQTT-SN Subscribe: topic name = %s", subscribe.topicNameId); rc = SN_Client_Subscribe(&mqttCtx->client, &subscribe); - PRINTF("....MQTT-SN Subscribe Ack: topic id = %d, rc = %d", + PRINTF("....MQTT-SN Subscribe Ack: topic id = %hu, rc = %d", subscribe.subAck.topicId, subscribe.subAck.return_code); if ((rc == 0) && (subscribe.subAck.return_code == SN_RC_ACCEPTED)) { @@ -278,7 +291,7 @@ int sn_test(MQTTCtx *mqttCtx) PRINTF("MQTT-SN Subscribe: topic name = %s", subscribe.topicNameId); rc = SN_Client_Subscribe(&mqttCtx->client, &subscribe); - PRINTF("....MQTT-SN Subscribe Ack: topic id = %d, rc = %d", + PRINTF("....MQTT-SN Subscribe Ack: topic id = %hu, rc = %d", subscribe.subAck.topicId, (rc == 0) ? subscribe.subAck.return_code : rc); } @@ -305,8 +318,8 @@ int sn_test(MQTTCtx *mqttCtx) rc = SN_Client_Publish(&mqttCtx->client, &mqttCtx->publishSN); - PRINTF("MQTT-SN Publish: topic id = %d, rc = %d\r\nPayload = %s", - (word16)*mqttCtx->publishSN.topic_name, + PRINTF("MQTT-SN Publish: topic id = %hu, rc = %d\r\nPayload = %s", + *(word16*)mqttCtx->publishSN.topic_name, mqttCtx->publishSN.return_code, mqttCtx->publishSN.buffer); if (rc != MQTT_CODE_SUCCESS) { @@ -343,12 +356,12 @@ int sn_test(MQTTCtx *mqttCtx) subscribe.topicNameId = pd_topic_id; subscribe.packet_id = mqtt_get_packetid(); - PRINTF("MQTT-SN Predefined Subscribe: topic id = %d", + PRINTF("MQTT-SN Predefined Subscribe: topic id = %hu", subscribe.topicNameId[1]); rc = SN_Client_Subscribe(&mqttCtx->client, &subscribe); if (rc == MQTT_CODE_SUCCESS) { - PRINTF("....MQTT-SN Predefined Subscribe Ack: topic id = %d, rc = %d", + PRINTF("....MQTT-SN Predefined Subscribe Ack: topic id = %hu, rc = %d", subscribe.subAck.topicId, subscribe.subAck.return_code); } if ((rc == MQTT_CODE_SUCCESS) && (subscribe.subAck.return_code != 0)) { @@ -376,7 +389,7 @@ int sn_test(MQTTCtx *mqttCtx) rc = SN_Client_Publish(&mqttCtx->client, &publish); - PRINTF("MQTT-SN Predefined Publish: topic id = %d, rc = %d\r\nPayload = %s", + PRINTF("MQTT-SN Predefined Publish: topic id = %hu, rc = %d\r\nPayload = %s", publish.topic_name[1], publish.return_code, publish.buffer); @@ -392,7 +405,7 @@ int sn_test(MQTTCtx *mqttCtx) unsub.topicNameId = pd_topic_id; unsub.packet_id = mqtt_get_packetid(); - PRINTF("MQTT-SN Unsubscribe Predefined Topic: topic ID = %d", + PRINTF("MQTT-SN Unsubscribe Predefined Topic: topic id = %hu", unsub.topicNameId[1]); rc = SN_Client_Unsubscribe(&mqttCtx->client, &unsub); PRINTF("....MQTT-SN Unsubscribe Predefined Topic Ack: rc = %d", rc); @@ -496,10 +509,6 @@ int sn_test(MQTTCtx *mqttCtx) PRINTF("MQTT Waiting for message..."); do { - /* Try and read packet */ - rc = SN_Client_WaitMessage(&mqttCtx->client, - mqttCtx->cmd_timeout_ms); - /* check for test mode */ if (mStopRead) { rc = MQTT_CODE_SUCCESS; @@ -507,9 +516,13 @@ int sn_test(MQTTCtx *mqttCtx) break; } + /* Try and read packet */ + rc = SN_Client_WaitMessage(&mqttCtx->client, + mqttCtx->cmd_timeout_ms); + /* check return code */ #ifdef WOLFMQTT_ENABLE_STDIN_CAP - else if (rc == MQTT_CODE_STDIN_WAKE) { + if (rc == MQTT_CODE_STDIN_WAKE) { XMEMSET(mqttCtx->rx_buf, 0, MAX_BUFFER_SIZE); if (XFGETS((char*)mqttCtx->rx_buf, MAX_BUFFER_SIZE - 1, stdin) != NULL) @@ -534,7 +547,7 @@ int sn_test(MQTTCtx *mqttCtx) mqttCtx->publishSN.total_len = (word16)rc; rc = SN_Client_Publish(&mqttCtx->client, &mqttCtx->publishSN); - PRINTF("MQTT-SN Publish: topic id = %d, rc = %d\r\nPayload = %s", + PRINTF("MQTT-SN Publish: topic id = %hu, rc = %d\r\nPayload = %s", (word16)*mqttCtx->publishSN.topic_name, mqttCtx->publishSN.return_code, mqttCtx->publishSN.buffer); @@ -543,8 +556,9 @@ int sn_test(MQTTCtx *mqttCtx) } } } + else #endif - else if (rc == MQTT_CODE_ERROR_TIMEOUT) { + if (rc == MQTT_CODE_ERROR_TIMEOUT) { /* Keep Alive */ PRINTF("Keep-alive timeout, sending ping"); @@ -730,6 +744,6 @@ int main(int argc, char** argv) #endif - return (rc == 0) ? 0 : EXIT_FAILURE; + return (rc == MQTT_CODE_SUCCESS) ? 0 : EXIT_FAILURE; } diff --git a/examples/sn-client/sn-client_qos-1.c b/examples/sn-client/sn-client_qos-1.c index cee87110a..ed57b09b2 100644 --- a/examples/sn-client/sn-client_qos-1.c +++ b/examples/sn-client/sn-client_qos-1.c @@ -40,7 +40,9 @@ static int mStopRead = 0; /* Configuration */ /* Maximum size for network read/write callbacks. */ +#ifndef MAX_BUFFER_SIZE #define MAX_BUFFER_SIZE 1024 +#endif #define TEST_MESSAGE "QoS-1 test message" char SHORT_TOPIC_NAME[] = {1}; diff --git a/examples/sn-client/sn-multithread.c b/examples/sn-client/sn-multithread.c index 4e7d934ee..7ca310201 100644 --- a/examples/sn-client/sn-multithread.c +++ b/examples/sn-client/sn-multithread.c @@ -36,7 +36,9 @@ /* Configuration */ /* Maximum size for network read/write callbacks. */ +#ifndef MAX_BUFFER_SIZE #define MAX_BUFFER_SIZE 1024 +#endif #define TEST_MESSAGE "test00" /* Number of publish tasks. Each will send a unique message to the broker. */ #define NUM_PUB_TASKS 10 diff --git a/examples/wiot/wiot.c b/examples/wiot/wiot.c index a68cc69b4..28a06aa41 100644 --- a/examples/wiot/wiot.c +++ b/examples/wiot/wiot.c @@ -38,9 +38,12 @@ /* Locals */ static int mStopRead = 0; +static int mTestDone = 0; /* Configuration */ +#ifndef MAX_BUFFER_SIZE #define MAX_BUFFER_SIZE 1024 /* Maximum size for network read/write callbacks */ +#endif /* Undefine if using an IBM WIOT Platform account that you created. */ #define WIOT_USE_QUICKSTART @@ -101,7 +104,7 @@ static int mqtt_message_cb(MqttClient *client, MqttMessage *msg, if (mqttCtx->test_mode) { if (XSTRLEN(TEST_MESSAGE) == msg->buffer_len && XSTRNCMP(TEST_MESSAGE, (char*)msg->buffer, msg->buffer_len) == 0) { - mStopRead = 1; + mTestDone = 1; } } } @@ -262,8 +265,9 @@ int wiot_test(MQTTCtx *mqttCtx) rc = MqttClient_Publish(&mqttCtx->client, &mqttCtx->publish); - PRINTF("MQTT Publish: Topic %s, %s (%d)", - mqttCtx->publish.topic_name, MqttClient_ReturnCodeToString(rc), rc); + PRINTF("MQTT Publish: Topic %s, ID %d, %s (%d)", + mqttCtx->publish.topic_name, mqttCtx->publish.packet_id, + MqttClient_ReturnCodeToString(rc), rc); if (rc != MQTT_CODE_SUCCESS) { goto disconn; } @@ -276,16 +280,28 @@ int wiot_test(MQTTCtx *mqttCtx) rc = MqttClient_WaitMessage(&mqttCtx->client, mqttCtx->cmd_timeout_ms); + #ifdef WOLFMQTT_NONBLOCK + /* Track elapsed time with no activity and trigger timeout */ + rc = mqtt_check_timeout(rc, &mqttCtx->start_sec, + mqttCtx->cmd_timeout_ms/1000); + #endif + + /* check return code */ + if (rc == MQTT_CODE_CONTINUE) { + return rc; + } + /* check for test mode */ - if (mStopRead) { + if (mStopRead || mTestDone) { rc = MQTT_CODE_SUCCESS; + mqttCtx->stat = WMQ_DISCONNECT; PRINTF("MQTT Exiting..."); break; } /* check return code */ #ifdef WOLFMQTT_ENABLE_STDIN_CAP - else if (rc == MQTT_CODE_STDIN_WAKE) { + if (rc == MQTT_CODE_STDIN_WAKE) { XMEMSET(mqttCtx->rx_buf, 0, MAX_BUFFER_SIZE); if (XFGETS((char*)mqttCtx->rx_buf, MAX_BUFFER_SIZE - 1, stdin) != NULL) { rc = (int)XSTRLEN((char*)mqttCtx->rx_buf); @@ -301,13 +317,14 @@ int wiot_test(MQTTCtx *mqttCtx) mqttCtx->publish.buffer = mqttCtx->rx_buf; mqttCtx->publish.total_len = (word16)rc; rc = MqttClient_Publish(&mqttCtx->client, &mqttCtx->publish); - PRINTF("MQTT Publish: Topic %s, %s (%d)", - mqttCtx->publish.topic_name, + PRINTF("MQTT Publish: Topic %s, ID %d, %s (%d)", + mqttCtx->publish.topic_name, mqttCtx->publish.packet_id, MqttClient_ReturnCodeToString(rc), rc); } } + else #endif - else if (rc == MQTT_CODE_ERROR_TIMEOUT) { + if (rc == MQTT_CODE_ERROR_TIMEOUT) { /* Keep Alive */ PRINTF("Keep-alive timeout, sending ping"); @@ -437,7 +454,7 @@ int main(int argc, char** argv) do { rc = wiot_test(&mqttCtx); - } while (rc == MQTT_CODE_CONTINUE); + } while (!mStopRead && rc == MQTT_CODE_CONTINUE); mqtt_free_ctx(&mqttCtx); diff --git a/examples/wiot/wiot.h b/examples/wiot/wiot.h old mode 100755 new mode 100644 diff --git a/scripts/awsiot.test b/scripts/awsiot.test index 9bff5f945..ec65c8122 100755 --- a/scripts/awsiot.test +++ b/scripts/awsiot.test @@ -5,18 +5,28 @@ # Check for application [ ! -x ./examples/aws/awsiot ] && echo -e "\n\nAWS IoT MQTT Client doesn't exist" && exit 1 -def_args="-T -C 2000" - -# Run with TLS and QoS 0-1 - -./examples/aws/awsiot $def_args -t -q 0 $1 -RESULT=$? -[ $RESULT -ne 0 ] && echo -e "\n\nAWS IoT MQTT Client failed! TLS=On, QoS=0" && exit 1 - -./examples/aws/awsiot $def_args -t -q 1 $1 -RESULT=$? -[ $RESULT -ne 0 ] && echo -e "\n\nAWS IoT MQTT Client failed! TLS=On, QoS=1" && exit 1 - -echo -e "\n\nAWS IoT MQTT Client Tests Passed" +# Check for TLS support +has_tls=no +./examples/aws/awsiot -? 2>&1 | grep -- 'Enable TLS' +if [ $? -eq 0 ]; then + has_tls=yes +fi + +if test -n "$WOLFMQTT_NO_EXTERNAL_BROKER_TESTS" && test $has_tls == yes; then + echo "WOLFMQTT_NO_EXTERNAL_BROKER_TESTS set or no TLS, won't run" +else + def_args="-T -C 2000" + + # Run with TLS and QoS 0-1 + ./examples/aws/awsiot $def_args -t -q 0 $1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "\n\nAWS IoT MQTT Client failed! TLS=On, QoS=0" && exit 1 + + ./examples/aws/awsiot $def_args -t -q 1 $1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "\n\nAWS IoT MQTT Client failed! TLS=On, QoS=1" && exit 1 + + echo -e "\n\nAWS IoT MQTT Client Tests Passed" +fi exit 0 diff --git a/scripts/azureiothub.test b/scripts/azureiothub.test index e180e45fe..2c8a37d3d 100755 --- a/scripts/azureiothub.test +++ b/scripts/azureiothub.test @@ -5,18 +5,30 @@ # Check for application [ ! -x ./examples/azure/azureiothub ] && echo -e "\n\nAzureIotHub MQTT Client doesn't exist" && exit 1 -def_args="-T -C 2000" - -# Run with TLS and QoS 0-1 - -./examples/azure/azureiothub $def_args -t -q 0 $1 -RESULT=$? -[ $RESULT -ne 0 ] && echo -e "\n\nAzureIotHub MQTT Client failed! TLS=On, QoS=0" && exit 1 - -./examples/azure/azureiothub $def_args -t -q 1 $1 -RESULT=$? -[ $RESULT -ne 0 ] && echo -e "\n\nAzureIotHub MQTT Client failed! TLS=On, QoS=1" && exit 1 - -echo -e "\n\nAzureIotHub MQTT Client Tests Passed" +# Check for TLS support +has_tls=no +./examples/azure/azureiothub -? 2>&1 | grep -- 'Enable TLS' +if [ $? -eq 0 ]; then + has_tls=yes +fi + +if test -n "$WOLFMQTT_NO_EXTERNAL_BROKER_TESTS" && test $has_tls == yes; then + echo "WOLFMQTT_NO_EXTERNAL_BROKER_TESTS set or no TLS, won't run" +else + # Use short timeout here, since we can't get a publish response to complete test + # So use the timeout and ping response to complete test + def_args="-T -C 2000" + + # Run with TLS and QoS 0-1 + ./examples/azure/azureiothub $def_args -t -q 0 $1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "\n\nAzureIotHub MQTT Client failed! TLS=On, QoS=0" && exit 1 + + ./examples/azure/azureiothub $def_args -t -q 1 $1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "\n\nAzureIotHub MQTT Client failed! TLS=On, QoS=1" && exit 1 + + echo -e "\n\nAzureIotHub MQTT Client Tests Passed" +fi exit 0 diff --git a/scripts/broker_test/ca-cert.pem b/scripts/broker_test/ca-cert.pem index 47a3ba0a4..58688a0eb 100644 --- a/scripts/broker_test/ca-cert.pem +++ b/scripts/broker_test/ca-cert.pem @@ -1,16 +1,17 @@ Certificate: Data: Version: 3 (0x2) - Serial Number: 12309252214903945037 (0xaad33fac180a374d) - Signature Algorithm: sha256WithRSAEncryption - Issuer: C=US, ST=Montana, L=Bozeman, O=Sawtooth, OU=Consulting, CN=www.wolfssl.com/emailAddress=info@wolfssl.com + Serial Number: + 2c:80:ce:db:47:9d:07:66:92:3d:68:d7:ca:ac:90:4f:ca:69:41:4b + Signature Algorithm: sha256WithRSAEncryption + Issuer: C = US, ST = Montana, L = Bozeman, O = Sawtooth, OU = Consulting, CN = www.wolfssl.com, emailAddress = info@wolfssl.com Validity - Not Before: Feb 10 19:49:52 2021 GMT - Not After : Nov 7 19:49:52 2023 GMT - Subject: C=US, ST=Montana, L=Bozeman, O=Sawtooth, OU=Consulting, CN=www.wolfssl.com/emailAddress=info@wolfssl.com + Not Before: Dec 16 21:17:49 2022 GMT + Not After : Sep 11 21:17:49 2025 GMT + Subject: C = US, ST = Montana, L = Bozeman, O = Sawtooth, OU = Consulting, CN = www.wolfssl.com, emailAddress = info@wolfssl.com Subject Public Key Info: Public Key Algorithm: rsaEncryption - Public-Key: (2048 bit) + RSA Public-Key: (2048 bit) Modulus: 00:bf:0c:ca:2d:14:b2:1e:84:42:5b:cd:38:1f:4a: f2:4d:75:10:f1:b6:35:9f:df:ca:7d:03:98:d3:ac: @@ -37,7 +38,7 @@ Certificate: X509v3 Authority Key Identifier: keyid:27:8E:67:11:74:C3:26:1D:3F:ED:33:63:B3:A4:D8:1D:30:E5:E8:D5 DirName:/C=US/ST=Montana/L=Bozeman/O=Sawtooth/OU=Consulting/CN=www.wolfssl.com/emailAddress=info@wolfssl.com - serial:AA:D3:3F:AC:18:0A:37:4D + serial:2C:80:CE:DB:47:9D:07:66:92:3D:68:D7:CA:AC:90:4F:CA:69:41:4B X509v3 Basic Constraints: CA:TRUE @@ -46,47 +47,47 @@ Certificate: X509v3 Extended Key Usage: TLS Web Server Authentication, TLS Web Client Authentication Signature Algorithm: sha256WithRSAEncryption - 62:98:c8:58:cf:56:03:86:5b:1b:71:49:7d:05:03:5d:e0:08: - 86:ad:db:4a:de:ab:22:96:a8:c3:59:68:c1:37:90:40:df:bd: - 89:d0:bc:da:8e:ef:87:b2:c2:62:52:e1:1a:29:17:6a:96:99: - c8:4e:d8:32:fe:b8:d1:5c:3b:0a:c2:3c:5f:a1:1e:98:7f:ce: - 89:26:21:1f:64:9c:15:7a:9c:ef:fb:1d:85:6a:fa:98:ce:a8: - a9:ab:c3:a2:c0:eb:87:ed:bc:21:df:f3:07:5b:ae:fd:40:d4: - ae:20:d0:76:8a:31:0a:a2:62:7c:61:0d:ce:5d:9a:1e:e4:20: - 88:51:49:fb:77:a9:cd:4d:c6:bf:54:99:33:ef:4b:a0:73:70: - 6d:2e:d9:3d:08:f6:12:39:31:68:c6:61:5c:41:b5:1b:f4:38: - 7d:fc:be:73:66:2d:f7:ca:5b:2c:5b:31:aa:cf:f6:7f:30:e4: - 12:2c:8e:d6:38:51:e6:45:ee:d5:da:c3:83:d6:ed:5e:ec:d6: - b6:14:b3:93:59:e1:55:4a:7f:04:df:ce:65:d4:df:18:4f:dd: - b4:45:7f:a6:56:30:c4:05:44:98:9d:4f:26:6d:84:80:a0:5e: - ed:23:d1:48:87:0e:05:06:91:3b:b0:3c:bb:8c:8f:3c:7b:4c: - 4f:a1:ca:98 + ae:b0:a4:35:8e:8a:1b:a6:eb:b3:a2:57:cf:3a:1f:dc:6e:bc: + d2:d0:a6:4a:8f:88:0a:6e:74:d5:d1:7c:d1:44:b1:d4:3b:17: + 03:09:5a:46:ed:08:08:cf:f1:fd:20:07:67:c0:97:ec:35:f3: + 75:ca:20:61:98:3e:f5:4d:be:e6:9d:75:1e:e4:03:ad:8c:a6: + 1e:3d:ec:e4:1a:92:5b:f9:a3:ad:83:ca:4f:cd:aa:38:bb:6e: + ae:ad:fa:a7:46:f1:8b:73:ec:09:23:bc:f2:18:e5:b7:92:86: + 3e:a4:75:60:c7:3d:0f:3f:83:00:c3:06:08:9c:d1:54:d6:ba: + 6d:95:3d:34:a1:be:24:91:cc:20:03:11:5b:72:1c:d4:65:d0: + 11:88:75:26:04:26:ef:66:70:e6:3b:38:87:9c:53:71:1b:09: + 51:70:50:99:4c:31:0c:62:44:57:30:60:04:fc:12:2c:a3:24: + b4:f7:11:d5:0e:b5:21:0b:ed:86:11:67:4d:36:fa:57:a0:59: + 55:21:b3:6d:e4:77:5e:ec:7e:f0:09:13:8e:99:98:b2:e1:82: + b6:4b:3e:0f:41:a6:0c:cd:49:99:7e:e4:8a:cb:37:ed:53:cf: + 86:5d:a9:26:a8:e5:01:25:5a:b4:bc:25:35:f1:fa:5a:5c:ce: + d4:b8:9a:2c -----BEGIN CERTIFICATE----- -MIIE6TCCA9GgAwIBAgIJAKrTP6wYCjdNMA0GCSqGSIb3DQEBCwUAMIGUMQswCQYD +MIIE/zCCA+egAwIBAgIULIDO20edB2aSPWjXyqyQT8ppQUswDQYJKoZIhvcNAQEL +BQAwgZQxCzAJBgNVBAYTAlVTMRAwDgYDVQQIDAdNb250YW5hMRAwDgYDVQQHDAdC +b3plbWFuMREwDwYDVQQKDAhTYXd0b290aDETMBEGA1UECwwKQ29uc3VsdGluZzEY +MBYGA1UEAwwPd3d3LndvbGZzc2wuY29tMR8wHQYJKoZIhvcNAQkBFhBpbmZvQHdv +bGZzc2wuY29tMB4XDTIyMTIxNjIxMTc0OVoXDTI1MDkxMTIxMTc0OVowgZQxCzAJ +BgNVBAYTAlVTMRAwDgYDVQQIDAdNb250YW5hMRAwDgYDVQQHDAdCb3plbWFuMREw +DwYDVQQKDAhTYXd0b290aDETMBEGA1UECwwKQ29uc3VsdGluZzEYMBYGA1UEAwwP +d3d3LndvbGZzc2wuY29tMR8wHQYJKoZIhvcNAQkBFhBpbmZvQHdvbGZzc2wuY29t +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvwzKLRSyHoRCW804H0ry +TXUQ8bY1n9/KfQOY06zeA2buKvHYsH1uB1QLEJghTYDLEiDnzE/eRX3Jcncy6sqQ +u2lSEAMvqPOVxfGLYlYb72dvpBBBla0Km+OlwLDScHZQMFuo6AgsfO2nonqNOCkc +rMft8nyVsJWCfUlcOM13Je+9gHVTlDw9ymNbnxW10x0TLxnRPNt2Osy4fcnlwtfa +QG/YIdxzG0ItU5z+Gvx9q3o2P5jehHwFZ85qFDiHqfGMtWjLaH9xICv1oGP1Vi+j +JtK3b7FaF9c4mQj+k1hv/sMTSQgWC6dNZwBSMWcjTpjtUUUduQTZC+zYKLNLve02 +eQIDAQABo4IBRTCCAUEwHQYDVR0OBBYEFCeOZxF0wyYdP+0zY7Ok2B0w5ejVMIHU +BgNVHSMEgcwwgcmAFCeOZxF0wyYdP+0zY7Ok2B0w5ejVoYGapIGXMIGUMQswCQYD VQQGEwJVUzEQMA4GA1UECAwHTW9udGFuYTEQMA4GA1UEBwwHQm96ZW1hbjERMA8G A1UECgwIU2F3dG9vdGgxEzARBgNVBAsMCkNvbnN1bHRpbmcxGDAWBgNVBAMMD3d3 -dy53b2xmc3NsLmNvbTEfMB0GCSqGSIb3DQEJARYQaW5mb0B3b2xmc3NsLmNvbTAe -Fw0yMTAyMTAxOTQ5NTJaFw0yMzExMDcxOTQ5NTJaMIGUMQswCQYDVQQGEwJVUzEQ -MA4GA1UECAwHTW9udGFuYTEQMA4GA1UEBwwHQm96ZW1hbjERMA8GA1UECgwIU2F3 -dG9vdGgxEzARBgNVBAsMCkNvbnN1bHRpbmcxGDAWBgNVBAMMD3d3dy53b2xmc3Ns -LmNvbTEfMB0GCSqGSIb3DQEJARYQaW5mb0B3b2xmc3NsLmNvbTCCASIwDQYJKoZI -hvcNAQEBBQADggEPADCCAQoCggEBAL8Myi0Ush6EQlvNOB9K8k11EPG2NZ/fyn0D -mNOs3gNm7irx2LB9bgdUCxCYIU2AyxIg58xP3kV9yXJ3MurKkLtpUhADL6jzlcXx -i2JWG+9nb6QQQZWtCpvjpcCw0nB2UDBbqOgILHztp6J6jTgpHKzH7fJ8lbCVgn1J -XDjNdyXvvYB1U5Q8PcpjW58VtdMdEy8Z0TzbdjrMuH3J5cLX2kBv2CHccxtCLVOc -/hr8fat6Nj+Y3oR8BWfOahQ4h6nxjLVoy2h/cSAr9aBj9VYvoybSt2+xWhfXOJkI -/pNYb/7DE0kIFgunTWcAUjFnI06Y7VFFHbkE2Qvs2CizS73tNnkCAwEAAaOCATow -ggE2MB0GA1UdDgQWBBQnjmcRdMMmHT/tM2OzpNgdMOXo1TCByQYDVR0jBIHBMIG+ -gBQnjmcRdMMmHT/tM2OzpNgdMOXo1aGBmqSBlzCBlDELMAkGA1UEBhMCVVMxEDAO -BgNVBAgMB01vbnRhbmExEDAOBgNVBAcMB0JvemVtYW4xETAPBgNVBAoMCFNhd3Rv -b3RoMRMwEQYDVQQLDApDb25zdWx0aW5nMRgwFgYDVQQDDA93d3cud29sZnNzbC5j -b20xHzAdBgkqhkiG9w0BCQEWEGluZm9Ad29sZnNzbC5jb22CCQCq0z+sGAo3TTAM -BgNVHRMEBTADAQH/MBwGA1UdEQQVMBOCC2V4YW1wbGUuY29thwR/AAABMB0GA1Ud -JQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjANBgkqhkiG9w0BAQsFAAOCAQEAYpjI -WM9WA4ZbG3FJfQUDXeAIhq3bSt6rIpaow1lowTeQQN+9idC82o7vh7LCYlLhGikX -apaZyE7YMv640Vw7CsI8X6EemH/OiSYhH2ScFXqc7/sdhWr6mM6oqavDosDrh+28 -Id/zB1uu/UDUriDQdooxCqJifGENzl2aHuQgiFFJ+3epzU3Gv1SZM+9LoHNwbS7Z -PQj2EjkxaMZhXEG1G/Q4ffy+c2Yt98pbLFsxqs/2fzDkEiyO1jhR5kXu1drDg9bt -XuzWthSzk1nhVUp/BN/OZdTfGE/dtEV/plYwxAVEmJ1PJm2EgKBe7SPRSIcOBQaR -O7A8u4yPPHtMT6HKmA== +dy53b2xmc3NsLmNvbTEfMB0GCSqGSIb3DQEJARYQaW5mb0B3b2xmc3NsLmNvbYIU +LIDO20edB2aSPWjXyqyQT8ppQUswDAYDVR0TBAUwAwEB/zAcBgNVHREEFTATggtl +eGFtcGxlLmNvbYcEfwAAATAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIw +DQYJKoZIhvcNAQELBQADggEBAK6wpDWOihum67OiV886H9xuvNLQpkqPiApudNXR +fNFEsdQ7FwMJWkbtCAjP8f0gB2fAl+w183XKIGGYPvVNvuaddR7kA62Mph497OQa +klv5o62Dyk/Nqji7bq6t+qdG8Ytz7AkjvPIY5beShj6kdWDHPQ8/gwDDBgic0VTW +um2VPTShviSRzCADEVtyHNRl0BGIdSYEJu9mcOY7OIecU3EbCVFwUJlMMQxiRFcw +YAT8EiyjJLT3EdUOtSEL7YYRZ002+legWVUhs23kd17sfvAJE46ZmLLhgrZLPg9B +pgzNSZl+5IrLN+1Tz4ZdqSao5QElWrS8JTXx+lpcztS4miw= -----END CERTIFICATE----- diff --git a/scripts/broker_test/mosquitto.conf b/scripts/broker_test/mosquitto.conf index 23e3be18e..7e27dc735 100644 --- a/scripts/broker_test/mosquitto.conf +++ b/scripts/broker_test/mosquitto.conf @@ -7,7 +7,7 @@ # ================================================================= # Port to use for the default listener. -listener 1883 +listener 11883 allow_anonymous true # ----------------------------------------------------------------- @@ -19,7 +19,7 @@ allow_anonymous true # # See also the mosquitto-tls man page. -listener 8883 +listener 18883 allow_anonymous true # At least one of cafile or capath must be defined. They both diff --git a/scripts/broker_test/server-cert.pem b/scripts/broker_test/server-cert.pem index 54dd74e32..de754a263 100644 --- a/scripts/broker_test/server-cert.pem +++ b/scripts/broker_test/server-cert.pem @@ -2,15 +2,15 @@ Certificate: Data: Version: 3 (0x2) Serial Number: 1 (0x1) - Signature Algorithm: sha256WithRSAEncryption - Issuer: C=US, ST=Montana, L=Bozeman, O=Sawtooth, OU=Consulting, CN=www.wolfssl.com/emailAddress=info@wolfssl.com + Signature Algorithm: sha256WithRSAEncryption + Issuer: C = US, ST = Montana, L = Bozeman, O = Sawtooth, OU = Consulting, CN = www.wolfssl.com, emailAddress = info@wolfssl.com Validity - Not Before: Feb 10 19:49:53 2021 GMT - Not After : Nov 7 19:49:53 2023 GMT - Subject: C=US, ST=Montana, L=Bozeman, O=wolfSSL, OU=Support, CN=www.wolfssl.com/emailAddress=info@wolfssl.com + Not Before: Dec 16 21:17:49 2022 GMT + Not After : Sep 11 21:17:49 2025 GMT + Subject: C = US, ST = Montana, L = Bozeman, O = wolfSSL, OU = Support, CN = www.wolfssl.com, emailAddress = info@wolfssl.com Subject Public Key Info: Public Key Algorithm: rsaEncryption - Public-Key: (2048 bit) + RSA Public-Key: (2048 bit) Modulus: 00:c0:95:08:e1:57:41:f2:71:6d:b7:d2:45:41:27: 01:65:c6:45:ae:f2:bc:24:30:b8:95:ce:2f:4e:d6: @@ -37,7 +37,7 @@ Certificate: X509v3 Authority Key Identifier: keyid:27:8E:67:11:74:C3:26:1D:3F:ED:33:63:B3:A4:D8:1D:30:E5:E8:D5 DirName:/C=US/ST=Montana/L=Bozeman/O=Sawtooth/OU=Consulting/CN=www.wolfssl.com/emailAddress=info@wolfssl.com - serial:AA:D3:3F:AC:18:0A:37:4D + serial:2C:80:CE:DB:47:9D:07:66:92:3D:68:D7:CA:AC:90:4F:CA:69:41:4B X509v3 Basic Constraints: CA:TRUE @@ -46,27 +46,27 @@ Certificate: X509v3 Extended Key Usage: TLS Web Server Authentication, TLS Web Client Authentication Signature Algorithm: sha256WithRSAEncryption - 1b:0d:a6:44:93:0d:0e:0c:35:28:26:40:31:d2:eb:26:4c:47: - 5b:19:fb:ad:fe:3a:f5:30:3a:28:d7:aa:69:a4:15:e7:26:6e: - b7:33:56:ac:8f:34:3d:f3:21:2f:53:58:91:d0:3e:b4:39:48: - bf:93:11:74:36:d3:87:49:c3:34:0d:30:30:ab:f4:4c:27:19: - d5:c4:0c:ad:49:bd:91:f8:da:9e:c8:2d:2a:ac:e2:75:8e:aa: - 08:d9:bf:65:ff:a3:b1:4f:f0:60:6f:4d:95:c4:06:7f:af:66: - 6a:23:3b:3a:a4:61:b6:6c:ca:be:e1:b0:77:f3:ec:83:d5:8c: - 1d:85:7f:8d:74:c8:ec:1e:49:ec:57:4a:cc:fd:e2:3a:3e:54: - 50:ae:67:cd:17:b0:67:a5:53:7f:c3:0e:3e:a7:58:e8:df:d5: - 0c:f2:64:f3:ad:12:70:e3:b9:42:bc:08:60:76:d5:0c:a5:31: - 77:50:e0:c8:f3:3a:3d:45:cf:32:75:ef:10:dd:b5:ed:6e:d2: - 2d:57:82:95:38:bc:7d:54:c4:84:5e:fb:7e:83:f5:f1:2d:9c: - 98:ac:73:e3:a7:d2:02:30:d6:1f:06:1e:d0:dc:3a:ac:f4:c2: - c2:be:72:40:9a:ea:cf:35:21:3b:56:6d:e1:52:f2:80:d7:35: - 83:97:07:cc + b9:10:f0:be:fe:c8:67:5e:7d:0f:36:33:c7:17:2a:01:c4:bb: + 74:83:4c:bc:bb:e2:ba:92:82:3a:d9:2d:8c:0e:e3:75:1b:c0: + 14:aa:40:1e:a8:11:7d:94:9c:3d:74:7a:3b:16:7b:d8:9d:f0: + e8:7d:1d:fa:3b:14:42:20:e3:05:a3:fd:b1:0c:f1:2a:c4:00: + 50:8d:1e:97:93:6a:de:82:13:24:9e:2b:fa:08:85:e3:4f:40: + fd:63:c7:3d:e9:bd:6f:7c:03:98:85:fe:b4:51:5d:7f:8c:83: + b3:ad:4a:88:e9:f3:4c:33:84:77:d3:02:35:59:e3:4e:64:a1: + b7:bb:fb:f8:fb:14:2a:ae:36:bf:d9:82:e7:cb:98:48:16:c8: + 81:d6:a0:f1:74:14:e3:74:4a:72:4a:f1:6f:dd:be:86:1e:20: + f3:05:16:83:1f:aa:7c:59:35:97:24:b8:27:b7:56:9f:30:2e: + 90:e0:19:e0:21:ca:9d:3f:da:99:07:94:79:49:53:14:5c:a2: + 2c:56:5b:b2:55:68:5c:1f:91:58:9a:cd:53:b5:ea:63:5a:72: + 49:41:cc:76:9f:88:35:86:0d:60:5d:e5:91:bd:ac:6f:cf:d5: + 92:27:72:4a:21:f4:58:98:8e:3b:d2:29:e6:ee:fa:e6:b0:6c: + 8b:1e:e0:54 -----BEGIN CERTIFICATE----- -MIIE3TCCA8WgAwIBAgIBATANBgkqhkiG9w0BAQsFADCBlDELMAkGA1UEBhMCVVMx +MIIE6DCCA9CgAwIBAgIBATANBgkqhkiG9w0BAQsFADCBlDELMAkGA1UEBhMCVVMx EDAOBgNVBAgMB01vbnRhbmExEDAOBgNVBAcMB0JvemVtYW4xETAPBgNVBAoMCFNh d3Rvb3RoMRMwEQYDVQQLDApDb25zdWx0aW5nMRgwFgYDVQQDDA93d3cud29sZnNz -bC5jb20xHzAdBgkqhkiG9w0BCQEWEGluZm9Ad29sZnNzbC5jb20wHhcNMjEwMjEw -MTk0OTUzWhcNMjMxMTA3MTk0OTUzWjCBkDELMAkGA1UEBhMCVVMxEDAOBgNVBAgM +bC5jb20xHzAdBgkqhkiG9w0BCQEWEGluZm9Ad29sZnNzbC5jb20wHhcNMjIxMjE2 +MjExNzQ5WhcNMjUwOTExMjExNzQ5WjCBkDELMAkGA1UEBhMCVVMxEDAOBgNVBAgM B01vbnRhbmExEDAOBgNVBAcMB0JvemVtYW4xEDAOBgNVBAoMB3dvbGZTU0wxEDAO BgNVBAsMB1N1cHBvcnQxGDAWBgNVBAMMD3d3dy53b2xmc3NsLmNvbTEfMB0GCSqG SIb3DQEJARYQaW5mb0B3b2xmc3NsLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEP @@ -75,34 +75,35 @@ f/5cnFF194rKB+c1L4/hvXvAL3yrZKgX/Mpde7rgIeVyLm8uhtiVc9qsG1O5Xz/X GQ0lT+FjY1GLC2Q/rUO4pRxcNLOuAKBjxfZ/C1loeHOmjBipAm2vwxkBLrgQ48bM QLRpo0YzaYduxLsXpvPo3a1zvHsvIbX9ZlEMvVSz4W1fHLwjc9EJA4kU0hC5ZMMq 0KGWSrzh1Bpbx6DAwWN4D0Q3MDKWgDIjlaF3uhPSl3PiXSXJag3DOWCktLBpQkIJ -6dgIvDMgs1gip6rrxOHmYYPF0pbf2dBPrdcCAwEAAaOCATowggE2MB0GA1UdDgQW -BBSzETLJkpiE4sn40DtuA0LKHw6OPDCByQYDVR0jBIHBMIG+gBQnjmcRdMMmHT/t +6dgIvDMgs1gip6rrxOHmYYPF0pbf2dBPrdcCAwEAAaOCAUUwggFBMB0GA1UdDgQW +BBSzETLJkpiE4sn40DtuA0LKHw6OPDCB1AYDVR0jBIHMMIHJgBQnjmcRdMMmHT/t M2OzpNgdMOXo1aGBmqSBlzCBlDELMAkGA1UEBhMCVVMxEDAOBgNVBAgMB01vbnRh bmExEDAOBgNVBAcMB0JvemVtYW4xETAPBgNVBAoMCFNhd3Rvb3RoMRMwEQYDVQQL DApDb25zdWx0aW5nMRgwFgYDVQQDDA93d3cud29sZnNzbC5jb20xHzAdBgkqhkiG -9w0BCQEWEGluZm9Ad29sZnNzbC5jb22CCQCq0z+sGAo3TTAMBgNVHRMEBTADAQH/ -MBwGA1UdEQQVMBOCC2V4YW1wbGUuY29thwR/AAABMB0GA1UdJQQWMBQGCCsGAQUF -BwMBBggrBgEFBQcDAjANBgkqhkiG9w0BAQsFAAOCAQEAGw2mRJMNDgw1KCZAMdLr -JkxHWxn7rf469TA6KNeqaaQV5yZutzNWrI80PfMhL1NYkdA+tDlIv5MRdDbTh0nD -NA0wMKv0TCcZ1cQMrUm9kfjansgtKqzidY6qCNm/Zf+jsU/wYG9NlcQGf69maiM7 -OqRhtmzKvuGwd/Psg9WMHYV/jXTI7B5J7FdKzP3iOj5UUK5nzRewZ6VTf8MOPqdY -6N/VDPJk860ScOO5QrwIYHbVDKUxd1DgyPM6PUXPMnXvEN217W7SLVeClTi8fVTE -hF77foP18S2cmKxz46fSAjDWHwYe0Nw6rPTCwr5yQJrqzzUhO1Zt4VLygNc1g5cH -zA== +9w0BCQEWEGluZm9Ad29sZnNzbC5jb22CFCyAzttHnQdmkj1o18qskE/KaUFLMAwG +A1UdEwQFMAMBAf8wHAYDVR0RBBUwE4ILZXhhbXBsZS5jb22HBH8AAAEwHQYDVR0l +BBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMA0GCSqGSIb3DQEBCwUAA4IBAQC5EPC+ +/shnXn0PNjPHFyoBxLt0g0y8u+K6koI62S2MDuN1G8AUqkAeqBF9lJw9dHo7FnvY +nfDofR36OxRCIOMFo/2xDPEqxABQjR6Xk2reghMkniv6CIXjT0D9Y8c96b1vfAOY +hf60UV1/jIOzrUqI6fNMM4R30wI1WeNOZKG3u/v4+xQqrja/2YLny5hIFsiB1qDx +dBTjdEpySvFv3b6GHiDzBRaDH6p8WTWXJLgnt1afMC6Q4BngIcqdP9qZB5R5SVMU +XKIsVluyVWhcH5FYms1TtepjWnJJQcx2n4g1hg1gXeWRvaxvz9WSJ3JKIfRYmI47 +0inm7vrmsGyLHuBU -----END CERTIFICATE----- Certificate: Data: Version: 3 (0x2) - Serial Number: 12309252214903945037 (0xaad33fac180a374d) - Signature Algorithm: sha256WithRSAEncryption - Issuer: C=US, ST=Montana, L=Bozeman, O=Sawtooth, OU=Consulting, CN=www.wolfssl.com/emailAddress=info@wolfssl.com + Serial Number: + 2c:80:ce:db:47:9d:07:66:92:3d:68:d7:ca:ac:90:4f:ca:69:41:4b + Signature Algorithm: sha256WithRSAEncryption + Issuer: C = US, ST = Montana, L = Bozeman, O = Sawtooth, OU = Consulting, CN = www.wolfssl.com, emailAddress = info@wolfssl.com Validity - Not Before: Feb 10 19:49:52 2021 GMT - Not After : Nov 7 19:49:52 2023 GMT - Subject: C=US, ST=Montana, L=Bozeman, O=Sawtooth, OU=Consulting, CN=www.wolfssl.com/emailAddress=info@wolfssl.com + Not Before: Dec 16 21:17:49 2022 GMT + Not After : Sep 11 21:17:49 2025 GMT + Subject: C = US, ST = Montana, L = Bozeman, O = Sawtooth, OU = Consulting, CN = www.wolfssl.com, emailAddress = info@wolfssl.com Subject Public Key Info: Public Key Algorithm: rsaEncryption - Public-Key: (2048 bit) + RSA Public-Key: (2048 bit) Modulus: 00:bf:0c:ca:2d:14:b2:1e:84:42:5b:cd:38:1f:4a: f2:4d:75:10:f1:b6:35:9f:df:ca:7d:03:98:d3:ac: @@ -129,7 +130,7 @@ Certificate: X509v3 Authority Key Identifier: keyid:27:8E:67:11:74:C3:26:1D:3F:ED:33:63:B3:A4:D8:1D:30:E5:E8:D5 DirName:/C=US/ST=Montana/L=Bozeman/O=Sawtooth/OU=Consulting/CN=www.wolfssl.com/emailAddress=info@wolfssl.com - serial:AA:D3:3F:AC:18:0A:37:4D + serial:2C:80:CE:DB:47:9D:07:66:92:3D:68:D7:CA:AC:90:4F:CA:69:41:4B X509v3 Basic Constraints: CA:TRUE @@ -138,47 +139,47 @@ Certificate: X509v3 Extended Key Usage: TLS Web Server Authentication, TLS Web Client Authentication Signature Algorithm: sha256WithRSAEncryption - 62:98:c8:58:cf:56:03:86:5b:1b:71:49:7d:05:03:5d:e0:08: - 86:ad:db:4a:de:ab:22:96:a8:c3:59:68:c1:37:90:40:df:bd: - 89:d0:bc:da:8e:ef:87:b2:c2:62:52:e1:1a:29:17:6a:96:99: - c8:4e:d8:32:fe:b8:d1:5c:3b:0a:c2:3c:5f:a1:1e:98:7f:ce: - 89:26:21:1f:64:9c:15:7a:9c:ef:fb:1d:85:6a:fa:98:ce:a8: - a9:ab:c3:a2:c0:eb:87:ed:bc:21:df:f3:07:5b:ae:fd:40:d4: - ae:20:d0:76:8a:31:0a:a2:62:7c:61:0d:ce:5d:9a:1e:e4:20: - 88:51:49:fb:77:a9:cd:4d:c6:bf:54:99:33:ef:4b:a0:73:70: - 6d:2e:d9:3d:08:f6:12:39:31:68:c6:61:5c:41:b5:1b:f4:38: - 7d:fc:be:73:66:2d:f7:ca:5b:2c:5b:31:aa:cf:f6:7f:30:e4: - 12:2c:8e:d6:38:51:e6:45:ee:d5:da:c3:83:d6:ed:5e:ec:d6: - b6:14:b3:93:59:e1:55:4a:7f:04:df:ce:65:d4:df:18:4f:dd: - b4:45:7f:a6:56:30:c4:05:44:98:9d:4f:26:6d:84:80:a0:5e: - ed:23:d1:48:87:0e:05:06:91:3b:b0:3c:bb:8c:8f:3c:7b:4c: - 4f:a1:ca:98 + ae:b0:a4:35:8e:8a:1b:a6:eb:b3:a2:57:cf:3a:1f:dc:6e:bc: + d2:d0:a6:4a:8f:88:0a:6e:74:d5:d1:7c:d1:44:b1:d4:3b:17: + 03:09:5a:46:ed:08:08:cf:f1:fd:20:07:67:c0:97:ec:35:f3: + 75:ca:20:61:98:3e:f5:4d:be:e6:9d:75:1e:e4:03:ad:8c:a6: + 1e:3d:ec:e4:1a:92:5b:f9:a3:ad:83:ca:4f:cd:aa:38:bb:6e: + ae:ad:fa:a7:46:f1:8b:73:ec:09:23:bc:f2:18:e5:b7:92:86: + 3e:a4:75:60:c7:3d:0f:3f:83:00:c3:06:08:9c:d1:54:d6:ba: + 6d:95:3d:34:a1:be:24:91:cc:20:03:11:5b:72:1c:d4:65:d0: + 11:88:75:26:04:26:ef:66:70:e6:3b:38:87:9c:53:71:1b:09: + 51:70:50:99:4c:31:0c:62:44:57:30:60:04:fc:12:2c:a3:24: + b4:f7:11:d5:0e:b5:21:0b:ed:86:11:67:4d:36:fa:57:a0:59: + 55:21:b3:6d:e4:77:5e:ec:7e:f0:09:13:8e:99:98:b2:e1:82: + b6:4b:3e:0f:41:a6:0c:cd:49:99:7e:e4:8a:cb:37:ed:53:cf: + 86:5d:a9:26:a8:e5:01:25:5a:b4:bc:25:35:f1:fa:5a:5c:ce: + d4:b8:9a:2c -----BEGIN CERTIFICATE----- -MIIE6TCCA9GgAwIBAgIJAKrTP6wYCjdNMA0GCSqGSIb3DQEBCwUAMIGUMQswCQYD +MIIE/zCCA+egAwIBAgIULIDO20edB2aSPWjXyqyQT8ppQUswDQYJKoZIhvcNAQEL +BQAwgZQxCzAJBgNVBAYTAlVTMRAwDgYDVQQIDAdNb250YW5hMRAwDgYDVQQHDAdC +b3plbWFuMREwDwYDVQQKDAhTYXd0b290aDETMBEGA1UECwwKQ29uc3VsdGluZzEY +MBYGA1UEAwwPd3d3LndvbGZzc2wuY29tMR8wHQYJKoZIhvcNAQkBFhBpbmZvQHdv +bGZzc2wuY29tMB4XDTIyMTIxNjIxMTc0OVoXDTI1MDkxMTIxMTc0OVowgZQxCzAJ +BgNVBAYTAlVTMRAwDgYDVQQIDAdNb250YW5hMRAwDgYDVQQHDAdCb3plbWFuMREw +DwYDVQQKDAhTYXd0b290aDETMBEGA1UECwwKQ29uc3VsdGluZzEYMBYGA1UEAwwP +d3d3LndvbGZzc2wuY29tMR8wHQYJKoZIhvcNAQkBFhBpbmZvQHdvbGZzc2wuY29t +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvwzKLRSyHoRCW804H0ry +TXUQ8bY1n9/KfQOY06zeA2buKvHYsH1uB1QLEJghTYDLEiDnzE/eRX3Jcncy6sqQ +u2lSEAMvqPOVxfGLYlYb72dvpBBBla0Km+OlwLDScHZQMFuo6AgsfO2nonqNOCkc +rMft8nyVsJWCfUlcOM13Je+9gHVTlDw9ymNbnxW10x0TLxnRPNt2Osy4fcnlwtfa +QG/YIdxzG0ItU5z+Gvx9q3o2P5jehHwFZ85qFDiHqfGMtWjLaH9xICv1oGP1Vi+j +JtK3b7FaF9c4mQj+k1hv/sMTSQgWC6dNZwBSMWcjTpjtUUUduQTZC+zYKLNLve02 +eQIDAQABo4IBRTCCAUEwHQYDVR0OBBYEFCeOZxF0wyYdP+0zY7Ok2B0w5ejVMIHU +BgNVHSMEgcwwgcmAFCeOZxF0wyYdP+0zY7Ok2B0w5ejVoYGapIGXMIGUMQswCQYD VQQGEwJVUzEQMA4GA1UECAwHTW9udGFuYTEQMA4GA1UEBwwHQm96ZW1hbjERMA8G A1UECgwIU2F3dG9vdGgxEzARBgNVBAsMCkNvbnN1bHRpbmcxGDAWBgNVBAMMD3d3 -dy53b2xmc3NsLmNvbTEfMB0GCSqGSIb3DQEJARYQaW5mb0B3b2xmc3NsLmNvbTAe -Fw0yMTAyMTAxOTQ5NTJaFw0yMzExMDcxOTQ5NTJaMIGUMQswCQYDVQQGEwJVUzEQ -MA4GA1UECAwHTW9udGFuYTEQMA4GA1UEBwwHQm96ZW1hbjERMA8GA1UECgwIU2F3 -dG9vdGgxEzARBgNVBAsMCkNvbnN1bHRpbmcxGDAWBgNVBAMMD3d3dy53b2xmc3Ns -LmNvbTEfMB0GCSqGSIb3DQEJARYQaW5mb0B3b2xmc3NsLmNvbTCCASIwDQYJKoZI -hvcNAQEBBQADggEPADCCAQoCggEBAL8Myi0Ush6EQlvNOB9K8k11EPG2NZ/fyn0D -mNOs3gNm7irx2LB9bgdUCxCYIU2AyxIg58xP3kV9yXJ3MurKkLtpUhADL6jzlcXx -i2JWG+9nb6QQQZWtCpvjpcCw0nB2UDBbqOgILHztp6J6jTgpHKzH7fJ8lbCVgn1J -XDjNdyXvvYB1U5Q8PcpjW58VtdMdEy8Z0TzbdjrMuH3J5cLX2kBv2CHccxtCLVOc -/hr8fat6Nj+Y3oR8BWfOahQ4h6nxjLVoy2h/cSAr9aBj9VYvoybSt2+xWhfXOJkI -/pNYb/7DE0kIFgunTWcAUjFnI06Y7VFFHbkE2Qvs2CizS73tNnkCAwEAAaOCATow -ggE2MB0GA1UdDgQWBBQnjmcRdMMmHT/tM2OzpNgdMOXo1TCByQYDVR0jBIHBMIG+ -gBQnjmcRdMMmHT/tM2OzpNgdMOXo1aGBmqSBlzCBlDELMAkGA1UEBhMCVVMxEDAO -BgNVBAgMB01vbnRhbmExEDAOBgNVBAcMB0JvemVtYW4xETAPBgNVBAoMCFNhd3Rv -b3RoMRMwEQYDVQQLDApDb25zdWx0aW5nMRgwFgYDVQQDDA93d3cud29sZnNzbC5j -b20xHzAdBgkqhkiG9w0BCQEWEGluZm9Ad29sZnNzbC5jb22CCQCq0z+sGAo3TTAM -BgNVHRMEBTADAQH/MBwGA1UdEQQVMBOCC2V4YW1wbGUuY29thwR/AAABMB0GA1Ud -JQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjANBgkqhkiG9w0BAQsFAAOCAQEAYpjI -WM9WA4ZbG3FJfQUDXeAIhq3bSt6rIpaow1lowTeQQN+9idC82o7vh7LCYlLhGikX -apaZyE7YMv640Vw7CsI8X6EemH/OiSYhH2ScFXqc7/sdhWr6mM6oqavDosDrh+28 -Id/zB1uu/UDUriDQdooxCqJifGENzl2aHuQgiFFJ+3epzU3Gv1SZM+9LoHNwbS7Z -PQj2EjkxaMZhXEG1G/Q4ffy+c2Yt98pbLFsxqs/2fzDkEiyO1jhR5kXu1drDg9bt -XuzWthSzk1nhVUp/BN/OZdTfGE/dtEV/plYwxAVEmJ1PJm2EgKBe7SPRSIcOBQaR -O7A8u4yPPHtMT6HKmA== +dy53b2xmc3NsLmNvbTEfMB0GCSqGSIb3DQEJARYQaW5mb0B3b2xmc3NsLmNvbYIU +LIDO20edB2aSPWjXyqyQT8ppQUswDAYDVR0TBAUwAwEB/zAcBgNVHREEFTATggtl +eGFtcGxlLmNvbYcEfwAAATAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIw +DQYJKoZIhvcNAQELBQADggEBAK6wpDWOihum67OiV886H9xuvNLQpkqPiApudNXR +fNFEsdQ7FwMJWkbtCAjP8f0gB2fAl+w183XKIGGYPvVNvuaddR7kA62Mph497OQa +klv5o62Dyk/Nqji7bq6t+qdG8Ytz7AkjvPIY5beShj6kdWDHPQ8/gwDDBgic0VTW +um2VPTShviSRzCADEVtyHNRl0BGIdSYEJu9mcOY7OIecU3EbCVFwUJlMMQxiRFcw +YAT8EiyjJLT3EdUOtSEL7YYRZ002+legWVUhs23kd17sfvAJE46ZmLLhgrZLPg9B +pgzNSZl+5IrLN+1Tz4ZdqSao5QElWrS8JTXx+lpcztS4miw= -----END CERTIFICATE----- diff --git a/scripts/client.test b/scripts/client.test index fc241e1e2..086f19f69 100755 --- a/scripts/client.test +++ b/scripts/client.test @@ -8,76 +8,118 @@ broker_pid=$no_pid do_cleanup() { if [ $broker_pid != $no_pid ] then - kill -6 $broker_pid + kill -9 $broker_pid + echo "Killed broker PID $broker_pid" + broker_pid=$no_pid fi - + if [ $1 -ne 0 ] then exit 1 fi } +generate_port() { # function to produce a random port number + if [[ "$OSTYPE" == "linux"* ]]; then + port=$(($(od -An -N2 /dev/urandom) % (65535-49152) + 49152)) + elif [[ "$OSTYPE" == "darwin"* ]]; then + port=$(($(od -An -N2 /dev/random) % (65535-49152) + 49152)) + else + echo "Unknown OS TYPE" + exit 1 + fi + echo -e "Using port $port" +} + + # Check for application [ ! -x ./examples/mqttclient/mqttclient ] && echo -e "\n\nMQTT Client doesn't exist" && exit 1 +# Check for TLS support +has_tls=no +./examples/mqttclient/mqttclient -? 2>&1 | grep -- 'Enable TLS' +if [ $? -eq 0 ]; then + has_tls=yes +fi + def_args="-T -C 2000" # Check for mosquitto if command -v mosquitto then - # bwrap only if using a local mosquitto instance - if [ "${AM_BWRAPPED-}" != "yes" ]; then - bwrap_path="$(command -v bwrap)" - if [ -n "$bwrap_path" ]; then + bwrap_path="$(command -v bwrap)" + if [ -n "$bwrap_path" ]; then + # bwrap only if using a local mosquitto instance + if [ "${AM_BWRAPPED-}" != "yes" ]; then + echo "Using bwrap" export AM_BWRAPPED=yes exec "$bwrap_path" --unshare-net --dev-bind / / "$0" "$@" fi + unset AM_BWRAPPED + + broker_args="-c scripts/broker_test/mosquitto.conf" + port=11883 + else + # mosquitto broker custom port non-TLS only + has_tls=no + generate_port + broker_args="-p $port" fi - # Run mosquitto broker - mosquitto -c scripts/broker_test/mosquitto.conf & + mosquitto $broker_args & broker_pid=$! + echo "Broker PID is $broker_pid" + sleep 0.1 + def_args="${def_args} -h localhost" - mutual_auth_args="${mutual_auth_args} -c certs/client-cert.pem -K certs/client-key.pem" - ecc_mutual_auth_args="${mutual_auth_args} -c certs/client-ecc-cert.pem -K certs/ecc-client-key.pem" + tls_port_args="-p 18883" + port_args="-p ${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 -# Run with and without TLS and QoS 0-2 +echo -e "Base args: $def_args $port_args" -./examples/mqttclient/mqttclient $def_args -q 0 $1 +# Run without TLS and QoS 0-2 +./examples/mqttclient/mqttclient $def_args $port_args -q 0 $1 RESULT=$? [ $RESULT -ne 0 ] && echo -e "\n\nMQTT Client failed! TLS=Off, QoS=0" && do_cleanup "-1" -./examples/mqttclient/mqttclient $def_args -q 1 $1 +./examples/mqttclient/mqttclient $def_args $port_args -q 1 $1 RESULT=$? [ $RESULT -ne 0 ] && echo -e "\n\nMQTT Client failed! TLS=Off, QoS=1" && do_cleanup "-1" -./examples/mqttclient/mqttclient $def_args -q 2 $1 +./examples/mqttclient/mqttclient $def_args $port_args -q 2 $1 RESULT=$? [ $RESULT -ne 0 ] && echo -e "\n\nMQTT Client failed! TLS=Off, QoS=2" && do_cleanup "-1" -./examples/mqttclient/mqttclient $def_args -t -q 0 $1 -RESULT=$? -[ $RESULT -ne 0 ] && echo -e "\n\nMQTT Client failed! TLS=On, QoS=0" && do_cleanup "-1" - -./examples/mqttclient/mqttclient $def_args -t -q 1 $1 -RESULT=$? -[ $RESULT -ne 0 ] && echo -e "\n\nMQTT Client failed! TLS=On, QoS=1" && do_cleanup "-1" - -./examples/mqttclient/mqttclient $def_args -t -q 2 $1 -RESULT=$? -[ $RESULT -ne 0 ] && echo -e "\n\nMQTT Client failed! TLS=On, QoS=2" && do_cleanup "-1" - -./examples/mqttclient/mqttclient $def_args $mutual_auth_args -t -q 0 $1 -RESULT=$? -[ $RESULT -ne 0 ] && echo -e "\n\nMQTT Client failed! TLS=On, QoS=0, RSA mutual auth" && do_cleanup "-1" - -./examples/mqttclient/mqttclient $def_args $ecc_mutual_auth_args -t -q 0 $1 -RESULT=$? -[ $RESULT -ne 0 ] && echo -e "\n\nMQTT Client failed! TLS=On, QoS=0, ECC mutual auth" && do_cleanup "-1" +if test $has_tls == yes +then + # Run with TLS and QoS 0-2 + ./examples/mqttclient/mqttclient $def_args $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 $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 $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 $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 $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 # End broker do_cleanup "0" - + echo -e "\n\nMQTT Client Tests Passed" exit 0 diff --git a/scripts/firmware.test b/scripts/firmware.test index a8c3cfb1e..706729141 100755 --- a/scripts/firmware.test +++ b/scripts/firmware.test @@ -6,9 +6,14 @@ no_pid=-1 broker_pid=$no_pid do_cleanup() { + # Delete file + rm $fileout + if [ $broker_pid != $no_pid ] then - kill -6 $broker_pid + kill -9 $broker_pid + echo "Killed broker PID $broker_pid" + broker_pid=$no_pid fi if [ $1 -ne 0 ] @@ -17,52 +22,101 @@ do_cleanup() { fi } +generate_port() { # function to produce a random port number + if [[ "$OSTYPE" == "linux"* ]]; then + port=$(($(od -An -N2 /dev/urandom) % (65535-49152) + 49152)) + elif [[ "$OSTYPE" == "darwin"* ]]; then + port=$(($(od -An -N2 /dev/random) % (65535-49152) + 49152)) + else + echo "Unknown OS TYPE" + exit 1 + fi + echo -e "Using port $port" +} + + # Check for application [ ! -x ./examples/firmware/fwpush ] && echo -e "\n\nMQTT Example fwpush doesn't exist" && exit 1 [ ! -x ./examples/firmware/fwclient ] && echo -e "\n\nMQTT Example fwclient doesn't exist" && exit 1 -def_args="-t -T -C 2000" +# Check for TLS support +has_tls=no +./examples/mqttclient/mqttclient -? 2>&1 | grep -- 'Enable TLS' +if [ $? -eq 0 ]; then + has_tls=yes +fi + +def_args="-T -C 5000 -n wolfMQTT/example/firmware_$((RANDOM))" filein=./examples/publish.dat fileout=./examples/publish.dat.trs + # Check for mosquitto if command -v mosquitto then - # bwrap only if using a local mosquitto instance - if [ "${AM_BWRAPPED-}" != "yes" ]; then - bwrap_path="$(command -v bwrap)" - if [ -n "$bwrap_path" ]; then + bwrap_path="$(command -v bwrap)" + if [ -n "$bwrap_path" ]; then + # bwrap only if using a local mosquitto instance + if [ "${AM_BWRAPPED-}" != "yes" ]; then + echo "Using bwrap" export AM_BWRAPPED=yes exec "$bwrap_path" --unshare-net --dev-bind / / "$0" "$@" fi + unset AM_BWRAPPED + + broker_args="-c scripts/broker_test/mosquitto.conf" + if test $has_tls == yes + then + port=18883 + else + port=11883 + fi + else + # mosquitto broker custom port non-TLS only + has_tls=no + generate_port + broker_args="-p $port" fi - # Run mosquitto broker - mosquitto -c scripts/broker_test/mosquitto.conf & + mosquitto $broker_args & broker_pid=$! - def_args="${def_args} -h localhost" + echo "Broker PID is $broker_pid" + sleep 0.1 + + def_args="${def_args} -h localhost -p ${port}" +fi + +if test $has_tls == yes +then + def_args="${def_args} -A scripts/broker_test/ca-cert.pem -t" fi +echo -e "Base args: $def_args" + +# Start firmware client +./examples/firmware/fwclient $def_args -f $fileout $1 & +client_result=$? +[ $client_result -ne 0 ] && echo -e "\n\nMQTT Example fwclient failed!" && do_cleanup "-1" + +# give some time for the client to connect and wait (it starts a new session) +sleep 0.5 + # Start firmware push ./examples/firmware/fwpush $def_args -r -f $filein $1 server_result=$? [ $server_result -ne 0 ] && echo -e "\n\nMQTT Example fwpush failed!" && do_cleanup "-1" -# Start firmware client -./examples/firmware/fwclient $def_args -f $fileout $1 -client_result=$? -[ $client_result -ne 0 ] && echo -e "\n\nMQTT Example fwclient failed!" && do_cleanup "-1" +# give some time for the client complete +sleep 0.5 # Compare files +echo "Comparing files" md5sum -b $filein $fileout compare_result=$? -[ $client_result -ne 0 ] && echo -e "\n\nMQTT Example firmware compare failed!" && do_cleanup "-1" - -# Delete file -rm $fileout +[ $compare_result -ne 0 ] && echo -e "\n\nMQTT Example firmware compare failed!" && do_cleanup "-1" # End broker do_cleanup "0" - + echo -e "\n\nFirmware Example MQTT Client Tests Passed" exit 0 diff --git a/scripts/multithread.test b/scripts/multithread.test index 0cf80148d..e7d13b7eb 100755 --- a/scripts/multithread.test +++ b/scripts/multithread.test @@ -1,6 +1,6 @@ #!/bin/bash -# MQTT Client test +# MQTT Multithread Client test no_pid=-1 broker_pid=$no_pid @@ -8,7 +8,9 @@ broker_pid=$no_pid do_cleanup() { if [ $broker_pid != $no_pid ] then - kill -6 $broker_pid + kill -9 $broker_pid + echo "Killed broker PID $broker_pid" + broker_pid=$no_pid fi if [ $1 -ne 0 ] @@ -17,57 +19,96 @@ do_cleanup() { fi } +generate_port() { # function to produce a random port number + if [[ "$OSTYPE" == "linux"* ]]; then + port=$(($(od -An -N2 /dev/urandom) % (65535-49152) + 49152)) + elif [[ "$OSTYPE" == "darwin"* ]]; then + port=$(($(od -An -N2 /dev/random) % (65535-49152) + 49152)) + else + echo "Unknown OS TYPE" + exit 1 + fi + echo -e "Using port $port" +} + # Check for application [ ! -x ./examples/multithread/multithread ] && echo -e "\n\nMultithread Client doesn't exist" && exit 1 +# Check for TLS support +has_tls=no +./examples/multithread/multithread -? 2>&1 | grep -- 'Enable TLS' +if [ $? -eq 0 ]; then + has_tls=yes +fi + def_args="-T -C 2000" # Check for mosquitto if command -v mosquitto then - # bwrap only if using a local mosquitto instance - if [ "${AM_BWRAPPED-}" != "yes" ]; then - bwrap_path="$(command -v bwrap)" - if [ -n "$bwrap_path" ]; then + bwrap_path="$(command -v bwrap)" + if [ -n "$bwrap_path" ]; then + # bwrap only if using a local mosquitto instance + if [ "${AM_BWRAPPED-}" != "yes" ]; then + echo "Using bwrap" export AM_BWRAPPED=yes exec "$bwrap_path" --unshare-net --dev-bind / / "$0" "$@" fi + unset AM_BWRAPPED + + broker_args="-c scripts/broker_test/mosquitto.conf" + port=11883 + else + # mosquitto broker custom port non-TLS only + has_tls=no + generate_port + broker_args="-p $port" fi - # Run mosquitto broker - mosquitto -c scripts/broker_test/mosquitto.conf & + mosquitto $broker_args & broker_pid=$! + echo "Broker PID is $broker_pid" + sleep 0.1 + def_args="${def_args} -h localhost" + tls_port_args="-p 18883" + port_args="-p ${port}" + cacert_args="-A scripts/broker_test/ca-cert.pem" fi -# Run with and without TLS and QoS 0-2 +echo -e "Base args: $def_args $port_args" -./examples/multithread/multithread $def_args -q 0 $1 +# Run without TLS and QoS 0-2 +./examples/multithread/multithread $def_args $port_args -q 0 $1 RESULT=$? [ $RESULT -ne 0 ] && echo -e "\n\nMultithread Client failed! TLS=Off, QoS=0" && do_cleanup "-1" -./examples/multithread/multithread $def_args -q 1 $1 +./examples/multithread/multithread $def_args $port_args -q 1 $1 RESULT=$? [ $RESULT -ne 0 ] && echo -e "\n\nMultithread Client failed! TLS=Off, QoS=1" && do_cleanup "-1" -./examples/multithread/multithread $def_args -q 2 $1 +./examples/multithread/multithread $def_args $port_args -q 2 $1 RESULT=$? [ $RESULT -ne 0 ] && echo -e "\n\nMultithread Client failed! TLS=Off, QoS=2" && do_cleanup "-1" -./examples/multithread/multithread $def_args -t -q 0 $1 -RESULT=$? -[ $RESULT -ne 0 ] && echo -e "\n\nMultithread Client failed! TLS=On, QoS=0" && do_cleanup "-1" - -./examples/multithread/multithread $def_args -t -q 1 $1 -RESULT=$? -[ $RESULT -ne 0 ] && echo -e "\n\nMultithread Client failed! TLS=On, QoS=1" && do_cleanup "-1" - -./examples/multithread/multithread $def_args -t -q 2 $1 -RESULT=$? -[ $RESULT -ne 0 ] && echo -e "\n\nMultithread Client failed! TLS=On, QoS=2" && do_cleanup "-1" +if test $has_tls == yes +then + # Run with TLS and QoS 0-2 + ./examples/multithread/multithread $def_args $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 $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 $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 # End broker do_cleanup "0" - + echo -e "\n\nMultithread MQTT Client Tests Passed" exit 0 diff --git a/scripts/nbclient.test b/scripts/nbclient.test index 940dab8a9..91624a920 100755 --- a/scripts/nbclient.test +++ b/scripts/nbclient.test @@ -8,7 +8,9 @@ broker_pid=$no_pid do_cleanup() { if [ $broker_pid != $no_pid ] then - kill -6 $broker_pid + kill -9 $broker_pid + echo "Killed broker PID $broker_pid" + broker_pid=$no_pid fi if [ $1 -ne 0 ] @@ -17,9 +19,28 @@ do_cleanup() { fi } +generate_port() { # function to produce a random port number + if [[ "$OSTYPE" == "linux"* ]]; then + port=$(($(od -An -N2 /dev/urandom) % (65535-49152) + 49152)) + elif [[ "$OSTYPE" == "darwin"* ]]; then + port=$(($(od -An -N2 /dev/random) % (65535-49152) + 49152)) + else + echo "Unknown OS TYPE" + exit 1 + fi + echo -e "Using port $port" +} + # Check for application [ ! -x ./examples/nbclient/nbclient ] && echo -e "\n\nNon-blocking Client doesn't exist" && exit 1 +# Check for TLS support +has_tls=no +./examples/nbclient/nbclient -? 2>&1 | grep -- 'Enable TLS' +if [ $? -eq 0 ]; then + has_tls=yes +fi + # Use minimum of 2 seconds # The check timout will sometimes incorrectly trigger if 1 second is used def_args="-T -C 2000" @@ -27,49 +48,69 @@ def_args="-T -C 2000" # Check for mosquitto if command -v mosquitto then - # bwrap only if using a local mosquitto instance - if [ "${AM_BWRAPPED-}" != "yes" ]; then - bwrap_path="$(command -v bwrap)" - if [ -n "$bwrap_path" ]; then + bwrap_path="$(command -v bwrap)" + if [ -n "$bwrap_path" ]; then + # bwrap only if using a local mosquitto instance + if [ "${AM_BWRAPPED-}" != "yes" ]; then + echo "Using bwrap" export AM_BWRAPPED=yes exec "$bwrap_path" --unshare-net --dev-bind / / "$0" "$@" fi + unset AM_BWRAPPED + + broker_args="-c scripts/broker_test/mosquitto.conf" + port=11883 + else + # mosquitto broker custom port non-TLS only + has_tls=no + generate_port + broker_args="-p $port" fi - # Run mosquitto broker - mosquitto -c scripts/broker_test/mosquitto.conf & + mosquitto $broker_args & broker_pid=$! + echo "Broker PID is $broker_pid" + sleep 0.1 + def_args="${def_args} -h localhost" + tls_port_args="-p 18883" + port_args="-p ${port}" + cacert_args="-A scripts/broker_test/ca-cert.pem" fi -# Run with and without TLS and QoS 0-2 +echo -e "Base args: $def_args $port_args" -./examples/nbclient/nbclient $def_args -q 0 $1 +# Run without TLS and QoS 0-2 +./examples/nbclient/nbclient $def_args $port_args -q 0 $1 RESULT=$? [ $RESULT -ne 0 ] && echo -e "\n\nNon-blocking Client failed! TLS=Off, QoS=0" && do_cleanup "-1" -./examples/nbclient/nbclient $def_args -q 1 $1 +./examples/nbclient/nbclient $def_args $port_args -q 1 $1 RESULT=$? [ $RESULT -ne 0 ] && echo -e "\n\nNon-blocking Client failed! TLS=Off, QoS=1" && do_cleanup "-1" -./examples/nbclient/nbclient $def_args -q 2 $1 +./examples/nbclient/nbclient $def_args $port_args -q 2 $1 RESULT=$? [ $RESULT -ne 0 ] && echo -e "\n\nNon-blocking Client failed! TLS=Off, QoS=2" && do_cleanup "-1" -./examples/nbclient/nbclient $def_args -t -q 0 $1 -RESULT=$? -[ $RESULT -ne 0 ] && echo -e "\n\nNon-blocking Client failed! TLS=On, QoS=0" && do_cleanup "-1" - -./examples/nbclient/nbclient $def_args -t -q 1 $1 -RESULT=$? -[ $RESULT -ne 0 ] && echo -e "\n\nNon-blocking Client failed! TLS=On, QoS=1" && do_cleanup "-1" - -./examples/nbclient/nbclient $def_args -t -q 2 $1 -RESULT=$? -[ $RESULT -ne 0 ] && echo -e "\n\nNon-blocking Client failed! TLS=On, QoS=2" && do_cleanup "-1" +if test $has_tls == yes +then + # Run with TLS and QoS 0-2 + ./examples/nbclient/nbclient $def_args $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 $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 $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 # End broker do_cleanup "0" - + echo -e "\n\nNon-blocking MQTT Client Tests Passed" exit 0 diff --git a/scripts/wiot.test b/scripts/wiot.test index 9d1e9493b..0d14e930a 100755 --- a/scripts/wiot.test +++ b/scripts/wiot.test @@ -7,14 +7,25 @@ # Check for application [ ! -x ./examples/wiot/wiot ] && echo -e "\n\nWatson IoT MQTT Client doesn't exist" && exit 1 +# Check for TLS support +has_tls=no +./examples/azure/azureiothub -? 2>&1 | grep -- 'Enable TLS' +if [ $? -eq 0 ]; then + has_tls=yes +fi + def_args="-T -C 2000" -# Run +if test -n "$WOLFMQTT_NO_EXTERNAL_BROKER_TESTS" && test $has_tls == yes; then + echo "WOLFMQTT_NO_EXTERNAL_BROKER_TESTS set or no TLS, won't run" +else + # Run -./examples/wiot/wiot $def_args $1 -RESULT=$? -[ $RESULT -ne 0 ] && echo -e "\n\nWatson IoT MQTT Client failed! TLS=On, QoS=0" && exit 1 + ./examples/wiot/wiot $def_args $1 + RESULT=$? + [ $RESULT -ne 0 ] && echo -e "\n\nWatson IoT MQTT Client failed! TLS=On, QoS=0" && exit 1 -echo -e "\n\nWatson IoT MQTT Client Tests Passed" + echo -e "\n\nWatson IoT MQTT Client Tests Passed" +fi exit 0 diff --git a/src/include.am b/src/include.am index c3b7bab37..08f9cd162 100644 --- a/src/include.am +++ b/src/include.am @@ -8,6 +8,11 @@ src_libwolfmqtt_la_SOURCES = src/mqtt_client.c \ src/mqtt_packet.c \ src/mqtt_socket.c +if BUILD_SN +src_libwolfmqtt_la_SOURCES += src/mqtt_sn_client.c \ + src/mqtt_sn_packet.c +endif + src_libwolfmqtt_la_CFLAGS = -DBUILDING_WOLFMQTT $(AM_CFLAGS) src_libwolfmqtt_la_CPPFLAGS = -DBUILDING_WOLFMQTT $(AM_CPPFLAGS) src_libwolfmqtt_la_LDFLAGS = ${AM_LDFLAGS} -no-undefined -version-info ${WOLFMQTT_LIBRARY_VERSION} diff --git a/src/mqtt_client.c b/src/mqtt_client.c index b2ab4747e..57ee94628 100644 --- a/src/mqtt_client.c +++ b/src/mqtt_client.c @@ -26,11 +26,41 @@ #include "wolfmqtt/mqtt_client.h" +/* DOCUMENTED BUILD OPTIONS: + * + * WOLFMQTT_MULTITHREAD: Enables multi-thread support with mutex protection on + * client struct, write and read. When a pending response is needed its added + * to a linked list and if another thread reads the expected response it is + * flagged, so the other thread knows it completed. + * + * WOLFMQTT_NONBLOCK: Enabled transport support for returning WANT READ/WRITE, + * which becomes WOLFMQTT_CODE_CONTINUE. This prevents blocking if the + * transport (socket) has no data. + * + * WOLFMQTT_V5: Enables MQTT v5.0 support + * + * WOLFMQTT_ALLOW_NODATA_UNLOCK: Used with multi-threading and non-blocking to + * allow unlock if no data was sent/received. Note the TLS stack typically + * requires an attempt to write to continue with same write, not different. + * By default if we attempt a write we keep the mutex locked and return + * MQTT_CODE_CONTINUE + * + * WOLFMQTT_USER_THREADING: Allows custom mutex functions to be defined by the + * user. Example: wm_SemInit + * + * WOLFMQTT_DEBUG_CLIENT: Enables verbose PRINTF for the client code. + */ + + /* Private functions */ /* forward declarations */ static int MqttClient_Publish_ReadPayload(MqttClient* client, MqttPublish* publish, int timeout_ms); +#if !defined(WOLFMQTT_MULTITHREAD) && !defined(WOLFMQTT_NONBLOCK) +static int MqttClient_CancelMessage(MqttClient *client, MqttObject* msg); +#endif + #ifdef WOLFMQTT_MULTITHREAD @@ -46,10 +76,10 @@ static int MqttClient_Publish_ReadPayload(MqttClient* client, #elif defined(__MACH__) /* Apple style dispatch semaphore */ - int wm_SemInit(wm_Sem *s){ + int wm_SemInit(wm_Sem *s) { /* dispatch_release() fails hard, with Trace/BPT trap signal, if the * sem's internal count is less than the value passed in with - * dispatch_semaphore_create(). work around this by initing + * dispatch_semaphore_create(). work around this by initializing * with 0, then incrementing it afterwards. */ s->sem = dispatch_semaphore_create(0); @@ -62,7 +92,7 @@ static int MqttClient_Publish_ReadPayload(MqttClient* client, return 0; } - int wm_SemFree(wm_Sem *s){ + int wm_SemFree(wm_Sem *s) { if ((s == NULL) || (s->sem == NULL)) return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); @@ -81,29 +111,39 @@ static int MqttClient_Publish_ReadPayload(MqttClient* client, } #elif defined(WOLFMQTT_POSIX_SEMAPHORES) /* Posix style semaphore */ - int wm_SemInit(wm_Sem *s){ + int wm_SemInit(wm_Sem *s) { + #ifndef WOLFMQTT_NO_COND_SIGNAL s->lockCount = 0; - pthread_mutex_init(&s->mutex, NULL); pthread_cond_init(&s->cond, NULL); + #endif + pthread_mutex_init(&s->mutex, NULL); return 0; } - int wm_SemFree(wm_Sem *s){ + int wm_SemFree(wm_Sem *s) { pthread_mutex_destroy(&s->mutex); + #ifndef WOLFMQTT_NO_COND_SIGNAL pthread_cond_destroy(&s->cond); + #endif return 0; } - int wm_SemLock(wm_Sem *s){ + int wm_SemLock(wm_Sem *s) { pthread_mutex_lock(&s->mutex); + #ifndef WOLFMQTT_NO_COND_SIGNAL while (s->lockCount > 0) pthread_cond_wait(&s->cond, &s->mutex); s->lockCount++; pthread_mutex_unlock(&s->mutex); + #endif return 0; } - int wm_SemUnlock(wm_Sem *s){ + int wm_SemUnlock(wm_Sem *s) { + #ifndef WOLFMQTT_NO_COND_SIGNAL pthread_mutex_lock(&s->mutex); - s->lockCount--; - pthread_cond_signal(&s->cond); + if (s->lockCount > 0) { + s->lockCount--; + pthread_cond_signal(&s->cond); + } + #endif pthread_mutex_unlock(&s->mutex); return 0; } @@ -147,10 +187,175 @@ static int MqttClient_Publish_ReadPayload(MqttClient* client, return 0; } +#endif /* MUTEX */ +#endif /* WOLFMQTT_MULTITHREAD */ + +static int MqttWriteStart(MqttClient* client, MqttMsgStat* stat) +{ + int rc = MQTT_CODE_SUCCESS; + +#if defined(WOLFMQTT_DEBUG_CLIENT) || !defined(WOLFMQTT_ALLOW_NODATA_UNLOCK) + #ifdef WOLFMQTT_DEBUG_CLIENT + if (stat->isWriteActive) { + MQTT_TRACE_MSG("Warning, send already locked!"); + rc = MQTT_CODE_ERROR_SYSTEM; + } + #endif + #ifndef WOLFMQTT_ALLOW_NODATA_UNLOCK + /* detect if a write is already in progress */ + #ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) + #endif + { + if (client->write.isActive) { + MQTT_TRACE_MSG("Partial write in progress!"); + rc = MQTT_CODE_CONTINUE; /* can't write yet */ + } + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockClient); + #endif + } + #endif /* WOLFMQTT_ALLOW_NODATA_UNLOCK */ + if (rc != MQTT_CODE_SUCCESS) { + return rc; + } +#endif + +#ifdef WOLFMQTT_MULTITHREAD + rc = wm_SemLock(&client->lockSend); +#endif + if (rc == MQTT_CODE_SUCCESS) { + stat->isWriteActive = 1; + + #ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) + #endif + { + client->write.isActive = 1; + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockClient); + #endif + } + + MQTT_TRACE_MSG("lockSend"); + } + + return rc; +} +static void MqttWriteStop(MqttClient* client, MqttMsgStat* stat) +{ +#ifdef WOLFMQTT_DEBUG_CLIENT + if (!stat->isWriteActive) { + MQTT_TRACE_MSG("Warning, send not locked!"); + return; + } +#endif + +#ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) +#endif + { + /* reset write */ + XMEMSET(&client->write, 0, sizeof(client->write)); + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockClient); + #endif + } + + if (stat->isWriteActive) { + MQTT_TRACE_MSG("unlockSend"); + stat->isWriteActive = 0; + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + } +} + +static int MqttReadStart(MqttClient* client, MqttMsgStat* stat) +{ + int rc = MQTT_CODE_SUCCESS; + +#ifdef WOLFMQTT_DEBUG_CLIENT + if (stat->isReadActive) { + MQTT_TRACE_MSG("Warning, recv already locked!"); + rc = MQTT_CODE_ERROR_SYSTEM; + } + /* detect if a read is already in progress */ + #ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) + #endif + { + if (client->read.isActive) { + MQTT_TRACE_MSG("Partial read in progress!"); + rc = MQTT_CODE_CONTINUE; /* can't read yet */ + } + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockClient); + #endif + } + if (rc != 0) + return rc; +#endif /* WOLFMQTT_DEBUG_CLIENT */ + +#ifdef WOLFMQTT_MULTITHREAD + rc = wm_SemLock(&client->lockRecv); +#endif + if (rc == MQTT_CODE_SUCCESS) { + stat->isReadActive = 1; + + #ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) + #endif + { + /* mark read active */ + client->read.isActive = 1; + + /* reset the packet state used by MqttPacket_Read */ + client->packet.stat = MQTT_PK_BEGIN; + + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockClient); + #endif + } + + MQTT_TRACE_MSG("lockRecv"); + } + + return rc; +} +static void MqttReadStop(MqttClient* client, MqttMsgStat* stat) +{ +#ifdef WOLFMQTT_DEBUG_CLIENT + if (!stat->isReadActive) { + MQTT_TRACE_MSG("Warning, recv not locked!"); + return; + } +#endif + +#ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) #endif + { + /* reset read */ + XMEMSET(&client->read, 0, sizeof(client->read)); + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockClient); + #endif + } + + if (stat->isReadActive) { + MQTT_TRACE_MSG("unlockRecv"); + stat->isReadActive = 0; + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockRecv); + #endif + } +} + +#ifdef WOLFMQTT_MULTITHREAD /* These RespList functions assume caller has locked client->lockClient mutex */ -static int MqttClient_RespList_Add(MqttClient *client, +int MqttClient_RespList_Add(MqttClient *client, MqttPacketType packet_type, word16 packet_id, MqttPendResp *newResp, void *packet_obj) { @@ -195,10 +400,10 @@ static int MqttClient_RespList_Add(MqttClient *client, client->lastPendResp->next = newResp; client->lastPendResp = newResp; } - return 0; + return MQTT_CODE_SUCCESS; } -static void MqttClient_RespList_Remove(MqttClient *client, MqttPendResp *rmResp) +void MqttClient_RespList_Remove(MqttClient *client, MqttPendResp *rmResp) { MqttPendResp *tmpResp; @@ -242,7 +447,8 @@ static void MqttClient_RespList_Remove(MqttClient *client, MqttPendResp *rmResp) #endif } -static int MqttClient_RespList_Find(MqttClient *client, +/* return codes: 0=not found, 1=found */ +int MqttClient_RespList_Find(MqttClient *client, MqttPacketType packet_type, word16 packet_id, MqttPendResp **retResp) { int rc = 0; @@ -575,7 +781,8 @@ static int MqttClient_DecodePacket(MqttClient* client, byte* rx_buf, #ifdef WOLFMQTT_DISCONNECT_CB /* Call disconnect callback with reason code */ if ((packet_obj != NULL) && client->disconnect_cb) { - client->disconnect_cb(client, p_disc->reason_code, client->disconnect_ctx); + client->disconnect_cb(client, p_disc->reason_code, + client->disconnect_ctx); } #endif #else @@ -944,24 +1151,14 @@ static int MqttClient_WaitType(MqttClient *client, void *packet_obj, #ifdef WOLFMQTT_MULTITHREAD /* Check to see if packet type and id have already completed */ rc = MqttClient_CheckPendResp(client, wait_type, wait_packet_id); - if (rc != MQTT_CODE_ERROR_NOT_FOUND - && rc != MQTT_CODE_CONTINUE - ) { + if (rc != MQTT_CODE_ERROR_NOT_FOUND && rc != MQTT_CODE_CONTINUE) { return rc; } + #endif - /* Lock recv socket mutex */ - rc = wm_SemLock(&client->lockRecv); - if (rc != 0) { - PRINTF("MqttClient_WaitType: recv lock error!"); + if ((rc = MqttReadStart(client, mms_stat)) != 0) { return rc; } - mms_stat->isReadLocked = 1; - MQTT_TRACE_MSG("lockRecv"); - #endif - - /* reset the packet state used by MqttPacket_Read */ - client->packet.stat = MQTT_PK_BEGIN; mms_stat->read = MQTT_MSG_WAIT; } @@ -977,7 +1174,9 @@ static int MqttClient_WaitType(MqttClient *client, void *packet_obj, if (rc <= 0) { #ifdef WOLFMQTT_NONBLOCK if (rc == MQTT_CODE_CONTINUE && - client->packet.stat > MQTT_PK_BEGIN) { + (client->packet.stat > MQTT_PK_BEGIN || + client->read.total > 0) + ) { /* advance state, since we received some data */ mms_stat->read = MQTT_MSG_HEADER; } @@ -1027,7 +1226,8 @@ static int MqttClient_WaitType(MqttClient *client, void *packet_obj, #ifdef WOLFMQTT_DEBUG_CLIENT PRINTF("Using INCOMING packet_obj %p", use_packet_obj); #endif - if (packet_type == wait_type || wait_type == MQTT_PACKET_TYPE_ANY) { + if (packet_type == wait_type || + wait_type == MQTT_PACKET_TYPE_ANY) { /* Only stop waiting when matched or waiting for "any" */ waitMatchFound = 1; } @@ -1113,6 +1313,10 @@ static int MqttClient_WaitType(MqttClient *client, void *packet_obj, if (rc >= 0) { rc = MQTT_CODE_SUCCESS; } + else { + /* error, break */ + break; + } #ifdef WOLFMQTT_MULTITHREAD if (pendResp) { @@ -1129,48 +1333,21 @@ static int MqttClient_WaitType(MqttClient *client, void *packet_obj, } #endif /* WOLFMQTT_MULTITHREAD */ - /* are we sending ACK or done with message? */ + /* Determine if we are sending ACK or done */ if (MqttIsPubRespPacket(resp.packet_type)) { + /* if we get here, then we are sending an ACK */ mms_stat->read = MQTT_MSG_ACK; - } - else { - mms_stat->read = MQTT_MSG_BEGIN; - } - - #ifdef WOLFMQTT_MULTITHREAD - /* release read lock, done reading */ - if (mms_stat->isReadLocked) { - mms_stat->isReadLocked = 0; - MQTT_TRACE_MSG("unlockRecv"); - wm_SemUnlock(&client->lockRecv); - } - #endif - - /* if error, leave */ - if (rc != MQTT_CODE_SUCCESS) { - break; - } + mms_stat->ack = MQTT_MSG_WAIT; - /* if not sending an ACK, we are done */ - if (!MqttIsPubRespPacket(resp.packet_type)) { - break; + /* setup ACK in shared context */ + XMEMCPY(&client->packetAck, &resp, sizeof(MqttPublishResp)); + #ifdef WOLFMQTT_V5 + client->packetAck.protocol_level = client->protocol_level; + #endif } - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) break; - mms_stat->isWriteLocked = 1; - MQTT_TRACE_MSG("lockSend"); - #endif - - /* setup ACK in shared context */ - XMEMCPY(&client->packetAck, &resp, sizeof(MqttPublishResp)); - #ifdef WOLFMQTT_V5 - client->packetAck.protocol_level = client->protocol_level; - #endif - - mms_stat->write = MQTT_MSG_ACK; + /* done reading */ + MqttReadStop(client, mms_stat); break; } @@ -1190,24 +1367,24 @@ static int MqttClient_WaitType(MqttClient *client, void *packet_obj, } } /* switch (mms_stat->read) */ - switch (mms_stat->write) + switch (mms_stat->ack) { case MQTT_MSG_BEGIN: - case MQTT_MSG_WAIT: /* wait for read to set ack */ break; - case MQTT_MSG_ACK: + case MQTT_MSG_WAIT: { - #ifdef WOLFMQTT_MULTITHREAD - if (!mms_stat->isWriteLocked) { - rc = wm_SemLock(&client->lockSend); - if (rc != 0) break; - mms_stat->isWriteLocked = 1; - MQTT_TRACE_MSG("lockSend"); + /* Flag write active / lock mutex */ + if ((rc = MqttWriteStart(client, mms_stat)) != 0) { + break; } - #endif + mms_stat->ack = MQTT_MSG_ACK; + } + FALL_THROUGH; + case MQTT_MSG_ACK: + { /* send ack */ rc = MqttEncode_PublishResp(client->tx_buf, client->tx_buf_len, client->packetAck.packet_type, &client->packetAck); @@ -1216,52 +1393,64 @@ static int MqttClient_WaitType(MqttClient *client, void *packet_obj, rc, MqttPacket_TypeDesc(client->packetAck.packet_type), client->packetAck.packet_type, client->packetAck.packet_id); #endif - if (rc > 0) { - client->write.len = rc; - - /* Send publish response packet */ - rc = MqttPacket_Write(client, client->tx_buf, - client->write.len); - #ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE) - break; - #endif - if (rc == client->write.len) { - rc = 0; /* success */ - } + if (rc < 0) { + MqttWriteStop(client, mms_stat); + break; } - mms_stat->write = MQTT_MSG_BEGIN; - #ifdef WOLFMQTT_MULTITHREAD - MQTT_TRACE_MSG("unlockSend"); - mms_stat->isWriteLocked = 0; - wm_SemUnlock(&client->lockSend); + client->write.len = rc; + /* Note: static analyzer complains about set, but not used here. + * Keeping it to ensure no future issues with rc > 0 */ + rc = MQTT_CODE_SUCCESS; + (void)rc; /* inhibit clang-analyzer-deadcode.DeadStores */ + + mms_stat->ack = MQTT_MSG_HEADER; + } + FALL_THROUGH; + + case MQTT_MSG_HEADER: + { + int xfer = client->write.len; + + /* Send publish response packet */ + rc = MqttPacket_Write(client, client->tx_buf, xfer); + #ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE) { + /* keep send mutex locked and return to caller */ + /* must keep send locked */ + return rc; + } #endif + MqttWriteStop(client, mms_stat); + if (rc == xfer) { + rc = MQTT_CODE_SUCCESS; /* success */ + } + + mms_stat->ack = MQTT_MSG_BEGIN; /* reset write state */ break; } case MQTT_MSG_AUTH: - case MQTT_MSG_HEADER: case MQTT_MSG_PAYLOAD: case MQTT_MSG_PAYLOAD2: default: #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("MqttClient_WaitType: Invalid write state %d!", - mms_stat->write); + PRINTF("MqttClient_WaitType: Invalid ack state %d!", + mms_stat->ack); #endif rc = MQTT_TRACE_ERROR(MQTT_CODE_ERROR_STAT); break; - } /* switch (mms_stat->write) */ + } /* switch (mms_stat->ack) */ #ifdef WOLFMQTT_DEBUG_CLIENT if (rc != MQTT_CODE_CONTINUE) { - PRINTF("MqttClient_WaitType: rc %d, state %d-%d", - rc, mms_stat->read, mms_stat->write); + PRINTF("MqttClient_WaitType: rc %d, state %d-%d-%d", + rc, mms_stat->read, mms_stat->write, mms_stat->ack); } #endif /* no data read or ack done, then reset state */ - if (mms_stat->read == MQTT_MSG_WAIT || mms_stat->read == MQTT_MSG_ACK) { + if (mms_stat->read == MQTT_MSG_WAIT) { mms_stat->read = MQTT_MSG_BEGIN; } @@ -1272,18 +1461,20 @@ static int MqttClient_WaitType(MqttClient *client, void *packet_obj, } #endif -#ifdef WOLFMQTT_MULTITHREAD - if (mms_stat->isReadLocked) { - mms_stat->isReadLocked = 0; - MQTT_TRACE_MSG("unlockRecv"); - wm_SemUnlock(&client->lockRecv); - } -#endif + MqttReadStop(client, mms_stat); #ifdef WOLFMQTT_NONBLOCK #ifdef WOLFMQTT_DEBUG_CLIENT - client->lastRc = rc; + #ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) #endif + { + client->lastRc = rc; + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockClient); + #endif + } + #endif /* WOLFMQTT_DEBUG_CLIENT */ if (rc == MQTT_CODE_CONTINUE) { return rc; } @@ -1302,7 +1493,6 @@ static int MqttClient_WaitType(MqttClient *client, void *packet_obj, if (!waitMatchFound) { /* if we get here, then the we are still waiting for a packet */ mms_stat->read = MQTT_MSG_BEGIN; - MQTT_TRACE_MSG("Wait Again"); #ifdef WOLFMQTT_NONBLOCK /* for non-blocking return with code continue instead of waiting again * if called with packet type and id of 'any' */ @@ -1310,6 +1500,7 @@ static int MqttClient_WaitType(MqttClient *client, void *packet_obj, return MQTT_CODE_CONTINUE; } #endif + MQTT_TRACE_MSG("Wait Again"); goto wait_again; } @@ -1427,13 +1618,10 @@ int MqttClient_Connect(MqttClient *client, MqttConnect *mc_connect) } if (mc_connect->stat.write == MQTT_MSG_BEGIN) { - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { + /* Flag write active / lock mutex */ + if ((rc = MqttWriteStart(client, &mc_connect->stat)) != 0) { return rc; } - #endif #ifdef WOLFMQTT_V5 /* Use specified protocol version if set */ @@ -1448,9 +1636,7 @@ int MqttClient_Connect(MqttClient *client, MqttConnect *mc_connect) MQTT_PACKET_TYPE_CONNECT, 0, 0); #endif if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif + MqttWriteStop(client, &mc_connect->stat); return rc; } client->write.len = rc; @@ -1464,26 +1650,34 @@ int MqttClient_Connect(MqttClient *client, MqttConnect *mc_connect) wm_SemUnlock(&client->lockClient); } if (rc != 0) { - wm_SemUnlock(&client->lockSend); + MqttWriteStop(client, &mc_connect->stat); return rc; /* Error locking client */ } #endif + mc_connect->stat.write = MQTT_MSG_HEADER; + } + if (mc_connect->stat.write == MQTT_MSG_HEADER) { + int xfer = client->write.len; + /* Send connect packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc != client->write.len) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &mc_connect->pendResp); - wm_SemUnlock(&client->lockClient); - } + rc = MqttPacket_Write(client, client->tx_buf, xfer); + #ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE + #ifdef WOLFMQTT_ALLOW_NODATA_UNLOCK + && client->write.total > 0 #endif + ) { + /* keep send locked and return early */ return rc; } - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); #endif + MqttWriteStop(client, &mc_connect->stat); + if (rc != xfer) { + MqttClient_CancelMessage(client, (MqttObject*)mc_connect); + return rc; + } + #ifdef WOLFMQTT_V5 /* Enhanced authentication */ if (client->enable_eauth == 1) { @@ -1547,6 +1741,7 @@ int MqttClient_Connect(MqttClient *client, MqttConnect *mc_connect) #endif return rc; } + mc_connect->stat.write = MQTT_MSG_WAIT; } #endif /* WOLFMQTT_V5 */ @@ -1623,15 +1818,6 @@ static int MqttClient_Publish_ReadPayload(MqttClient* client, /* make sure there is something to read */ if (msg_len > 0) { - #if defined(WOLFMQTT_NONBLOCK) && defined(WOLFMQTT_TEST_NONBLOCK) - static int testNbAlt = 0; - if (!testNbAlt) { - testNbAlt = 1; - return MQTT_CODE_CONTINUE; - } - testNbAlt = 0; - #endif - rc = MqttSocket_Read(client, client->rx_buf, msg_len, timeout_ms); if (rc < 0) { @@ -1669,13 +1855,20 @@ static int MqttClient_Publish_WritePayload(MqttClient *client, if (client == NULL || publish == NULL) return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - if (pubCb) { + if (pubCb) { /* use publish callback to get data */ word32 tmp_len = publish->buffer_len; do { - /* Use the callback to get payload */ - if ((client->write.len = pubCb(publish)) < 0) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_CALLBACK); + /* use the client->write.len to handle non-blocking re-entry when + * new publish callback data is needed */ + if (client->write.len == 0) { + /* Use the callback to get payload */ + if ((client->write.len = pubCb(publish)) < 0) { + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("Publish callback error %d", client->write.len); + #endif + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_CALLBACK); + } } if ((word32)client->write.len < publish->buffer_len) { @@ -1705,6 +1898,7 @@ static int MqttClient_Publish_WritePayload(MqttClient *client, publish->buffer_pos += publish->intBuf_pos; publish->intBuf_pos = 0; + client->write.len = 0; /* reset current write len */ } while (publish->buffer_pos < publish->total_len); } @@ -1736,8 +1930,17 @@ static int MqttClient_Publish_WritePayload(MqttClient *client, /* Check if we are done sending publish message */ if (publish->buffer_pos < publish->buffer_len) { + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("Publish Write: not done (%d remain)", + publish->buffer_len - publish->buffer_pos); + #endif return MQTT_CODE_PUB_CONTINUE; } + #ifdef WOLFMQTT_DEBUG_CLIENT + else { + PRINTF("Publish Write: done"); + } + #endif #else do { rc = MqttPacket_Write(client, client->tx_buf, client->write.len); @@ -1769,6 +1972,11 @@ static int MqttClient_Publish_WritePayload(MqttClient *client, /* If transferring more chunks */ publish->buffer_pos += publish->intBuf_pos; if (publish->buffer_pos < publish->total_len) { + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("Publish Write: chunk (%d remain)", + publish->total_len - publish->buffer_pos); + #endif + /* Build next payload to send */ client->write.len = (publish->total_len - publish->buffer_pos); if (client->write.len > client->tx_buf_len) { @@ -1776,6 +1984,11 @@ static int MqttClient_Publish_WritePayload(MqttClient *client, } rc = MQTT_CODE_PUB_CONTINUE; } + #ifdef WOLFMQTT_DEBUG_CLIENT + else { + PRINTF("Publish Write: chunked done"); + } + #endif } } return rc; @@ -1808,15 +2021,10 @@ static int MqttPublishMsg(MqttClient *client, MqttPublish *publish, { case MQTT_MSG_BEGIN: { - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { + /* Flag write active / lock mutex */ + if ((rc = MqttWriteStart(client, &publish->stat)) != 0) { return rc; } - publish->stat.isWriteLocked = 1; - MQTT_TRACE_MSG("lockSend"); - #endif /* Encode the publish packet */ rc = MqttEncode_Publish(client->tx_buf, client->tx_buf_len, @@ -1829,11 +2037,7 @@ static int MqttPublishMsg(MqttClient *client, MqttPublish *publish, publish->qos); #endif if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - MQTT_TRACE_MSG("unlockSend"); - publish->stat.isWriteLocked = 0; - wm_SemUnlock(&client->lockSend); - #endif + MqttWriteStop(client, &publish->stat); return rc; } client->write.len = rc; @@ -1852,9 +2056,7 @@ static int MqttPublishMsg(MqttClient *client, MqttPublish *publish, wm_SemUnlock(&client->lockClient); } if (rc != 0) { - MQTT_TRACE_MSG("unlockSend"); - publish->stat.isWriteLocked = 0; - wm_SemUnlock(&client->lockSend); + MqttWriteStop(client, &publish->stat); return rc; /* Error locking client */ } } @@ -1866,24 +2068,26 @@ static int MqttPublishMsg(MqttClient *client, MqttPublish *publish, case MQTT_MSG_HEADER: { - /* Send packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + int xfer = client->write.len; + + /* Send publish packet */ + rc = MqttPacket_Write(client, client->tx_buf, xfer); #ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE) + if (rc == MQTT_CODE_CONTINUE + #ifdef WOLFMQTT_ALLOW_NODATA_UNLOCK + && client->write.total > 0 + #endif + ) { + /* keep send locked and return early */ return rc; + } #endif - if (rc < 0) { - #ifdef WOLFMQTT_MULTITHREAD - MQTT_TRACE_MSG("unlockSend"); - publish->stat.isWriteLocked = 0; - wm_SemUnlock(&client->lockSend); - #endif - #ifdef WOLFMQTT_MULTITHREAD - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &publish->pendResp); - wm_SemUnlock(&client->lockClient); - } - #endif + client->write.len = 0; /* reset len, so publish chunk resets */ + + /* if failure or no data was written yet */ + if (rc != xfer) { + MqttWriteStop(client, &publish->stat); + MqttClient_CancelMessage(client, (MqttObject*)publish); return rc; } @@ -1896,22 +2100,12 @@ static int MqttPublishMsg(MqttClient *client, MqttPublish *publish, { rc = MqttClient_Publish_WritePayload(client, publish, pubCb); #ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE) + if (rc == MQTT_CODE_CONTINUE || rc == MQTT_CODE_PUB_CONTINUE) return rc; #endif - #ifdef WOLFMQTT_MULTITHREAD - MQTT_TRACE_MSG("unlockSend"); - publish->stat.isWriteLocked = 0; - wm_SemUnlock(&client->lockSend); - #endif - + MqttWriteStop(client, &publish->stat); if (rc < 0) { - #ifdef WOLFMQTT_MULTITHREAD - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &publish->pendResp); - wm_SemUnlock(&client->lockClient); - } - #endif + MqttClient_CancelMessage(client, (MqttObject*)publish); break; } @@ -1934,9 +2128,16 @@ static int MqttPublishMsg(MqttClient *client, MqttPublish *publish, #ifdef WOLFMQTT_MULTITHREAD if (writeOnly) { - /* another thread will handle the wait type */ + /* another thread will handle response */ + /* check if response already received from other thread */ rc = MqttClient_CheckPendResp(client, resp_type, publish->packet_id); + #ifndef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE) { + /* mark success, let other thread handle response */ + rc = MQTT_CODE_SUCCESS; + } + #endif } else #endif @@ -1947,6 +2148,7 @@ static int MqttPublishMsg(MqttClient *client, MqttPublish *publish, rc = MqttClient_WaitType(client, &publish->resp, resp_type, publish->packet_id, client->cmd_timeout_ms); } + #if defined(WOLFMQTT_NONBLOCK) || defined(WOLFMQTT_MULTITHREAD) if (rc == MQTT_CODE_CONTINUE) break; @@ -2024,13 +2226,10 @@ int MqttClient_Subscribe(MqttClient *client, MqttSubscribe *subscribe) #endif if (subscribe->stat.write == MQTT_MSG_BEGIN) { - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { + /* Flag write active / lock mutex */ + if ((rc = MqttWriteStart(client, &subscribe->stat)) != 0) { return rc; } - #endif /* Encode the subscribe packet */ rc = MqttEncode_Subscribe(client->tx_buf, client->tx_buf_len, @@ -2041,9 +2240,7 @@ int MqttClient_Subscribe(MqttClient *client, MqttSubscribe *subscribe) MQTT_PACKET_TYPE_SUBSCRIBE, subscribe->packet_id); #endif if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif + MqttWriteStop(client, &subscribe->stat); return rc; } client->write.len = rc; @@ -2057,26 +2254,33 @@ int MqttClient_Subscribe(MqttClient *client, MqttSubscribe *subscribe) wm_SemUnlock(&client->lockClient); } if (rc != 0) { - wm_SemUnlock(&client->lockSend); + MqttWriteStop(client, &subscribe->stat); return rc; /* Error locking client */ } #endif + subscribe->stat.write = MQTT_MSG_HEADER; + } + if (subscribe->stat.write == MQTT_MSG_HEADER) { + int xfer = client->write.len; + /* Send subscribe packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc != client->write.len) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &subscribe->pendResp); - wm_SemUnlock(&client->lockClient); - } + rc = MqttPacket_Write(client, client->tx_buf, xfer); + #ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE + #ifdef WOLFMQTT_ALLOW_NODATA_UNLOCK + && client->write.total > 0 #endif + ) { + /* keep send locked and return early */ return rc; } - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); #endif + MqttWriteStop(client, &subscribe->stat); + if (rc != xfer) { + MqttClient_CancelMessage(client, (MqttObject*)subscribe); + return rc; + } subscribe->stat.write = MQTT_MSG_WAIT; } @@ -2126,13 +2330,10 @@ int MqttClient_Unsubscribe(MqttClient *client, MqttUnsubscribe *unsubscribe) #endif if (unsubscribe->stat.write == MQTT_MSG_BEGIN) { - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { + /* Flag write active / lock mutex */ + if ((rc = MqttWriteStart(client, &unsubscribe->stat)) != 0) { return rc; } - #endif /* Encode the subscribe packet */ rc = MqttEncode_Unsubscribe(client->tx_buf, client->tx_buf_len, @@ -2143,9 +2344,7 @@ int MqttClient_Unsubscribe(MqttClient *client, MqttUnsubscribe *unsubscribe) MQTT_PACKET_TYPE_UNSUBSCRIBE, unsubscribe->packet_id, 0); #endif if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif + MqttWriteStop(client, &unsubscribe->stat); return rc; } client->write.len = rc; @@ -2160,26 +2359,33 @@ int MqttClient_Unsubscribe(MqttClient *client, MqttUnsubscribe *unsubscribe) wm_SemUnlock(&client->lockClient); } if (rc != 0) { - wm_SemUnlock(&client->lockSend); /* Error locking client */ + MqttWriteStop(client, &unsubscribe->stat); return rc; } #endif + unsubscribe->stat.write = MQTT_MSG_HEADER; + } + if (unsubscribe->stat.write == MQTT_MSG_HEADER) { + int xfer = client->write.len; + /* Send unsubscribe packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc != client->write.len) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &unsubscribe->pendResp); - wm_SemUnlock(&client->lockClient); - } + rc = MqttPacket_Write(client, client->tx_buf, xfer); + #ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE + #ifdef WOLFMQTT_ALLOW_NODATA_UNLOCK + && client->write.total > 0 #endif + ) { + /* keep send locked and return early */ return rc; } - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); #endif + MqttWriteStop(client, &unsubscribe->stat); + if (rc != xfer) { + MqttClient_CancelMessage(client, (MqttObject*)unsubscribe); + return rc; + } unsubscribe->stat.write = MQTT_MSG_WAIT; } @@ -2223,13 +2429,10 @@ int MqttClient_Ping_ex(MqttClient *client, MqttPing* ping) } if (ping->stat.write == MQTT_MSG_BEGIN) { - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { + /* Flag write active / lock mutex */ + if ((rc = MqttWriteStart(client, &ping->stat)) != 0) { return rc; } - #endif /* Encode the subscribe packet */ rc = MqttEncode_Ping(client->tx_buf, client->tx_buf_len, ping); @@ -2239,9 +2442,7 @@ int MqttClient_Ping_ex(MqttClient *client, MqttPing* ping) MQTT_PACKET_TYPE_PING_REQ, 0, 0); #endif if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif + MqttWriteStop(client, &ping->stat); return rc; } client->write.len = rc; @@ -2255,26 +2456,34 @@ int MqttClient_Ping_ex(MqttClient *client, MqttPing* ping) wm_SemUnlock(&client->lockClient); } if (rc != 0) { - wm_SemUnlock(&client->lockSend); + MqttWriteStop(client, &ping->stat); return rc; /* Error locking client */ } #endif + ping->stat.write = MQTT_MSG_HEADER; + } + if (ping->stat.write == MQTT_MSG_HEADER) { + int xfer = client->write.len; + /* Send ping req packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc != client->write.len) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &ping->pendResp); - wm_SemUnlock(&client->lockClient); - } + rc = MqttPacket_Write(client, client->tx_buf, xfer); + #ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE + #ifdef WOLFMQTT_ALLOW_NODATA_UNLOCK + && client->write.total > 0 #endif + ) { + /* keep send locked and return early */ return rc; } - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); #endif + MqttWriteStop(client, &ping->stat); + if (rc != xfer) { + MqttClient_CancelMessage(client, (MqttObject*)ping); + return rc; + } + ping->stat.write = MQTT_MSG_WAIT; } @@ -2309,67 +2518,85 @@ int MqttClient_Disconnect(MqttClient *client) return MqttClient_Disconnect_ex(client, NULL); } -int MqttClient_Disconnect_ex(MqttClient *client, MqttDisconnect *disconnect) +int MqttClient_Disconnect_ex(MqttClient *client, MqttDisconnect *p_disconnect) { - int rc; + int rc, xfer; + MqttDisconnect *disconnect = p_disconnect, lcl_disconnect; /* Validate required arguments */ if (client == NULL) { return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); } + if (disconnect == NULL) { + disconnect = &lcl_disconnect; + XMEMSET(disconnect, 0, sizeof(*disconnect)); + } -#ifdef WOLFMQTT_V5 - if (disconnect != NULL) { + if (disconnect->stat.write == MQTT_MSG_BEGIN) { + #ifdef WOLFMQTT_V5 /* Use specified protocol version if set */ disconnect->protocol_level = client->protocol_level; - } -#endif + #endif -#ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } -#endif + /* Flag write active / lock mutex */ + if ((rc = MqttWriteStart(client, &disconnect->stat)) != 0) { + return rc; + } - /* Encode the disconnect packet */ - rc = MqttEncode_Disconnect(client->tx_buf, client->tx_buf_len, disconnect); -#ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d), ID %d, QoS %d", - rc, MqttPacket_TypeDesc(MQTT_PACKET_TYPE_DISCONNECT), - MQTT_PACKET_TYPE_DISCONNECT, 0, 0); -#endif - if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); + /* Encode the disconnect packet */ + rc = MqttEncode_Disconnect(client->tx_buf, client->tx_buf_len, + disconnect); + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d), ID %d, QoS %d", + rc, MqttPacket_TypeDesc(MQTT_PACKET_TYPE_DISCONNECT), + MQTT_PACKET_TYPE_DISCONNECT, 0, 0); #endif - return rc; + if (rc <= 0) { + MqttWriteStop(client, &disconnect->stat); + return rc; + } + client->write.len = rc; + + disconnect->stat.write = MQTT_MSG_HEADER; } - client->write.len = rc; /* Send disconnect packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc != client->write.len) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); + xfer = client->write.len; + rc = MqttPacket_Write(client, client->tx_buf, xfer); +#ifdef WOLFMQTT_NONBLOCK + /* if disconnect context avail allow partial write in non-blocking mode */ + if (p_disconnect != NULL && rc == MQTT_CODE_CONTINUE + #ifdef WOLFMQTT_ALLOW_NODATA_UNLOCK + && client->write.total > 0 #endif + ) { + /* keep send locked and return early */ return rc; } -#ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); #endif - - rc = MQTT_CODE_SUCCESS; + MqttWriteStop(client, &disconnect->stat); + if (rc == xfer) { + rc = MQTT_CODE_SUCCESS; + } #if defined(WOLFMQTT_DISCONNECT_CB) && defined(WOLFMQTT_USE_CB_ON_DISCONNECT) - /* Trigger disconnect callback */ - if (client->disconnect_cb) + /* Trigger disconnect callback - for intentional disconnect + * This callback may occur on a network failure during an intentional + * disconnect if the transport/socket is not setup yet. */ + if (client->disconnect_cb + #ifdef WOLFMQTT_NONBLOCK + && rc != MQTT_CODE_CONTINUE + #endif + ) { client->disconnect_cb(client, rc, client->disconnect_ctx); + } #endif /* No response for MQTT disconnect packet */ + /* reset state */ + disconnect->stat.write = MQTT_MSG_BEGIN; + return rc; } @@ -2384,13 +2611,10 @@ int MqttClient_Auth(MqttClient *client, MqttAuth* auth) } if (auth->stat.write == MQTT_MSG_BEGIN) { - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { + /* Flag write active / lock mutex */ + if ((rc = MqttWriteStart(client, &auth->stat)) != 0) { return rc; } - #endif /* Encode the authentication packet */ rc = MqttEncode_Auth(client->tx_buf, client->tx_buf_len, auth); @@ -2400,9 +2624,7 @@ int MqttClient_Auth(MqttClient *client, MqttAuth* auth) MQTT_PACKET_TYPE_AUTH, 0, 0); #endif if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif + MqttWriteStop(client, &auth->stat); return rc; } client->write.len = rc; @@ -2416,26 +2638,33 @@ int MqttClient_Auth(MqttClient *client, MqttAuth* auth) wm_SemUnlock(&client->lockClient); } if (rc != 0) { - wm_SemUnlock(&client->lockSend); + MqttWriteStop(client, &auth->stat); return rc; /* Error locking client */ } #endif + auth->stat.write = MQTT_MSG_HEADER; + } + if (auth->stat.write == MQTT_MSG_BEGIN) { + int xfer = client->write.len; + /* Send authentication packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc != client->write.len) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &auth->pendResp); - wm_SemUnlock(&client->lockClient); - } + rc = MqttPacket_Write(client, client->tx_buf, xfer); + #ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE + #ifdef WOLFMQTT_ALLOW_NODATA_UNLOCK + && client->write.total > 0 #endif + ) { + /* keep send locked and return early */ return rc; } - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); #endif + MqttWriteStop(client, &auth->stat); + if (rc != xfer) { + MqttClient_CancelMessage(client, (MqttObject*)auth); + return rc; + } auth->stat.write = MQTT_MSG_WAIT; } @@ -2486,20 +2715,21 @@ int MqttClient_WaitMessage(MqttClient *client, int timeout_ms) return MqttClient_WaitMessage_ex(client, &client->msg, timeout_ms); } -#if defined(WOLFMQTT_MULTITHREAD) || defined(WOLFMQTT_NONBLOCK) +#if !defined(WOLFMQTT_MULTITHREAD) && !defined(WOLFMQTT_NONBLOCK) +static +#endif int MqttClient_CancelMessage(MqttClient *client, MqttObject* msg) { int rc = MQTT_CODE_SUCCESS; + MqttMsgStat* mms_stat; #ifdef WOLFMQTT_MULTITHREAD MqttPendResp* tmpResp; - MqttMsgStat* mms_stat; #endif if (client == NULL || msg == NULL) { return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); } -#ifdef WOLFMQTT_MULTITHREAD /* all packet type structures must have MqttMsgStat at top */ mms_stat = (MqttMsgStat*)msg; @@ -2507,7 +2737,12 @@ int MqttClient_CancelMessage(MqttClient *client, MqttObject* msg) PRINTF("Cancel Msg: %p", msg); #endif - /* Find pending response entry and remove */ + /* reset states */ + mms_stat->write = MQTT_MSG_BEGIN; + mms_stat->read = MQTT_MSG_BEGIN; + +#ifdef WOLFMQTT_MULTITHREAD + /* Remove any pending responses expected */ rc = wm_SemLock(&client->lockClient); if (rc != MQTT_CODE_SUCCESS) { return rc; @@ -2539,26 +2774,24 @@ int MqttClient_CancelMessage(MqttClient *client, MqttObject* msg) } } wm_SemUnlock(&client->lockClient); +#endif /* WOLFMQTT_MULTITHREAD */ - /* clear any locks */ - if (mms_stat->isReadLocked) { + /* cancel any active flags / locks */ + if (mms_stat->isReadActive) { #ifdef WOLFMQTT_DEBUG_CLIENT PRINTF("Cancel Read Lock"); #endif - mms_stat->isReadLocked = 0; - wm_SemUnlock(&client->lockRecv); + MqttReadStop(client, mms_stat); } - if (mms_stat->isWriteLocked) { + if (mms_stat->isWriteActive) { #ifdef WOLFMQTT_DEBUG_CLIENT PRINTF("Cancel Write Lock"); #endif - mms_stat->isWriteLocked = 0; - wm_SemUnlock(&client->lockSend); + MqttWriteStop(client, mms_stat); } -#endif + return rc; } -#endif /* WOLFMQTT_MULTITHREAD || WOLFMQTT_NONBLOCK */ #ifdef WOLFMQTT_NONBLOCK static inline int IsMessageActive(MqttObject *msg) @@ -2621,9 +2854,19 @@ int MqttClient_NetDisconnect(MqttClient *client) /* Get client lock on to ensure no other threads are active */ wm_SemLock(&client->lockClient); +#ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("Net Disconnect: Removing pending responses"); +#endif for (tmpResp = client->firstPendResp; tmpResp != NULL; tmpResp = tmpResp->next) { + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("\tPendResp: %p (obj %p), Type %s (%d), ID %d, InProc %d, Done %d", + tmpResp, tmpResp->packet_obj, + MqttPacket_TypeDesc(tmpResp->packet_type), + tmpResp->packet_type, tmpResp->packet_id, + tmpResp->packetProcessing, tmpResp->packetDone); + #endif MqttClient_RespList_Remove(client, tmpResp); } wm_SemUnlock(&client->lockClient); @@ -2701,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 */ @@ -2797,1848 +3044,3 @@ word32 MqttClient_Flags(MqttClient *client, word32 mask, word32 flags) } return ret; } - -#ifdef WOLFMQTT_SN - -/* Private functions */ -static int SN_Client_HandlePacket(MqttClient* client, SN_MsgType packet_type, - void* packet_obj, int timeout) -{ - int rc = MQTT_CODE_SUCCESS; - word16 packet_id = 0; - - (void)timeout; - - switch ((int)packet_type) - { - case SN_MSG_TYPE_GWINFO: - { - SN_GwInfo info, *p_info = &info; - if (packet_obj) { - p_info = (SN_GwInfo*)packet_obj; - } - else { - XMEMSET(p_info, 0, sizeof(SN_GwInfo)); - } - - rc = SN_Decode_GWInfo(client->rx_buf, client->packet.buf_len, - p_info); - if (rc <= 0) { - return rc; - } - break; - } - case SN_MSG_TYPE_CONNACK: - { - /* Decode connect ack */ - SN_ConnectAck connect_ack, *p_connect_ack = &connect_ack; - if (packet_obj) { - p_connect_ack = (SN_ConnectAck*)packet_obj; - } - else { - XMEMSET(p_connect_ack, 0, sizeof(SN_ConnectAck)); - } - p_connect_ack->return_code = - client->rx_buf[client->packet.buf_len-1]; - - break; - } - case SN_MSG_TYPE_WILLTOPICREQ: - { - rc = SN_Decode_WillTopicReq(client->rx_buf, client->packet.buf_len); - break; - } - case SN_MSG_TYPE_WILLMSGREQ: - { - rc = SN_Decode_WillMsgReq(client->rx_buf, client->packet.buf_len); - break; - } - case SN_MSG_TYPE_REGISTER: - { - /* Decode register */ - SN_Register reg_s; - - XMEMSET(®_s, 0, sizeof(SN_Register)); - - rc = SN_Decode_Register(client->rx_buf, client->packet.buf_len, - ®_s); - - if (rc > 0) { - /* Initialize the regack */ - reg_s.regack.packet_id = reg_s.packet_id; - reg_s.regack.topicId = reg_s.topicId; - reg_s.regack.return_code = SN_RC_NOTSUPPORTED; - - /* Call the register callback to allow app to - handle new topic ID assignment. */ - if (client->reg_cb != NULL) { - rc = client->reg_cb(reg_s.topicId, - reg_s.topicName, client->reg_ctx); - /* Set the regack return code */ - reg_s.regack.return_code = (rc >= 0) ? SN_RC_ACCEPTED : - SN_RC_INVTOPICNAME; - } - - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } - #endif - - /* Encode the register acknowledgment */ - rc = SN_Encode_RegAck(client->tx_buf, client->tx_buf_len, - ®_s.regack); - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d), ID %d", - rc, SN_Packet_TypeDesc(SN_MSG_TYPE_REGACK), - SN_MSG_TYPE_REGACK, reg_s.packet_id); - #endif - if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - client->write.len = rc; - - /* Send regack packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc != client->write.len) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - } - - break; - } - case SN_MSG_TYPE_REGACK: - { - /* Decode register ack */ - SN_RegAck regack_s, *p_regack = ®ack_s; - if (packet_obj) { - p_regack = (SN_RegAck*)packet_obj; - } - else { - XMEMSET(p_regack, 0, sizeof(SN_RegAck)); - } - - rc = SN_Decode_RegAck(client->rx_buf, client->packet.buf_len, - p_regack); - if (rc > 0) { - packet_id = p_regack->packet_id; - } - - break; - } - case SN_MSG_TYPE_PUBLISH: - { - SN_Publish pub, *p_pub = &pub; - if (packet_obj) { - p_pub = (SN_Publish*)packet_obj; - } - else { - XMEMSET(p_pub, 0, sizeof(SN_Publish)); - } - - /* Decode publish message */ - rc = SN_Decode_Publish(client->rx_buf, client->packet.buf_len, - p_pub); - if (rc <= 0) { - return rc; - } - - /* Issue callback for new message */ - if (client->msg_cb) { - /* if using the temp publish message buffer, - then populate message context with client context */ - if (&client->msgSN.publish == p_pub) - p_pub->ctx = client->ctx; - rc = client->msg_cb(client, (MqttMessage*)p_pub, 1, 1); - if (rc != MQTT_CODE_SUCCESS) { - return rc; - }; - } - - /* Handle Qos */ - if (p_pub->qos > MQTT_QOS_0) { - SN_MsgType type; - - packet_id = p_pub->packet_id; - - /* Determine packet type to write */ - type = (p_pub->qos == MQTT_QOS_1) ? - SN_MSG_TYPE_PUBACK : - SN_MSG_TYPE_PUBREC; - p_pub->resp.packet_id = packet_id; - - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } - #endif - - /* Encode publish response */ - rc = SN_Encode_PublishResp(client->tx_buf, - client->tx_buf_len, type, &p_pub->resp); - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d), ID %d," - " QoS %d", - rc, SN_Packet_TypeDesc(type), type, packet_id, - p_pub->qos); - #endif - if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - client->write.len = rc; - - /* Send packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - } - break; - } - case SN_MSG_TYPE_PUBACK: - case SN_MSG_TYPE_PUBCOMP: - case SN_MSG_TYPE_PUBREC: - case SN_MSG_TYPE_PUBREL: - { - SN_PublishResp publish_resp, *p_publish_resp = &publish_resp; - if (packet_obj) { - p_publish_resp = (SN_PublishResp*)packet_obj; - } - else { - XMEMSET(p_publish_resp, 0, sizeof(SN_PublishResp)); - } - - /* Decode publish response message */ - rc = SN_Decode_PublishResp(client->rx_buf, client->packet.buf_len, - packet_type, p_publish_resp); - if (rc <= 0) { - return rc; - } - packet_id = p_publish_resp->packet_id; - - /* If Qos then send response */ - if (packet_type == SN_MSG_TYPE_PUBREC || - packet_type == SN_MSG_TYPE_PUBREL) { - - byte resp_type = (packet_type == SN_MSG_TYPE_PUBREC) ? - SN_MSG_TYPE_PUBREL : SN_MSG_TYPE_PUBCOMP; - - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } - #endif - - /* Encode publish response */ - p_publish_resp->packet_id = packet_id; - rc = SN_Encode_PublishResp(client->tx_buf, - client->tx_buf_len, resp_type, p_publish_resp); - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d), ID %d", - rc, MqttPacket_TypeDesc(resp_type), resp_type, packet_id); - #endif - if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - client->write.len = rc; - - /* Send packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - } - break; - } - case SN_MSG_TYPE_SUBACK: - { - /* Decode subscribe ack */ - SN_SubAck subscribe_ack, *p_subscribe_ack = &subscribe_ack; - if (packet_obj) { - p_subscribe_ack = (SN_SubAck*)packet_obj; - } - else { - XMEMSET(p_subscribe_ack, 0, sizeof(SN_SubAck)); - } - - rc = SN_Decode_SubscribeAck(client->rx_buf, client->packet.buf_len, - p_subscribe_ack); - if (rc <= 0) { - return rc; - } - packet_id = p_subscribe_ack->packet_id; - - break; - } - case SN_MSG_TYPE_UNSUBACK: - { - /* Decode unsubscribe ack */ - SN_UnsubscribeAck unsubscribe_ack, - *p_unsubscribe_ack = &unsubscribe_ack; - if (packet_obj) { - p_unsubscribe_ack = (SN_UnsubscribeAck*)packet_obj; - } - else { - XMEMSET(p_unsubscribe_ack, 0, sizeof(SN_UnsubscribeAck)); - } - rc = SN_Decode_UnsubscribeAck(client->rx_buf, - client->packet.buf_len, p_unsubscribe_ack); - if (rc <= 0) { - return rc; - } - packet_id = p_unsubscribe_ack->packet_id; - - break; - } - case SN_MSG_TYPE_PING_RESP: - { - /* Decode ping */ - rc = SN_Decode_Ping(client->rx_buf, client->packet.buf_len); - break; - } - case SN_MSG_TYPE_PING_REQ: - { - /* Decode ping */ - rc = SN_Decode_Ping(client->rx_buf, client->packet.buf_len); - if (rc <= 0) { return rc; } - - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } - #endif - - /* Encode the ping packet as a response */ - rc = SN_Encode_Ping(client->tx_buf, client->tx_buf_len, NULL, - SN_MSG_TYPE_PING_RESP); - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d)", - rc, SN_Packet_TypeDesc(SN_MSG_TYPE_PING_RESP), - SN_MSG_TYPE_PING_RESP); - #endif - if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - client->write.len = rc; - - /* Send ping resp packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc != client->write.len) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - - break; - } - case SN_MSG_TYPE_WILLTOPICRESP: - { - /* Decode Will Topic Response */ - SN_WillTopicResp resp_s, *resp = &resp_s; - if (packet_obj) { - resp = (SN_WillTopicResp*)packet_obj; - } - else { - XMEMSET(resp, 0, sizeof(SN_WillTopicResp)); - } - rc = SN_Decode_WillTopicResponse(client->rx_buf, - client->packet.buf_len, &resp->return_code); - break; - } - case SN_MSG_TYPE_WILLMSGRESP: - { - /* Decode Will Message Response */ - SN_WillMsgResp resp_s, *resp = &resp_s; - if (packet_obj) { - resp = (SN_WillMsgResp*)packet_obj; - } - else { - XMEMSET(resp, 0, sizeof(SN_WillMsgResp)); - } - rc = SN_Decode_WillMsgResponse(client->rx_buf, - client->packet.buf_len, &resp->return_code); - break; - } - case SN_MSG_TYPE_DISCONNECT: - { - SN_Disconnect disc_s, *disc = &disc_s; - if (packet_obj) { - disc = (SN_Disconnect*)packet_obj; - } - else { - XMEMSET(disc, 0, sizeof(SN_Disconnect)); - } - /* Decode Disconnect */ - rc = SN_Decode_Disconnect(client->rx_buf, client->packet.buf_len); - -#ifdef WOLFMQTT_DISCONNECT_CB - /* Call disconnect callback to allow handling broker disconnect */ - if ((client->disconnect_cb != NULL) && (disc->sleepTmr == 0)) { - client->disconnect_cb(client, rc, client->disconnect_ctx); - } -#endif - break; - } - - default: - { - /* Other types are server side only, ignore */ - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("SN_Client_HandlePacket: Invalid client packet type %u!", - packet_type); - #endif - break; - } - } /* switch (packet_type) */ - - (void)packet_id; - - return rc; -} - -static int SN_Client_WaitType(MqttClient *client, void* packet_obj, - byte wait_type, word16 wait_packet_id, int timeout_ms) -{ - int rc; - word16 packet_id; - SN_MsgType packet_type; -#ifdef WOLFMQTT_MULTITHREAD - MqttPendResp *pendResp; -#endif - MqttMsgStat* mms_stat; - int waitMatchFound; - void* use_packet_obj = NULL; - - if (client == NULL || packet_obj == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* all packet type structures must have MqttMsgStat at top */ - mms_stat = (MqttMsgStat*)packet_obj; - -wait_again: - - /* initialize variables */ - packet_id = 0; - packet_type = SN_MSG_TYPE_RESERVED; -#ifdef WOLFMQTT_MULTITHREAD - pendResp = NULL; -#endif - waitMatchFound = 0; - -#ifdef WOLFMQTT_DEBUG_CLIENT - #ifdef WOLFMQTT_NONBLOCK - if (client->lastRc != MQTT_CODE_CONTINUE) - #endif - { - PRINTF("SN_Client_WaitType: Type %s (%d), ID %d", - SN_Packet_TypeDesc((SN_MsgType)wait_type), - wait_type, wait_packet_id); - } -#endif - - switch (mms_stat->read) - { - case MQTT_MSG_BEGIN: - { - #ifdef WOLFMQTT_MULTITHREAD - /* Lock recv socket mutex */ - rc = wm_SemLock(&client->lockRecv); - if (rc != 0) { - PRINTF("SN_Client_WaitType recv lock error"); - return rc; - } - mms_stat->isReadLocked = 1; - MQTT_TRACE_MSG("SN lockRecv"); - #endif - - /* reset the packet state used by SN_Packet_Read */ - client->packet.stat = MQTT_PK_BEGIN; - } - FALL_THROUGH; - - case MQTT_MSG_WAIT: - { - #ifdef WOLFMQTT_MULTITHREAD - /* Check to see if packet type and id have already completed */ - pendResp = NULL; - rc = wm_SemLock(&client->lockClient); - if (rc == 0) { - if (MqttClient_RespList_Find(client, (MqttPacketType)wait_type, - wait_packet_id, &pendResp)) { - if (pendResp->packetDone) { - /* pending response is already done, so return */ - rc = pendResp->packet_ret; - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("PendResp already Done %p: Rc %d", pendResp, rc); - #endif - MqttClient_RespList_Remove(client, pendResp); - wm_SemUnlock(&client->lockClient); - MQTT_TRACE_MSG("SN unlockRecv"); - wm_SemUnlock(&client->lockRecv); - return rc; - } - } - wm_SemUnlock(&client->lockClient); - } - else { - break; /* error */ - } - #endif /* WOLFMQTT_MULTITHREAD */ - - mms_stat->read = MQTT_MSG_WAIT; - - /* Wait for packet */ - rc = SN_Packet_Read(client, client->rx_buf, client->rx_buf_len, - timeout_ms); - if (rc <= 0) { - break; - } - - client->packet.buf_len = rc; - - /* Decode header */ - rc = SN_Decode_Header(client->rx_buf, client->packet.buf_len, - &packet_type, &packet_id); - if (rc < 0) { - break; - } - - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("Read Packet: Len %d, Type %d, ID %d", - client->packet.buf_len, packet_type, packet_id); - #endif - - mms_stat->read = MQTT_MSG_HEADER; - } - FALL_THROUGH; - - case MQTT_MSG_HEADER: - case MQTT_MSG_PAYLOAD: - case MQTT_MSG_PAYLOAD2: - { - SN_MsgType use_packet_type; - - /* Determine if we received data for this request */ - if ((wait_type == SN_MSG_TYPE_ANY || wait_type == packet_type) && - (wait_packet_id == 0 || wait_packet_id == packet_id)) - { - use_packet_obj = packet_obj; - waitMatchFound = 1; - } - else { - /* use generic packet object */ - use_packet_obj = &client->msgSN; - } - use_packet_type = packet_type; - - #ifdef WOLFMQTT_MULTITHREAD - /* Check to see if we have a pending response for this packet */ - pendResp = NULL; - rc = wm_SemLock(&client->lockClient); - if (rc == 0) { - if (MqttClient_RespList_Find(client, - (MqttPacketType)packet_type, packet_id, &pendResp)) { - /* we found packet match this incoming read packet */ - pendResp->packetProcessing = 1; - if (pendResp->packet_obj != packet_obj) { - use_packet_obj = pendResp->packet_obj; - use_packet_type = (SN_MsgType)pendResp->packet_type; - /* req from another thread... not a match */ - waitMatchFound = 0; - } - } - wm_SemUnlock(&client->lockClient); - } - else { - break; /* error */ - } - #endif /* WOLFMQTT_MULTITHREAD */ - - rc = SN_Client_HandlePacket(client, use_packet_type, use_packet_obj, - timeout_ms); - - #ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE) { - break; - } - #endif - - /* handle success case */ - if (rc >= 0) { - rc = MQTT_CODE_SUCCESS; - } - - #ifdef WOLFMQTT_MULTITHREAD - if (pendResp) { - /* Mark pending response entry done */ - if (wm_SemLock(&client->lockClient) == 0) { - pendResp->packetDone = 1; - pendResp->packet_ret = rc; - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("PendResp Marked Done %p", pendResp); - #endif - pendResp = NULL; - wm_SemUnlock(&client->lockClient); - } - } - #endif /* WOLFMQTT_MULTITHREAD */ - break; - } - - case MQTT_MSG_ACK: /* ack handled in SN_Client_HandlePacket */ - case MQTT_MSG_AUTH: - default: - { - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("SN_Client_WaitType: Invalid state %d!", mms_stat->read); - #endif - rc = MQTT_TRACE_ERROR(MQTT_CODE_ERROR_STAT); - break; - } - } /* switch (msg->stat) */ - -#ifdef WOLFMQTT_DEBUG_CLIENT - if (rc != MQTT_CODE_CONTINUE) { - PRINTF("SN_Client_WaitType: rc %d, state %d", rc, mms_stat->read); - } -#endif - - if (mms_stat->read == MQTT_MSG_WAIT || rc != MQTT_CODE_CONTINUE) { - /* reset state */ - mms_stat->read = MQTT_MSG_BEGIN; - - #ifdef WOLFMQTT_MULTITHREAD - if (mms_stat->isReadLocked) { - mms_stat->isReadLocked = 0; - wm_SemUnlock(&client->lockRecv); - } - #endif - } - -#ifdef WOLFMQTT_NONBLOCK - #ifdef WOLFMQTT_DEBUG_CLIENT - client->lastRc = rc; - #endif - if (rc == MQTT_CODE_CONTINUE) { - return rc; - } -#endif - - /* Clear shared union for next call */ - if ((MqttObject*)use_packet_obj == &client->msg) { - /* reset the members, but not the stat */ - XMEMSET(((byte*)&client->msg.stat) + sizeof(client->msg.stat), 0, - sizeof(client->msg)-sizeof(client->msg.stat)); - } - - if (rc < 0) { - #ifdef WOLFMQTT_DEBUG_CLIENT - if (rc != MQTT_CODE_CONTINUE) { - PRINTF("SN_Client_WaitType: Failure: %s (%d)", - MqttClient_ReturnCodeToString(rc), rc); - } - #endif - return rc; - } - - if (!waitMatchFound) { - /* if we get here, then the we are still waiting for a packet */ - goto wait_again; - } - - return rc; -} - -/* Public Functions */ - -int SN_Client_SetRegisterCallback(MqttClient *client, - SN_ClientRegisterCb regCb, - void* ctx) -{ - int rc = MQTT_CODE_SUCCESS; - - if (client == NULL) - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - -#ifdef WOLFMQTT_MULTITHREAD - rc = wm_SemLock(&client->lockClient); - if (rc == 0) { -#endif - - client->reg_cb = regCb; - client->reg_ctx = ctx; - -#ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockClient); - } -#endif - - return rc; -} - -int SN_Client_SearchGW(MqttClient *client, SN_SearchGw *search) -{ - int rc; - - /* Validate required arguments */ - if (client == NULL || search == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - if (search->stat.write == MQTT_MSG_BEGIN) { - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } - #endif - - /* Encode the search packet */ - rc = SN_Encode_SearchGW(client->tx_buf, client->tx_buf_len, - search->radius); - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d)", - rc, SN_Packet_TypeDesc(SN_MSG_TYPE_SEARCHGW), - SN_MSG_TYPE_SEARCHGW); - #endif - if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - client->write.len = rc; - - #ifdef WOLFMQTT_MULTITHREAD - rc = wm_SemLock(&client->lockClient); - if (rc == 0) { - /* inform other threads of expected response */ - rc = MqttClient_RespList_Add(client, - (MqttPacketType)SN_MSG_TYPE_GWINFO, 0, - &search->pendResp, &search->gwInfo); - wm_SemUnlock(&client->lockClient); - } - if (rc != 0) { - wm_SemUnlock(&client->lockSend); - return rc; /* Error locking client */ - } - #endif - - /* Send search for gateway packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc != client->write.len) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &search->pendResp); - wm_SemUnlock(&client->lockClient); - } - #endif - return rc; - } - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - - search->stat.write = MQTT_MSG_WAIT; - } - - /* Wait for gateway info packet */ - rc = SN_Client_WaitType(client, &search->gwInfo, SN_MSG_TYPE_GWINFO, 0, - client->cmd_timeout_ms); -#ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE) - return rc; -#endif -#ifdef WOLFMQTT_MULTITHREAD - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &search->pendResp); - wm_SemUnlock(&client->lockClient); - } -#endif - - /* reset state */ - search->stat.write = MQTT_MSG_BEGIN; - - return rc; -} - -static int SN_WillTopic(MqttClient *client, SN_Will *will) -{ - int rc; - - /* Validate required arguments */ - if (client == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - -#ifdef WOLFMQTT_MULTITHREAD - rc = wm_SemLock(&client->lockClient); - if (rc == 0) { - /* inform other threads of expected response */ - rc = MqttClient_RespList_Add(client, - (MqttPacketType)SN_MSG_TYPE_WILLTOPICREQ, 0, - &will->pendResp, &will->resp.topicResp); - wm_SemUnlock(&client->lockClient); - } - if (rc != 0) { - return rc; /* Error locking client */ - } -#endif - - /* Wait for Will Topic Request packet */ - rc = SN_Client_WaitType(client, will, - SN_MSG_TYPE_WILLTOPICREQ, 0, client->cmd_timeout_ms); -#ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE) - return rc; -#endif - -#ifdef WOLFMQTT_MULTITHREAD - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &will->pendResp); - wm_SemUnlock(&client->lockClient); - } -#endif - if (rc == 0) { - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } - #endif - - /* Encode Will Topic */ - rc = SN_Encode_WillTopic(client->tx_buf, client->tx_buf_len, - will); - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("EncodePacket: Len %d, Type %s (%d)", - rc, SN_Packet_TypeDesc(SN_MSG_TYPE_WILLTOPIC), - SN_MSG_TYPE_WILLTOPIC); - #endif - if (rc > 0) { - /* Send Will Topic packet */ - client->write.len = rc; - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc == client->write.len) { - rc = 0; - } - } - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - } - - return rc; -} - -static int SN_WillMessage(MqttClient *client, SN_Will *will) -{ - int rc; - - /* Validate required arguments */ - if (client == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - -#ifdef WOLFMQTT_MULTITHREAD - rc = wm_SemLock(&client->lockClient); - if (rc == 0) { - /* inform other threads of expected response */ - rc = MqttClient_RespList_Add(client, - (MqttPacketType)SN_MSG_TYPE_WILLMSGREQ, 0, - &will->pendResp, &will->resp.msgResp); - wm_SemUnlock(&client->lockClient); - } - if (rc != 0) { - return rc; /* Error locking client */ - } -#endif - - /* Wait for Will Message Request */ - rc = SN_Client_WaitType(client, &will->resp.msgResp, - SN_MSG_TYPE_WILLMSGREQ, 0, client->cmd_timeout_ms); - -#ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE) - return rc; -#endif - -#ifdef WOLFMQTT_MULTITHREAD - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &will->pendResp); - wm_SemUnlock(&client->lockClient); - } -#endif - - if (rc == 0) { - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } - #endif - /* Encode Will Message */ - rc = SN_Encode_WillMsg(client->tx_buf, - client->tx_buf_len, will); - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("EncodePacket: Len %d, Type %s (%d)", - rc, SN_Packet_TypeDesc(SN_MSG_TYPE_WILLMSG), - SN_MSG_TYPE_WILLMSG); - #endif - if (rc > 0) { - /* Send Will Topic packet */ - client->write.len = rc; - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc == client->write.len) { - rc = 0; - } - } - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - } - - return rc; -} - -int SN_Client_Connect(MqttClient *client, SN_Connect *mc_connect) -{ - int rc = 0; - static byte will_done; - - /* Validate required arguments */ - if ((client == NULL) || (mc_connect == NULL)) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - if (mc_connect->stat.write == MQTT_MSG_BEGIN) { - - will_done = 0; - - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } - #endif - - /* Encode the connect packet */ - rc = SN_Encode_Connect(client->tx_buf, client->tx_buf_len, mc_connect); -#ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d), ID %d, QoS %d", - rc, SN_Packet_TypeDesc(SN_MSG_TYPE_CONNECT), - SN_MSG_TYPE_CONNECT, 0, 0); -#endif - if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - client->write.len = rc; - - #ifdef WOLFMQTT_MULTITHREAD - rc = wm_SemLock(&client->lockClient); - if (rc == 0) { - /* inform other threads of expected response */ - rc = MqttClient_RespList_Add(client, - (MqttPacketType)SN_MSG_TYPE_CONNACK, 0, - &mc_connect->pendResp, &mc_connect->ack); - wm_SemUnlock(&client->lockClient); - } - if (rc != 0) { - wm_SemUnlock(&client->lockSend); - return rc; /* Error locking client */ - } - #endif - - /* Send connect packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc != client->write.len) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - - mc_connect->stat.write = MQTT_MSG_WAIT; - } - - if ((mc_connect->enable_lwt == 1) && (will_done != 1)) { - /* If the will is enabled, then the gateway requests the topic and - message in separate packets. */ - rc = SN_WillTopic(client, &mc_connect->will); - if (rc != 0) { - return rc; - } - - rc = SN_WillMessage(client, &mc_connect->will); - if (rc != 0) { - return rc; - } - will_done = 1; - } - - /* Wait for connect ack packet */ - rc = SN_Client_WaitType(client, &mc_connect->ack, - SN_MSG_TYPE_CONNACK, 0, client->cmd_timeout_ms); -#ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE) - return rc; -#endif - -#ifdef WOLFMQTT_MULTITHREAD - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &mc_connect->pendResp); - wm_SemUnlock(&client->lockClient); - } -#endif - - /* reset state */ - mc_connect->stat.write = MQTT_MSG_BEGIN; - - return rc; -} - -int SN_Client_WillTopicUpdate(MqttClient *client, SN_Will *will) -{ - int rc = 0; - - /* Validate required arguments */ - if (client == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - if (will->stat.write == MQTT_MSG_BEGIN) { - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } - #endif - - /* Encode Will Topic Update */ - rc = SN_Encode_WillTopicUpdate(client->tx_buf, - client->tx_buf_len, will); - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d)", - rc, SN_Packet_TypeDesc(SN_MSG_TYPE_WILLTOPICUPD), - SN_MSG_TYPE_WILLTOPICUPD); - #endif - if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - client->write.len = rc; - - #ifdef WOLFMQTT_MULTITHREAD - rc = wm_SemLock(&client->lockClient); - if (rc == 0) { - /* inform other threads of expected response */ - rc = MqttClient_RespList_Add(client, - (MqttPacketType)SN_MSG_TYPE_WILLTOPICRESP, - 0, &will->pendResp, &will->resp.topicResp); - wm_SemUnlock(&client->lockClient); - } - if (rc != 0) { - wm_SemUnlock(&client->lockSend); - return rc; /* Error locking client */ - } - #endif - - /* Send Will Topic Update packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc != client->write.len) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &will->pendResp); - wm_SemUnlock(&client->lockClient); - } - #endif - } - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - - will->stat.write = MQTT_MSG_WAIT; - } - - /* Wait for Will Topic Update Response packet */ - rc = SN_Client_WaitType(client, &will->resp.topicResp, - SN_MSG_TYPE_WILLTOPICRESP, 0, client->cmd_timeout_ms); -#ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE) - return rc; -#endif -#ifdef WOLFMQTT_MULTITHREAD - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &will->pendResp); - wm_SemUnlock(&client->lockClient); - } -#endif - - /* reset state */ - will->stat.write = MQTT_MSG_BEGIN; - - return rc; -} - -int SN_Client_WillMsgUpdate(MqttClient *client, SN_Will *will) -{ - int rc = 0; - - /* Validate required arguments */ - if ((client == NULL) || (will == NULL)) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - if (will->stat.write == MQTT_MSG_BEGIN) { - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } - #endif - /* Encode Will Message Update */ - rc = SN_Encode_WillMsgUpdate(client->tx_buf, - client->tx_buf_len, will); - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d)", - rc, SN_Packet_TypeDesc(SN_MSG_TYPE_WILLTOPICUPD), - SN_MSG_TYPE_WILLTOPICUPD); - #endif - if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - client->write.len = rc; - - #ifdef WOLFMQTT_MULTITHREAD - rc = wm_SemLock(&client->lockClient); - if (rc == 0) { - /* inform other threads of expected response */ - rc = MqttClient_RespList_Add(client, - (MqttPacketType)SN_MSG_TYPE_WILLMSGRESP, - 0, &will->pendResp, &will->resp.msgResp); - wm_SemUnlock(&client->lockClient); - } - if (rc != 0) { - wm_SemUnlock(&client->lockSend); - return rc; /* Error locking client */ - } - #endif - - /* Send Will Message Update packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc != client->write.len) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &will->pendResp); - wm_SemUnlock(&client->lockClient); - } - #endif - } - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - - will->stat.write = MQTT_MSG_WAIT; - } - - /* Wait for Will Message Update Response packet */ - rc = SN_Client_WaitType(client, &will->resp.msgResp, - SN_MSG_TYPE_WILLMSGRESP, 0, client->cmd_timeout_ms); -#ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE) - return rc; -#endif -#ifdef WOLFMQTT_MULTITHREAD - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &will->pendResp); - wm_SemUnlock(&client->lockClient); - } -#endif - - /* reset state */ - will->stat.write = MQTT_MSG_BEGIN; - - return rc; - -} - -int SN_Client_Subscribe(MqttClient *client, SN_Subscribe *subscribe) -{ - int rc = -1; - - /* Validate required arguments */ - if (client == NULL || subscribe == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - if (subscribe->stat.write == MQTT_MSG_BEGIN) { - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } - #endif - - /* Encode the subscribe packet */ - rc = SN_Encode_Subscribe(client->tx_buf, client->tx_buf_len, - subscribe); - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d), QoS %d", - rc, SN_Packet_TypeDesc(SN_MSG_TYPE_SUBSCRIBE), - SN_MSG_TYPE_SUBSCRIBE, subscribe->qos); - #endif - if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - client->write.len = rc; - - #ifdef WOLFMQTT_MULTITHREAD - rc = wm_SemLock(&client->lockClient); - if (rc == 0) { - /* inform other threads of expected response */ - rc = MqttClient_RespList_Add(client, - (MqttPacketType)SN_MSG_TYPE_SUBACK, subscribe->packet_id, - &subscribe->pendResp, &subscribe->subAck); - wm_SemUnlock(&client->lockClient); - } - if (rc != 0) { - wm_SemUnlock(&client->lockSend); - return rc; /* Error locking client */ - } - #endif - - /* Send subscribe packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc != client->write.len) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &subscribe->pendResp); - wm_SemUnlock(&client->lockClient); - } - #endif - return rc; - } - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - - subscribe->stat.write = MQTT_MSG_WAIT; - } - - /* Wait for subscribe ack packet */ - rc = SN_Client_WaitType(client, &subscribe->subAck, - SN_MSG_TYPE_SUBACK, subscribe->packet_id, client->cmd_timeout_ms); - -#ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE) - return rc; -#endif -#ifdef WOLFMQTT_MULTITHREAD - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &subscribe->pendResp); - wm_SemUnlock(&client->lockClient); - } -#endif - - /* reset state */ - subscribe->stat.write = MQTT_MSG_BEGIN; - - return rc; -} - -int SN_Client_Publish(MqttClient *client, SN_Publish *publish) -{ - int rc = MQTT_CODE_SUCCESS; - SN_MsgType resp_type; - - /* Validate required arguments */ - if (client == NULL || publish == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - switch (publish->stat.write) - { - case MQTT_MSG_BEGIN: - { - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } - #endif - - /* Encode the publish packet */ - rc = SN_Encode_Publish(client->tx_buf, client->tx_buf_len, - publish); - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d), ID %d," - " QoS %d", - rc, SN_Packet_TypeDesc(SN_MSG_TYPE_PUBLISH), - SN_MSG_TYPE_PUBLISH, publish->packet_id, - publish->qos); - #endif - if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - - client->write.len = rc; - publish->buffer_pos = 0; - - #ifdef WOLFMQTT_MULTITHREAD - if ((publish->qos == MQTT_QOS_1) || - (publish->qos == MQTT_QOS_2)) { - resp_type = (publish->qos == MQTT_QOS_1) ? - SN_MSG_TYPE_PUBACK : - SN_MSG_TYPE_PUBCOMP; - - rc = wm_SemLock(&client->lockClient); - if (rc == 0) { - /* inform other threads of expected response */ - rc = MqttClient_RespList_Add(client, - (MqttPacketType)resp_type, publish->packet_id, - &publish->pendResp, &publish->resp); - wm_SemUnlock(&client->lockClient); - } - if (rc != 0) { - wm_SemUnlock(&client->lockSend); - return rc; /* Error locking client */ - } - } - #endif - - publish->stat.write = MQTT_MSG_HEADER; - } - FALL_THROUGH; - - case MQTT_MSG_HEADER: - case MQTT_MSG_PAYLOAD: - case MQTT_MSG_PAYLOAD2: - { - /* Send packet and payload */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - #ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE) - return rc; - #endif - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - - if (rc < 0) { - #ifdef WOLFMQTT_MULTITHREAD - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &publish->pendResp); - wm_SemUnlock(&client->lockClient); - } - #endif - return rc; - } - - if (rc == client->write.len) { - rc = MQTT_CODE_SUCCESS; - } - else { - rc = -1; - } - - /* if not expecting a reply, the reset state and exit */ - if ((publish->qos == MQTT_QOS_0) || - (publish->qos == MQTT_QOS_3)) { - break; - } - - publish->stat.write = MQTT_MSG_WAIT; - } - FALL_THROUGH; - - case MQTT_MSG_WAIT: - { - /* Handle QoS */ - if ((publish->qos == MQTT_QOS_1) || - (publish->qos == MQTT_QOS_2)) { - - /* Determine packet type to wait for */ - resp_type = (publish->qos == MQTT_QOS_1) ? - SN_MSG_TYPE_PUBACK : - SN_MSG_TYPE_PUBCOMP; - - /* Wait for publish response packet */ - rc = SN_Client_WaitType(client, &publish->resp, - resp_type, publish->packet_id, client->cmd_timeout_ms); - #ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE) - break; - #endif - #ifdef WOLFMQTT_MULTITHREAD - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &publish->pendResp); - wm_SemUnlock(&client->lockClient); - } - #endif - - publish->return_code = publish->resp.return_code; - } - - break; - } - - case MQTT_MSG_ACK: - case MQTT_MSG_AUTH: - default: - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("SN_Client_Publish: Invalid state %d!", - publish->stat.write); - #endif - rc = MQTT_TRACE_ERROR(MQTT_CODE_ERROR_STAT); - break; - } /* switch (publish->stat) */ - - /* reset state */ -#ifdef WOLFMQTT_NONBLOCK - if (rc != MQTT_CODE_CONTINUE) -#endif - { - publish->stat.write = MQTT_MSG_BEGIN; - } - if (rc > 0) { - rc = MQTT_CODE_SUCCESS; - } - - return rc; -} - -int SN_Client_Unsubscribe(MqttClient *client, SN_Unsubscribe *unsubscribe) -{ - int rc; - - /* Validate required arguments */ - if (client == NULL || unsubscribe == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - if (unsubscribe->stat.write == MQTT_MSG_BEGIN) { - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } - #endif - - /* Encode the subscribe packet */ - rc = SN_Encode_Unsubscribe(client->tx_buf, client->tx_buf_len, - unsubscribe); - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d)", - rc, SN_Packet_TypeDesc(SN_MSG_TYPE_UNSUBSCRIBE), - SN_MSG_TYPE_UNSUBSCRIBE); - #endif - if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - client->write.len = rc; - - #ifdef WOLFMQTT_MULTITHREAD - rc = wm_SemLock(&client->lockClient); - if (rc == 0) { - /* inform other threads of expected response */ - rc = MqttClient_RespList_Add(client, - (MqttPacketType)SN_MSG_TYPE_UNSUBACK, - 0, &unsubscribe->pendResp, &unsubscribe->ack); - wm_SemUnlock(&client->lockClient); - } - if (rc != 0) { - wm_SemUnlock(&client->lockSend); - return rc; /* Error locking client */ - } - #endif - - /* Send unsubscribe packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc != client->write.len) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &unsubscribe->pendResp); - wm_SemUnlock(&client->lockClient); - } - #endif - } - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - - unsubscribe->stat.write = MQTT_MSG_WAIT; - } - - /* Wait for unsubscribe ack packet */ - rc = SN_Client_WaitType(client, &unsubscribe->ack, - SN_MSG_TYPE_UNSUBACK, unsubscribe->packet_id, - client->cmd_timeout_ms); - #ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE) - return rc; - #endif - #ifdef WOLFMQTT_MULTITHREAD - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &unsubscribe->pendResp); - wm_SemUnlock(&client->lockClient); - } - #endif - - /* reset state */ - unsubscribe->stat.write = MQTT_MSG_BEGIN; - - return rc; -} - -int SN_Client_Register(MqttClient *client, SN_Register *regist) -{ - int rc; - - /* Validate required arguments */ - if (client == NULL || regist == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - if (regist->stat.write == MQTT_MSG_BEGIN) { - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } - #endif - - /* Encode the register packet */ - rc = SN_Encode_Register(client->tx_buf, client->tx_buf_len, regist); - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d)", - rc, SN_Packet_TypeDesc(SN_MSG_TYPE_REGISTER), - SN_MSG_TYPE_REGISTER); - #endif - if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - client->write.len = rc; - - #ifdef WOLFMQTT_MULTITHREAD - rc = wm_SemLock(&client->lockClient); - if (rc == 0) { - /* inform other threads of expected response */ - rc = MqttClient_RespList_Add(client, - (MqttPacketType)SN_MSG_TYPE_REGACK, - regist->packet_id, ®ist->pendResp, ®ist->regack); - wm_SemUnlock(&client->lockClient); - } - if (rc != 0) { - wm_SemUnlock(&client->lockSend); - return rc; /* Error locking client */ - } - #endif - - /* Send register packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc != client->write.len) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, ®ist->pendResp); - wm_SemUnlock(&client->lockClient); - } - #endif - return rc; - } - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - - regist->stat.write = MQTT_MSG_WAIT; - } - - /* Wait for register acknowledge packet */ - rc = SN_Client_WaitType(client, ®ist->regack, - SN_MSG_TYPE_REGACK, regist->packet_id, client->cmd_timeout_ms); -#ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE) - return rc; -#endif -#ifdef WOLFMQTT_MULTITHREAD - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, ®ist->pendResp); - wm_SemUnlock(&client->lockClient); - } -#endif - - /* reset state */ - regist->stat.write = MQTT_MSG_BEGIN; - - return rc; -} - -int SN_Client_Ping(MqttClient *client, SN_PingReq *ping) -{ - int rc; - SN_PingReq loc_ping; - - /* Validate required arguments */ - if (client == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - if (ping == NULL) { - XMEMSET(&loc_ping, 0, sizeof(SN_PingReq)); - ping = &loc_ping; - } - - if (ping->stat.write == MQTT_MSG_BEGIN) { - #ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } - #endif - - /* Encode the ping packet as a request */ - rc = SN_Encode_Ping(client->tx_buf, client->tx_buf_len, ping, - SN_MSG_TYPE_PING_REQ); - #ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d)", - rc, SN_Packet_TypeDesc(SN_MSG_TYPE_PING_REQ), - SN_MSG_TYPE_PING_REQ); - #endif - if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - client->write.len = rc; - - #ifdef WOLFMQTT_MULTITHREAD - rc = wm_SemLock(&client->lockClient); - if (rc == 0) { - /* inform other threads of expected response */ - rc = MqttClient_RespList_Add(client, - (MqttPacketType)SN_MSG_TYPE_PING_RESP, 0, - &ping->pendResp, NULL); - wm_SemUnlock(&client->lockClient); - } - if (rc != 0) { - wm_SemUnlock(&client->lockSend); - return rc; /* Error locking client */ - } - #endif - - /* Send ping req packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc != client->write.len) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &ping->pendResp); - wm_SemUnlock(&client->lockClient); - } - #endif - return rc; - } - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - - ping->stat.write = MQTT_MSG_WAIT; - } - - /* Wait for ping resp packet */ - rc = SN_Client_WaitType(client, ping, - SN_MSG_TYPE_PING_RESP, 0, client->cmd_timeout_ms); -#ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE) - return rc; -#endif -#ifdef WOLFMQTT_MULTITHREAD - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &ping->pendResp); - wm_SemUnlock(&client->lockClient); - } -#endif - - /* reset state */ - ping->stat.write = MQTT_MSG_BEGIN; - - return rc; -} - -int SN_Client_Disconnect(MqttClient *client) -{ - return SN_Client_Disconnect_ex(client, NULL); -} - -int SN_Client_Disconnect_ex(MqttClient *client, SN_Disconnect *disconnect) -{ - int rc; - - /* Validate required arguments */ - if (client == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - -#ifdef WOLFMQTT_MULTITHREAD - /* Lock send socket mutex */ - rc = wm_SemLock(&client->lockSend); - if (rc != 0) { - return rc; - } -#endif - - /* Encode the disconnect packet */ - rc = SN_Encode_Disconnect(client->tx_buf, client->tx_buf_len, disconnect); -#ifdef WOLFMQTT_DEBUG_CLIENT - PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d)", - rc, SN_Packet_TypeDesc(SN_MSG_TYPE_DISCONNECT), - SN_MSG_TYPE_DISCONNECT); -#endif - if (rc <= 0) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - #endif - return rc; - } - client->write.len = rc; - -#ifdef WOLFMQTT_MULTITHREAD - if ((disconnect != NULL) && (disconnect->sleepTmr != 0)) { - rc = wm_SemLock(&client->lockClient); - if (rc == 0) { - /* inform other threads of expected response */ - rc = MqttClient_RespList_Add(client, - (MqttPacketType)SN_MSG_TYPE_DISCONNECT, 0, - &disconnect->pendResp, NULL); - wm_SemUnlock(&client->lockClient); - } - if (rc != 0) { - wm_SemUnlock(&client->lockSend); - return rc; /* Error locking client */ - } - } -#endif - - /* Send disconnect packet */ - rc = MqttPacket_Write(client, client->tx_buf, client->write.len); - if (rc != client->write.len) { - #ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &disconnect->pendResp); - wm_SemUnlock(&client->lockClient); - } - #endif - return rc; - } -#ifdef WOLFMQTT_MULTITHREAD - wm_SemUnlock(&client->lockSend); -#endif - - rc = MQTT_CODE_SUCCESS; - - /* If sleep was set, wait for response disconnect packet */ - if ((disconnect != NULL) && (disconnect->sleepTmr != 0)) { - rc = SN_Client_WaitType(client, disconnect, - SN_MSG_TYPE_DISCONNECT, 0, client->cmd_timeout_ms); - #ifdef WOLFMQTT_NONBLOCK - if (rc == MQTT_CODE_CONTINUE) - return rc; - #endif - #ifdef WOLFMQTT_MULTITHREAD - if (wm_SemLock(&client->lockClient) == 0) { - MqttClient_RespList_Remove(client, &disconnect->pendResp); - wm_SemUnlock(&client->lockClient); - } - #endif - } - - return rc; -} - -int SN_Client_WaitMessage_ex(MqttClient *client, SN_Object* packet_obj, - int timeout_ms) -{ - return SN_Client_WaitType(client, packet_obj, - SN_MSG_TYPE_ANY, 0, timeout_ms); -} - -int SN_Client_WaitMessage(MqttClient *client, int timeout_ms) -{ - if (client == NULL) - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - return SN_Client_WaitMessage_ex(client, &client->msgSN, timeout_ms); -} - -#endif /* defined WOLFMQTT_SN */ diff --git a/src/mqtt_packet.c b/src/mqtt_packet.c index 895449ad5..16e6d45d7 100644 --- a/src/mqtt_packet.c +++ b/src/mqtt_packet.c @@ -128,6 +128,8 @@ static const struct MqttPropMatrix gPropMatrix[] = { /* WOLFMQTT_DYN_PROP allows property allocation using malloc */ #ifndef WOLFMQTT_DYN_PROP + +/* Maximum number of active static properties - overridable */ #ifndef MQTT_MAX_PROPS #define MQTT_MAX_PROPS 30 #endif @@ -135,11 +137,12 @@ static const struct MqttPropMatrix gPropMatrix[] = { /* Property structure allocation array. Property type equal to zero indicates unused element. */ static MqttProp clientPropStack[MQTT_MAX_PROPS]; -#endif /* WOLFMQTT_DYN_PROP */ #ifdef WOLFMQTT_MULTITHREAD +static volatile int clientPropStack_lockInit = 0; static wm_Sem clientPropStack_lock; #endif +#endif /* WOLFMQTT_DYN_PROP */ #endif /* WOLFMQTT_V5 */ /* Positive return value is header length, zero or negative indicates error */ @@ -365,7 +368,6 @@ int MqttEncode_Props(MqttPacketType packet, MqttProp* props, byte* buf) { int rc = 0, tmp; MqttProp* cur_prop = props; - int num_props = 0; /* TODO: Check against max size. Sometimes all properties are not expected to be added */ @@ -469,8 +471,6 @@ int MqttEncode_Props(MqttPacketType packet, MqttProp* props, byte* buf) } } - num_props++; - cur_prop = cur_prop->next; } @@ -488,7 +488,6 @@ int MqttDecode_Props(MqttPacketType packet, MqttProp** props, byte* pbuf, MqttProp* cur_prop; byte* buf = pbuf; - *props = NULL; total = 0; while (((int)prop_len > 0) && (rc >= 0)) @@ -606,15 +605,20 @@ int MqttDecode_Props(MqttPacketType packet, MqttProp** props, byte* pbuf, buf += tmp; total += tmp; prop_len -= (word32)tmp; - - tmp = MqttDecode_String(buf, - (const char**)&cur_prop->data_str2.str, - &cur_prop->data_str2.len); - if (cur_prop->data_str2.len <= - (buf_len - (buf - pbuf))) { - buf += tmp; - total += tmp; - prop_len -= (word32)tmp; + if ((buf_len - (buf - pbuf)) > 0) { + tmp = MqttDecode_String(buf, + (const char**)&cur_prop->data_str2.str, + &cur_prop->data_str2.len); + if (cur_prop->data_str2.len <= + (buf_len - (buf - pbuf))) { + buf += tmp; + total += tmp; + prop_len -= (word32)tmp; + } + else { + /* Invalid length */ + rc = MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PROPERTY); + } } else { /* Invalid length */ @@ -628,11 +632,8 @@ int MqttDecode_Props(MqttPacketType packet, MqttProp** props, byte* pbuf, break; } case MQTT_DATA_TYPE_NONE: - PRINTF("DATA TYPE NONE"); - break; default: { - PRINTF("INVALID DATA TYPE"); /* Invalid property data type */ rc = MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PROPERTY); break; @@ -824,6 +825,7 @@ int MqttDecode_ConnectAck(byte *rx_buf, int rx_buf_len, connect_ack->return_code = *rx_payload++; #ifdef WOLFMQTT_V5 + connect_ack->props = NULL; if (connect_ack->protocol_level >= MQTT_CONNECT_PROTOCOL_LEVEL_5) { word32 props_len = 0; int tmp; @@ -1004,6 +1006,7 @@ int MqttDecode_Publish(byte *rx_buf, int rx_buf_len, MqttPublish *publish) } #ifdef WOLFMQTT_V5 + publish->props = NULL; if (publish->protocol_level >= MQTT_CONNECT_PROTOCOL_LEVEL_5) { word32 props_len = 0; int tmp; @@ -1152,6 +1155,7 @@ int MqttDecode_PublishResp(byte* rx_buf, int rx_buf_len, byte type, rx_payload += MqttDecode_Num(rx_payload, &publish_resp->packet_id); #ifdef WOLFMQTT_V5 + publish_resp->props = NULL; if (publish_resp->protocol_level >= MQTT_CONNECT_PROTOCOL_LEVEL_5) { if (remain_len > MQTT_DATA_LEN_SIZE) { /* Decode the Reason Code */ @@ -1303,6 +1307,7 @@ int MqttDecode_SubscribeAck(byte* rx_buf, int rx_buf_len, rx_payload += MqttDecode_Num(rx_payload, &subscribe_ack->packet_id); #ifdef WOLFMQTT_V5 + subscribe_ack->props = NULL; if ((subscribe_ack->protocol_level >= MQTT_CONNECT_PROTOCOL_LEVEL_5) && (remain_len > MQTT_DATA_LEN_SIZE)) { word32 props_len = 0; @@ -1441,6 +1446,7 @@ int MqttDecode_UnsubscribeAck(byte *rx_buf, int rx_buf_len, if (unsubscribe_ack) { rx_payload += MqttDecode_Num(rx_payload, &unsubscribe_ack->packet_id); #ifdef WOLFMQTT_V5 + unsubscribe_ack->props = NULL; if (unsubscribe_ack->protocol_level >= MQTT_CONNECT_PROTOCOL_LEVEL_5) { if (remain_len > MQTT_DATA_LEN_SIZE) { word32 props_len = 0; @@ -1623,6 +1629,7 @@ int MqttDecode_Disconnect(byte *rx_buf, int rx_buf_len, MqttDisconnect *disc) } rx_payload = &rx_buf[header_len]; + disc->props = NULL; if (remain_len > 0) { /* Decode variable header */ disc->reason_code = *rx_payload++; @@ -1741,6 +1748,8 @@ int MqttDecode_Auth(byte *rx_buf, int rx_buf_len, MqttAuth *auth) if ((auth->reason_code == MQTT_REASON_SUCCESS) || (auth->reason_code == MQTT_REASON_CONT_AUTH)) { + auth->props = NULL; + /* Decode Length of Properties */ tmp = MqttDecode_Vbi(rx_payload, &props_len, (word32)(rx_buf_len - (rx_payload - rx_buf))); @@ -1793,20 +1802,28 @@ int MqttDecode_Auth(byte *rx_buf, int rx_buf_len, MqttAuth *auth) return header_len + remain_len; } -int MqttProps_Init(void) { -#ifdef WOLFMQTT_MULTITHREAD - return wm_SemInit(&clientPropStack_lock); -#else - return 0; +int MqttProps_Init(void) +{ + int ret = MQTT_CODE_SUCCESS; +#if !defined(WOLFMQTT_DYN_PROP) && defined(WOLFMQTT_MULTITHREAD) + if (clientPropStack_lockInit == 0) { + clientPropStack_lockInit++; + ret = wm_SemInit(&clientPropStack_lock); + } #endif + return ret; } -int MqttProps_ShutDown(void) { -#ifdef WOLFMQTT_MULTITHREAD - return wm_SemFree(&clientPropStack_lock); -#else - return 0; +int MqttProps_ShutDown(void) +{ + int ret = MQTT_CODE_SUCCESS; +#if !defined(WOLFMQTT_DYN_PROP) && defined(WOLFMQTT_MULTITHREAD) + clientPropStack_lockInit--; + if (clientPropStack_lockInit == 0) { + ret = wm_SemFree(&clientPropStack_lock); + } #endif + return ret; } /* Add property */ @@ -1821,7 +1838,7 @@ MqttProp* MqttProps_Add(MqttProp **head) return NULL; } -#ifdef WOLFMQTT_MULTITHREAD +#if !defined(WOLFMQTT_DYN_PROP) && defined(WOLFMQTT_MULTITHREAD) if (wm_SemLock(&clientPropStack_lock) != 0) { return NULL; } @@ -1870,7 +1887,7 @@ MqttProp* MqttProps_Add(MqttProp **head) (void)MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PROPERTY); } -#ifdef WOLFMQTT_MULTITHREAD +#if !defined(WOLFMQTT_DYN_PROP) && defined(WOLFMQTT_MULTITHREAD) (void)wm_SemUnlock(&clientPropStack_lock); #endif @@ -1881,7 +1898,7 @@ MqttProp* MqttProps_Add(MqttProp **head) int MqttProps_Free(MqttProp *head) { int ret = MQTT_CODE_SUCCESS; -#ifdef WOLFMQTT_MULTITHREAD +#if !defined(WOLFMQTT_DYN_PROP) && defined(WOLFMQTT_MULTITHREAD) if ((ret = wm_SemLock(&clientPropStack_lock)) != 0) { return ret; } @@ -1898,7 +1915,7 @@ int MqttProps_Free(MqttProp *head) head = tmp; #endif } -#ifdef WOLFMQTT_MULTITHREAD +#if !defined(WOLFMQTT_DYN_PROP) && defined(WOLFMQTT_MULTITHREAD) (void)wm_SemUnlock(&clientPropStack_lock); #endif return ret; @@ -1906,7 +1923,7 @@ int MqttProps_Free(MqttProp *head) #endif /* WOLFMQTT_V5 */ -static int MqttPacket_HandleNetError(MqttClient *client, int rc) +int MqttPacket_HandleNetError(MqttClient *client, int rc) { (void)client; #ifdef WOLFMQTT_DISCONNECT_CB @@ -1952,7 +1969,6 @@ int MqttPacket_Read(MqttClient *client, byte* rx_buf, int rx_buf_len, { case MQTT_PK_BEGIN: { - client->read.pos = 0; client->packet.header_len = MQTT_PACKET_HEADER_MIN_SIZE; client->packet.remain_len = 0; @@ -1974,7 +1990,9 @@ int MqttPacket_Read(MqttClient *client, byte* rx_buf, int rx_buf_len, int i; client->packet.stat = MQTT_PK_READ_HEAD; - for (i = 0; i < MQTT_PACKET_MAX_LEN_BYTES; i++) { + for (i = (client->packet.header_len - MQTT_PACKET_HEADER_MIN_SIZE); + i < MQTT_PACKET_MAX_LEN_BYTES; + i++) { /* Check if another byte is needed */ if ((header->len[i] & MQTT_PACKET_LEN_ENCODE_MASK) == 0) { /* Variable byte length can be determined */ @@ -2095,1551 +2113,3 @@ const char* MqttPacket_TypeDesc(MqttPacketType packet_type) } #endif - -#ifdef WOLFMQTT_SN -const char* SN_Packet_TypeDesc(SN_MsgType packet_type) -{ - switch (packet_type) { - case SN_MSG_TYPE_ADVERTISE: - return "Advertise"; - case SN_MSG_TYPE_SEARCHGW: - return "Search gateway"; - case SN_MSG_TYPE_GWINFO: - return "Gateway info"; - case SN_MSG_TYPE_CONNECT: - return "Connect"; - case SN_MSG_TYPE_CONNACK: - return "Connect Ack"; - case SN_MSG_TYPE_WILLTOPICREQ: - return "Will topic request"; - case SN_MSG_TYPE_WILLTOPIC: - return "Will topic set"; - case SN_MSG_TYPE_WILLMSGREQ: - return "Will message request"; - case SN_MSG_TYPE_WILLMSG: - return "Will message set"; - case SN_MSG_TYPE_REGISTER: - return "Register"; - case SN_MSG_TYPE_REGACK: - return "Register Ack"; - case SN_MSG_TYPE_PUBLISH: - return "Publish"; - case SN_MSG_TYPE_PUBACK: - return "Publish Ack"; - case SN_MSG_TYPE_PUBCOMP: - return "Publish complete"; - case SN_MSG_TYPE_PUBREC: - return "Publish Received"; - case SN_MSG_TYPE_PUBREL: - return "Publish Release"; - case SN_MSG_TYPE_SUBSCRIBE: - return "Subscribe"; - case SN_MSG_TYPE_SUBACK: - return "Subscribe Ack"; - case SN_MSG_TYPE_UNSUBSCRIBE: - return "Unsubscribe"; - case SN_MSG_TYPE_UNSUBACK: - return "Unsubscribe Ack"; - case SN_MSG_TYPE_PING_REQ: - return "Ping Req"; - case SN_MSG_TYPE_PING_RESP: - return "Ping Resp"; - case SN_MSG_TYPE_DISCONNECT: - return "Disconnect"; - case SN_MSG_TYPE_WILLTOPICUPD: - return "Will topic update"; - case SN_MSG_TYPE_WILLTOPICRESP: - return "WIll topic response"; - case SN_MSG_TYPE_WILLMSGUPD: - return "Will message update"; - case SN_MSG_TYPE_WILLMSGRESP: - return "Will message response"; - case SN_MSG_TYPE_ENCAPMSG: - return "Encapsulated message"; - case SN_MSG_TYPE_ANY: - return "Any"; - default: - break; - } - return "Unknown"; -} - -int SN_Decode_Header(byte *rx_buf, int rx_buf_len, - SN_MsgType* p_packet_type, word16* p_packet_id) -{ - SN_MsgType packet_type; - word16 total_len; - - if (rx_buf == NULL || rx_buf_len < MQTT_PACKET_HEADER_MIN_SIZE) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Decode fixed header */ - total_len = *rx_buf++; - if (total_len == SN_PACKET_LEN_IND) { - /* The length is stored in the next two bytes */ - rx_buf += MqttDecode_Num(rx_buf, &total_len); - } - - if (total_len > rx_buf_len) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - /* Message Type */ - packet_type = (SN_MsgType)*rx_buf++; - - if (p_packet_type) - *p_packet_type = packet_type; - - if (p_packet_id) { - switch(packet_type) { - case SN_MSG_TYPE_REGACK: - case SN_MSG_TYPE_PUBACK: - /* octet 4-5 */ - MqttDecode_Num(rx_buf + 2, p_packet_id); - break; - case SN_MSG_TYPE_PUBCOMP: - case SN_MSG_TYPE_PUBREC: - case SN_MSG_TYPE_PUBREL: - case SN_MSG_TYPE_UNSUBACK: - /* octet 2-3 */ - MqttDecode_Num(rx_buf, p_packet_id); - break; - case SN_MSG_TYPE_SUBACK: - /* octet 5-6 */ - MqttDecode_Num(rx_buf + 3, p_packet_id); - break; - case SN_MSG_TYPE_ADVERTISE: - case SN_MSG_TYPE_SEARCHGW: - case SN_MSG_TYPE_GWINFO: - case SN_MSG_TYPE_CONNECT: - case SN_MSG_TYPE_CONNACK: - case SN_MSG_TYPE_WILLTOPICREQ: - case SN_MSG_TYPE_WILLTOPIC: - case SN_MSG_TYPE_WILLMSGREQ: - case SN_MSG_TYPE_WILLMSG: - case SN_MSG_TYPE_REGISTER: - case SN_MSG_TYPE_PUBLISH: - case SN_MSG_TYPE_SUBSCRIBE: - case SN_MSG_TYPE_UNSUBSCRIBE: - case SN_MSG_TYPE_PING_REQ: - case SN_MSG_TYPE_PING_RESP: - case SN_MSG_TYPE_DISCONNECT: - case SN_MSG_TYPE_WILLTOPICUPD: - case SN_MSG_TYPE_WILLTOPICRESP: - case SN_MSG_TYPE_WILLMSGUPD: - case SN_MSG_TYPE_WILLMSGRESP: - case SN_MSG_TYPE_ENCAPMSG: - case SN_MSG_TYPE_RESERVED: - default: - *p_packet_id = 0; - break; - } - } - - return (int)total_len; -} - -int SN_Decode_Advertise(byte *rx_buf, int rx_buf_len, SN_Advertise *gw_info) -{ - int total_len; - byte *rx_payload = rx_buf, type; - - /* Validate required arguments */ - if (rx_buf == NULL || rx_buf_len <= 0) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Decode fixed header */ - total_len = *rx_payload++; - - /* Check message type */ - type = *rx_payload++; - if (total_len != 5) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); - } - - if (type != SN_MSG_TYPE_ADVERTISE) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); - } - - /* Decode gateway info */ - if (gw_info != NULL) { - gw_info->gwId = *rx_payload++; - - rx_payload += MqttDecode_Num(rx_payload, &gw_info->duration); - } - (void)rx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Encode_SearchGW(byte *tx_buf, int tx_buf_len, byte hops) -{ - int total_len; - byte *tx_payload = tx_buf; - - /* Packet length is not variable */ - total_len = 3; - - /* Validate required arguments */ - if (tx_buf == NULL || tx_buf_len < total_len) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Encode length */ - *tx_payload++ = total_len; - - /* Encode message type */ - *tx_payload++ = SN_MSG_TYPE_SEARCHGW; - - /* Encode radius */ - *tx_payload++ = hops; - (void)tx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Decode_GWInfo(byte *rx_buf, int rx_buf_len, SN_GwInfo *gw_info) -{ - int total_len; - byte *rx_payload = rx_buf, type; - - /* Validate required arguments */ - if (rx_buf == NULL || rx_buf_len <= 0) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Decode fixed header */ - total_len = *rx_payload++; - if (total_len == SN_PACKET_LEN_IND) { - /* The length is stored in the next two bytes */ - rx_payload += MqttDecode_Num(rx_payload, (word16*)&total_len); - } - - if (total_len > rx_buf_len) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - if (total_len < 3) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); - } - /* Check message type */ - type = *rx_payload++; - if (type != SN_MSG_TYPE_GWINFO) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); - } - - /* Decode gateway info */ - if (gw_info != NULL) { - gw_info->gwId = *rx_payload++; - - /* TODO: validate size of gwAddr */ - if (total_len - 3 > 0) { - /* The gateway address is only present if sent by a client */ - XMEMCPY(gw_info->gwAddr, rx_payload, total_len - 3); - } - } - (void)rx_payload; - - /* Return total length of packet */ - return total_len; -} - -/* Packet Type Encoders/Decoders */ -int SN_Encode_Connect(byte *tx_buf, int tx_buf_len, SN_Connect *mc_connect) -{ - word16 total_len, id_len; - byte flags = 0; - byte *tx_payload = tx_buf; - - /* Validate required arguments */ - if ((tx_buf == NULL) || (mc_connect == NULL) || - (mc_connect->client_id == NULL) || (mc_connect->protocol_level == 0)) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Determine packet length */ - total_len = 6; /* Len + Message Type + Flags + ProtocolID + Duration(2) */ - - /* Client ID size */ - id_len = (word16)XSTRLEN(mc_connect->client_id); - id_len = (id_len <= SN_CLIENTID_MAX_LEN) ? id_len : SN_CLIENTID_MAX_LEN; - - total_len += id_len; - - if (total_len > SN_PACKET_MAX_SMALL_SIZE) { - total_len += 2; /* Store len in three bytes */ - } - - if (total_len > tx_buf_len) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - /* Encode length */ - if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { - *tx_payload++ = (byte)total_len; - } - else { - *tx_payload++ = SN_PACKET_LEN_IND; - tx_payload += MqttEncode_Num(tx_payload, total_len); - } - - /* Encode message type */ - *tx_payload++ = SN_MSG_TYPE_CONNECT; - - /* Encode flags */ - if (mc_connect->clean_session) { - flags |= SN_PACKET_FLAG_CLEANSESSION; - } - if (mc_connect->enable_lwt) { - flags |= SN_PACKET_FLAG_WILL; - } - *tx_payload++ = flags; - - /* Protocol version */ - *tx_payload++ = mc_connect->protocol_level; - - /* Encode duration (keep-alive) */ - tx_payload += MqttEncode_Num(tx_payload, mc_connect->keep_alive_sec); - - /* Encode Client ID */ - XMEMCPY(tx_payload, mc_connect->client_id, id_len); - tx_payload += id_len; - (void)tx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Decode_WillTopicReq(byte *rx_buf, int rx_buf_len) -{ - int total_len; - byte *rx_payload = rx_buf, type; - - /* Validate required arguments */ - if (rx_buf == NULL || rx_buf_len <= 0) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Length and MsgType */ - total_len = *rx_payload++; - if (total_len != 2) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); - } - - type = *rx_payload++; - if (type != SN_MSG_TYPE_WILLTOPICREQ) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); - } - (void)rx_payload; - - /* Return total length of packet */ - return total_len; -} - -/* An empty WILLTOPIC message is a WILLTOPIC message without Flags and - WillTopic field (i.e. it is exactly 2 octets long). It is used by a client - to delete the Will topic and the Will message stored in the server */ -int SN_Encode_WillTopic(byte *tx_buf, int tx_buf_len, SN_Will *willTopic) -{ - int total_len; - byte *tx_payload, flags = 0; - - /* Validate required arguments */ - if (tx_buf == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Length and MsgType */ - total_len = 2; - - /* Determine packet length */ - if (willTopic != NULL) { - /* Will Topic is a string */ - total_len += (int)XSTRLEN(willTopic->willTopic); - - /* Flags */ - total_len++; - - if (total_len > SN_PACKET_MAX_SMALL_SIZE) { - /* Length is stored in bytes 1 and 2 */ - total_len += 2; - } - } - - if (total_len > tx_buf_len) { - /* Buffer too small */ - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - /* Encode length */ - tx_payload = tx_buf; - - if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { - *tx_payload++ = total_len; - } - else { - *tx_payload++ = SN_PACKET_LEN_IND; - tx_payload += MqttEncode_Num(tx_payload, total_len); - } - - /* Encode message type */ - *tx_payload++ = SN_MSG_TYPE_WILLTOPIC; - - if (willTopic != NULL) { - int will_len; - - /* Encode flags */ - flags |= ((willTopic->qos << SN_PACKET_FLAG_QOS_SHIFT) & - SN_PACKET_FLAG_QOS_MASK); - flags |= (willTopic->retain != 0) ? SN_PACKET_FLAG_RETAIN : 0; - *tx_payload++ = flags; - - /* Encode Will Topic */ - will_len = (int)XSTRLEN(willTopic->willTopic); - XMEMCPY(tx_payload, willTopic->willTopic, will_len); - tx_payload += will_len; - } - (void)tx_payload; - - return total_len; -} - -int SN_Decode_WillMsgReq(byte *rx_buf, int rx_buf_len) -{ - int total_len; - byte *rx_payload = rx_buf, type; - - /* Validate required arguments */ - if (rx_buf == NULL || rx_buf_len <= 0) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Decode fixed header */ - total_len = *rx_payload++; - - /* Length and MsgType */ - if (total_len != 2){ - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); - } - - /* Message Type */ - type = *rx_payload++; - if (type != SN_MSG_TYPE_WILLMSGREQ) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); - } - (void)rx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Encode_WillMsg(byte *tx_buf, int tx_buf_len, SN_Will *willMsg) -{ - int total_len; - byte *tx_payload; - - /* Validate required arguments */ - if ((tx_buf == NULL) || (willMsg == NULL)) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Length and MsgType */ - total_len = 2; - - /* Determine packet length */ - /* Add Will Message len */ - total_len += willMsg->willMsgLen; - - if (total_len > SN_PACKET_MAX_SMALL_SIZE) { - /* Length is stored in bytes 1 and 2 */ - total_len += 2; - } - - if (total_len > tx_buf_len) { - /* Buffer too small */ - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - /* Encode length */ - tx_payload = tx_buf; - - if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { - *tx_payload++ = total_len; - } - else { - *tx_payload++ = SN_PACKET_LEN_IND; - tx_payload += MqttEncode_Num(tx_payload, total_len); - } - - /* Encode message type */ - *tx_payload++ = SN_MSG_TYPE_WILLMSG; - - /* Encode Will Message */ - XMEMCPY(tx_payload, willMsg->willMsg, willMsg->willMsgLen); - tx_payload += willMsg->willMsgLen; - (void)tx_payload; - - return total_len; -} - -int SN_Encode_WillTopicUpdate(byte *tx_buf, int tx_buf_len, SN_Will *willTopic) -{ - int total_len; - byte *tx_payload, flags = 0; - - /* Validate required arguments */ - if (tx_buf == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Length and MsgType */ - total_len = 2; - - /* Determine packet length */ - if (willTopic != NULL) { - /* Will Topic is a string */ - total_len += (int)XSTRLEN(willTopic->willTopic); - - /* Flags */ - total_len++; - - if (total_len > SN_PACKET_MAX_SMALL_SIZE) { - /* Length is stored in bytes 1 and 2 */ - total_len += 2; - } - } - - if (total_len > tx_buf_len) { - /* Buffer too small */ - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - /* Encode length */ - tx_payload = tx_buf; - - if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { - *tx_payload++ = total_len; - } - else { - *tx_payload++ = SN_PACKET_LEN_IND; - tx_payload += MqttEncode_Num(tx_payload, total_len); - } - - /* Encode message type */ - *tx_payload++ = SN_MSG_TYPE_WILLTOPICUPD; - - if (willTopic != NULL) { - int will_len; - - /* Encode flags */ - flags |= ((willTopic->qos << SN_PACKET_FLAG_QOS_SHIFT) & - SN_PACKET_FLAG_QOS_MASK); - flags |= (willTopic->retain != 0) ? SN_PACKET_FLAG_RETAIN : 0; - *tx_payload++ = flags; - - /* Encode Will Topic */ - will_len = (int)XSTRLEN(willTopic->willTopic); - XMEMCPY(tx_payload, willTopic->willTopic, will_len); - tx_payload += will_len; - } - (void)tx_payload; - - return total_len; - -} - -int SN_Decode_WillTopicResponse(byte *rx_buf, int rx_buf_len, byte *ret_code) -{ - int total_len; - byte *rx_payload = rx_buf, type; - - /* Validate required arguments */ - if (rx_buf == NULL || rx_buf_len <= 0) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Decode fixed header */ - total_len = *rx_payload++; - if (total_len != 3) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); - } - - type = *rx_payload++; - if (type != SN_MSG_TYPE_WILLTOPICRESP) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); - } - - /* Return Code */ - *ret_code = *rx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Encode_WillMsgUpdate(byte *tx_buf, int tx_buf_len, SN_Will *willMsg) -{ - int total_len; - byte *tx_payload; - - /* Validate required arguments */ - if ((tx_buf == NULL) || (willMsg == NULL)) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Length and MsgType */ - total_len = 2; - - /* Determine packet length */ - /* Add Will Message len */ - total_len += willMsg->willMsgLen; - - if (total_len > SN_PACKET_MAX_SMALL_SIZE) { - /* Length is stored in bytes 1 and 2 */ - total_len += 2; - } - - if (total_len > tx_buf_len) { - /* Buffer too small */ - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - /* Encode length */ - tx_payload = tx_buf; - - if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { - *tx_payload++ = total_len; - } - else { - *tx_payload++ = SN_PACKET_LEN_IND; - tx_payload += MqttEncode_Num(tx_payload, total_len); - } - - /* Encode message type */ - *tx_payload++ = SN_MSG_TYPE_WILLMSGUPD; - - /* Encode Will Message */ - XMEMCPY(tx_payload, willMsg->willMsg, willMsg->willMsgLen); - tx_payload += willMsg->willMsgLen; - (void)tx_payload; - - return total_len; -} - -int SN_Decode_WillMsgResponse(byte *rx_buf, int rx_buf_len, byte *ret_code) -{ - int total_len; - byte *rx_payload = rx_buf, type; - - /* Validate required arguments */ - if (rx_buf == NULL || rx_buf_len <= 0) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Decode fixed header */ - total_len = *rx_payload++; - if (total_len != 3) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); - } - - type = *rx_payload++; - if (type != SN_MSG_TYPE_WILLMSGRESP) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); - } - - /* Return Code */ - *ret_code = *rx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Decode_ConnectAck(byte *rx_buf, int rx_buf_len, - SN_ConnectAck *connect_ack) -{ - int total_len; - byte *rx_payload = rx_buf, type; - - /* Validate required arguments */ - if (rx_buf == NULL || rx_buf_len <= 0) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Decode fixed header */ - total_len = *rx_payload++; - if (total_len != 3) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); - } - - type = *rx_payload++; - if (type != SN_MSG_TYPE_CONNACK) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); - } - - /* Decode variable header */ - if (connect_ack) { - connect_ack->return_code = *rx_payload++; - } - (void)rx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Encode_Register(byte *tx_buf, int tx_buf_len, SN_Register *regist) -{ - int total_len, topic_len; - byte *tx_payload; - - /* Validate required arguments */ - if (tx_buf == NULL || regist == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Determine packet length */ - /* Topic name is a string */ - total_len = (int)XSTRLEN(regist->topicName); - - /* Length, MsgType, TopicID (2), and packet_id (2) */ - total_len += 6; - - if (total_len > SN_PACKET_MAX_SMALL_SIZE) { - /* Length is stored in bytes 1 and 2 */ - total_len += 2; - } - - if (total_len > tx_buf_len) { - /* Buffer too small */ - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - /* Encode length */ - tx_payload = tx_buf; - - if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { - *tx_payload++ = total_len; - } - else { - *tx_payload++ = SN_PACKET_LEN_IND; - tx_payload += MqttEncode_Num(tx_payload, total_len); - } - - /* Encode message type */ - *tx_payload++ = SN_MSG_TYPE_REGISTER; - - /* Encode Topic ID */ - tx_payload += MqttEncode_Num(tx_payload, regist->topicId); - - /* Encode Packet ID */ - tx_payload += MqttEncode_Num(tx_payload, regist->packet_id); - - /* Encode Topic Name */ - topic_len = (int)XSTRLEN(regist->topicName); - XMEMCPY(tx_payload, regist->topicName, topic_len); - tx_payload += topic_len; - (void)tx_payload; - - return total_len; -} - -int SN_Decode_Register(byte *rx_buf, int rx_buf_len, SN_Register *regist) -{ - int total_len; - byte *rx_payload = rx_buf, type; - - /* Validate required arguments */ - if (rx_buf == NULL || rx_buf_len <= 0) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Decode fixed header */ - total_len = *rx_payload++; - if (total_len == SN_PACKET_LEN_IND) { - /* The length is stored in the next two bytes */ - rx_payload += MqttDecode_Num(rx_payload, (word16*)&total_len); - } - - if (total_len > rx_buf_len) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - if (total_len < 7) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); - } - - /* Check message type */ - type = *rx_payload++; - if (type != SN_MSG_TYPE_REGISTER) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); - } - - if (regist != NULL) { - /* Decode Topic ID assigned by GW */ - rx_payload += MqttDecode_Num(rx_payload, ®ist->topicId); - - /* Decode packet ID */ - rx_payload += MqttDecode_Num(rx_payload, ®ist->packet_id); - - /* Decode Topic Name */ - regist->topicName = (char*)rx_payload; - - /* Terminate the string */ - rx_payload[total_len-6] = '\0'; - } - (void)rx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Encode_RegAck(byte *tx_buf, int tx_buf_len, SN_RegAck *regack) -{ - int total_len; - byte *tx_payload; - - /* Validate required arguments */ - if (tx_buf == NULL || regack == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Determine packet length */ - /* Length, MsgType, TopicID (2), and MsgId (2), Return Code */ - total_len = 7; - - if (total_len > tx_buf_len) { - /* Buffer too small */ - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - tx_payload = tx_buf; - - /* Encode length */ - *tx_payload++ = total_len; - - /* Encode message type */ - *tx_payload++ = SN_MSG_TYPE_REGACK; - - /* Encode Topic ID */ - tx_payload += MqttEncode_Num(tx_payload, regack->topicId); - - /* Encode Message ID */ - tx_payload += MqttEncode_Num(tx_payload, regack->packet_id); - - /* Encode Return Code */ - *tx_payload += regack->return_code; - - (void)tx_payload; - - return total_len; -} - -int SN_Decode_RegAck(byte *rx_buf, int rx_buf_len, SN_RegAck *regack) -{ - int total_len; - byte *rx_payload = rx_buf, type; - - /* Validate required arguments */ - if (rx_buf == NULL || rx_buf_len <= 0) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Decode fixed header */ - total_len = *rx_payload++; - if (total_len != 7) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); - } - - type = *rx_payload++; - if (type != SN_MSG_TYPE_REGACK) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); - } - - if (regack != NULL) { - /* Decode Topic ID assigned by GW */ - rx_payload += MqttDecode_Num(rx_payload, ®ack->topicId); - - /* Decode packet ID */ - rx_payload += MqttDecode_Num(rx_payload, ®ack->packet_id); - - /* Decode return code */ - regack->return_code = *rx_payload++; - } - (void)rx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Encode_Subscribe(byte *tx_buf, int tx_buf_len, SN_Subscribe *subscribe) -{ - int total_len; - byte *tx_payload, flags = 0x00; - - /* Validate required arguments */ - if (tx_buf == NULL || subscribe == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Determine packet length */ - if (subscribe->topic_type == SN_TOPIC_ID_TYPE_NORMAL) { - /* Topic name is a string */ - total_len = (int)XSTRLEN(subscribe->topicNameId); - } - else { - /* Topic ID or Short name */ - total_len = 2; - } - - /* Length, MsgType, Flags, and MsgID (2) */ - total_len += 5; - - if (total_len > SN_PACKET_MAX_SMALL_SIZE) { - /* Length is stored in bytes 1 and 2 */ - total_len += 2; - } - - if (total_len > tx_buf_len) { - /* Buffer too small */ - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - /* Encode length */ - tx_payload = tx_buf; - - if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { - *tx_payload++ = total_len; - } - else { - *tx_payload++ = SN_PACKET_LEN_IND; - tx_payload += MqttEncode_Num(tx_payload, total_len); - } - - /* Encode message type */ - *tx_payload++ = SN_MSG_TYPE_SUBSCRIBE; - - /* Set flags */ - if (subscribe->duplicate) - flags |= SN_PACKET_FLAG_DUPLICATE; - flags |= (SN_PACKET_FLAG_QOS_MASK & - (subscribe->qos << SN_PACKET_FLAG_QOS_SHIFT)); - flags |= (SN_PACKET_FLAG_TOPICIDTYPE_MASK & subscribe->topic_type); - - *tx_payload++ = flags; - - /* Encode packet ID */ - tx_payload += MqttEncode_Num(tx_payload, subscribe->packet_id); - - /* Encode topic */ - if (subscribe->topic_type == SN_TOPIC_ID_TYPE_NORMAL) { - /* Topic name is a string */ - XMEMCPY(tx_payload, subscribe->topicNameId, XSTRLEN(subscribe->topicNameId)); - } - else { - /* Topic ID */ - XMEMCPY(tx_payload, subscribe->topicNameId, 2); - } - (void)tx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Decode_SubscribeAck(byte* rx_buf, int rx_buf_len, - SN_SubAck *subscribe_ack) -{ - word16 total_len; - byte* rx_payload = rx_buf, type; - - /* Validate required arguments */ - if (rx_buf == NULL || rx_buf_len <= 0) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Decode fixed header */ - total_len = *rx_payload++; - if (total_len != 8) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); - } - - type = *rx_payload++; - if (type != SN_MSG_TYPE_SUBACK) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - /* Decode SubAck fields */ - if (subscribe_ack) { - subscribe_ack->flags = *rx_payload++; - rx_payload += MqttDecode_Num(rx_payload, &subscribe_ack->topicId); - rx_payload += MqttDecode_Num(rx_payload, &subscribe_ack->packet_id); - subscribe_ack->return_code = *rx_payload++; - } - (void)rx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Encode_Publish(byte *tx_buf, int tx_buf_len, SN_Publish *publish) -{ - word16 total_len; - byte *tx_payload = tx_buf; - byte flags = 0; - - /* Validate required arguments */ - if (tx_buf == NULL || publish == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Determine packet length */ - total_len = publish->total_len; - - /* Add length, msgType, flags, topic ID (2), and msgID (2) */ - total_len += 7; - - if (total_len > SN_PACKET_MAX_SMALL_SIZE) { - /* Length is stored in bytes 1 and 2 */ - total_len += 2; - } - - if (total_len > tx_buf_len) { - /* Buffer too small */ - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - /* Encode header */ - if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { - *tx_payload++ = (byte)total_len; - } - else { - *tx_payload++ = SN_PACKET_LEN_IND; - tx_payload += MqttEncode_Num(tx_payload, total_len); - } - - *tx_payload++ = SN_MSG_TYPE_PUBLISH; - - /* Set flags */ - if (publish->duplicate) - flags |= SN_PACKET_FLAG_DUPLICATE; - flags |= (SN_PACKET_FLAG_QOS_MASK & - (publish->qos << SN_PACKET_FLAG_QOS_SHIFT)); - if (publish->retain) - flags |= SN_PACKET_FLAG_RETAIN; - flags |= (SN_PACKET_FLAG_TOPICIDTYPE_MASK & publish->topic_type); - - *tx_payload++ = flags; - - /* Encode topic */ - if ((publish->topic_type == SN_TOPIC_ID_TYPE_SHORT) || - (publish->topic_type == SN_TOPIC_ID_TYPE_PREDEF)) { - /* Short and predefined topic names are 2 chars */ - XMEMCPY(tx_payload, publish->topic_name, 2); - tx_payload += 2; - } - else { - /* Topic ID */ - tx_payload += MqttEncode_Num(tx_payload, (word16)*publish->topic_name); - } - - tx_payload += MqttEncode_Num(tx_payload, publish->packet_id); - - /* Encode payload */ - XMEMCPY(tx_payload, publish->buffer, publish->total_len); - tx_payload += publish->total_len; - - (void)tx_payload; - - /* Return length of packet placed into tx_buf */ - return total_len; -} - -int SN_Decode_Publish(byte *rx_buf, int rx_buf_len, SN_Publish *publish) -{ - word16 total_len; - byte *rx_payload = rx_buf; - byte flags = 0, type; - - /* Validate required arguments */ - if (rx_buf == NULL || publish == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Decode fixed header */ - total_len = *rx_payload++; - if (total_len == SN_PACKET_LEN_IND) { - /* The length is stored in the next two bytes */ - rx_payload += MqttDecode_Num(rx_payload, &total_len); - } - - if (total_len > rx_buf_len) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - if (total_len < 7) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); - } - - /* Message Type */ - type = *rx_payload++; - if (type != SN_MSG_TYPE_PUBLISH) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); - } - - flags = *rx_payload++; - - publish->topic_name = (char*)rx_payload; - rx_payload += MQTT_DATA_LEN_SIZE; - publish->topic_name_len = MQTT_DATA_LEN_SIZE; - - rx_payload += MqttDecode_Num(rx_payload, &publish->packet_id); - - /* Set flags */ - publish->duplicate = flags & SN_PACKET_FLAG_DUPLICATE; - - publish->qos = (MqttQoS)((flags & SN_PACKET_FLAG_QOS_MASK) >> - SN_PACKET_FLAG_QOS_SHIFT); - - publish->retain = flags & SN_PACKET_FLAG_RETAIN; - - publish->type = flags & SN_PACKET_FLAG_TOPICIDTYPE_MASK; - - /* Decode payload */ - - publish->total_len = total_len - 7; - publish->buffer = rx_payload; - publish->buffer_pos = 0; - publish->buffer_len = publish->total_len; - - /* Return length of packet read from rx_buf */ - return total_len; -} - -int SN_Encode_PublishResp(byte* tx_buf, int tx_buf_len, byte type, - SN_PublishResp *publish_resp) -{ - int total_len; - byte *tx_payload = tx_buf; - - /* Validate required arguments */ - if (tx_buf == NULL || publish_resp == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Determine packet length */ - total_len = (type == SN_MSG_TYPE_PUBACK) ? 7 : 4; - - if (total_len > tx_buf_len) - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - - /* Encode */ - *tx_payload++ = (byte)total_len; - - *tx_payload++ = type; - - if (type == SN_MSG_TYPE_PUBACK) { - tx_payload += MqttEncode_Num(tx_payload, publish_resp->topicId); - } - - tx_payload += MqttEncode_Num(tx_payload, publish_resp->packet_id); - - if (type == SN_MSG_TYPE_PUBACK) { - *tx_payload++ = publish_resp->return_code; - } - (void)tx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Decode_PublishResp(byte* rx_buf, int rx_buf_len, byte type, - SN_PublishResp *publish_resp) -{ - int total_len; - byte rec_type, *rx_payload = rx_buf; - - /* Validate required arguments */ - if (rx_buf == NULL || rx_buf_len <= 0) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Decode */ - total_len = *rx_payload++; - - if(total_len > rx_buf_len) { - /* Buffer too small */ - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - /* Validate packet type */ - rec_type = *rx_payload++; - if (rec_type != type) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); - } - - if (publish_resp) { - if (type == SN_MSG_TYPE_PUBACK) { - rx_payload += MqttDecode_Num(rx_payload, &publish_resp->topicId); - } - - rx_payload += MqttDecode_Num(rx_payload, &publish_resp->packet_id); - - if (type == SN_MSG_TYPE_PUBACK) { - publish_resp->return_code = *rx_payload++; - } - } - (void)rx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Encode_Unsubscribe(byte *tx_buf, int tx_buf_len, - SN_Unsubscribe *unsubscribe) -{ - int total_len; - byte *tx_payload, flags = 0x00; - - /* Validate required arguments */ - if (tx_buf == NULL || unsubscribe == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Determine packet length */ - if (unsubscribe->topic_type == SN_TOPIC_ID_TYPE_NORMAL) { - /* Topic name is a string */ - total_len = (int)XSTRLEN(unsubscribe->topicNameId); - } - else { - /* Topic ID or Short name */ - total_len = 2; - } - - /* Length, MsgType, Flags, and MsgID (2) */ - total_len += 5; - - if (total_len > SN_PACKET_MAX_SMALL_SIZE) { - /* Length is stored in bytes 1 and 2 */ - total_len += 2; - } - - if (total_len > tx_buf_len) { - /* Buffer too small */ - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - /* Encode header */ - tx_payload = tx_buf; - - if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { - *tx_payload++ = total_len; - } - else { - *tx_payload++ = SN_PACKET_LEN_IND; - tx_payload += MqttEncode_Num(tx_payload, total_len); - } - - *tx_payload++ = SN_MSG_TYPE_UNSUBSCRIBE; - - /* Set flags */ - if (unsubscribe->duplicate) - flags |= SN_PACKET_FLAG_DUPLICATE; - flags |= (SN_PACKET_FLAG_QOS_MASK & - (unsubscribe->qos << SN_PACKET_FLAG_QOS_SHIFT)); - flags |= (SN_PACKET_FLAG_TOPICIDTYPE_MASK & unsubscribe->topic_type); - - *tx_payload++ = flags; - - tx_payload += MqttEncode_Num(tx_payload, unsubscribe->packet_id); - - /* Encode topic */ - if (unsubscribe->topic_type == SN_TOPIC_ID_TYPE_NORMAL) { - /* Topic name is a string */ - XMEMCPY(tx_payload, unsubscribe->topicNameId, - XSTRLEN(unsubscribe->topicNameId)); - } - else { - /* Topic ID or Short name */ - XMEMCPY(tx_payload, unsubscribe->topicNameId, 2); - } - - (void)tx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Decode_UnsubscribeAck(byte *rx_buf, int rx_buf_len, - SN_UnsubscribeAck *unsubscribe_ack) -{ - word16 total_len; - byte *rx_payload = rx_buf, type; - - /* Validate required arguments */ - if (rx_buf == NULL || rx_buf_len <= 0) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Decode fixed header */ - total_len = *rx_payload++; - if (total_len != 4) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); - } - - type = *rx_payload++; - if (type != SN_MSG_TYPE_UNSUBACK) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); - } - - /* Decode SubAck fields */ - if (unsubscribe_ack) { - rx_payload += MqttDecode_Num(rx_payload, &unsubscribe_ack->packet_id); - } - (void)rx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Encode_Disconnect(byte *tx_buf, int tx_buf_len, - SN_Disconnect* disconnect) -{ - int total_len = 2; /* length and message type */ - byte *tx_payload = tx_buf;; - - /* Validate required arguments */ - if (tx_buf == NULL) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - if ((disconnect != NULL) && (disconnect->sleepTmr > 0)) { - total_len += 2; /* Sleep duration is set */ - } - - if (total_len > tx_buf_len) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - /* Encode message */ - *tx_payload++ = total_len; - - *tx_payload++ = SN_MSG_TYPE_DISCONNECT; - - if ((disconnect != NULL) && (disconnect->sleepTmr > 0)) { - tx_payload += MqttEncode_Num(tx_payload, disconnect->sleepTmr); - } - (void)tx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Decode_Disconnect(byte *rx_buf, int rx_buf_len) -{ - word16 total_len; - byte *rx_payload = rx_buf, type; - - /* Validate required arguments */ - if (rx_buf == NULL || rx_buf_len <= 0) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - /* Decode fixed header */ - total_len = *rx_payload++; - if (total_len != 2) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); - } - - type = *rx_payload++; - if (type != SN_MSG_TYPE_DISCONNECT) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); - } - - (void)rx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Encode_Ping(byte *tx_buf, int tx_buf_len, SN_PingReq *ping, byte type) -{ - int total_len = 2, clientId_len = 0; - byte *tx_payload = tx_buf; - - /* Validate required arguments */ - if ((tx_buf == NULL) || - ((type != SN_MSG_TYPE_PING_REQ) && (type != SN_MSG_TYPE_PING_RESP))) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - if ((type == SN_MSG_TYPE_PING_REQ) && (ping != NULL) && - (ping->clientId != NULL)) { - total_len += clientId_len = (int)XSTRLEN(ping->clientId); - } - - if (total_len > tx_buf_len) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); - } - - *tx_payload++ = (byte)total_len; - - *tx_payload++ = type; - - if (clientId_len > 0) { - XMEMCPY(tx_payload, ping->clientId, clientId_len); - tx_payload += clientId_len; - } - (void)tx_payload; - - /* Return total length of packet */ - return total_len; -} - -int SN_Decode_Ping(byte *rx_buf, int rx_buf_len) -{ - int total_len; - byte *rx_payload = rx_buf, type; - - /* Validate required arguments */ - if (rx_buf == NULL || rx_buf_len <= 0) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); - } - - total_len = *rx_payload++; - if (total_len != 2) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); - } - - type = *rx_payload++; - if ((type != SN_MSG_TYPE_PING_REQ) && - (type != SN_MSG_TYPE_PING_RESP)) { - return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); - } - - /* Return total length of packet */ - return total_len; -} - -/* Read return code is length when > 0 */ -int SN_Packet_Read(MqttClient *client, byte* rx_buf, int rx_buf_len, - int timeout_ms) -{ - int rc, len = 0, remain_read = 0; - word16 total_len = 0, idx = 0; - - switch (client->packet.stat) - { - case MQTT_PK_BEGIN: - { - /* Read first 2 bytes */ - if (MqttClient_Flags(client,0,0) & MQTT_CLIENT_FLAG_IS_DTLS) { - rc = MqttSocket_Read(client, rx_buf, 2, timeout_ms); - } else { - rc = MqttSocket_Peek(client, rx_buf, 2, timeout_ms); - } - if (rc < 0) { - return MqttPacket_HandleNetError(client, rc); - } - else if (rc != 2) { - return MqttPacket_HandleNetError(client, - MQTT_TRACE_ERROR(MQTT_CODE_ERROR_NETWORK)); - } - - len = rc; - - if (rx_buf[0] == SN_PACKET_LEN_IND){ - /* Read length stored in first three bytes, type in fourth */ - if (MqttClient_Flags(client,0,0) & MQTT_CLIENT_FLAG_IS_DTLS) { - rc = MqttSocket_Read(client, rx_buf+len, 2, timeout_ms); - if (rc < 0) { - return MqttPacket_HandleNetError(client, rc); - } - else if (rc != 2) { - return MqttPacket_HandleNetError(client, - MQTT_TRACE_ERROR(MQTT_CODE_ERROR_NETWORK)); - } - rc += len; - } - else { - rc = MqttSocket_Peek(client, rx_buf, 4, timeout_ms); - if (rc < 0) { - return MqttPacket_HandleNetError(client, rc); - } - else if (rc != 4) { - return MqttPacket_HandleNetError(client, - MQTT_TRACE_ERROR(MQTT_CODE_ERROR_NETWORK)); - } - len = rc; - } - - (void)MqttDecode_Num(&rx_buf[1], &total_len); - client->packet.header_len = len; - } - else { - /* Length is stored in first byte, type in second */ - total_len = rx_buf[0]; - client->packet.header_len = len; - } - } - FALL_THROUGH; - - case MQTT_PK_READ_HEAD: - { - client->packet.stat = MQTT_PK_READ_HEAD; - } - FALL_THROUGH; - - case MQTT_PK_READ: - { - client->packet.stat = MQTT_PK_READ; - - if (total_len > len) { - client->packet.remain_len = total_len - len; - } - else if ((total_len == 2) || (total_len == 4)) { - /* Handle peek */ - if (MqttClient_Flags(client,0,0) & MQTT_CLIENT_FLAG_IS_DTLS) { - client->packet.remain_len = total_len - len; - } - else { - client->packet.remain_len = total_len; - } - } - else { - client->packet.remain_len = 0; - } - - /* Make sure it does not overflow rx_buf */ - if (client->packet.remain_len > - (rx_buf_len - client->packet.header_len)) { - client->packet.remain_len = rx_buf_len - - client->packet.header_len; - } - if (MqttClient_Flags(client,0,0) & MQTT_CLIENT_FLAG_IS_DTLS) { - total_len -= client->packet.header_len; - idx = client->packet.header_len; - } - /* Read whole message */ - if (client->packet.remain_len > 0) { - rc = MqttSocket_Read(client, &rx_buf[idx], - total_len, timeout_ms); - if (rc <= 0) { - return MqttPacket_HandleNetError(client, rc); - } - remain_read = rc; - } - if (MqttClient_Flags(client,0,0) & MQTT_CLIENT_FLAG_IS_DTLS) { - remain_read += client->packet.header_len; - } - - break; - } - } /* switch (client->packet.stat) */ - - /* reset state */ - client->packet.stat = MQTT_PK_BEGIN; - - /* Return read length */ - return remain_read; -} - -#endif /* WOLFMQTT_SN */ diff --git a/src/mqtt_sn_client.c b/src/mqtt_sn_client.c new file mode 100644 index 000000000..1144d6362 --- /dev/null +++ b/src/mqtt_sn_client.c @@ -0,0 +1,1872 @@ +/* mqtt_sn_client.c + * + * Copyright (C) 2006-2023 wolfSSL Inc. + * + * This file is part of wolfMQTT. + * + * wolfMQTT is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfMQTT is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Include the autoconf generated config.h */ +#ifdef HAVE_CONFIG_H + #include +#endif + +#include "wolfmqtt/mqtt_sn_client.h" + +#ifdef WOLFMQTT_SN + +/* Private functions */ +static int SN_Client_HandlePacket(MqttClient* client, SN_MsgType packet_type, + void* packet_obj, int timeout) +{ + int rc = MQTT_CODE_SUCCESS; + word16 packet_id = 0; + + (void)timeout; + + switch ((int)packet_type) + { + case SN_MSG_TYPE_GWINFO: + { + SN_GwInfo info, *p_info = &info; + if (packet_obj) { + p_info = (SN_GwInfo*)packet_obj; + } + else { + XMEMSET(p_info, 0, sizeof(SN_GwInfo)); + } + + rc = SN_Decode_GWInfo(client->rx_buf, client->packet.buf_len, + p_info); + if (rc <= 0) { + return rc; + } + break; + } + case SN_MSG_TYPE_CONNACK: + { + /* Decode connect ack */ + SN_ConnectAck connect_ack, *p_connect_ack = &connect_ack; + if (packet_obj) { + p_connect_ack = (SN_ConnectAck*)packet_obj; + } + else { + XMEMSET(p_connect_ack, 0, sizeof(SN_ConnectAck)); + } + p_connect_ack->return_code = + client->rx_buf[client->packet.buf_len-1]; + + break; + } + case SN_MSG_TYPE_WILLTOPICREQ: + { + rc = SN_Decode_WillTopicReq(client->rx_buf, client->packet.buf_len); + break; + } + case SN_MSG_TYPE_WILLMSGREQ: + { + rc = SN_Decode_WillMsgReq(client->rx_buf, client->packet.buf_len); + break; + } + case SN_MSG_TYPE_REGISTER: + { + /* Decode register */ + SN_Register reg_s; + + XMEMSET(®_s, 0, sizeof(SN_Register)); + + rc = SN_Decode_Register(client->rx_buf, client->packet.buf_len, + ®_s); + + if (rc > 0) { + /* Initialize the regack */ + reg_s.regack.packet_id = reg_s.packet_id; + reg_s.regack.topicId = reg_s.topicId; + reg_s.regack.return_code = SN_RC_NOTSUPPORTED; + + /* Call the register callback to allow app to + handle new topic ID assignment. */ + if (client->reg_cb != NULL) { + rc = client->reg_cb(reg_s.topicId, + reg_s.topicName, client->reg_ctx); + /* Set the regack return code */ + reg_s.regack.return_code = (rc >= 0) ? SN_RC_ACCEPTED : + SN_RC_INVTOPICNAME; + } + + #ifdef WOLFMQTT_MULTITHREAD + /* Lock send socket mutex */ + rc = wm_SemLock(&client->lockSend); + if (rc != 0) { + return rc; + } + #endif + + /* Encode the register acknowledgment */ + rc = SN_Encode_RegAck(client->tx_buf, client->tx_buf_len, + ®_s.regack); + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d), ID %d", + rc, SN_Packet_TypeDesc(SN_MSG_TYPE_REGACK), + SN_MSG_TYPE_REGACK, reg_s.packet_id); + #endif + if (rc <= 0) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + client->write.len = rc; + + /* Send regack packet */ + rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + if (rc != client->write.len) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + } + + break; + } + case SN_MSG_TYPE_REGACK: + { + /* Decode register ack */ + SN_RegAck regack_s, *p_regack = ®ack_s; + if (packet_obj) { + p_regack = (SN_RegAck*)packet_obj; + } + else { + XMEMSET(p_regack, 0, sizeof(SN_RegAck)); + } + + rc = SN_Decode_RegAck(client->rx_buf, client->packet.buf_len, + p_regack); + if (rc > 0) { + packet_id = p_regack->packet_id; + } + + break; + } + case SN_MSG_TYPE_PUBLISH: + { + SN_Publish pub, *p_pub = &pub; + if (packet_obj) { + p_pub = (SN_Publish*)packet_obj; + } + else { + XMEMSET(p_pub, 0, sizeof(SN_Publish)); + } + + /* Decode publish message */ + rc = SN_Decode_Publish(client->rx_buf, client->packet.buf_len, + p_pub); + if (rc <= 0) { + return rc; + } + + /* Issue callback for new message */ + if (client->msg_cb) { + /* if using the temp publish message buffer, + then populate message context with client context */ + if (&client->msgSN.publish == p_pub) + p_pub->ctx = client->ctx; + rc = client->msg_cb(client, (MqttMessage*)p_pub, 1, 1); + if (rc != MQTT_CODE_SUCCESS) { + return rc; + }; + } + + /* Handle Qos */ + if (p_pub->qos > MQTT_QOS_0) { + SN_MsgType type; + + packet_id = p_pub->packet_id; + + /* Determine packet type to write */ + type = (p_pub->qos == MQTT_QOS_1) ? + SN_MSG_TYPE_PUBACK : + SN_MSG_TYPE_PUBREC; + p_pub->resp.packet_id = packet_id; + + #ifdef WOLFMQTT_MULTITHREAD + /* Lock send socket mutex */ + rc = wm_SemLock(&client->lockSend); + if (rc != 0) { + return rc; + } + #endif + + /* Encode publish response */ + rc = SN_Encode_PublishResp(client->tx_buf, + client->tx_buf_len, type, &p_pub->resp); + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d), ID %d," + " QoS %d", + rc, SN_Packet_TypeDesc(type), type, packet_id, + p_pub->qos); + #endif + if (rc <= 0) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + client->write.len = rc; + + /* Send packet */ + rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + } + break; + } + case SN_MSG_TYPE_PUBACK: + case SN_MSG_TYPE_PUBCOMP: + case SN_MSG_TYPE_PUBREC: + case SN_MSG_TYPE_PUBREL: + { + SN_PublishResp publish_resp, *p_publish_resp = &publish_resp; + if (packet_obj) { + p_publish_resp = (SN_PublishResp*)packet_obj; + } + else { + XMEMSET(p_publish_resp, 0, sizeof(SN_PublishResp)); + } + + /* Decode publish response message */ + rc = SN_Decode_PublishResp(client->rx_buf, client->packet.buf_len, + packet_type, p_publish_resp); + if (rc <= 0) { + return rc; + } + packet_id = p_publish_resp->packet_id; + + /* If Qos then send response */ + if (packet_type == SN_MSG_TYPE_PUBREC || + packet_type == SN_MSG_TYPE_PUBREL) { + + byte resp_type = (packet_type == SN_MSG_TYPE_PUBREC) ? + SN_MSG_TYPE_PUBREL : SN_MSG_TYPE_PUBCOMP; + + #ifdef WOLFMQTT_MULTITHREAD + /* Lock send socket mutex */ + rc = wm_SemLock(&client->lockSend); + if (rc != 0) { + return rc; + } + #endif + + /* Encode publish response */ + p_publish_resp->packet_id = packet_id; + rc = SN_Encode_PublishResp(client->tx_buf, + client->tx_buf_len, resp_type, p_publish_resp); + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d), ID %d", + rc, MqttPacket_TypeDesc(resp_type), resp_type, packet_id); + #endif + if (rc <= 0) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + client->write.len = rc; + + /* Send packet */ + rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + } + break; + } + case SN_MSG_TYPE_SUBACK: + { + /* Decode subscribe ack */ + SN_SubAck subscribe_ack, *p_subscribe_ack = &subscribe_ack; + if (packet_obj) { + p_subscribe_ack = (SN_SubAck*)packet_obj; + } + else { + XMEMSET(p_subscribe_ack, 0, sizeof(SN_SubAck)); + } + + rc = SN_Decode_SubscribeAck(client->rx_buf, client->packet.buf_len, + p_subscribe_ack); + if (rc <= 0) { + return rc; + } + packet_id = p_subscribe_ack->packet_id; + + break; + } + case SN_MSG_TYPE_UNSUBACK: + { + /* Decode unsubscribe ack */ + SN_UnsubscribeAck unsubscribe_ack, + *p_unsubscribe_ack = &unsubscribe_ack; + if (packet_obj) { + p_unsubscribe_ack = (SN_UnsubscribeAck*)packet_obj; + } + else { + XMEMSET(p_unsubscribe_ack, 0, sizeof(SN_UnsubscribeAck)); + } + rc = SN_Decode_UnsubscribeAck(client->rx_buf, + client->packet.buf_len, p_unsubscribe_ack); + if (rc <= 0) { + return rc; + } + packet_id = p_unsubscribe_ack->packet_id; + + break; + } + case SN_MSG_TYPE_PING_RESP: + { + /* Decode ping */ + rc = SN_Decode_Ping(client->rx_buf, client->packet.buf_len); + break; + } + case SN_MSG_TYPE_PING_REQ: + { + /* Decode ping */ + rc = SN_Decode_Ping(client->rx_buf, client->packet.buf_len); + if (rc <= 0) { return rc; } + + #ifdef WOLFMQTT_MULTITHREAD + /* Lock send socket mutex */ + rc = wm_SemLock(&client->lockSend); + if (rc != 0) { + return rc; + } + #endif + + /* Encode the ping packet as a response */ + rc = SN_Encode_Ping(client->tx_buf, client->tx_buf_len, NULL, + SN_MSG_TYPE_PING_RESP); + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d)", + rc, SN_Packet_TypeDesc(SN_MSG_TYPE_PING_RESP), + SN_MSG_TYPE_PING_RESP); + #endif + if (rc <= 0) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + client->write.len = rc; + + /* Send ping resp packet */ + rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + if (rc != client->write.len) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + + break; + } + case SN_MSG_TYPE_WILLTOPICRESP: + { + /* Decode Will Topic Response */ + SN_WillTopicResp resp_s, *resp = &resp_s; + if (packet_obj) { + resp = (SN_WillTopicResp*)packet_obj; + } + else { + XMEMSET(resp, 0, sizeof(SN_WillTopicResp)); + } + rc = SN_Decode_WillTopicResponse(client->rx_buf, + client->packet.buf_len, &resp->return_code); + break; + } + case SN_MSG_TYPE_WILLMSGRESP: + { + /* Decode Will Message Response */ + SN_WillMsgResp resp_s, *resp = &resp_s; + if (packet_obj) { + resp = (SN_WillMsgResp*)packet_obj; + } + else { + XMEMSET(resp, 0, sizeof(SN_WillMsgResp)); + } + rc = SN_Decode_WillMsgResponse(client->rx_buf, + client->packet.buf_len, &resp->return_code); + break; + } + case SN_MSG_TYPE_DISCONNECT: + { + SN_Disconnect disc_s, *disc = &disc_s; + if (packet_obj) { + disc = (SN_Disconnect*)packet_obj; + } + else { + XMEMSET(disc, 0, sizeof(SN_Disconnect)); + } + /* Decode Disconnect */ + rc = SN_Decode_Disconnect(client->rx_buf, client->packet.buf_len); + +#ifdef WOLFMQTT_DISCONNECT_CB + /* Call disconnect callback to allow handling broker disconnect */ + if ((client->disconnect_cb != NULL) && (disc->sleepTmr == 0)) { + client->disconnect_cb(client, rc, client->disconnect_ctx); + } +#endif + break; + } + + default: + { + /* Other types are server side only, ignore */ + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("SN_Client_HandlePacket: Invalid client packet type %u!", + packet_type); + #endif + break; + } + } /* switch (packet_type) */ + + (void)packet_id; + + return rc; +} + +static int SN_Client_WaitType(MqttClient *client, void* packet_obj, + byte wait_type, word16 wait_packet_id, int timeout_ms) +{ + int rc; + word16 packet_id; + SN_MsgType packet_type; +#ifdef WOLFMQTT_MULTITHREAD + MqttPendResp *pendResp; +#endif + MqttMsgStat* mms_stat; + int waitMatchFound; + void* use_packet_obj = NULL; + + if (client == NULL || packet_obj == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* all packet type structures must have MqttMsgStat at top */ + mms_stat = (MqttMsgStat*)packet_obj; + +wait_again: + + /* initialize variables */ + packet_id = 0; + packet_type = SN_MSG_TYPE_RESERVED; +#ifdef WOLFMQTT_MULTITHREAD + pendResp = NULL; +#endif + waitMatchFound = 0; + +#ifdef WOLFMQTT_DEBUG_CLIENT + #ifdef WOLFMQTT_NONBLOCK + if (client->lastRc != MQTT_CODE_CONTINUE) + #endif + { + PRINTF("SN_Client_WaitType: Type %s (%d), ID %d", + SN_Packet_TypeDesc((SN_MsgType)wait_type), + wait_type, wait_packet_id); + } +#endif + + switch (mms_stat->read) + { + case MQTT_MSG_BEGIN: + { + #ifdef WOLFMQTT_MULTITHREAD + /* Lock recv socket mutex */ + rc = wm_SemLock(&client->lockRecv); + if (rc != 0) { + PRINTF("SN_Client_WaitType recv lock error"); + return rc; + } + mms_stat->isReadActive = 1; + MQTT_TRACE_MSG("SN lockRecv"); + #endif + + /* reset the packet state used by SN_Packet_Read */ + client->packet.stat = MQTT_PK_BEGIN; + } + FALL_THROUGH; + + case MQTT_MSG_WAIT: + { + #ifdef WOLFMQTT_MULTITHREAD + /* Check to see if packet type and id have already completed */ + pendResp = NULL; + rc = wm_SemLock(&client->lockClient); + if (rc == 0) { + if (MqttClient_RespList_Find(client, (MqttPacketType)wait_type, + wait_packet_id, &pendResp)) { + if (pendResp->packetDone) { + /* pending response is already done, so return */ + rc = pendResp->packet_ret; + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("PendResp already Done %p: Rc %d", pendResp, rc); + #endif + MqttClient_RespList_Remove(client, pendResp); + wm_SemUnlock(&client->lockClient); + MQTT_TRACE_MSG("SN unlockRecv"); + wm_SemUnlock(&client->lockRecv); + return rc; + } + } + wm_SemUnlock(&client->lockClient); + } + else { + break; /* error */ + } + #endif /* WOLFMQTT_MULTITHREAD */ + + mms_stat->read = MQTT_MSG_WAIT; + + /* Wait for packet */ + rc = SN_Packet_Read(client, client->rx_buf, client->rx_buf_len, + timeout_ms); + if (rc <= 0) { + break; + } + + client->packet.buf_len = rc; + + /* Decode header */ + rc = SN_Decode_Header(client->rx_buf, client->packet.buf_len, + &packet_type, &packet_id); + if (rc < 0) { + break; + } + + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("Read Packet: Len %d, Type %d, ID %d", + client->packet.buf_len, packet_type, packet_id); + #endif + + mms_stat->read = MQTT_MSG_HEADER; + } + FALL_THROUGH; + + case MQTT_MSG_HEADER: + case MQTT_MSG_PAYLOAD: + case MQTT_MSG_PAYLOAD2: + { + SN_MsgType use_packet_type; + + /* Determine if we received data for this request */ + if ((wait_type == SN_MSG_TYPE_ANY || wait_type == packet_type) && + (wait_packet_id == 0 || wait_packet_id == packet_id)) + { + use_packet_obj = packet_obj; + waitMatchFound = 1; + } + else { + /* use generic packet object */ + use_packet_obj = &client->msgSN; + } + use_packet_type = packet_type; + + #ifdef WOLFMQTT_MULTITHREAD + /* Check to see if we have a pending response for this packet */ + pendResp = NULL; + rc = wm_SemLock(&client->lockClient); + if (rc == 0) { + if (MqttClient_RespList_Find(client, + (MqttPacketType)packet_type, packet_id, &pendResp)) { + /* we found packet match this incoming read packet */ + pendResp->packetProcessing = 1; + if (pendResp->packet_obj != packet_obj) { + use_packet_obj = pendResp->packet_obj; + use_packet_type = (SN_MsgType)pendResp->packet_type; + /* req from another thread... not a match */ + waitMatchFound = 0; + } + } + wm_SemUnlock(&client->lockClient); + } + else { + break; /* error */ + } + #endif /* WOLFMQTT_MULTITHREAD */ + + rc = SN_Client_HandlePacket(client, use_packet_type, use_packet_obj, + timeout_ms); + + #ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE) { + break; + } + #endif + + /* handle success case */ + if (rc >= 0) { + rc = MQTT_CODE_SUCCESS; + } + + #ifdef WOLFMQTT_MULTITHREAD + if (pendResp) { + /* Mark pending response entry done */ + if (wm_SemLock(&client->lockClient) == 0) { + pendResp->packetDone = 1; + pendResp->packet_ret = rc; + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("PendResp Marked Done %p", pendResp); + #endif + pendResp = NULL; + wm_SemUnlock(&client->lockClient); + } + } + #endif /* WOLFMQTT_MULTITHREAD */ + break; + } + + case MQTT_MSG_ACK: /* ack handled in SN_Client_HandlePacket */ + case MQTT_MSG_AUTH: + default: + { + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("SN_Client_WaitType: Invalid state %d!", mms_stat->read); + #endif + rc = MQTT_TRACE_ERROR(MQTT_CODE_ERROR_STAT); + break; + } + } /* switch (msg->stat) */ + +#ifdef WOLFMQTT_DEBUG_CLIENT + if (rc != MQTT_CODE_CONTINUE) { + PRINTF("SN_Client_WaitType: rc %d, state %d", rc, mms_stat->read); + } +#endif + + if (mms_stat->read == MQTT_MSG_WAIT || rc != MQTT_CODE_CONTINUE) { + /* reset state */ + mms_stat->read = MQTT_MSG_BEGIN; + + #ifdef WOLFMQTT_MULTITHREAD + if (mms_stat->isReadActive) { + mms_stat->isReadActive = 0; + wm_SemUnlock(&client->lockRecv); + } + #endif + } + +#ifdef WOLFMQTT_NONBLOCK + #ifdef WOLFMQTT_DEBUG_CLIENT + client->lastRc = rc; + #endif + if (rc == MQTT_CODE_CONTINUE) { + return rc; + } +#endif + + /* Clear shared union for next call */ + if ((MqttObject*)use_packet_obj == &client->msg) { + /* reset the members, but not the stat */ + XMEMSET(((byte*)&client->msg.stat) + sizeof(client->msg.stat), 0, + sizeof(client->msg)-sizeof(client->msg.stat)); + } + + if (rc < 0) { + #ifdef WOLFMQTT_DEBUG_CLIENT + if (rc != MQTT_CODE_CONTINUE) { + PRINTF("SN_Client_WaitType: Failure: %s (%d)", + MqttClient_ReturnCodeToString(rc), rc); + } + #endif + return rc; + } + + if (!waitMatchFound) { + /* if we get here, then the we are still waiting for a packet */ + goto wait_again; + } + + return rc; +} + +/* Public Functions */ + +int SN_Client_SetRegisterCallback(MqttClient *client, + SN_ClientRegisterCb regCb, + void* ctx) +{ + int rc = MQTT_CODE_SUCCESS; + + if (client == NULL) + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + +#ifdef WOLFMQTT_MULTITHREAD + rc = wm_SemLock(&client->lockClient); + if (rc == 0) { +#endif + + client->reg_cb = regCb; + client->reg_ctx = ctx; + +#ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockClient); + } +#endif + + return rc; +} + +int SN_Client_SearchGW(MqttClient *client, SN_SearchGw *search) +{ + int rc; + + /* Validate required arguments */ + if (client == NULL || search == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + if (search->stat.write == MQTT_MSG_BEGIN) { + #ifdef WOLFMQTT_MULTITHREAD + /* Lock send socket mutex */ + rc = wm_SemLock(&client->lockSend); + if (rc != 0) { + return rc; + } + #endif + + /* Encode the search packet */ + rc = SN_Encode_SearchGW(client->tx_buf, client->tx_buf_len, + search->radius); + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d)", + rc, SN_Packet_TypeDesc(SN_MSG_TYPE_SEARCHGW), + SN_MSG_TYPE_SEARCHGW); + #endif + if (rc <= 0) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + client->write.len = rc; + + #ifdef WOLFMQTT_MULTITHREAD + rc = wm_SemLock(&client->lockClient); + if (rc == 0) { + /* inform other threads of expected response */ + rc = MqttClient_RespList_Add(client, + (MqttPacketType)SN_MSG_TYPE_GWINFO, 0, + &search->pendResp, &search->gwInfo); + wm_SemUnlock(&client->lockClient); + } + if (rc != 0) { + wm_SemUnlock(&client->lockSend); + return rc; /* Error locking client */ + } + #endif + + /* Send search for gateway packet */ + rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + if (rc != client->write.len) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &search->pendResp); + wm_SemUnlock(&client->lockClient); + } + #endif + return rc; + } + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + + search->stat.write = MQTT_MSG_WAIT; + } + + /* Wait for gateway info packet */ + rc = SN_Client_WaitType(client, &search->gwInfo, SN_MSG_TYPE_GWINFO, 0, + client->cmd_timeout_ms); +#ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE) + return rc; +#endif +#ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &search->pendResp); + wm_SemUnlock(&client->lockClient); + } +#endif + + /* reset state */ + search->stat.write = MQTT_MSG_BEGIN; + + return rc; +} + +static int SN_WillTopic(MqttClient *client, SN_Will *will) +{ + int rc; + + /* Validate required arguments */ + if (client == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + +#ifdef WOLFMQTT_MULTITHREAD + rc = wm_SemLock(&client->lockClient); + if (rc == 0) { + /* inform other threads of expected response */ + rc = MqttClient_RespList_Add(client, + (MqttPacketType)SN_MSG_TYPE_WILLTOPICREQ, 0, + &will->pendResp, &will->resp.topicResp); + wm_SemUnlock(&client->lockClient); + } + if (rc != 0) { + return rc; /* Error locking client */ + } +#endif + + /* Wait for Will Topic Request packet */ + rc = SN_Client_WaitType(client, will, + SN_MSG_TYPE_WILLTOPICREQ, 0, client->cmd_timeout_ms); +#ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE) + return rc; +#endif + +#ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &will->pendResp); + wm_SemUnlock(&client->lockClient); + } +#endif + if (rc == 0) { + #ifdef WOLFMQTT_MULTITHREAD + /* Lock send socket mutex */ + rc = wm_SemLock(&client->lockSend); + if (rc != 0) { + return rc; + } + #endif + + /* Encode Will Topic */ + rc = SN_Encode_WillTopic(client->tx_buf, client->tx_buf_len, + will); + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("EncodePacket: Len %d, Type %s (%d)", + rc, SN_Packet_TypeDesc(SN_MSG_TYPE_WILLTOPIC), + SN_MSG_TYPE_WILLTOPIC); + #endif + if (rc > 0) { + /* Send Will Topic packet */ + client->write.len = rc; + rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + if (rc == client->write.len) { + rc = 0; + } + } + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + } + + return rc; +} + +static int SN_WillMessage(MqttClient *client, SN_Will *will) +{ + int rc; + + /* Validate required arguments */ + if (client == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + +#ifdef WOLFMQTT_MULTITHREAD + rc = wm_SemLock(&client->lockClient); + if (rc == 0) { + /* inform other threads of expected response */ + rc = MqttClient_RespList_Add(client, + (MqttPacketType)SN_MSG_TYPE_WILLMSGREQ, 0, + &will->pendResp, &will->resp.msgResp); + wm_SemUnlock(&client->lockClient); + } + if (rc != 0) { + return rc; /* Error locking client */ + } +#endif + + /* Wait for Will Message Request */ + rc = SN_Client_WaitType(client, &will->resp.msgResp, + SN_MSG_TYPE_WILLMSGREQ, 0, client->cmd_timeout_ms); + +#ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE) + return rc; +#endif + +#ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &will->pendResp); + wm_SemUnlock(&client->lockClient); + } +#endif + + if (rc == 0) { + #ifdef WOLFMQTT_MULTITHREAD + /* Lock send socket mutex */ + rc = wm_SemLock(&client->lockSend); + if (rc != 0) { + return rc; + } + #endif + /* Encode Will Message */ + rc = SN_Encode_WillMsg(client->tx_buf, + client->tx_buf_len, will); + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("EncodePacket: Len %d, Type %s (%d)", + rc, SN_Packet_TypeDesc(SN_MSG_TYPE_WILLMSG), + SN_MSG_TYPE_WILLMSG); + #endif + if (rc > 0) { + /* Send Will Topic packet */ + client->write.len = rc; + rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + if (rc == client->write.len) { + rc = 0; + } + } + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + } + + return rc; +} + +int SN_Client_Connect(MqttClient *client, SN_Connect *mc_connect) +{ + int rc = 0; + static byte will_done; + + /* Validate required arguments */ + if ((client == NULL) || (mc_connect == NULL)) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + if (mc_connect->stat.write == MQTT_MSG_BEGIN) { + + will_done = 0; + + #ifdef WOLFMQTT_MULTITHREAD + /* Lock send socket mutex */ + rc = wm_SemLock(&client->lockSend); + if (rc != 0) { + return rc; + } + #endif + + /* Encode the connect packet */ + rc = SN_Encode_Connect(client->tx_buf, client->tx_buf_len, mc_connect); +#ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d), ID %d, QoS %d", + rc, SN_Packet_TypeDesc(SN_MSG_TYPE_CONNECT), + SN_MSG_TYPE_CONNECT, 0, 0); +#endif + if (rc <= 0) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + client->write.len = rc; + + #ifdef WOLFMQTT_MULTITHREAD + rc = wm_SemLock(&client->lockClient); + if (rc == 0) { + /* inform other threads of expected response */ + rc = MqttClient_RespList_Add(client, + (MqttPacketType)SN_MSG_TYPE_CONNACK, 0, + &mc_connect->pendResp, &mc_connect->ack); + wm_SemUnlock(&client->lockClient); + } + if (rc != 0) { + wm_SemUnlock(&client->lockSend); + return rc; /* Error locking client */ + } + #endif + + /* Send connect packet */ + rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + if (rc != client->write.len) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + + mc_connect->stat.write = MQTT_MSG_WAIT; + } + + if ((mc_connect->enable_lwt == 1) && (will_done != 1)) { + /* If the will is enabled, then the gateway requests the topic and + message in separate packets. */ + rc = SN_WillTopic(client, &mc_connect->will); + if (rc != 0) { + return rc; + } + + rc = SN_WillMessage(client, &mc_connect->will); + if (rc != 0) { + return rc; + } + will_done = 1; + } + + /* Wait for connect ack packet */ + rc = SN_Client_WaitType(client, &mc_connect->ack, + SN_MSG_TYPE_CONNACK, 0, client->cmd_timeout_ms); +#ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE) + return rc; +#endif + +#ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &mc_connect->pendResp); + wm_SemUnlock(&client->lockClient); + } +#endif + + /* reset state */ + mc_connect->stat.write = MQTT_MSG_BEGIN; + + return rc; +} + +int SN_Client_WillTopicUpdate(MqttClient *client, SN_Will *will) +{ + int rc = 0; + + /* Validate required arguments */ + if (client == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + if (will->stat.write == MQTT_MSG_BEGIN) { + #ifdef WOLFMQTT_MULTITHREAD + /* Lock send socket mutex */ + rc = wm_SemLock(&client->lockSend); + if (rc != 0) { + return rc; + } + #endif + + /* Encode Will Topic Update */ + rc = SN_Encode_WillTopicUpdate(client->tx_buf, + client->tx_buf_len, will); + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d)", + rc, SN_Packet_TypeDesc(SN_MSG_TYPE_WILLTOPICUPD), + SN_MSG_TYPE_WILLTOPICUPD); + #endif + if (rc <= 0) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + client->write.len = rc; + + #ifdef WOLFMQTT_MULTITHREAD + rc = wm_SemLock(&client->lockClient); + if (rc == 0) { + /* inform other threads of expected response */ + rc = MqttClient_RespList_Add(client, + (MqttPacketType)SN_MSG_TYPE_WILLTOPICRESP, + 0, &will->pendResp, &will->resp.topicResp); + wm_SemUnlock(&client->lockClient); + } + if (rc != 0) { + wm_SemUnlock(&client->lockSend); + return rc; /* Error locking client */ + } + #endif + + /* Send Will Topic Update packet */ + rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + if (rc != client->write.len) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &will->pendResp); + wm_SemUnlock(&client->lockClient); + } + #endif + } + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + + will->stat.write = MQTT_MSG_WAIT; + } + + /* Wait for Will Topic Update Response packet */ + rc = SN_Client_WaitType(client, &will->resp.topicResp, + SN_MSG_TYPE_WILLTOPICRESP, 0, client->cmd_timeout_ms); +#ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE) + return rc; +#endif +#ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &will->pendResp); + wm_SemUnlock(&client->lockClient); + } +#endif + + /* reset state */ + will->stat.write = MQTT_MSG_BEGIN; + + return rc; +} + +int SN_Client_WillMsgUpdate(MqttClient *client, SN_Will *will) +{ + int rc = 0; + + /* Validate required arguments */ + if ((client == NULL) || (will == NULL)) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + if (will->stat.write == MQTT_MSG_BEGIN) { + #ifdef WOLFMQTT_MULTITHREAD + /* Lock send socket mutex */ + rc = wm_SemLock(&client->lockSend); + if (rc != 0) { + return rc; + } + #endif + /* Encode Will Message Update */ + rc = SN_Encode_WillMsgUpdate(client->tx_buf, + client->tx_buf_len, will); + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d)", + rc, SN_Packet_TypeDesc(SN_MSG_TYPE_WILLTOPICUPD), + SN_MSG_TYPE_WILLTOPICUPD); + #endif + if (rc <= 0) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + client->write.len = rc; + + #ifdef WOLFMQTT_MULTITHREAD + rc = wm_SemLock(&client->lockClient); + if (rc == 0) { + /* inform other threads of expected response */ + rc = MqttClient_RespList_Add(client, + (MqttPacketType)SN_MSG_TYPE_WILLMSGRESP, + 0, &will->pendResp, &will->resp.msgResp); + wm_SemUnlock(&client->lockClient); + } + if (rc != 0) { + wm_SemUnlock(&client->lockSend); + return rc; /* Error locking client */ + } + #endif + + /* Send Will Message Update packet */ + rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + if (rc != client->write.len) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &will->pendResp); + wm_SemUnlock(&client->lockClient); + } + #endif + } + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + + will->stat.write = MQTT_MSG_WAIT; + } + + /* Wait for Will Message Update Response packet */ + rc = SN_Client_WaitType(client, &will->resp.msgResp, + SN_MSG_TYPE_WILLMSGRESP, 0, client->cmd_timeout_ms); +#ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE) + return rc; +#endif +#ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &will->pendResp); + wm_SemUnlock(&client->lockClient); + } +#endif + + /* reset state */ + will->stat.write = MQTT_MSG_BEGIN; + + return rc; + +} + +int SN_Client_Subscribe(MqttClient *client, SN_Subscribe *subscribe) +{ + int rc = -1; + + /* Validate required arguments */ + if (client == NULL || subscribe == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + if (subscribe->stat.write == MQTT_MSG_BEGIN) { + #ifdef WOLFMQTT_MULTITHREAD + /* Lock send socket mutex */ + rc = wm_SemLock(&client->lockSend); + if (rc != 0) { + return rc; + } + #endif + + /* Encode the subscribe packet */ + rc = SN_Encode_Subscribe(client->tx_buf, client->tx_buf_len, + subscribe); + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d), QoS %d", + rc, SN_Packet_TypeDesc(SN_MSG_TYPE_SUBSCRIBE), + SN_MSG_TYPE_SUBSCRIBE, subscribe->qos); + #endif + if (rc <= 0) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + client->write.len = rc; + + #ifdef WOLFMQTT_MULTITHREAD + rc = wm_SemLock(&client->lockClient); + if (rc == 0) { + /* inform other threads of expected response */ + rc = MqttClient_RespList_Add(client, + (MqttPacketType)SN_MSG_TYPE_SUBACK, subscribe->packet_id, + &subscribe->pendResp, &subscribe->subAck); + wm_SemUnlock(&client->lockClient); + } + if (rc != 0) { + wm_SemUnlock(&client->lockSend); + return rc; /* Error locking client */ + } + #endif + + /* Send subscribe packet */ + rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + if (rc != client->write.len) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &subscribe->pendResp); + wm_SemUnlock(&client->lockClient); + } + #endif + return rc; + } + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + + subscribe->stat.write = MQTT_MSG_WAIT; + } + + /* Wait for subscribe ack packet */ + rc = SN_Client_WaitType(client, &subscribe->subAck, + SN_MSG_TYPE_SUBACK, subscribe->packet_id, client->cmd_timeout_ms); + +#ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE) + return rc; +#endif +#ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &subscribe->pendResp); + wm_SemUnlock(&client->lockClient); + } +#endif + + /* reset state */ + subscribe->stat.write = MQTT_MSG_BEGIN; + + return rc; +} + +int SN_Client_Publish(MqttClient *client, SN_Publish *publish) +{ + int rc = MQTT_CODE_SUCCESS; + SN_MsgType resp_type; + + /* Validate required arguments */ + if (client == NULL || publish == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + switch (publish->stat.write) + { + case MQTT_MSG_BEGIN: + { + #ifdef WOLFMQTT_MULTITHREAD + /* Lock send socket mutex */ + rc = wm_SemLock(&client->lockSend); + if (rc != 0) { + return rc; + } + #endif + + /* Encode the publish packet */ + rc = SN_Encode_Publish(client->tx_buf, client->tx_buf_len, + publish); + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d), ID %d," + " QoS %d", + rc, SN_Packet_TypeDesc(SN_MSG_TYPE_PUBLISH), + SN_MSG_TYPE_PUBLISH, publish->packet_id, + publish->qos); + #endif + if (rc <= 0) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + + client->write.len = rc; + publish->buffer_pos = 0; + + #ifdef WOLFMQTT_MULTITHREAD + if ((publish->qos == MQTT_QOS_1) || + (publish->qos == MQTT_QOS_2)) { + resp_type = (publish->qos == MQTT_QOS_1) ? + SN_MSG_TYPE_PUBACK : + SN_MSG_TYPE_PUBCOMP; + + rc = wm_SemLock(&client->lockClient); + if (rc == 0) { + /* inform other threads of expected response */ + rc = MqttClient_RespList_Add(client, + (MqttPacketType)resp_type, publish->packet_id, + &publish->pendResp, &publish->resp); + wm_SemUnlock(&client->lockClient); + } + if (rc != 0) { + wm_SemUnlock(&client->lockSend); + return rc; /* Error locking client */ + } + } + #endif + + publish->stat.write = MQTT_MSG_HEADER; + } + FALL_THROUGH; + + case MQTT_MSG_HEADER: + case MQTT_MSG_PAYLOAD: + case MQTT_MSG_PAYLOAD2: + { + /* Send packet and payload */ + rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + #ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE) + return rc; + #endif + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + + if (rc < 0) { + #ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &publish->pendResp); + wm_SemUnlock(&client->lockClient); + } + #endif + return rc; + } + + if (rc == client->write.len) { + rc = MQTT_CODE_SUCCESS; + } + else { + rc = -1; + } + + /* if not expecting a reply, the reset state and exit */ + if ((publish->qos == MQTT_QOS_0) || + (publish->qos == MQTT_QOS_3)) { + break; + } + + publish->stat.write = MQTT_MSG_WAIT; + } + FALL_THROUGH; + + case MQTT_MSG_WAIT: + { + /* Handle QoS */ + if ((publish->qos == MQTT_QOS_1) || + (publish->qos == MQTT_QOS_2)) { + + /* Determine packet type to wait for */ + resp_type = (publish->qos == MQTT_QOS_1) ? + SN_MSG_TYPE_PUBACK : + SN_MSG_TYPE_PUBCOMP; + + /* Wait for publish response packet */ + rc = SN_Client_WaitType(client, &publish->resp, + resp_type, publish->packet_id, client->cmd_timeout_ms); + #ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE) + break; + #endif + #ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &publish->pendResp); + wm_SemUnlock(&client->lockClient); + } + #endif + + publish->return_code = publish->resp.return_code; + } + + break; + } + + case MQTT_MSG_ACK: + case MQTT_MSG_AUTH: + default: + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("SN_Client_Publish: Invalid state %d!", + publish->stat.write); + #endif + rc = MQTT_TRACE_ERROR(MQTT_CODE_ERROR_STAT); + break; + } /* switch (publish->stat) */ + + /* reset state */ +#ifdef WOLFMQTT_NONBLOCK + if (rc != MQTT_CODE_CONTINUE) +#endif + { + publish->stat.write = MQTT_MSG_BEGIN; + } + if (rc > 0) { + rc = MQTT_CODE_SUCCESS; + } + + return rc; +} + +int SN_Client_Unsubscribe(MqttClient *client, SN_Unsubscribe *unsubscribe) +{ + int rc; + + /* Validate required arguments */ + if (client == NULL || unsubscribe == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + if (unsubscribe->stat.write == MQTT_MSG_BEGIN) { + #ifdef WOLFMQTT_MULTITHREAD + /* Lock send socket mutex */ + rc = wm_SemLock(&client->lockSend); + if (rc != 0) { + return rc; + } + #endif + + /* Encode the subscribe packet */ + rc = SN_Encode_Unsubscribe(client->tx_buf, client->tx_buf_len, + unsubscribe); + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d)", + rc, SN_Packet_TypeDesc(SN_MSG_TYPE_UNSUBSCRIBE), + SN_MSG_TYPE_UNSUBSCRIBE); + #endif + if (rc <= 0) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + client->write.len = rc; + + #ifdef WOLFMQTT_MULTITHREAD + rc = wm_SemLock(&client->lockClient); + if (rc == 0) { + /* inform other threads of expected response */ + rc = MqttClient_RespList_Add(client, + (MqttPacketType)SN_MSG_TYPE_UNSUBACK, + 0, &unsubscribe->pendResp, &unsubscribe->ack); + wm_SemUnlock(&client->lockClient); + } + if (rc != 0) { + wm_SemUnlock(&client->lockSend); + return rc; /* Error locking client */ + } + #endif + + /* Send unsubscribe packet */ + rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + if (rc != client->write.len) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &unsubscribe->pendResp); + wm_SemUnlock(&client->lockClient); + } + #endif + } + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + + unsubscribe->stat.write = MQTT_MSG_WAIT; + } + + /* Wait for unsubscribe ack packet */ + rc = SN_Client_WaitType(client, &unsubscribe->ack, + SN_MSG_TYPE_UNSUBACK, unsubscribe->packet_id, + client->cmd_timeout_ms); + #ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE) + return rc; + #endif + #ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &unsubscribe->pendResp); + wm_SemUnlock(&client->lockClient); + } + #endif + + /* reset state */ + unsubscribe->stat.write = MQTT_MSG_BEGIN; + + return rc; +} + +int SN_Client_Register(MqttClient *client, SN_Register *regist) +{ + int rc; + + /* Validate required arguments */ + if (client == NULL || regist == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + if (regist->stat.write == MQTT_MSG_BEGIN) { + #ifdef WOLFMQTT_MULTITHREAD + /* Lock send socket mutex */ + rc = wm_SemLock(&client->lockSend); + if (rc != 0) { + return rc; + } + #endif + + /* Encode the register packet */ + rc = SN_Encode_Register(client->tx_buf, client->tx_buf_len, regist); + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d)", + rc, SN_Packet_TypeDesc(SN_MSG_TYPE_REGISTER), + SN_MSG_TYPE_REGISTER); + #endif + if (rc <= 0) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + client->write.len = rc; + + #ifdef WOLFMQTT_MULTITHREAD + rc = wm_SemLock(&client->lockClient); + if (rc == 0) { + /* inform other threads of expected response */ + rc = MqttClient_RespList_Add(client, + (MqttPacketType)SN_MSG_TYPE_REGACK, + regist->packet_id, ®ist->pendResp, ®ist->regack); + wm_SemUnlock(&client->lockClient); + } + if (rc != 0) { + wm_SemUnlock(&client->lockSend); + return rc; /* Error locking client */ + } + #endif + + /* Send register packet */ + rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + if (rc != client->write.len) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, ®ist->pendResp); + wm_SemUnlock(&client->lockClient); + } + #endif + return rc; + } + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + + regist->stat.write = MQTT_MSG_WAIT; + } + + /* Wait for register acknowledge packet */ + rc = SN_Client_WaitType(client, ®ist->regack, + SN_MSG_TYPE_REGACK, regist->packet_id, client->cmd_timeout_ms); +#ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE) + return rc; +#endif +#ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, ®ist->pendResp); + wm_SemUnlock(&client->lockClient); + } +#endif + + /* reset state */ + regist->stat.write = MQTT_MSG_BEGIN; + + return rc; +} + +int SN_Client_Ping(MqttClient *client, SN_PingReq *ping) +{ + int rc; + SN_PingReq loc_ping; + + /* Validate required arguments */ + if (client == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + if (ping == NULL) { + XMEMSET(&loc_ping, 0, sizeof(SN_PingReq)); + ping = &loc_ping; + } + + if (ping->stat.write == MQTT_MSG_BEGIN) { + #ifdef WOLFMQTT_MULTITHREAD + /* Lock send socket mutex */ + rc = wm_SemLock(&client->lockSend); + if (rc != 0) { + return rc; + } + #endif + + /* Encode the ping packet as a request */ + rc = SN_Encode_Ping(client->tx_buf, client->tx_buf_len, ping, + SN_MSG_TYPE_PING_REQ); + #ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d)", + rc, SN_Packet_TypeDesc(SN_MSG_TYPE_PING_REQ), + SN_MSG_TYPE_PING_REQ); + #endif + if (rc <= 0) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + client->write.len = rc; + + #ifdef WOLFMQTT_MULTITHREAD + rc = wm_SemLock(&client->lockClient); + if (rc == 0) { + /* inform other threads of expected response */ + rc = MqttClient_RespList_Add(client, + (MqttPacketType)SN_MSG_TYPE_PING_RESP, 0, + &ping->pendResp, NULL); + wm_SemUnlock(&client->lockClient); + } + if (rc != 0) { + wm_SemUnlock(&client->lockSend); + return rc; /* Error locking client */ + } + #endif + + /* Send ping req packet */ + rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + if (rc != client->write.len) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &ping->pendResp); + wm_SemUnlock(&client->lockClient); + } + #endif + return rc; + } + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + + ping->stat.write = MQTT_MSG_WAIT; + } + + /* Wait for ping resp packet */ + rc = SN_Client_WaitType(client, ping, + SN_MSG_TYPE_PING_RESP, 0, client->cmd_timeout_ms); +#ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE) + return rc; +#endif +#ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &ping->pendResp); + wm_SemUnlock(&client->lockClient); + } +#endif + + /* reset state */ + ping->stat.write = MQTT_MSG_BEGIN; + + return rc; +} + +int SN_Client_Disconnect(MqttClient *client) +{ + return SN_Client_Disconnect_ex(client, NULL); +} + +int SN_Client_Disconnect_ex(MqttClient *client, SN_Disconnect *disconnect) +{ + int rc; + + /* Validate required arguments */ + if (client == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + +#ifdef WOLFMQTT_MULTITHREAD + /* Lock send socket mutex */ + rc = wm_SemLock(&client->lockSend); + if (rc != 0) { + return rc; + } +#endif + + /* Encode the disconnect packet */ + rc = SN_Encode_Disconnect(client->tx_buf, client->tx_buf_len, disconnect); +#ifdef WOLFMQTT_DEBUG_CLIENT + PRINTF("MqttClient_EncodePacket: Len %d, Type %s (%d)", + rc, SN_Packet_TypeDesc(SN_MSG_TYPE_DISCONNECT), + SN_MSG_TYPE_DISCONNECT); +#endif + if (rc <= 0) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + #endif + return rc; + } + client->write.len = rc; + +#ifdef WOLFMQTT_MULTITHREAD + if ((disconnect != NULL) && (disconnect->sleepTmr != 0)) { + rc = wm_SemLock(&client->lockClient); + if (rc == 0) { + /* inform other threads of expected response */ + rc = MqttClient_RespList_Add(client, + (MqttPacketType)SN_MSG_TYPE_DISCONNECT, 0, + &disconnect->pendResp, NULL); + wm_SemUnlock(&client->lockClient); + } + if (rc != 0) { + wm_SemUnlock(&client->lockSend); + return rc; /* Error locking client */ + } + } +#endif + + /* Send disconnect packet */ + rc = MqttPacket_Write(client, client->tx_buf, client->write.len); + if (rc != client->write.len) { + #ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &disconnect->pendResp); + wm_SemUnlock(&client->lockClient); + } + #endif + return rc; + } +#ifdef WOLFMQTT_MULTITHREAD + wm_SemUnlock(&client->lockSend); +#endif + + rc = MQTT_CODE_SUCCESS; + + /* If sleep was set, wait for response disconnect packet */ + if ((disconnect != NULL) && (disconnect->sleepTmr != 0)) { + rc = SN_Client_WaitType(client, disconnect, + SN_MSG_TYPE_DISCONNECT, 0, client->cmd_timeout_ms); + #ifdef WOLFMQTT_NONBLOCK + if (rc == MQTT_CODE_CONTINUE) + return rc; + #endif + #ifdef WOLFMQTT_MULTITHREAD + if (wm_SemLock(&client->lockClient) == 0) { + MqttClient_RespList_Remove(client, &disconnect->pendResp); + wm_SemUnlock(&client->lockClient); + } + #endif + } + + return rc; +} + +int SN_Client_WaitMessage_ex(MqttClient *client, SN_Object* packet_obj, + int timeout_ms) +{ + return SN_Client_WaitType(client, packet_obj, + SN_MSG_TYPE_ANY, 0, timeout_ms); +} + +int SN_Client_WaitMessage(MqttClient *client, int timeout_ms) +{ + if (client == NULL) + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + return SN_Client_WaitMessage_ex(client, &client->msgSN, timeout_ms); +} + +#endif /* defined WOLFMQTT_SN */ diff --git a/src/mqtt_sn_packet.c b/src/mqtt_sn_packet.c new file mode 100644 index 000000000..287142a2b --- /dev/null +++ b/src/mqtt_sn_packet.c @@ -0,0 +1,1576 @@ +/* mqtt_sn_packet.c + * + * Copyright (C) 2006-2023 wolfSSL Inc. + * + * This file is part of wolfMQTT. + * + * wolfMQTT is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfMQTT is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Include the autoconf generated config.h */ +#ifdef HAVE_CONFIG_H + #include +#endif + +#include "wolfmqtt/mqtt_sn_client.h" +#include "wolfmqtt/mqtt_sn_packet.h" + +#ifdef WOLFMQTT_SN +const char* SN_Packet_TypeDesc(SN_MsgType packet_type) +{ + switch (packet_type) { + case SN_MSG_TYPE_ADVERTISE: + return "Advertise"; + case SN_MSG_TYPE_SEARCHGW: + return "Search gateway"; + case SN_MSG_TYPE_GWINFO: + return "Gateway info"; + case SN_MSG_TYPE_CONNECT: + return "Connect"; + case SN_MSG_TYPE_CONNACK: + return "Connect Ack"; + case SN_MSG_TYPE_WILLTOPICREQ: + return "Will topic request"; + case SN_MSG_TYPE_WILLTOPIC: + return "Will topic set"; + case SN_MSG_TYPE_WILLMSGREQ: + return "Will message request"; + case SN_MSG_TYPE_WILLMSG: + return "Will message set"; + case SN_MSG_TYPE_REGISTER: + return "Register"; + case SN_MSG_TYPE_REGACK: + return "Register Ack"; + case SN_MSG_TYPE_PUBLISH: + return "Publish"; + case SN_MSG_TYPE_PUBACK: + return "Publish Ack"; + case SN_MSG_TYPE_PUBCOMP: + return "Publish complete"; + case SN_MSG_TYPE_PUBREC: + return "Publish Received"; + case SN_MSG_TYPE_PUBREL: + return "Publish Release"; + case SN_MSG_TYPE_SUBSCRIBE: + return "Subscribe"; + case SN_MSG_TYPE_SUBACK: + return "Subscribe Ack"; + case SN_MSG_TYPE_UNSUBSCRIBE: + return "Unsubscribe"; + case SN_MSG_TYPE_UNSUBACK: + return "Unsubscribe Ack"; + case SN_MSG_TYPE_PING_REQ: + return "Ping Req"; + case SN_MSG_TYPE_PING_RESP: + return "Ping Resp"; + case SN_MSG_TYPE_DISCONNECT: + return "Disconnect"; + case SN_MSG_TYPE_WILLTOPICUPD: + return "Will topic update"; + case SN_MSG_TYPE_WILLTOPICRESP: + return "WIll topic response"; + case SN_MSG_TYPE_WILLMSGUPD: + return "Will message update"; + case SN_MSG_TYPE_WILLMSGRESP: + return "Will message response"; + case SN_MSG_TYPE_ENCAPMSG: + return "Encapsulated message"; + case SN_MSG_TYPE_ANY: + return "Any"; + default: + break; + } + return "Unknown"; +} + +int SN_Decode_Header(byte *rx_buf, int rx_buf_len, + SN_MsgType* p_packet_type, word16* p_packet_id) +{ + SN_MsgType packet_type; + word16 total_len; + + if (rx_buf == NULL || rx_buf_len < MQTT_PACKET_HEADER_MIN_SIZE) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Decode fixed header */ + total_len = *rx_buf++; + if (total_len == SN_PACKET_LEN_IND) { + /* The length is stored in the next two bytes */ + rx_buf += MqttDecode_Num(rx_buf, &total_len); + } + + if (total_len > rx_buf_len) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + /* Message Type */ + packet_type = (SN_MsgType)*rx_buf++; + + if (p_packet_type) + *p_packet_type = packet_type; + + if (p_packet_id) { + switch(packet_type) { + case SN_MSG_TYPE_REGACK: + case SN_MSG_TYPE_PUBACK: + /* octet 4-5 */ + MqttDecode_Num(rx_buf + 2, p_packet_id); + break; + case SN_MSG_TYPE_PUBCOMP: + case SN_MSG_TYPE_PUBREC: + case SN_MSG_TYPE_PUBREL: + case SN_MSG_TYPE_UNSUBACK: + /* octet 2-3 */ + MqttDecode_Num(rx_buf, p_packet_id); + break; + case SN_MSG_TYPE_SUBACK: + /* octet 5-6 */ + MqttDecode_Num(rx_buf + 3, p_packet_id); + break; + case SN_MSG_TYPE_ADVERTISE: + case SN_MSG_TYPE_SEARCHGW: + case SN_MSG_TYPE_GWINFO: + case SN_MSG_TYPE_CONNECT: + case SN_MSG_TYPE_CONNACK: + case SN_MSG_TYPE_WILLTOPICREQ: + case SN_MSG_TYPE_WILLTOPIC: + case SN_MSG_TYPE_WILLMSGREQ: + case SN_MSG_TYPE_WILLMSG: + case SN_MSG_TYPE_REGISTER: + case SN_MSG_TYPE_PUBLISH: + case SN_MSG_TYPE_SUBSCRIBE: + case SN_MSG_TYPE_UNSUBSCRIBE: + case SN_MSG_TYPE_PING_REQ: + case SN_MSG_TYPE_PING_RESP: + case SN_MSG_TYPE_DISCONNECT: + case SN_MSG_TYPE_WILLTOPICUPD: + case SN_MSG_TYPE_WILLTOPICRESP: + case SN_MSG_TYPE_WILLMSGUPD: + case SN_MSG_TYPE_WILLMSGRESP: + case SN_MSG_TYPE_ENCAPMSG: + case SN_MSG_TYPE_RESERVED: + default: + *p_packet_id = 0; + break; + } + } + + return (int)total_len; +} + +int SN_Decode_Advertise(byte *rx_buf, int rx_buf_len, SN_Advertise *gw_info) +{ + int total_len; + byte *rx_payload = rx_buf, type; + + /* Validate required arguments */ + if (rx_buf == NULL || rx_buf_len <= 0) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Decode fixed header */ + total_len = *rx_payload++; + + /* Check message type */ + type = *rx_payload++; + if (total_len != 5) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); + } + + if (type != SN_MSG_TYPE_ADVERTISE) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); + } + + /* Decode gateway info */ + if (gw_info != NULL) { + gw_info->gwId = *rx_payload++; + + rx_payload += MqttDecode_Num(rx_payload, &gw_info->duration); + } + (void)rx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Encode_SearchGW(byte *tx_buf, int tx_buf_len, byte hops) +{ + int total_len; + byte *tx_payload = tx_buf; + + /* Packet length is not variable */ + total_len = 3; + + /* Validate required arguments */ + if (tx_buf == NULL || tx_buf_len < total_len) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Encode length */ + *tx_payload++ = total_len; + + /* Encode message type */ + *tx_payload++ = SN_MSG_TYPE_SEARCHGW; + + /* Encode radius */ + *tx_payload++ = hops; + (void)tx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Decode_GWInfo(byte *rx_buf, int rx_buf_len, SN_GwInfo *gw_info) +{ + int total_len; + byte *rx_payload = rx_buf, type; + + /* Validate required arguments */ + if (rx_buf == NULL || rx_buf_len <= 0) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Decode fixed header */ + total_len = *rx_payload++; + if (total_len == SN_PACKET_LEN_IND) { + /* The length is stored in the next two bytes */ + rx_payload += MqttDecode_Num(rx_payload, (word16*)&total_len); + } + + if (total_len > rx_buf_len) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + if (total_len < 3) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); + } + /* Check message type */ + type = *rx_payload++; + if (type != SN_MSG_TYPE_GWINFO) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); + } + + /* Decode gateway info */ + if (gw_info != NULL) { + gw_info->gwId = *rx_payload++; + + /* TODO: validate size of gwAddr */ + if (total_len - 3 > 0) { + /* The gateway address is only present if sent by a client */ + XMEMCPY(gw_info->gwAddr, rx_payload, total_len - 3); + } + } + (void)rx_payload; + + /* Return total length of packet */ + return total_len; +} + +/* Packet Type Encoders/Decoders */ +int SN_Encode_Connect(byte *tx_buf, int tx_buf_len, SN_Connect *mc_connect) +{ + word16 total_len, id_len; + byte flags = 0; + byte *tx_payload = tx_buf; + + /* Validate required arguments */ + if ((tx_buf == NULL) || (mc_connect == NULL) || + (mc_connect->client_id == NULL) || (mc_connect->protocol_level == 0)) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Determine packet length */ + total_len = 6; /* Len + Message Type + Flags + ProtocolID + Duration(2) */ + + /* Client ID size */ + id_len = (word16)XSTRLEN(mc_connect->client_id); + id_len = (id_len <= SN_CLIENTID_MAX_LEN) ? id_len : SN_CLIENTID_MAX_LEN; + + total_len += id_len; + + if (total_len > SN_PACKET_MAX_SMALL_SIZE) { + total_len += 2; /* Store len in three bytes */ + } + + if (total_len > tx_buf_len) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + /* Encode length */ + if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { + *tx_payload++ = (byte)total_len; + } + else { + *tx_payload++ = SN_PACKET_LEN_IND; + tx_payload += MqttEncode_Num(tx_payload, total_len); + } + + /* Encode message type */ + *tx_payload++ = SN_MSG_TYPE_CONNECT; + + /* Encode flags */ + if (mc_connect->clean_session) { + flags |= SN_PACKET_FLAG_CLEANSESSION; + } + if (mc_connect->enable_lwt) { + flags |= SN_PACKET_FLAG_WILL; + } + *tx_payload++ = flags; + + /* Protocol version */ + *tx_payload++ = mc_connect->protocol_level; + + /* Encode duration (keep-alive) */ + tx_payload += MqttEncode_Num(tx_payload, mc_connect->keep_alive_sec); + + /* Encode Client ID */ + XMEMCPY(tx_payload, mc_connect->client_id, id_len); + tx_payload += id_len; + (void)tx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Decode_WillTopicReq(byte *rx_buf, int rx_buf_len) +{ + int total_len; + byte *rx_payload = rx_buf, type; + + /* Validate required arguments */ + if (rx_buf == NULL || rx_buf_len <= 0) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Length and MsgType */ + total_len = *rx_payload++; + if (total_len != 2) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); + } + + type = *rx_payload++; + if (type != SN_MSG_TYPE_WILLTOPICREQ) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); + } + (void)rx_payload; + + /* Return total length of packet */ + return total_len; +} + +/* An empty WILLTOPIC message is a WILLTOPIC message without Flags and + WillTopic field (i.e. it is exactly 2 octets long). It is used by a client + to delete the Will topic and the Will message stored in the server */ +int SN_Encode_WillTopic(byte *tx_buf, int tx_buf_len, SN_Will *willTopic) +{ + int total_len; + byte *tx_payload, flags = 0; + + /* Validate required arguments */ + if (tx_buf == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Length and MsgType */ + total_len = 2; + + /* Determine packet length */ + if (willTopic != NULL) { + /* Will Topic is a string */ + total_len += (int)XSTRLEN(willTopic->willTopic); + + /* Flags */ + total_len++; + + if (total_len > SN_PACKET_MAX_SMALL_SIZE) { + /* Length is stored in bytes 1 and 2 */ + total_len += 2; + } + } + + if (total_len > tx_buf_len) { + /* Buffer too small */ + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + /* Encode length */ + tx_payload = tx_buf; + + if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { + *tx_payload++ = total_len; + } + else { + *tx_payload++ = SN_PACKET_LEN_IND; + tx_payload += MqttEncode_Num(tx_payload, total_len); + } + + /* Encode message type */ + *tx_payload++ = SN_MSG_TYPE_WILLTOPIC; + + if (willTopic != NULL) { + int will_len; + + /* Encode flags */ + flags |= ((willTopic->qos << SN_PACKET_FLAG_QOS_SHIFT) & + SN_PACKET_FLAG_QOS_MASK); + flags |= (willTopic->retain != 0) ? SN_PACKET_FLAG_RETAIN : 0; + *tx_payload++ = flags; + + /* Encode Will Topic */ + will_len = (int)XSTRLEN(willTopic->willTopic); + XMEMCPY(tx_payload, willTopic->willTopic, will_len); + tx_payload += will_len; + } + (void)tx_payload; + + return total_len; +} + +int SN_Decode_WillMsgReq(byte *rx_buf, int rx_buf_len) +{ + int total_len; + byte *rx_payload = rx_buf, type; + + /* Validate required arguments */ + if (rx_buf == NULL || rx_buf_len <= 0) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Decode fixed header */ + total_len = *rx_payload++; + + /* Length and MsgType */ + if (total_len != 2){ + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); + } + + /* Message Type */ + type = *rx_payload++; + if (type != SN_MSG_TYPE_WILLMSGREQ) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); + } + (void)rx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Encode_WillMsg(byte *tx_buf, int tx_buf_len, SN_Will *willMsg) +{ + int total_len; + byte *tx_payload; + + /* Validate required arguments */ + if ((tx_buf == NULL) || (willMsg == NULL)) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Length and MsgType */ + total_len = 2; + + /* Determine packet length */ + /* Add Will Message len */ + total_len += willMsg->willMsgLen; + + if (total_len > SN_PACKET_MAX_SMALL_SIZE) { + /* Length is stored in bytes 1 and 2 */ + total_len += 2; + } + + if (total_len > tx_buf_len) { + /* Buffer too small */ + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + /* Encode length */ + tx_payload = tx_buf; + + if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { + *tx_payload++ = total_len; + } + else { + *tx_payload++ = SN_PACKET_LEN_IND; + tx_payload += MqttEncode_Num(tx_payload, total_len); + } + + /* Encode message type */ + *tx_payload++ = SN_MSG_TYPE_WILLMSG; + + /* Encode Will Message */ + XMEMCPY(tx_payload, willMsg->willMsg, willMsg->willMsgLen); + tx_payload += willMsg->willMsgLen; + (void)tx_payload; + + return total_len; +} + +int SN_Encode_WillTopicUpdate(byte *tx_buf, int tx_buf_len, SN_Will *willTopic) +{ + int total_len; + byte *tx_payload, flags = 0; + + /* Validate required arguments */ + if (tx_buf == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Length and MsgType */ + total_len = 2; + + /* Determine packet length */ + if (willTopic != NULL) { + /* Will Topic is a string */ + total_len += (int)XSTRLEN(willTopic->willTopic); + + /* Flags */ + total_len++; + + if (total_len > SN_PACKET_MAX_SMALL_SIZE) { + /* Length is stored in bytes 1 and 2 */ + total_len += 2; + } + } + + if (total_len > tx_buf_len) { + /* Buffer too small */ + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + /* Encode length */ + tx_payload = tx_buf; + + if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { + *tx_payload++ = total_len; + } + else { + *tx_payload++ = SN_PACKET_LEN_IND; + tx_payload += MqttEncode_Num(tx_payload, total_len); + } + + /* Encode message type */ + *tx_payload++ = SN_MSG_TYPE_WILLTOPICUPD; + + if (willTopic != NULL) { + int will_len; + + /* Encode flags */ + flags |= ((willTopic->qos << SN_PACKET_FLAG_QOS_SHIFT) & + SN_PACKET_FLAG_QOS_MASK); + flags |= (willTopic->retain != 0) ? SN_PACKET_FLAG_RETAIN : 0; + *tx_payload++ = flags; + + /* Encode Will Topic */ + will_len = (int)XSTRLEN(willTopic->willTopic); + XMEMCPY(tx_payload, willTopic->willTopic, will_len); + tx_payload += will_len; + } + (void)tx_payload; + + return total_len; + +} + +int SN_Decode_WillTopicResponse(byte *rx_buf, int rx_buf_len, byte *ret_code) +{ + int total_len; + byte *rx_payload = rx_buf, type; + + /* Validate required arguments */ + if (rx_buf == NULL || rx_buf_len <= 0) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Decode fixed header */ + total_len = *rx_payload++; + if (total_len != 3) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); + } + + type = *rx_payload++; + if (type != SN_MSG_TYPE_WILLTOPICRESP) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); + } + + /* Return Code */ + *ret_code = *rx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Encode_WillMsgUpdate(byte *tx_buf, int tx_buf_len, SN_Will *willMsg) +{ + int total_len; + byte *tx_payload; + + /* Validate required arguments */ + if ((tx_buf == NULL) || (willMsg == NULL)) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Length and MsgType */ + total_len = 2; + + /* Determine packet length */ + /* Add Will Message len */ + total_len += willMsg->willMsgLen; + + if (total_len > SN_PACKET_MAX_SMALL_SIZE) { + /* Length is stored in bytes 1 and 2 */ + total_len += 2; + } + + if (total_len > tx_buf_len) { + /* Buffer too small */ + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + /* Encode length */ + tx_payload = tx_buf; + + if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { + *tx_payload++ = total_len; + } + else { + *tx_payload++ = SN_PACKET_LEN_IND; + tx_payload += MqttEncode_Num(tx_payload, total_len); + } + + /* Encode message type */ + *tx_payload++ = SN_MSG_TYPE_WILLMSGUPD; + + /* Encode Will Message */ + XMEMCPY(tx_payload, willMsg->willMsg, willMsg->willMsgLen); + tx_payload += willMsg->willMsgLen; + (void)tx_payload; + + return total_len; +} + +int SN_Decode_WillMsgResponse(byte *rx_buf, int rx_buf_len, byte *ret_code) +{ + int total_len; + byte *rx_payload = rx_buf, type; + + /* Validate required arguments */ + if (rx_buf == NULL || rx_buf_len <= 0) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Decode fixed header */ + total_len = *rx_payload++; + if (total_len != 3) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); + } + + type = *rx_payload++; + if (type != SN_MSG_TYPE_WILLMSGRESP) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); + } + + /* Return Code */ + *ret_code = *rx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Decode_ConnectAck(byte *rx_buf, int rx_buf_len, + SN_ConnectAck *connect_ack) +{ + int total_len; + byte *rx_payload = rx_buf, type; + + /* Validate required arguments */ + if (rx_buf == NULL || rx_buf_len <= 0) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Decode fixed header */ + total_len = *rx_payload++; + if (total_len != 3) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); + } + + type = *rx_payload++; + if (type != SN_MSG_TYPE_CONNACK) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); + } + + /* Decode variable header */ + if (connect_ack) { + connect_ack->return_code = *rx_payload++; + } + (void)rx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Encode_Register(byte *tx_buf, int tx_buf_len, SN_Register *regist) +{ + int total_len, topic_len; + byte *tx_payload; + + /* Validate required arguments */ + if (tx_buf == NULL || regist == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Determine packet length */ + /* Topic name is a string */ + total_len = (int)XSTRLEN(regist->topicName); + + /* Length, MsgType, TopicID (2), and packet_id (2) */ + total_len += 6; + + if (total_len > SN_PACKET_MAX_SMALL_SIZE) { + /* Length is stored in bytes 1 and 2 */ + total_len += 2; + } + + if (total_len > tx_buf_len) { + /* Buffer too small */ + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + /* Encode length */ + tx_payload = tx_buf; + + if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { + *tx_payload++ = total_len; + } + else { + *tx_payload++ = SN_PACKET_LEN_IND; + tx_payload += MqttEncode_Num(tx_payload, total_len); + } + + /* Encode message type */ + *tx_payload++ = SN_MSG_TYPE_REGISTER; + + /* Encode Topic ID */ + tx_payload += MqttEncode_Num(tx_payload, regist->topicId); + + /* Encode Packet ID */ + tx_payload += MqttEncode_Num(tx_payload, regist->packet_id); + + /* Encode Topic Name */ + topic_len = (int)XSTRLEN(regist->topicName); + XMEMCPY(tx_payload, regist->topicName, topic_len); + tx_payload += topic_len; + (void)tx_payload; + + return total_len; +} + +int SN_Decode_Register(byte *rx_buf, int rx_buf_len, SN_Register *regist) +{ + int total_len; + byte *rx_payload = rx_buf, type; + + /* Validate required arguments */ + if (rx_buf == NULL || rx_buf_len <= 0) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Decode fixed header */ + total_len = *rx_payload++; + if (total_len == SN_PACKET_LEN_IND) { + /* The length is stored in the next two bytes */ + rx_payload += MqttDecode_Num(rx_payload, (word16*)&total_len); + } + + if (total_len > rx_buf_len) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + if (total_len < 7) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); + } + + /* Check message type */ + type = *rx_payload++; + if (type != SN_MSG_TYPE_REGISTER) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); + } + + if (regist != NULL) { + /* Decode Topic ID assigned by GW */ + rx_payload += MqttDecode_Num(rx_payload, ®ist->topicId); + + /* Decode packet ID */ + rx_payload += MqttDecode_Num(rx_payload, ®ist->packet_id); + + /* Decode Topic Name */ + regist->topicName = (char*)rx_payload; + + /* Terminate the string */ + rx_payload[total_len-6] = '\0'; + } + (void)rx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Encode_RegAck(byte *tx_buf, int tx_buf_len, SN_RegAck *regack) +{ + int total_len; + byte *tx_payload; + + /* Validate required arguments */ + if (tx_buf == NULL || regack == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Determine packet length */ + /* Length, MsgType, TopicID (2), and MsgId (2), Return Code */ + total_len = 7; + + if (total_len > tx_buf_len) { + /* Buffer too small */ + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + tx_payload = tx_buf; + + /* Encode length */ + *tx_payload++ = total_len; + + /* Encode message type */ + *tx_payload++ = SN_MSG_TYPE_REGACK; + + /* Encode Topic ID */ + tx_payload += MqttEncode_Num(tx_payload, regack->topicId); + + /* Encode Message ID */ + tx_payload += MqttEncode_Num(tx_payload, regack->packet_id); + + /* Encode Return Code */ + *tx_payload += regack->return_code; + + (void)tx_payload; + + return total_len; +} + +int SN_Decode_RegAck(byte *rx_buf, int rx_buf_len, SN_RegAck *regack) +{ + int total_len; + byte *rx_payload = rx_buf, type; + + /* Validate required arguments */ + if (rx_buf == NULL || rx_buf_len <= 0) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Decode fixed header */ + total_len = *rx_payload++; + if (total_len != 7) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); + } + + type = *rx_payload++; + if (type != SN_MSG_TYPE_REGACK) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); + } + + if (regack != NULL) { + /* Decode Topic ID assigned by GW */ + rx_payload += MqttDecode_Num(rx_payload, ®ack->topicId); + + /* Decode packet ID */ + rx_payload += MqttDecode_Num(rx_payload, ®ack->packet_id); + + /* Decode return code */ + regack->return_code = *rx_payload++; + } + (void)rx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Encode_Subscribe(byte *tx_buf, int tx_buf_len, SN_Subscribe *subscribe) +{ + int total_len; + byte *tx_payload, flags = 0x00; + + /* Validate required arguments */ + if (tx_buf == NULL || subscribe == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Determine packet length */ + if (subscribe->topic_type == SN_TOPIC_ID_TYPE_NORMAL) { + /* Topic name is a string */ + total_len = (int)XSTRLEN(subscribe->topicNameId); + } + else { + /* Topic ID or Short name */ + total_len = 2; + } + + /* Length, MsgType, Flags, and MsgID (2) */ + total_len += 5; + + if (total_len > SN_PACKET_MAX_SMALL_SIZE) { + /* Length is stored in bytes 1 and 2 */ + total_len += 2; + } + + if (total_len > tx_buf_len) { + /* Buffer too small */ + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + /* Encode length */ + tx_payload = tx_buf; + + if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { + *tx_payload++ = total_len; + } + else { + *tx_payload++ = SN_PACKET_LEN_IND; + tx_payload += MqttEncode_Num(tx_payload, total_len); + } + + /* Encode message type */ + *tx_payload++ = SN_MSG_TYPE_SUBSCRIBE; + + /* Set flags */ + if (subscribe->duplicate) + flags |= SN_PACKET_FLAG_DUPLICATE; + flags |= (SN_PACKET_FLAG_QOS_MASK & + (subscribe->qos << SN_PACKET_FLAG_QOS_SHIFT)); + flags |= (SN_PACKET_FLAG_TOPICIDTYPE_MASK & subscribe->topic_type); + + *tx_payload++ = flags; + + /* Encode packet ID */ + tx_payload += MqttEncode_Num(tx_payload, subscribe->packet_id); + + /* Encode topic */ + if (subscribe->topic_type == SN_TOPIC_ID_TYPE_NORMAL) { + /* Topic name is a string */ + XMEMCPY(tx_payload, subscribe->topicNameId, XSTRLEN(subscribe->topicNameId)); + } + else { + /* Topic ID */ + XMEMCPY(tx_payload, subscribe->topicNameId, 2); + } + (void)tx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Decode_SubscribeAck(byte* rx_buf, int rx_buf_len, + SN_SubAck *subscribe_ack) +{ + word16 total_len; + byte* rx_payload = rx_buf, type; + + /* Validate required arguments */ + if (rx_buf == NULL || rx_buf_len <= 0) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Decode fixed header */ + total_len = *rx_payload++; + if (total_len != 8) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); + } + + type = *rx_payload++; + if (type != SN_MSG_TYPE_SUBACK) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + /* Decode SubAck fields */ + if (subscribe_ack) { + subscribe_ack->flags = *rx_payload++; + rx_payload += MqttDecode_Num(rx_payload, &subscribe_ack->topicId); + rx_payload += MqttDecode_Num(rx_payload, &subscribe_ack->packet_id); + subscribe_ack->return_code = *rx_payload++; + } + (void)rx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Encode_Publish(byte *tx_buf, int tx_buf_len, SN_Publish *publish) +{ + word16 total_len; + byte *tx_payload = tx_buf; + byte flags = 0; + + /* Validate required arguments */ + if (tx_buf == NULL || publish == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Determine packet length */ + total_len = publish->total_len; + + /* Add length, msgType, flags, topic ID (2), and msgID (2) */ + total_len += 7; + + if (total_len > SN_PACKET_MAX_SMALL_SIZE) { + /* Length is stored in bytes 1 and 2 */ + total_len += 2; + } + + if (total_len > tx_buf_len) { + /* Buffer too small */ + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + /* Encode header */ + if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { + *tx_payload++ = (byte)total_len; + } + else { + *tx_payload++ = SN_PACKET_LEN_IND; + tx_payload += MqttEncode_Num(tx_payload, total_len); + } + + *tx_payload++ = SN_MSG_TYPE_PUBLISH; + + /* Set flags */ + if (publish->duplicate) + flags |= SN_PACKET_FLAG_DUPLICATE; + flags |= (SN_PACKET_FLAG_QOS_MASK & + (publish->qos << SN_PACKET_FLAG_QOS_SHIFT)); + if (publish->retain) + flags |= SN_PACKET_FLAG_RETAIN; + flags |= (SN_PACKET_FLAG_TOPICIDTYPE_MASK & publish->topic_type); + + *tx_payload++ = flags; + + /* Encode topic */ + if ((publish->topic_type == SN_TOPIC_ID_TYPE_SHORT) || + (publish->topic_type == SN_TOPIC_ID_TYPE_PREDEF)) { + /* Short and predefined topic names are 2 chars */ + XMEMCPY(tx_payload, publish->topic_name, 2); + tx_payload += 2; + } + else { + /* Topic ID */ + tx_payload += MqttEncode_Num(tx_payload, *(word16*)publish->topic_name); + } + + tx_payload += MqttEncode_Num(tx_payload, publish->packet_id); + + /* Encode payload */ + XMEMCPY(tx_payload, publish->buffer, publish->total_len); + tx_payload += publish->total_len; + + (void)tx_payload; + + /* Return length of packet placed into tx_buf */ + return total_len; +} + +int SN_Decode_Publish(byte *rx_buf, int rx_buf_len, SN_Publish *publish) +{ + word16 total_len; + byte *rx_payload = rx_buf; + byte flags = 0, type; + + /* Validate required arguments */ + if (rx_buf == NULL || publish == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Decode fixed header */ + total_len = *rx_payload++; + if (total_len == SN_PACKET_LEN_IND) { + /* The length is stored in the next two bytes */ + rx_payload += MqttDecode_Num(rx_payload, &total_len); + } + + if (total_len > rx_buf_len) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + if (total_len < 7) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); + } + + /* Message Type */ + type = *rx_payload++; + if (type != SN_MSG_TYPE_PUBLISH) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); + } + + flags = *rx_payload++; + + publish->topic_name = (char*)rx_payload; + rx_payload += MQTT_DATA_LEN_SIZE; + publish->topic_name_len = MQTT_DATA_LEN_SIZE; + + rx_payload += MqttDecode_Num(rx_payload, &publish->packet_id); + + /* Set flags */ + publish->duplicate = flags & SN_PACKET_FLAG_DUPLICATE; + + publish->qos = (MqttQoS)((flags & SN_PACKET_FLAG_QOS_MASK) >> + SN_PACKET_FLAG_QOS_SHIFT); + + publish->retain = flags & SN_PACKET_FLAG_RETAIN; + + publish->topic_type = flags & SN_PACKET_FLAG_TOPICIDTYPE_MASK; + + /* Decode payload */ + + publish->total_len = total_len - 7; + publish->buffer = rx_payload; + publish->buffer_pos = 0; + publish->buffer_len = publish->total_len; + + /* Return length of packet read from rx_buf */ + return total_len; +} + +int SN_Encode_PublishResp(byte* tx_buf, int tx_buf_len, byte type, + SN_PublishResp *publish_resp) +{ + int total_len; + byte *tx_payload = tx_buf; + + /* Validate required arguments */ + if (tx_buf == NULL || publish_resp == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Determine packet length */ + total_len = (type == SN_MSG_TYPE_PUBACK) ? 7 : 4; + + if (total_len > tx_buf_len) + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + + /* Encode */ + *tx_payload++ = (byte)total_len; + + *tx_payload++ = type; + + if (type == SN_MSG_TYPE_PUBACK) { + tx_payload += MqttEncode_Num(tx_payload, publish_resp->topicId); + } + + tx_payload += MqttEncode_Num(tx_payload, publish_resp->packet_id); + + if (type == SN_MSG_TYPE_PUBACK) { + *tx_payload++ = publish_resp->return_code; + } + (void)tx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Decode_PublishResp(byte* rx_buf, int rx_buf_len, byte type, + SN_PublishResp *publish_resp) +{ + int total_len; + byte rec_type, *rx_payload = rx_buf; + + /* Validate required arguments */ + if (rx_buf == NULL || rx_buf_len <= 0) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Decode */ + total_len = *rx_payload++; + + if(total_len > rx_buf_len) { + /* Buffer too small */ + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + /* Validate packet type */ + rec_type = *rx_payload++; + if (rec_type != type) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); + } + + if (publish_resp) { + if (type == SN_MSG_TYPE_PUBACK) { + rx_payload += MqttDecode_Num(rx_payload, &publish_resp->topicId); + } + + rx_payload += MqttDecode_Num(rx_payload, &publish_resp->packet_id); + + if (type == SN_MSG_TYPE_PUBACK) { + publish_resp->return_code = *rx_payload++; + } + } + (void)rx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Encode_Unsubscribe(byte *tx_buf, int tx_buf_len, + SN_Unsubscribe *unsubscribe) +{ + int total_len; + byte *tx_payload, flags = 0x00; + + /* Validate required arguments */ + if (tx_buf == NULL || unsubscribe == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Determine packet length */ + if (unsubscribe->topic_type == SN_TOPIC_ID_TYPE_NORMAL) { + /* Topic name is a string */ + total_len = (int)XSTRLEN(unsubscribe->topicNameId); + } + else { + /* Topic ID or Short name */ + total_len = 2; + } + + /* Length, MsgType, Flags, and MsgID (2) */ + total_len += 5; + + if (total_len > SN_PACKET_MAX_SMALL_SIZE) { + /* Length is stored in bytes 1 and 2 */ + total_len += 2; + } + + if (total_len > tx_buf_len) { + /* Buffer too small */ + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + /* Encode header */ + tx_payload = tx_buf; + + if (total_len <= SN_PACKET_MAX_SMALL_SIZE) { + *tx_payload++ = total_len; + } + else { + *tx_payload++ = SN_PACKET_LEN_IND; + tx_payload += MqttEncode_Num(tx_payload, total_len); + } + + *tx_payload++ = SN_MSG_TYPE_UNSUBSCRIBE; + + /* Set flags */ + if (unsubscribe->duplicate) + flags |= SN_PACKET_FLAG_DUPLICATE; + flags |= (SN_PACKET_FLAG_QOS_MASK & + (unsubscribe->qos << SN_PACKET_FLAG_QOS_SHIFT)); + flags |= (SN_PACKET_FLAG_TOPICIDTYPE_MASK & unsubscribe->topic_type); + + *tx_payload++ = flags; + + tx_payload += MqttEncode_Num(tx_payload, unsubscribe->packet_id); + + /* Encode topic */ + if (unsubscribe->topic_type == SN_TOPIC_ID_TYPE_NORMAL) { + /* Topic name is a string */ + XMEMCPY(tx_payload, unsubscribe->topicNameId, + XSTRLEN(unsubscribe->topicNameId)); + } + else { + /* Topic ID or Short name */ + XMEMCPY(tx_payload, unsubscribe->topicNameId, 2); + } + + (void)tx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Decode_UnsubscribeAck(byte *rx_buf, int rx_buf_len, + SN_UnsubscribeAck *unsubscribe_ack) +{ + word16 total_len; + byte *rx_payload = rx_buf, type; + + /* Validate required arguments */ + if (rx_buf == NULL || rx_buf_len <= 0) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Decode fixed header */ + total_len = *rx_payload++; + if (total_len != 4) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); + } + + type = *rx_payload++; + if (type != SN_MSG_TYPE_UNSUBACK) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); + } + + /* Decode SubAck fields */ + if (unsubscribe_ack) { + rx_payload += MqttDecode_Num(rx_payload, &unsubscribe_ack->packet_id); + } + (void)rx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Encode_Disconnect(byte *tx_buf, int tx_buf_len, + SN_Disconnect* disconnect) +{ + int total_len = 2; /* length and message type */ + byte *tx_payload = tx_buf;; + + /* Validate required arguments */ + if (tx_buf == NULL) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + if ((disconnect != NULL) && (disconnect->sleepTmr > 0)) { + total_len += 2; /* Sleep duration is set */ + } + + if (total_len > tx_buf_len) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + /* Encode message */ + *tx_payload++ = total_len; + + *tx_payload++ = SN_MSG_TYPE_DISCONNECT; + + if ((disconnect != NULL) && (disconnect->sleepTmr > 0)) { + tx_payload += MqttEncode_Num(tx_payload, disconnect->sleepTmr); + } + (void)tx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Decode_Disconnect(byte *rx_buf, int rx_buf_len) +{ + word16 total_len; + byte *rx_payload = rx_buf, type; + + /* Validate required arguments */ + if (rx_buf == NULL || rx_buf_len <= 0) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + /* Decode fixed header */ + total_len = *rx_payload++; + if (total_len != 2) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); + } + + type = *rx_payload++; + if (type != SN_MSG_TYPE_DISCONNECT) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); + } + + (void)rx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Encode_Ping(byte *tx_buf, int tx_buf_len, SN_PingReq *ping, byte type) +{ + int total_len = 2, clientId_len = 0; + byte *tx_payload = tx_buf; + + /* Validate required arguments */ + if ((tx_buf == NULL) || + ((type != SN_MSG_TYPE_PING_REQ) && (type != SN_MSG_TYPE_PING_RESP))) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + if ((type == SN_MSG_TYPE_PING_REQ) && (ping != NULL) && + (ping->clientId != NULL)) { + total_len += clientId_len = (int)XSTRLEN(ping->clientId); + } + + if (total_len > tx_buf_len) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_OUT_OF_BUFFER); + } + + *tx_payload++ = (byte)total_len; + + *tx_payload++ = type; + + if (clientId_len > 0) { + XMEMCPY(tx_payload, ping->clientId, clientId_len); + tx_payload += clientId_len; + } + (void)tx_payload; + + /* Return total length of packet */ + return total_len; +} + +int SN_Decode_Ping(byte *rx_buf, int rx_buf_len) +{ + int total_len; + byte *rx_payload = rx_buf, type; + + /* Validate required arguments */ + if (rx_buf == NULL || rx_buf_len <= 0) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_BAD_ARG); + } + + total_len = *rx_payload++; + if (total_len != 2) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_MALFORMED_DATA); + } + + type = *rx_payload++; + if ((type != SN_MSG_TYPE_PING_REQ) && + (type != SN_MSG_TYPE_PING_RESP)) { + return MQTT_TRACE_ERROR(MQTT_CODE_ERROR_PACKET_TYPE); + } + + /* Return total length of packet */ + return total_len; +} + +/* Read return code is length when > 0 */ +int SN_Packet_Read(MqttClient *client, byte* rx_buf, int rx_buf_len, + int timeout_ms) +{ + int rc, len = 0, remain_read = 0; + word16 total_len = 0, idx = 0; + + switch (client->packet.stat) + { + case MQTT_PK_BEGIN: + { + /* Read first 2 bytes */ + if (MqttClient_Flags(client,0,0) & MQTT_CLIENT_FLAG_IS_DTLS) { + rc = MqttSocket_Read(client, rx_buf, 2, timeout_ms); + } else { + rc = MqttSocket_Peek(client, rx_buf, 2, timeout_ms); + } + if (rc < 0) { + return MqttPacket_HandleNetError(client, rc); + } + else if (rc != 2) { + return MqttPacket_HandleNetError(client, + MQTT_TRACE_ERROR(MQTT_CODE_ERROR_NETWORK)); + } + + len = rc; + + if (rx_buf[0] == SN_PACKET_LEN_IND){ + /* Read length stored in first three bytes, type in fourth */ + if (MqttClient_Flags(client,0,0) & MQTT_CLIENT_FLAG_IS_DTLS) { + rc = MqttSocket_Read(client, rx_buf+len, 2, timeout_ms); + if (rc < 0) { + return MqttPacket_HandleNetError(client, rc); + } + else if (rc != 2) { + return MqttPacket_HandleNetError(client, + MQTT_TRACE_ERROR(MQTT_CODE_ERROR_NETWORK)); + } + len += rc; + } + else { + rc = MqttSocket_Peek(client, rx_buf, 4, timeout_ms); + if (rc < 0) { + return MqttPacket_HandleNetError(client, rc); + } + else if (rc != 4) { + return MqttPacket_HandleNetError(client, + MQTT_TRACE_ERROR(MQTT_CODE_ERROR_NETWORK)); + } + len = rc; + } + + (void)MqttDecode_Num(&rx_buf[1], &total_len); + client->packet.header_len = len; + } + else { + /* Length is stored in first byte, type in second */ + total_len = rx_buf[0]; + client->packet.header_len = len; + } + } + FALL_THROUGH; + + case MQTT_PK_READ_HEAD: + { + client->packet.stat = MQTT_PK_READ_HEAD; + } + FALL_THROUGH; + + case MQTT_PK_READ: + { + client->packet.stat = MQTT_PK_READ; + + if (total_len > len) { + client->packet.remain_len = total_len - len; + } + else if ((total_len == 2) || (total_len == 4)) { + /* Handle peek */ + if (MqttClient_Flags(client,0,0) & MQTT_CLIENT_FLAG_IS_DTLS) { + client->packet.remain_len = total_len - len; + } + else { + client->packet.remain_len = total_len; + } + } + else { + client->packet.remain_len = 0; + } + + /* Make sure it does not overflow rx_buf */ + if (client->packet.remain_len > + (rx_buf_len - client->packet.header_len)) { + client->packet.remain_len = rx_buf_len - + client->packet.header_len; + } + if (MqttClient_Flags(client,0,0) & MQTT_CLIENT_FLAG_IS_DTLS) { + total_len -= client->packet.header_len; + idx = client->packet.header_len; + } + /* Read whole message */ + if (client->packet.remain_len > 0) { + rc = MqttSocket_Read(client, &rx_buf[idx], + total_len, timeout_ms); + if (rc <= 0) { + return MqttPacket_HandleNetError(client, rc); + } + remain_read = rc; + } + if (MqttClient_Flags(client,0,0) & MQTT_CLIENT_FLAG_IS_DTLS) { + remain_read += client->packet.header_len; + } + + break; + } + } /* switch (client->packet.stat) */ + + /* reset state */ + client->packet.stat = MQTT_PK_BEGIN; + + /* Return read length */ + return remain_read; +} + +#endif /* WOLFMQTT_SN */ diff --git a/src/mqtt_socket.c b/src/mqtt_socket.c index ce6b2cefb..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" @@ -41,6 +45,7 @@ #ifdef WOLFMQTT_NO_STDIO #undef WOLFMQTT_DEBUG_SOCKET #endif + /* lwip */ #ifdef WOLFSSL_LWIP #undef read @@ -50,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) { @@ -95,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; @@ -123,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 */ @@ -133,8 +142,11 @@ static int MqttSocket_WriteDo(MqttClient *client, const byte* buf, int buf_len, int error = wolfSSL_get_error(client->tls.ssl, 0); #endif #ifdef WOLFMQTT_DEBUG_SOCKET - if (error != WOLFSSL_ERROR_WANT_WRITE && - error != WC_PENDING_E) { + if (error != WOLFSSL_ERROR_WANT_WRITE + #ifdef WOLFSSL_ASYNC_CRYPT + && error != WC_PENDING_E + #endif + ) { PRINTF("MqttSocket_WriteDo: SSL Error=%d (rc %d, sockrc %d)", error, rc, client->tls.sockRcWrite); } @@ -150,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); @@ -186,6 +198,7 @@ int MqttSocket_Write(MqttClient *client, const byte* buf, int buf_len, buf_len - client->write.pos, timeout_ms); if (rc >= 0) { client->write.pos += rc; + client->write.total += rc; if (client->write.pos < buf_len) { rc = MQTT_CODE_CONTINUE; } @@ -202,6 +215,7 @@ int MqttSocket_Write(MqttClient *client, const byte* buf, int buf_len, break; } client->write.pos += rc; + client->write.total += rc; } while (client->write.pos < buf_len); #endif /* WOLFMQTT_NONBLOCK */ @@ -220,27 +234,19 @@ static int MqttSocket_ReadDo(MqttClient *client, byte* buf, int buf_len, { int rc; -#if defined(WOLFMQTT_NONBLOCK) && defined(WOLFMQTT_TEST_NONBLOCK) - static int testNbAlt = 0; - if (!testNbAlt) { - testNbAlt = 1; - return MQTT_CODE_CONTINUE; - } - testNbAlt = 0; -#endif - -#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 */ rc = wolfSSL_read(client->tls.ssl, (char*)buf, buf_len); if (rc < 0) { - #if defined(WOLFMQTT_DEBUG_SOCKET) || defined(WOLFSSL_ASYNC_CRYPT) int error = wolfSSL_get_error(client->tls.ssl, 0); - #endif #ifdef WOLFMQTT_DEBUG_SOCKET - if (error != WOLFSSL_ERROR_WANT_READ && - error != WC_PENDING_E) { + if (error != WOLFSSL_ERROR_WANT_READ + #ifdef WOLFSSL_ASYNC_CRYPT + && error != WC_PENDING_E + #endif + ) { PRINTF("MqttSocket_ReadDo: SSL Error=%d (rc %d, sockrc %d)", error, rc, client->tls.sockRcRead); } @@ -252,24 +258,17 @@ static int MqttSocket_ReadDo(MqttClient *client, byte* buf, int buf_len, if (error == WC_PENDING_E) { rc = MQTT_CODE_CONTINUE; } + else #endif + /* used with compatibility layer to communicate peer close */ + if (error == WOLFSSL_ERROR_ZERO_RETURN) { + rc = MQTT_CODE_ERROR_NETWORK; + } } } else -#endif /* ENABLE_MQTT_TLS */ +#endif /* ENABLE_MQTT_TLS && !ENABLE_MQTT_CURL*/ { - #if defined(WOLFMQTT_NONBLOCK) && defined(WOLFMQTT_TEST_NONBLOCK) - static int testSmallerRead = 0; - if (!testSmallerRead) { - if (buf_len > 100) { - buf_len /= 2; - } - testSmallerRead = 1; - } - else { - testSmallerRead = 0; - } - #endif rc = client->net->read(client->net->context, buf, buf_len, timeout_ms); } @@ -302,6 +301,7 @@ int MqttSocket_Read(MqttClient *client, byte* buf, int buf_len, int timeout_ms) buf_len - client->read.pos, timeout_ms); if (rc >= 0) { client->read.pos += rc; + client->read.total += rc; if (client->read.pos < buf_len) { rc = MQTT_CODE_CONTINUE; } @@ -318,6 +318,7 @@ int MqttSocket_Read(MqttClient *client, byte* buf, int buf_len, int timeout_ms) break; } client->read.pos += rc; + client->read.total += rc; } while (client->read.pos < buf_len); #endif /* WOLFMQTT_NONBLOCK */ @@ -393,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 @@ -485,9 +486,12 @@ int MqttSocket_Connect(MqttClient *client, const char* host, word16 port, int errnum = 0; if (client->tls.ssl) { errnum = wolfSSL_get_error(client->tls.ssl, 0); - if (errnum == WOLFSSL_ERROR_WANT_READ || - errnum == WOLFSSL_ERROR_WANT_WRITE || - errnum == WC_PENDING_E) { + if ( errnum == WOLFSSL_ERROR_WANT_READ + || errnum == WOLFSSL_ERROR_WANT_WRITE + #ifdef WOLFSSL_ASYNC_CRYPT + || errnum == WC_PENDING_E + #endif + ) { return MQTT_CODE_CONTINUE; } #ifdef WOLFMQTT_DEBUG_SOCKET @@ -506,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); @@ -519,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; @@ -529,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 @@ -538,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.vcxproj b/wolfmqtt.vcxproj index 9f79e47e0..406d8842b 100755 --- a/wolfmqtt.vcxproj +++ b/wolfmqtt.vcxproj @@ -145,10 +145,14 @@ + + + + diff --git a/wolfmqtt/include.am b/wolfmqtt/include.am index 0096389b2..77dfcc9da 100644 --- a/wolfmqtt/include.am +++ b/wolfmqtt/include.am @@ -11,3 +11,8 @@ nobase_include_HEADERS+= \ wolfmqtt/visibility.h \ wolfmqtt/options.h \ wolfmqtt/vs_settings.h + +if BUILD_SN +nobase_include_HEADERS+= wolfmqtt/mqtt_sn_client.h \ + wolfmqtt/mqtt_sn_packet.h +endif diff --git a/wolfmqtt/mqtt_client.h b/wolfmqtt/mqtt_client.h index 8c8346067..ddb70afe1 100644 --- a/wolfmqtt/mqtt_client.h +++ b/wolfmqtt/mqtt_client.h @@ -42,6 +42,11 @@ #include "wolfmqtt/mqtt_packet.h" #include "wolfmqtt/mqtt_socket.h" +#ifdef WOLFMQTT_SN +#include "wolfmqtt/mqtt_sn_packet.h" +#endif + + /* This macro allows the disconnect callback to be triggered when * MqttClient_Disconnect_ex is called. Normally the CB is only used to handle * errors from MqttPacket_HandleNetError. @@ -110,7 +115,7 @@ enum MqttClientFlags { WOLFMQTT_API word32 MqttClient_Flags(struct _MqttClient *client, word32 mask, word32 flags); typedef enum _MqttPkStat { - MQTT_PK_BEGIN, + MQTT_PK_BEGIN = 0, MQTT_PK_READ_HEAD, MQTT_PK_READ } MqttPkStat; @@ -123,8 +128,12 @@ typedef struct _MqttPkRead { } MqttPkRead; typedef struct _MqttSk { - int pos; - int len; + int pos; /* position inside current buffer */ + int len; /* length of current segment being sent */ + int total; /* number bytes sent or received */ + + /* status bit for if client read or write is active */ + byte isActive:1; } MqttSk; #ifdef WOLFMQTT_DISCONNECT_CB @@ -133,6 +142,8 @@ typedef struct _MqttSk { #ifdef WOLFMQTT_PROPERTY_CB typedef int (*MqttPropertyCb)(struct _MqttClient* client, MqttProp* head, void* ctx); #endif + + #ifdef WOLFMQTT_SN /*! \brief Mqtt-SN Register Callback. * A GW sends a REGISTER message to a client if it wants to @@ -206,6 +217,9 @@ typedef struct _MqttClient { #endif } MqttClient; +#ifdef WOLFMQTT_SN +#include "wolfmqtt/mqtt_sn_client.h" +#endif /* Application Interfaces */ @@ -567,190 +581,17 @@ WOLFMQTT_API const char* MqttClient_ReturnCodeToString( "not compiled in" #endif /* WOLFMQTT_NO_ERROR_STRINGS */ -#ifdef WOLFMQTT_SN -/*! \brief Encodes and sends the a message to search for a gateway and - waits for the gateway info response message. - * \note This is a blocking function that will wait for MqttNet.read - * \param client Pointer to MqttClient structure - * \param search Pointer to SN_SearchGW structure initialized - with hop radius. - * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* - (see enum MqttPacketResponseCodes) - */ -WOLFMQTT_API int SN_Client_SearchGW( - MqttClient *client, - SN_SearchGw *search); - -/*! \brief Encodes and sends the Connect packet and waits for the - Connect Acknowledgment packet. If Will is enabled, the gateway - prompts for LWT Topic and Message. Sending an empty will topic - indicates that the client wishes to delete the Will topic and - the Will message stored in the server. - * \note This is a blocking function that will wait for MqttNet.read - * \param client Pointer to MqttClient structure - * \param connect Pointer to SN_Connect structure initialized - with connect parameters - * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* - (see enum MqttPacketResponseCodes) - */ -WOLFMQTT_API int SN_Client_Connect( - MqttClient *client, - SN_Connect *connect); - -/*! \brief Encodes and sends the MQTT-SN Will Topic Update packet. Sending - a NULL 'will' indicates that the client wishes to delete the - Will topic and the Will message stored in the server. - * \note This is a blocking function that will wait for MqttNet.read - * \param client Pointer to MqttClient structure - * \param will Pointer to SN_Will structure initialized - with topic and message parameters. NULL is valid. - * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* - (see enum MqttPacketResponseCodes) - */ -WOLFMQTT_API int SN_Client_WillTopicUpdate(MqttClient *client, SN_Will *will); - -/*! \brief Encodes and sends the MQTT-SN Will Message Update packet. - * \note This is a blocking function that will wait for MqttNet.read - * \param client Pointer to MqttClient structure - * \param will Pointer to SN_Will structure initialized - with topic and message parameters. NULL is valid. - * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* - (see enum MqttPacketResponseCodes) - */ -WOLFMQTT_API int SN_Client_WillMsgUpdate(MqttClient *client, SN_Will *will); - -/*! \brief Encodes and sends the MQTT-SN Register packet and waits for the - Register Acknowledge packet. The Register packet is sent by a - client to a GW for requesting a topic id value for the included - topic name. It is also sent by a GW to inform a client about - the topic id value it has assigned to the included topic name. - * \note This is a blocking function that will wait for MqttNet.read - * \param client Pointer to MqttClient structure - * \param regist Pointer to SN_Register structure - * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* - (see enum MqttPacketResponseCodes) - */ -WOLFMQTT_API int SN_Client_Register( - MqttClient *client, - SN_Register *regist); - - -/*! \brief Sets a register callback with custom context - * \param client Pointer to MqttClient structure - (uninitialized is okay) - * \param regCb Pointer to register callback function - * \param ctx Pointer to your own context - * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_BAD_ARG - */ -WOLFMQTT_API int SN_Client_SetRegisterCallback( - MqttClient *client, - SN_ClientRegisterCb regCb, - void* ctx); - - -/*! \brief Encodes and sends the MQTT-SN Publish packet and waits for the - Publish response (if QoS > 0). - * \note This is a blocking function that will wait for MqttNet.read - * If QoS level = 1 then will wait for PUBLISH_ACK. - * If QoS level = 2 then will wait for PUBLISH_REC then send - PUBLISH_REL and wait for PUBLISH_COMP. - * \param client Pointer to MqttClient structure - * \param publish Pointer to SN_Publish structure initialized - with message data - * Note: SN_Publish and MqttMessage are same - structure. - * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* - (see enum MqttPacketResponseCodes) - */ -WOLFMQTT_API int SN_Client_Publish( - MqttClient *client, - SN_Publish *publish); - -/*! \brief Encodes and sends the MQTT-SN Subscribe packet and waits for the - Subscribe Acknowledgment packet containing the assigned - topic ID. - * \note This is a blocking function that will wait for MqttNet.read - * \param client Pointer to MqttClient structure - * \param subscribe Pointer to SN_Subscribe structure initialized with - subscription topic list and desired QoS. - * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* - (see enum MqttPacketResponseCodes) - */ -WOLFMQTT_API int SN_Client_Subscribe( - MqttClient *client, - SN_Subscribe *subscribe); - -/*! \brief Encodes and sends the MQTT-SN Unsubscribe packet and waits for - the Unsubscribe Acknowledgment packet - * \note This is a blocking function that will wait for MqttNet.read - * \param client Pointer to MqttClient structure - * \param unsubscribe Pointer to SN_Unsubscribe structure initialized - with topic ID. - * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* - (see enum MqttPacketResponseCodes) - */ -WOLFMQTT_API int SN_Client_Unsubscribe( - MqttClient *client, - SN_Unsubscribe *unsubscribe); - -/*! \brief Encodes and sends the MQTT-SN Disconnect packet. Client may - send the disconnect with a duration to indicate the client is - entering the "asleep" state. - * \note This is a non-blocking function that will try and send using - MqttNet.write - * \param client Pointer to MqttClient structure - * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* - (see enum MqttPacketResponseCodes) - */ -WOLFMQTT_API int SN_Client_Disconnect( - MqttClient *client); - -/*! \brief Encodes and sends the MQTT-SN Disconnect packet. Client may - send the disconnect with a duration to indicate the client is - entering the "asleep" state. - * \note This is a non-blocking function that will try and send using - MqttNet.write - * \param client Pointer to MqttClient structure - * \param disconnect Pointer to SN_Disconnect structure. NULL is valid. - * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* - (see enum MqttPacketResponseCodes) - */ -WOLFMQTT_API int SN_Client_Disconnect_ex( - MqttClient *client, - SN_Disconnect *disconnect); - - -/*! \brief Encodes and sends the MQTT-SN Ping Request packet and waits - for the Ping Response packet. If client is in the "asleep" - state and wants to notify the gateway that it is entering the - "awake" state, it should add it's client ID to the ping - request. - * \note This is a blocking function that will wait for MqttNet.read - * \param client Pointer to MqttClient structure - * \param ping Pointer to SN_PingReq structure. NULL is valid. - * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* - (see enum MqttPacketResponseCodes) - */ -WOLFMQTT_API int SN_Client_Ping( - MqttClient *client, - SN_PingReq *ping); - -/*! \brief Waits for packets to arrive. Incoming publish messages - will arrive via callback provided in MqttClient_Init. - * \note This is a blocking function that will wait for MqttNet.read - * \param client Pointer to MqttClient structure - * \param timeout_ms Milliseconds until read timeout - * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* - (see enum MqttPacketResponseCodes) - */ -WOLFMQTT_API int SN_Client_WaitMessage( - MqttClient *client, - int timeout_ms); - -WOLFMQTT_API int SN_Client_WaitMessage_ex(MqttClient *client, SN_Object* packet_obj, - int timeout_ms); - -#endif /* WOLFMQTT_SN */ +/* Internal functions */ +#ifdef WOLFMQTT_MULTITHREAD +WOLFMQTT_LOCAL int MqttClient_RespList_Find(MqttClient *client, + MqttPacketType packet_type, word16 packet_id, MqttPendResp **retResp); +WOLFMQTT_LOCAL void MqttClient_RespList_Remove(MqttClient *client, + MqttPendResp *rmResp); +WOLFMQTT_LOCAL int MqttClient_RespList_Add(MqttClient *client, + MqttPacketType packet_type, word16 packet_id, MqttPendResp *newResp, + void *packet_obj); +#endif +WOLFMQTT_LOCAL int MqttPacket_HandleNetError(MqttClient *client, int rc); #ifdef __cplusplus } /* extern "C" */ diff --git a/wolfmqtt/mqtt_packet.h b/wolfmqtt/mqtt_packet.h index 73e6e0a96..efa2d46b5 100644 --- a/wolfmqtt/mqtt_packet.h +++ b/wolfmqtt/mqtt_packet.h @@ -254,7 +254,7 @@ enum MqttPacketFlags { MQTT_PACKET_FLAG_RETAIN = 0x1, MQTT_PACKET_FLAG_QOS_SHIFT = 0x1, MQTT_PACKET_FLAG_QOS_MASK = 0x6, - MQTT_PACKET_FLAG_DUPLICATE = 0x8, + MQTT_PACKET_FLAG_DUPLICATE = 0x8 }; /* Packet Header: Size is variable 2 - 5 bytes */ @@ -291,11 +291,10 @@ typedef enum _MqttMsgState { typedef struct _MqttMsgStat { MqttMsgState read; MqttMsgState write; + MqttMsgState ack; -#ifdef WOLFMQTT_MULTITHREAD - byte isReadLocked:1; - byte isWriteLocked:1; -#endif + byte isReadActive:1; + byte isWriteActive:1; } MqttMsgStat; #ifdef WOLFMQTT_MULTITHREAD @@ -720,463 +719,6 @@ WOLFMQTT_LOCAL MqttProp* MqttProps_FindType(MqttProp *head, #define MqttPacket_TypeDesc(x) "not compiled in" #endif - -#ifdef WOLFMQTT_SN - -/* Note that because MQTT-SN does not support message fragmentation and - reassembly, the maximum message length that could be used in a network is - governed by the maximum packet size that is supported by that network, - and not by the maximum length that could be encoded by MQTT-SN. */ -#ifndef WOLFMQTT_SN_MAXPACKET_SIZE -#define WOLFMQTT_SN_MAXPACKET_SIZE 1024 -#endif - -/* The SN_GwAddr field has a variable length and contains the address of a GW. - Its depends on the network over which MQTT-SN operates and is indicated in - the first octet of this field. For example, in a ZigBee network the network - address is 2-octet long. */ -typedef word16 SN_GwAddr ; - -/* RETURN CODE values */ -enum SN_ReturnCodes { - SN_RC_ACCEPTED = 0x00, - SN_RC_CONGESTION = 0x01, - SN_RC_INVTOPICNAME = 0x02, - SN_RC_NOTSUPPORTED = 0x03 - /* 0x04 - 0xFF reserved */ -}; - -/* MESSAGE HEADER */ -/* Message types: Located in last byte of header */ -typedef enum _SN_MsgType { - SN_MSG_TYPE_ADVERTISE = 0x00, - SN_MSG_TYPE_SEARCHGW = 0x01, - SN_MSG_TYPE_GWINFO = 0x02, - /* 0x03 reserved */ - SN_MSG_TYPE_CONNECT = 0x04, - SN_MSG_TYPE_CONNACK = 0x05, - SN_MSG_TYPE_WILLTOPICREQ = 0x06, - SN_MSG_TYPE_WILLTOPIC = 0x07, - SN_MSG_TYPE_WILLMSGREQ = 0x08, - SN_MSG_TYPE_WILLMSG = 0x09, - SN_MSG_TYPE_REGISTER = 0x0A, - SN_MSG_TYPE_REGACK = 0x0B, - SN_MSG_TYPE_PUBLISH = 0x0C, - SN_MSG_TYPE_PUBACK = 0x0D, - SN_MSG_TYPE_PUBCOMP = 0x0E, - SN_MSG_TYPE_PUBREC = 0x0F, - SN_MSG_TYPE_PUBREL = 0x10, - /* 0x11 reserved */ - SN_MSG_TYPE_SUBSCRIBE = 0x12, - SN_MSG_TYPE_SUBACK = 0x13, - SN_MSG_TYPE_UNSUBSCRIBE = 0x14, - SN_MSG_TYPE_UNSUBACK = 0x15, - SN_MSG_TYPE_PING_REQ = 0x16, - SN_MSG_TYPE_PING_RESP = 0x17, - SN_MSG_TYPE_DISCONNECT = 0x18, - /* 0x19 reserved */ - SN_MSG_TYPE_WILLTOPICUPD = 0x1A, - SN_MSG_TYPE_WILLTOPICRESP = 0x1B, - SN_MSG_TYPE_WILLMSGUPD = 0x1C, - SN_MSG_TYPE_WILLMSGRESP = 0x1D, - /* 0x1E - 0xFD reserved */ - SN_MSG_TYPE_ENCAPMSG = 0xFE, /* Encapsulated message */ - /* 0xFF reserved */ - SN_MSG_TYPE_RESERVED = 0xFF, - SN_MSG_TYPE_ANY = 0xFF -} SN_MsgType; - -/* Topic ID types */ -enum SN_TopicId_Types { - SN_TOPIC_ID_TYPE_NORMAL = 0x0, - SN_TOPIC_ID_TYPE_PREDEF = 0x1, - SN_TOPIC_ID_TYPE_SHORT = 0x2 -}; - -enum SN_PacketFlags { - SN_PACKET_FLAG_TOPICIDTYPE_MASK = 0x3, - SN_PACKET_FLAG_CLEANSESSION = 0x4, - SN_PACKET_FLAG_WILL = 0x8, - SN_PACKET_FLAG_RETAIN = 0x10, - SN_PACKET_FLAG_QOS_MASK = 0x60, - SN_PACKET_FLAG_QOS_SHIFT = 0x5, - SN_PACKET_FLAG_DUPLICATE = 0x80 -}; - -/* Message Header: Size is variable 2 - 4 bytes */ - -/* If the first byte of the packet len is 0x01, then the packet size is - greater than 0xFF and is stored in the next two bytes */ -#define SN_PACKET_LEN_IND 0x01 - -#define SN_PACKET_MAX_SMALL_SIZE 0xFF - -/* Gateway (GW) messages */ -/* Advertise message */ -typedef struct _SN_AdvertiseMsg { - MqttMsgStat stat; - - byte gwId; /* ID of the gateway that sent this message */ - word16 duration; /* Seconds until next Advertise - is broadcast by this gateway */ -} SN_Advertise; - -typedef struct _SN_GwInfo { - MqttMsgStat stat; - - byte gwId; /* ID of the gateway that sent this message */ - SN_GwAddr* gwAddr; /* Address of the indicated gateway */ -} SN_GwInfo; - -typedef struct _SN_SearchGw { - MqttMsgStat stat; -#ifdef WOLFMQTT_MULTITHREAD - MqttPendResp pendResp; -#endif - - byte radius; /* Broadcast radius (in hops) */ - SN_GwInfo gwInfo; -} SN_SearchGw; - -/* Connect Protocol */ -#define SN_PROTOCOL_ID_1 0x01 -#define SN_PROTOCOL_ID SN_PROTOCOL_ID_1 - -#define SN_CLIENTID_MAX_LEN 23 - -/* Connect Ack message structure */ -typedef struct _SN_ConnectAck { - MqttMsgStat stat; - - byte return_code; -} SN_ConnectAck; - -/* WILL TOPIC */ -typedef struct _SN_WillTopicUpd { - MqttMsgStat stat; -#ifdef WOLFMQTT_MULTITHREAD - MqttPendResp pendResp; -#endif - - byte flags; - char* willTopic; /* contains the Will topic name */ -} SN_WillTopicUpd; - -typedef struct _SN_WillMsgUpd { - MqttMsgStat stat; -#ifdef WOLFMQTT_MULTITHREAD - MqttPendResp pendResp; -#endif - - char* willMsg; -} SN_WillMsgUpd; - -typedef struct _SN_WillTopicResp { - MqttMsgStat stat; - - byte return_code; -} SN_WillTopicResp; - -typedef SN_WillTopicResp SN_WillMsgResp; - -typedef union _SN_WillResp { - SN_WillMsgResp msgResp; - SN_WillMsgUpd msgUpd; - SN_WillTopicResp topicResp; - SN_WillTopicUpd topicUpd; -} SN_WillResp; - -typedef struct _SN_Will { - MqttMsgStat stat; -#ifdef WOLFMQTT_MULTITHREAD - MqttPendResp pendResp; -#endif - - byte qos; - byte retain; - const char* willTopic; - byte* willMsg; - word16 willMsgLen; - - SN_WillResp resp; -} SN_Will; - -/* Connect */ -typedef struct _SN_Connect { - MqttMsgStat stat; -#ifdef WOLFMQTT_MULTITHREAD - MqttPendResp pendResp; -#endif - - word16 keep_alive_sec; - byte clean_session; - const char *client_id; - - /* Protocol version: 1=v1.2 (default) */ - byte protocol_level; - - /* Optional Last will and testament */ - byte enable_lwt; - SN_Will will; - - /* Ack data */ - SN_ConnectAck ack; -} SN_Connect; - -/* REGISTER protocol */ -typedef struct _SN_RegAck { - MqttMsgStat stat; - - word16 topicId; - word16 packet_id; - byte return_code; -} SN_RegAck; - -typedef struct _SN_Register { - MqttMsgStat stat; -#ifdef WOLFMQTT_MULTITHREAD - MqttPendResp pendResp; -#endif - - word16 topicId; - word16 packet_id; - const char* topicName; - SN_RegAck regack; -} SN_Register; - -/* PUBLISH RESPONSE */ -/* This is the response struct for PUBREC, PUBREL, and PUBCOMP */ -/* If QoS = 0: No response */ -/* If QoS = 1: Expect response packet with type = SN_MSG_TYPE_PUBACK */ -/* If QoS = 2: Expect response packet with type = SN_MSG_TYPE_PUBREC */ -/* Message ID required if QoS is 1 or 2 */ -/* If QoS = 2: Send SN_MSG_TYPE_PUBREL with msgId to complete - QoS2 protocol exchange */ -/* Expect response packet with type = SN_MSG_TYPE_PUBCOMP */ -typedef struct _SN_PublishResp { - MqttMsgStat stat; - - word16 packet_id; - word16 topicId; /* PUBACK Only */ - byte return_code; /* PUBACK Only */ -} SN_PublishResp; - -/* PUBLISH protocol */ -typedef struct _SN_Publish { - MqttMsgStat stat; /* must be first member at top */ -#ifdef WOLFMQTT_MULTITHREAD - MqttPendResp pendResp; -#endif - - /* BEGIN: THIS SECTION NEEDS TO MATCH MqttMessage */ - word16 packet_id; - byte type; - MqttQoS qos; - byte retain; - byte duplicate; - byte topic_type; - byte return_code; - - const char *topic_name; /* Pointer is valid only when - msg_new set in callback */ - word16 topic_name_len; - word32 total_len; /* Payload total length */ - byte *buffer; /* Payload buffer */ - word32 buffer_len; /* Payload buffer length */ - word32 buffer_pos; /* Payload buffer position */ - - /* Used internally for TX/RX buffers */ - byte buffer_new; /* flag to indicate new message */ - word32 intBuf_len; /* Buffer length */ - word32 intBuf_pos; /* Buffer position */ - - void* ctx; /* user supplied context for publish callbacks */ - /* END: THIS SECTION NEEDS TO MATCH MqttMessage */ - - SN_PublishResp resp; -} SN_Publish; - -/* SUBSCRIBE ACK */ -typedef struct _SN_SubAck { - MqttMsgStat stat; - - byte flags; - word16 topicId; - word16 packet_id; - byte return_code; -} SN_SubAck; - -/* SUBSCRIBE */ -typedef struct _SN_Subscribe { - MqttMsgStat stat; -#ifdef WOLFMQTT_MULTITHREAD - MqttPendResp pendResp; -#endif - - byte duplicate; - byte qos; - word16 packet_id; - byte topic_type; - /* 5.3.12 TopicName - The TopicName field has a variable length and contains an UTF8-encoded - string that specifies the topic name.*/ - const char* topicNameId; /* Contains topic name, ID, - or short name as indicated in topic type */ - SN_SubAck subAck; -} SN_Subscribe; - -/* UNSUBSCRIBE RESPONSE ACK */ -typedef struct _SN_UnsubscribeAck { - MqttMsgStat stat; /* must be first member at top */ - - word16 packet_id; -} SN_UnsubscribeAck; - -/* UNSUBSCRIBE */ -typedef struct _SN_Unsubscribe { - MqttMsgStat stat; -#ifdef WOLFMQTT_MULTITHREAD - MqttPendResp pendResp; -#endif - - byte duplicate; - byte qos; - word16 packet_id; - byte topic_type; - /* 5.3.12 TopicName - The TopicName field has a variable length and contains an UTF8-encoded - string that specifies the topic name.*/ - const char* topicNameId; /* Contains topic name, ID, - or short name as indicated in topic type */ - SN_UnsubscribeAck ack; -} SN_Unsubscribe; - -/* PING REQUEST / PING RESPONSE */ -typedef struct _SN_PingReq { - MqttMsgStat stat; -#ifdef WOLFMQTT_MULTITHREAD - MqttPendResp pendResp; -#endif - - /* clientId is optional and is included by a “sleeping” client when it - goes to the “awake” state and is waiting for messages sent by the - server/gateway. */ - char *clientId; -} SN_PingReq; - -/* DISCONNECT */ -typedef struct _SN_Disconnect { - MqttMsgStat stat; -#ifdef WOLFMQTT_MULTITHREAD - MqttPendResp pendResp; -#endif - - /* sleepTmr is optional and is included by a “sleeping” client - that wants to go the “asleep” state. The receipt of this message - is also acknowledged by the gateway by means of a DISCONNECT message - (without a duration field).*/ - word16 sleepTmr; -} SN_Disconnect; - -typedef union _SN_Object { - SN_Advertise advertise; - SN_GwInfo gwInfo; - SN_SearchGw searchGw; - - SN_Will will; - SN_Connect connect; - SN_ConnectAck connectAck; - - SN_Register reg; - SN_RegAck regAck; - - SN_Publish publish; - SN_PublishResp publishResp; - - SN_Subscribe sub; - SN_SubAck subAck; - SN_Unsubscribe unSub; - SN_UnsubscribeAck unSubAck; - - SN_PingReq pingReq; - SN_Disconnect disconnect; - - SN_WillMsgUpd willMsgUpd; - SN_WillMsgResp willMsgResp; - - SN_WillTopicUpd willTopicUpd; - SN_WillTopicResp willTopicResp; -} SN_Object; - - -/* Forward Encapsulation */ -// TODO - -WOLFMQTT_LOCAL int SN_Decode_Header(byte *rx_buf, int rx_buf_len, - SN_MsgType* p_packet_type, word16* p_packet_id); -WOLFMQTT_LOCAL int SN_Decode_Advertise(byte *rx_buf, int rx_buf_len, - SN_Advertise *gw_info); -WOLFMQTT_LOCAL int SN_Encode_SearchGW(byte *tx_buf, int tx_buf_len, byte hops); -WOLFMQTT_LOCAL int SN_Decode_GWInfo(byte *rx_buf, int rx_buf_len, - SN_GwInfo *gw_info); -WOLFMQTT_LOCAL int SN_Encode_Connect(byte *tx_buf, int tx_buf_len, - SN_Connect *connect); -WOLFMQTT_LOCAL int SN_Decode_ConnectAck(byte *rx_buf, int rx_buf_len, - SN_ConnectAck *connect_ack); -WOLFMQTT_LOCAL int SN_Decode_WillTopicReq(byte *rx_buf, int rx_buf_len); -WOLFMQTT_LOCAL int SN_Encode_WillTopic(byte *tx_buf, int tx_buf_len, - SN_Will *willTopic); -WOLFMQTT_LOCAL int SN_Decode_WillMsgReq(byte *rx_buf, int rx_buf_len); -WOLFMQTT_LOCAL int SN_Encode_WillMsg(byte *tx_buf, int tx_buf_len, - SN_Will *willMsg); -WOLFMQTT_LOCAL int SN_Encode_WillTopicUpdate(byte *tx_buf, int tx_buf_len, - SN_Will *willTopic); -WOLFMQTT_LOCAL int SN_Decode_WillTopicResponse(byte *rx_buf, int rx_buf_len, - byte *ret_code); -WOLFMQTT_LOCAL int SN_Encode_WillMsgUpdate(byte *tx_buf, int tx_buf_len, - SN_Will *willMsg); -WOLFMQTT_LOCAL int SN_Decode_WillMsgResponse(byte *rx_buf, int rx_buf_len, - byte *ret_code); -WOLFMQTT_LOCAL int SN_Encode_Register(byte *tx_buf, int tx_buf_len, - SN_Register *regist); -WOLFMQTT_LOCAL int SN_Decode_Register(byte *rx_buf, int rx_buf_len, - SN_Register *regist); -WOLFMQTT_LOCAL int SN_Encode_RegAck(byte *tx_buf, int tx_buf_len, - SN_RegAck *regack); -WOLFMQTT_LOCAL int SN_Decode_RegAck(byte *rx_buf, int rx_buf_len, - SN_RegAck *regack); -WOLFMQTT_LOCAL int SN_Encode_Subscribe(byte *tx_buf, int tx_buf_len, - SN_Subscribe *subscribe); -WOLFMQTT_LOCAL int SN_Decode_SubscribeAck(byte* rx_buf, int rx_buf_len, - SN_SubAck *subscribe_ack); -WOLFMQTT_LOCAL int SN_Encode_Publish(byte *tx_buf, int tx_buf_len, - SN_Publish *publish); -WOLFMQTT_LOCAL int SN_Decode_Publish(byte *rx_buf, int rx_buf_len, - SN_Publish *publish); -WOLFMQTT_LOCAL int SN_Encode_PublishResp(byte* tx_buf, int tx_buf_len, - byte type, SN_PublishResp *publish_resp); -WOLFMQTT_LOCAL int SN_Decode_PublishResp(byte* rx_buf, int rx_buf_len, - byte type, SN_PublishResp *publish_resp); -WOLFMQTT_LOCAL int SN_Encode_Unsubscribe(byte *tx_buf, int tx_buf_len, - SN_Unsubscribe *unsubscribe); -WOLFMQTT_LOCAL int SN_Decode_UnsubscribeAck(byte *rx_buf, int rx_buf_len, - SN_UnsubscribeAck *unsubscribe_ack); -WOLFMQTT_LOCAL int SN_Encode_Disconnect(byte *tx_buf, int tx_buf_len, - SN_Disconnect* disconnect); -WOLFMQTT_LOCAL int SN_Decode_Disconnect(byte *rx_buf, int rx_buf_len); -WOLFMQTT_LOCAL int SN_Encode_Ping(byte *tx_buf, int tx_buf_len, - SN_PingReq *ping, byte type); -WOLFMQTT_LOCAL int SN_Decode_Ping(byte *rx_buf, int rx_buf_len); -WOLFMQTT_LOCAL int SN_Packet_Read(struct _MqttClient *client, byte* rx_buf, - int rx_buf_len, int timeout_ms); - -#ifndef WOLFMQTT_NO_ERROR_STRINGS - WOLFMQTT_LOCAL const char* SN_Packet_TypeDesc(SN_MsgType packet_type); -#else - #define SN_Packet_TypeDesc(x) "not compiled in" -#endif -#endif /* WOLFMQTT_SN */ - #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/wolfmqtt/mqtt_sn_client.h b/wolfmqtt/mqtt_sn_client.h new file mode 100644 index 000000000..68124afad --- /dev/null +++ b/wolfmqtt/mqtt_sn_client.h @@ -0,0 +1,236 @@ +/* mqtt_sn_client.h + * + * Copyright (C) 2006-2023 wolfSSL Inc. + * + * This file is part of wolfMQTT. + * + * wolfMQTT is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfMQTT is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Implementation by: David Garske + * Based on specification for MQTT-SN v1.2 + * See http://mqtt.org/documentation for additional MQTT-SN documentation. + */ + +#ifndef WOLFMQTT_SN_CLIENT_H +#define WOLFMQTT_SN_CLIENT_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* Windows uses the vs_settings.h file included vis mqtt_types.h */ +#if !defined(WOLFMQTT_USER_SETTINGS) && \ + !defined(_WIN32) && !defined(USE_WINDOWS_API) + /* If options.h is missing use the "./configure" script. Otherwise, copy + * the template "wolfmqtt/options.h.in" into "wolfmqtt/options.h" */ + #include +#endif +#include "wolfmqtt/mqtt_client.h" +#include "wolfmqtt/mqtt_types.h" +#include "wolfmqtt/mqtt_packet.h" +#include "wolfmqtt/mqtt_socket.h" + +#ifdef WOLFMQTT_SN +/*! \brief Encodes and sends the a message to search for a gateway and + waits for the gateway info response message. + * \note This is a blocking function that will wait for MqttNet.read + * \param client Pointer to MqttClient structure + * \param search Pointer to SN_SearchGW structure initialized + with hop radius. + * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* + (see enum MqttPacketResponseCodes) + */ +WOLFMQTT_API int SN_Client_SearchGW( + MqttClient *client, + SN_SearchGw *search); + +/*! \brief Encodes and sends the Connect packet and waits for the + Connect Acknowledgment packet. If Will is enabled, the gateway + prompts for LWT Topic and Message. Sending an empty will topic + indicates that the client wishes to delete the Will topic and + the Will message stored in the server. + * \note This is a blocking function that will wait for MqttNet.read + * \param client Pointer to MqttClient structure + * \param connect Pointer to SN_Connect structure initialized + with connect parameters + * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* + (see enum MqttPacketResponseCodes) + */ +WOLFMQTT_API int SN_Client_Connect( + MqttClient *client, + SN_Connect *connect); + +/*! \brief Encodes and sends the MQTT-SN Will Topic Update packet. Sending + a NULL 'will' indicates that the client wishes to delete the + Will topic and the Will message stored in the server. + * \note This is a blocking function that will wait for MqttNet.read + * \param client Pointer to MqttClient structure + * \param will Pointer to SN_Will structure initialized + with topic and message parameters. NULL is valid. + * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* + (see enum MqttPacketResponseCodes) + */ +WOLFMQTT_API int SN_Client_WillTopicUpdate(MqttClient *client, SN_Will *will); + +/*! \brief Encodes and sends the MQTT-SN Will Message Update packet. + * \note This is a blocking function that will wait for MqttNet.read + * \param client Pointer to MqttClient structure + * \param will Pointer to SN_Will structure initialized + with topic and message parameters. NULL is valid. + * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* + (see enum MqttPacketResponseCodes) + */ +WOLFMQTT_API int SN_Client_WillMsgUpdate(MqttClient *client, SN_Will *will); + +/*! \brief Encodes and sends the MQTT-SN Register packet and waits for the + Register Acknowledge packet. The Register packet is sent by a + client to a GW for requesting a topic id value for the included + topic name. It is also sent by a GW to inform a client about + the topic id value it has assigned to the included topic name. + * \note This is a blocking function that will wait for MqttNet.read + * \param client Pointer to MqttClient structure + * \param regist Pointer to SN_Register structure + * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* + (see enum MqttPacketResponseCodes) + */ +WOLFMQTT_API int SN_Client_Register( + MqttClient *client, + SN_Register *regist); + + +/*! \brief Sets a register callback with custom context + * \param client Pointer to MqttClient structure + (uninitialized is okay) + * \param regCb Pointer to register callback function + * \param ctx Pointer to your own context + * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_BAD_ARG + */ +WOLFMQTT_API int SN_Client_SetRegisterCallback( + MqttClient *client, + SN_ClientRegisterCb regCb, + void* ctx); + + +/*! \brief Encodes and sends the MQTT-SN Publish packet and waits for the + Publish response (if QoS > 0). + * \note This is a blocking function that will wait for MqttNet.read + * If QoS level = 1 then will wait for PUBLISH_ACK. + * If QoS level = 2 then will wait for PUBLISH_REC then send + PUBLISH_REL and wait for PUBLISH_COMP. + * \param client Pointer to MqttClient structure + * \param publish Pointer to SN_Publish structure initialized + with message data + * Note: SN_Publish and MqttMessage are same + structure. + * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* + (see enum MqttPacketResponseCodes) + */ +WOLFMQTT_API int SN_Client_Publish( + MqttClient *client, + SN_Publish *publish); + +/*! \brief Encodes and sends the MQTT-SN Subscribe packet and waits for the + Subscribe Acknowledgment packet containing the assigned + topic ID. + * \note This is a blocking function that will wait for MqttNet.read + * \param client Pointer to MqttClient structure + * \param subscribe Pointer to SN_Subscribe structure initialized with + subscription topic list and desired QoS. + * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* + (see enum MqttPacketResponseCodes) + */ +WOLFMQTT_API int SN_Client_Subscribe( + MqttClient *client, + SN_Subscribe *subscribe); + +/*! \brief Encodes and sends the MQTT-SN Unsubscribe packet and waits for + the Unsubscribe Acknowledgment packet + * \note This is a blocking function that will wait for MqttNet.read + * \param client Pointer to MqttClient structure + * \param unsubscribe Pointer to SN_Unsubscribe structure initialized + with topic ID. + * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* + (see enum MqttPacketResponseCodes) + */ +WOLFMQTT_API int SN_Client_Unsubscribe( + MqttClient *client, + SN_Unsubscribe *unsubscribe); + +/*! \brief Encodes and sends the MQTT-SN Disconnect packet. Client may + send the disconnect with a duration to indicate the client is + entering the "asleep" state. + * \note This is a non-blocking function that will try and send using + MqttNet.write + * \param client Pointer to MqttClient structure + * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* + (see enum MqttPacketResponseCodes) + */ +WOLFMQTT_API int SN_Client_Disconnect( + MqttClient *client); + +/*! \brief Encodes and sends the MQTT-SN Disconnect packet. Client may + send the disconnect with a duration to indicate the client is + entering the "asleep" state. + * \note This is a non-blocking function that will try and send using + MqttNet.write + * \param client Pointer to MqttClient structure + * \param disconnect Pointer to SN_Disconnect structure. NULL is valid. + * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* + (see enum MqttPacketResponseCodes) + */ +WOLFMQTT_API int SN_Client_Disconnect_ex( + MqttClient *client, + SN_Disconnect *disconnect); + + +/*! \brief Encodes and sends the MQTT-SN Ping Request packet and waits + for the Ping Response packet. If client is in the "asleep" + state and wants to notify the gateway that it is entering the + "awake" state, it should add it's client ID to the ping + request. + * \note This is a blocking function that will wait for MqttNet.read + * \param client Pointer to MqttClient structure + * \param ping Pointer to SN_PingReq structure. NULL is valid. + * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* + (see enum MqttPacketResponseCodes) + */ +WOLFMQTT_API int SN_Client_Ping( + MqttClient *client, + SN_PingReq *ping); + +/*! \brief Waits for packets to arrive. Incoming publish messages + will arrive via callback provided in MqttClient_Init. + * \note This is a blocking function that will wait for MqttNet.read + * \param client Pointer to MqttClient structure + * \param timeout_ms Milliseconds until read timeout + * \return MQTT_CODE_SUCCESS or MQTT_CODE_ERROR_* + (see enum MqttPacketResponseCodes) + */ +WOLFMQTT_API int SN_Client_WaitMessage( + MqttClient *client, + int timeout_ms); + +WOLFMQTT_API int SN_Client_WaitMessage_ex(MqttClient *client, SN_Object* packet_obj, + int timeout_ms); + +#endif /* WOLFMQTT_SN */ + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif /* WOLFMQTT_SN_CLIENT_H */ + diff --git a/wolfmqtt/mqtt_sn_packet.h b/wolfmqtt/mqtt_sn_packet.h new file mode 100644 index 000000000..6729bc4f5 --- /dev/null +++ b/wolfmqtt/mqtt_sn_packet.h @@ -0,0 +1,497 @@ +/* mqtt_sn_packet.h + * + * Copyright (C) 2006-2023 wolfSSL Inc. + * + * This file is part of wolfMQTT. + * + * wolfMQTT is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * wolfMQTT is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* Implementation by: David Garske + * Based on specification for MQTT-SN v1.2 + * See http://mqtt.org/documentation for additional MQTT documentation. + */ + +#ifndef WOLFMQTT_SN_PACKET_H +#define WOLFMQTT_SN_PACKET_H + +#ifdef __cplusplus + extern "C" { +#endif + +#include "wolfmqtt/mqtt_types.h" +#include "wolfmqtt/mqtt_socket.h" + +#ifdef WOLFMQTT_SN + +/* Note that because MQTT-SN does not support message fragmentation and + reassembly, the maximum message length that could be used in a network is + governed by the maximum packet size that is supported by that network, + and not by the maximum length that could be encoded by MQTT-SN. */ +#ifndef WOLFMQTT_SN_MAXPACKET_SIZE +#define WOLFMQTT_SN_MAXPACKET_SIZE 1024 +#endif + +/* The SN_GwAddr field has a variable length and contains the address of a GW. + Its depends on the network over which MQTT-SN operates and is indicated in + the first octet of this field. For example, in a ZigBee network the network + address is 2-octet long. */ +typedef word16 SN_GwAddr ; + +/* RETURN CODE values */ +enum SN_ReturnCodes { + SN_RC_ACCEPTED = 0x00, + SN_RC_CONGESTION = 0x01, + SN_RC_INVTOPICNAME = 0x02, + SN_RC_NOTSUPPORTED = 0x03 + /* 0x04 - 0xFF reserved */ +}; + +/* MESSAGE HEADER */ +/* Message types: Located in last byte of header */ +typedef enum _SN_MsgType { + SN_MSG_TYPE_ADVERTISE = 0x00, + SN_MSG_TYPE_SEARCHGW = 0x01, + SN_MSG_TYPE_GWINFO = 0x02, + /* 0x03 reserved */ + SN_MSG_TYPE_CONNECT = 0x04, + SN_MSG_TYPE_CONNACK = 0x05, + SN_MSG_TYPE_WILLTOPICREQ = 0x06, + SN_MSG_TYPE_WILLTOPIC = 0x07, + SN_MSG_TYPE_WILLMSGREQ = 0x08, + SN_MSG_TYPE_WILLMSG = 0x09, + SN_MSG_TYPE_REGISTER = 0x0A, + SN_MSG_TYPE_REGACK = 0x0B, + SN_MSG_TYPE_PUBLISH = 0x0C, + SN_MSG_TYPE_PUBACK = 0x0D, + SN_MSG_TYPE_PUBCOMP = 0x0E, + SN_MSG_TYPE_PUBREC = 0x0F, + SN_MSG_TYPE_PUBREL = 0x10, + /* 0x11 reserved */ + SN_MSG_TYPE_SUBSCRIBE = 0x12, + SN_MSG_TYPE_SUBACK = 0x13, + SN_MSG_TYPE_UNSUBSCRIBE = 0x14, + SN_MSG_TYPE_UNSUBACK = 0x15, + SN_MSG_TYPE_PING_REQ = 0x16, + SN_MSG_TYPE_PING_RESP = 0x17, + SN_MSG_TYPE_DISCONNECT = 0x18, + /* 0x19 reserved */ + SN_MSG_TYPE_WILLTOPICUPD = 0x1A, + SN_MSG_TYPE_WILLTOPICRESP = 0x1B, + SN_MSG_TYPE_WILLMSGUPD = 0x1C, + SN_MSG_TYPE_WILLMSGRESP = 0x1D, + /* 0x1E - 0xFD reserved */ + SN_MSG_TYPE_ENCAPMSG = 0xFE, /* Encapsulated message */ + /* 0xFF reserved */ + SN_MSG_TYPE_RESERVED = 0xFF, + SN_MSG_TYPE_ANY = 0xFF +} SN_MsgType; + +/* Topic ID types */ +enum SN_TopicId_Types { + SN_TOPIC_ID_TYPE_NORMAL = 0x0, + SN_TOPIC_ID_TYPE_PREDEF = 0x1, + SN_TOPIC_ID_TYPE_SHORT = 0x2 +}; + +enum SN_PacketFlags { + SN_PACKET_FLAG_TOPICIDTYPE_MASK = 0x3, + SN_PACKET_FLAG_CLEANSESSION = 0x4, + SN_PACKET_FLAG_WILL = 0x8, + SN_PACKET_FLAG_RETAIN = 0x10, + SN_PACKET_FLAG_QOS_MASK = 0x60, + SN_PACKET_FLAG_QOS_SHIFT = 0x5, + SN_PACKET_FLAG_DUPLICATE = 0x80 +}; + +/* Message Header: Size is variable 2 - 4 bytes */ + +/* If the first byte of the packet len is 0x01, then the packet size is + greater than 0xFF and is stored in the next two bytes */ +#define SN_PACKET_LEN_IND 0x01 + +#define SN_PACKET_MAX_SMALL_SIZE 0xFF + +/* Gateway (GW) messages */ +/* Advertise message */ +typedef struct _SN_AdvertiseMsg { + MqttMsgStat stat; + + byte gwId; /* ID of the gateway that sent this message */ + word16 duration; /* Seconds until next Advertise + is broadcast by this gateway */ +} SN_Advertise; + +typedef struct _SN_GwInfo { + MqttMsgStat stat; + + byte gwId; /* ID of the gateway that sent this message */ + SN_GwAddr* gwAddr; /* Address of the indicated gateway */ +} SN_GwInfo; + +typedef struct _SN_SearchGw { + MqttMsgStat stat; +#ifdef WOLFMQTT_MULTITHREAD + MqttPendResp pendResp; +#endif + + byte radius; /* Broadcast radius (in hops) */ + SN_GwInfo gwInfo; +} SN_SearchGw; + +/* Connect Protocol */ +#define SN_PROTOCOL_ID_1 0x01 +#define SN_PROTOCOL_ID SN_PROTOCOL_ID_1 + +#define SN_CLIENTID_MAX_LEN 23 + +/* Connect Ack message structure */ +typedef struct _SN_ConnectAck { + MqttMsgStat stat; + + byte return_code; +} SN_ConnectAck; + +/* WILL TOPIC */ +typedef struct _SN_WillTopicUpd { + MqttMsgStat stat; +#ifdef WOLFMQTT_MULTITHREAD + MqttPendResp pendResp; +#endif + + byte flags; + char* willTopic; /* contains the Will topic name */ +} SN_WillTopicUpd; + +typedef struct _SN_WillMsgUpd { + MqttMsgStat stat; +#ifdef WOLFMQTT_MULTITHREAD + MqttPendResp pendResp; +#endif + + char* willMsg; +} SN_WillMsgUpd; + +typedef struct _SN_WillTopicResp { + MqttMsgStat stat; + + byte return_code; +} SN_WillTopicResp; + +typedef SN_WillTopicResp SN_WillMsgResp; + +typedef union _SN_WillResp { + SN_WillMsgResp msgResp; + SN_WillMsgUpd msgUpd; + SN_WillTopicResp topicResp; + SN_WillTopicUpd topicUpd; +} SN_WillResp; + +typedef struct _SN_Will { + MqttMsgStat stat; +#ifdef WOLFMQTT_MULTITHREAD + MqttPendResp pendResp; +#endif + + byte qos; + byte retain; + const char* willTopic; + byte* willMsg; + word16 willMsgLen; + + SN_WillResp resp; +} SN_Will; + +/* Connect */ +typedef struct _SN_Connect { + MqttMsgStat stat; +#ifdef WOLFMQTT_MULTITHREAD + MqttPendResp pendResp; +#endif + + word16 keep_alive_sec; + byte clean_session; + const char *client_id; + + /* Protocol version: 1=v1.2 (default) */ + byte protocol_level; + + /* Optional Last will and testament */ + byte enable_lwt; + SN_Will will; + + /* Ack data */ + SN_ConnectAck ack; +} SN_Connect; + +/* REGISTER protocol */ +typedef struct _SN_RegAck { + MqttMsgStat stat; + + word16 topicId; + word16 packet_id; + byte return_code; +} SN_RegAck; + +typedef struct _SN_Register { + MqttMsgStat stat; +#ifdef WOLFMQTT_MULTITHREAD + MqttPendResp pendResp; +#endif + + word16 topicId; + word16 packet_id; + const char* topicName; + SN_RegAck regack; +} SN_Register; + +/* PUBLISH RESPONSE */ +/* This is the response struct for PUBREC, PUBREL, and PUBCOMP */ +/* If QoS = 0: No response */ +/* If QoS = 1: Expect response packet with type = SN_MSG_TYPE_PUBACK */ +/* If QoS = 2: Expect response packet with type = SN_MSG_TYPE_PUBREC */ +/* Message ID required if QoS is 1 or 2 */ +/* If QoS = 2: Send SN_MSG_TYPE_PUBREL with msgId to complete + QoS2 protocol exchange */ +/* Expect response packet with type = SN_MSG_TYPE_PUBCOMP */ +typedef struct _SN_PublishResp { + MqttMsgStat stat; + + word16 packet_id; + word16 topicId; /* PUBACK Only */ + byte return_code; /* PUBACK Only */ +} SN_PublishResp; + +/* PUBLISH protocol */ +typedef struct _SN_Publish { + MqttMsgStat stat; /* must be first member at top */ +#ifdef WOLFMQTT_MULTITHREAD + MqttPendResp pendResp; +#endif + + /* BEGIN: THIS SECTION NEEDS TO MATCH MqttMessage */ + word16 packet_id; + byte type; + MqttQoS qos; + byte retain; + byte duplicate; + byte topic_type; + byte return_code; + + const char *topic_name; /* Pointer is valid only when + msg_new set in callback */ + word16 topic_name_len; + word32 total_len; /* Payload total length */ + byte *buffer; /* Payload buffer */ + word32 buffer_len; /* Payload buffer length */ + word32 buffer_pos; /* Payload buffer position */ + + /* Used internally for TX/RX buffers */ + byte buffer_new; /* flag to indicate new message */ + word32 intBuf_len; /* Buffer length */ + word32 intBuf_pos; /* Buffer position */ + + void* ctx; /* user supplied context for publish callbacks */ + /* END: THIS SECTION NEEDS TO MATCH MqttMessage */ + + SN_PublishResp resp; +} SN_Publish; + +/* SUBSCRIBE ACK */ +typedef struct _SN_SubAck { + MqttMsgStat stat; + + byte flags; + word16 topicId; + word16 packet_id; + byte return_code; +} SN_SubAck; + +/* SUBSCRIBE */ +typedef struct _SN_Subscribe { + MqttMsgStat stat; +#ifdef WOLFMQTT_MULTITHREAD + MqttPendResp pendResp; +#endif + + byte duplicate; + byte qos; + word16 packet_id; + byte topic_type; + /* 5.3.12 TopicName + The TopicName field has a variable length and contains an UTF8-encoded + string that specifies the topic name.*/ + const char* topicNameId; /* Contains topic name, ID, + or short name as indicated in topic type */ + SN_SubAck subAck; +} SN_Subscribe; + +/* UNSUBSCRIBE RESPONSE ACK */ +typedef struct _SN_UnsubscribeAck { + MqttMsgStat stat; /* must be first member at top */ + + word16 packet_id; +} SN_UnsubscribeAck; + +/* UNSUBSCRIBE */ +typedef struct _SN_Unsubscribe { + MqttMsgStat stat; +#ifdef WOLFMQTT_MULTITHREAD + MqttPendResp pendResp; +#endif + + byte duplicate; + byte qos; + word16 packet_id; + byte topic_type; + /* 5.3.12 TopicName + The TopicName field has a variable length and contains an UTF8-encoded + string that specifies the topic name.*/ + const char* topicNameId; /* Contains topic name, ID, + or short name as indicated in topic type */ + SN_UnsubscribeAck ack; +} SN_Unsubscribe; + +/* PING REQUEST / PING RESPONSE */ +typedef struct _SN_PingReq { + MqttMsgStat stat; +#ifdef WOLFMQTT_MULTITHREAD + MqttPendResp pendResp; +#endif + + /* clientId is optional and is included by a “sleeping” client when it + goes to the “awake” state and is waiting for messages sent by the + server/gateway. */ + char *clientId; +} SN_PingReq; + +/* DISCONNECT */ +typedef struct _SN_Disconnect { + MqttMsgStat stat; +#ifdef WOLFMQTT_MULTITHREAD + MqttPendResp pendResp; +#endif + + /* sleepTmr is optional and is included by a “sleeping” client + that wants to go the “asleep” state. The receipt of this message + is also acknowledged by the gateway by means of a DISCONNECT message + (without a duration field).*/ + word16 sleepTmr; +} SN_Disconnect; + +typedef union _SN_Object { + SN_Advertise advertise; + SN_GwInfo gwInfo; + SN_SearchGw searchGw; + + SN_Will will; + SN_Connect connect; + SN_ConnectAck connectAck; + + SN_Register reg; + SN_RegAck regAck; + + SN_Publish publish; + SN_PublishResp publishResp; + + SN_Subscribe sub; + SN_SubAck subAck; + SN_Unsubscribe unSub; + SN_UnsubscribeAck unSubAck; + + SN_PingReq pingReq; + SN_Disconnect disconnect; + + SN_WillMsgUpd willMsgUpd; + SN_WillMsgResp willMsgResp; + + SN_WillTopicUpd willTopicUpd; + SN_WillTopicResp willTopicResp; +} SN_Object; + + +/* Forward Encapsulation */ +// TODO + +WOLFMQTT_LOCAL int SN_Decode_Header(byte *rx_buf, int rx_buf_len, + SN_MsgType* p_packet_type, word16* p_packet_id); +WOLFMQTT_LOCAL int SN_Decode_Advertise(byte *rx_buf, int rx_buf_len, + SN_Advertise *gw_info); +WOLFMQTT_LOCAL int SN_Encode_SearchGW(byte *tx_buf, int tx_buf_len, byte hops); +WOLFMQTT_LOCAL int SN_Decode_GWInfo(byte *rx_buf, int rx_buf_len, + SN_GwInfo *gw_info); +WOLFMQTT_LOCAL int SN_Encode_Connect(byte *tx_buf, int tx_buf_len, + SN_Connect *connect); +WOLFMQTT_LOCAL int SN_Decode_ConnectAck(byte *rx_buf, int rx_buf_len, + SN_ConnectAck *connect_ack); +WOLFMQTT_LOCAL int SN_Decode_WillTopicReq(byte *rx_buf, int rx_buf_len); +WOLFMQTT_LOCAL int SN_Encode_WillTopic(byte *tx_buf, int tx_buf_len, + SN_Will *willTopic); +WOLFMQTT_LOCAL int SN_Decode_WillMsgReq(byte *rx_buf, int rx_buf_len); +WOLFMQTT_LOCAL int SN_Encode_WillMsg(byte *tx_buf, int tx_buf_len, + SN_Will *willMsg); +WOLFMQTT_LOCAL int SN_Encode_WillTopicUpdate(byte *tx_buf, int tx_buf_len, + SN_Will *willTopic); +WOLFMQTT_LOCAL int SN_Decode_WillTopicResponse(byte *rx_buf, int rx_buf_len, + byte *ret_code); +WOLFMQTT_LOCAL int SN_Encode_WillMsgUpdate(byte *tx_buf, int tx_buf_len, + SN_Will *willMsg); +WOLFMQTT_LOCAL int SN_Decode_WillMsgResponse(byte *rx_buf, int rx_buf_len, + byte *ret_code); +WOLFMQTT_LOCAL int SN_Encode_Register(byte *tx_buf, int tx_buf_len, + SN_Register *regist); +WOLFMQTT_LOCAL int SN_Decode_Register(byte *rx_buf, int rx_buf_len, + SN_Register *regist); +WOLFMQTT_LOCAL int SN_Encode_RegAck(byte *tx_buf, int tx_buf_len, + SN_RegAck *regack); +WOLFMQTT_LOCAL int SN_Decode_RegAck(byte *rx_buf, int rx_buf_len, + SN_RegAck *regack); +WOLFMQTT_LOCAL int SN_Encode_Subscribe(byte *tx_buf, int tx_buf_len, + SN_Subscribe *subscribe); +WOLFMQTT_LOCAL int SN_Decode_SubscribeAck(byte* rx_buf, int rx_buf_len, + SN_SubAck *subscribe_ack); +WOLFMQTT_LOCAL int SN_Encode_Publish(byte *tx_buf, int tx_buf_len, + SN_Publish *publish); +WOLFMQTT_LOCAL int SN_Decode_Publish(byte *rx_buf, int rx_buf_len, + SN_Publish *publish); +WOLFMQTT_LOCAL int SN_Encode_PublishResp(byte* tx_buf, int tx_buf_len, + byte type, SN_PublishResp *publish_resp); +WOLFMQTT_LOCAL int SN_Decode_PublishResp(byte* rx_buf, int rx_buf_len, + byte type, SN_PublishResp *publish_resp); +WOLFMQTT_LOCAL int SN_Encode_Unsubscribe(byte *tx_buf, int tx_buf_len, + SN_Unsubscribe *unsubscribe); +WOLFMQTT_LOCAL int SN_Decode_UnsubscribeAck(byte *rx_buf, int rx_buf_len, + SN_UnsubscribeAck *unsubscribe_ack); +WOLFMQTT_LOCAL int SN_Encode_Disconnect(byte *tx_buf, int tx_buf_len, + SN_Disconnect* disconnect); +WOLFMQTT_LOCAL int SN_Decode_Disconnect(byte *rx_buf, int rx_buf_len); +WOLFMQTT_LOCAL int SN_Encode_Ping(byte *tx_buf, int tx_buf_len, + SN_PingReq *ping, byte type); +WOLFMQTT_LOCAL int SN_Decode_Ping(byte *rx_buf, int rx_buf_len); +WOLFMQTT_LOCAL int SN_Packet_Read(struct _MqttClient *client, byte* rx_buf, + int rx_buf_len, int timeout_ms); + +#ifndef WOLFMQTT_NO_ERROR_STRINGS + WOLFMQTT_LOCAL const char* SN_Packet_TypeDesc(SN_MsgType packet_type); +#else + #define SN_Packet_TypeDesc(x) "not compiled in" +#endif +#endif /* WOLFMQTT_SN */ + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif /* WOLFMQTT_SN_PACKET_H */ 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 f18655ec6..ae32e70dc 100644 --- a/wolfmqtt/mqtt_types.h +++ b/wolfmqtt/mqtt_types.h @@ -106,9 +106,11 @@ #define WOLFMQTT_POSIX_SEMAPHORES #include typedef struct { + #ifndef WOLFMQTT_NO_COND_SIGNAL volatile int lockCount; - pthread_mutex_t mutex; pthread_cond_t cond; + #endif + pthread_mutex_t mutex; } wm_Sem; #elif defined(FREERTOS) @@ -192,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, diff --git a/wolfmqtt/version.h b/wolfmqtt/version.h index 170511f37..f8eedb3ed 100644 --- a/wolfmqtt/version.h +++ b/wolfmqtt/version.h @@ -34,8 +34,8 @@ extern "C" { #endif -#define LIBWOLFMQTT_VERSION_STRING "1.16.0" -#define LIBWOLFMQTT_VERSION_HEX 0x01016000 +#define LIBWOLFMQTT_VERSION_STRING "1.17.1" +#define LIBWOLFMQTT_VERSION_HEX 0x01017001 #ifdef __cplusplus } diff --git a/zephyr/samples/client.c b/zephyr/samples/client.c index c85f1fde3..07f71e76e 100644 --- a/zephyr/samples/client.c +++ b/zephyr/samples/client.c @@ -32,6 +32,13 @@ int main(void) mqttCtx.test_mode = 1; + /* Set port as configured in scripts/broker_test/mosquitto.conf */ +#if defined(WOLFMQTT_DEFAULT_TLS) && (WOLFMQTT_DEFAULT_TLS == 1) + mqttCtx.port = 18883; +#else + mqttCtx.port = 11883; +#endif + rc = mqttclient_test(&mqttCtx); mqtt_free_ctx(&mqttCtx);