Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions .github/workflows/broker-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
name: Broker Build Test

on:
push:
branches: [ 'master', 'main', 'release/**' ]
pull_request:
branches: [ '*' ]

jobs:
build:

runs-on: ubuntu-22.04
timeout-minutes: 5

strategy:
fail-fast: false
matrix:
include:
- name: "Broker default (dynamic alloc)"
cflags: ""
wolfmqtt_opts: "--enable-broker"
- name: "Broker static memory"
cflags: "-DWOLFMQTT_STATIC_MEMORY"
wolfmqtt_opts: "--enable-broker"
- name: "Broker with TLS"
cflags: ""
wolfmqtt_opts: "--enable-broker --enable-tls"
- name: "Broker with TLS (static memory)"
cflags: "-DWOLFMQTT_STATIC_MEMORY"
wolfmqtt_opts: "--enable-broker --enable-tls"

steps:
- name: Install dependencies
run: |
export DEBIAN_FRONTEND=noninteractive
sudo apt-get update
sudo apt-get install -y mosquitto-clients

- 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 (${{ matrix.name }})"
run: ./configure ${{ matrix.wolfmqtt_opts }} CFLAGS="${{ matrix.cflags }}"
- name: wolfmqtt make
run: make

- name: "Run broker tests (${{ matrix.name }})"
run: ./scripts/broker.test

- name: Show logs on failure
if: failure() || cancelled()
run: |
ls -la /tmp/tmp.* 2>/dev/null || true
for d in /tmp/tmp.*; do
if [ -d "$d" ]; then
echo "=== Logs in $d ==="
cat "$d"/*.log 2>/dev/null || true
fi
done
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ lt*.m4
*.cache
config.*
configure
configure~
libtool
libtool.m4
*.log
Expand Down Expand Up @@ -123,3 +124,4 @@ examples/websocket/websocket_client
# Never exclude Espressif config.h files
!/IDE/Espressif/**/config.h

src/mqtt_broker
45 changes: 45 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,42 @@ if(ENABLE_WEBSOCKET)
endif()
endif()

# Broker
add_option(WOLFMQTT_BROKER
"Enable lightweight broker support"
"no" "yes;no")
if (WOLFMQTT_BROKER)
list(APPEND WOLFMQTT_DEFINITIONS "-DWOLFMQTT_BROKER")

add_option(WOLFMQTT_BROKER_RETAINED
"Enable broker retained message support"
"yes" "yes;no")
if (NOT WOLFMQTT_BROKER_RETAINED)
list(APPEND WOLFMQTT_DEFINITIONS "-DWOLFMQTT_BROKER_NO_RETAINED")
endif()

add_option(WOLFMQTT_BROKER_WILL
"Enable broker Last Will and Testament support"
"yes" "yes;no")
if (NOT WOLFMQTT_BROKER_WILL)
list(APPEND WOLFMQTT_DEFINITIONS "-DWOLFMQTT_BROKER_NO_WILL")
endif()

add_option(WOLFMQTT_BROKER_WILDCARDS
"Enable broker wildcard topic matching"
"yes" "yes;no")
if (NOT WOLFMQTT_BROKER_WILDCARDS)
list(APPEND WOLFMQTT_DEFINITIONS "-DWOLFMQTT_BROKER_NO_WILDCARDS")
endif()

add_option(WOLFMQTT_BROKER_AUTH
"Enable broker username/password authentication"
"yes" "yes;no")
if (NOT WOLFMQTT_BROKER_AUTH)
list(APPEND WOLFMQTT_DEFINITIONS "-DWOLFMQTT_BROKER_NO_AUTH")
endif()
endif()

# Note: not adding stress option to cmake build as of yet. stress is for
# testing only and requires the scripts/ dir to be useful.

Expand Down Expand Up @@ -286,6 +322,14 @@ if (WOLFMQTT_EXAMPLES)
add_mqtt_example(mqtt-sub pub-sub/mqtt-sub.c)
endif()

if (WOLFMQTT_BROKER)
add_executable(mqtt_broker src/mqtt_broker.c)
target_link_libraries(mqtt_broker wolfmqtt)
target_include_directories(mqtt_broker PRIVATE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>)
endif()

####################################################
# Installation
####################################################
Expand Down Expand Up @@ -326,4 +370,5 @@ message("\tExamples: ${ENABLE_EXAMPLES}")
message("\tFirmware Examples: ${ENABLE_FIRMWARE_EXAMPLES}")
message("\tMultithread: ${ENABLE_MULTITHREAD}")
message("\tCurl: ${ENABLE_CURL}")
message("\tBroker: ${WOLFMQTT_BROKER}")
message("-----------------------------------------------")
80 changes: 76 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ We setup an AWS IoT endpoint and testing device certificate for testing. The AWS

### Watson IoT Example
This example enables the wolfMQTT client to connect to the IBM Watson Internet of Things (WIOT) Platform. The WIOT Platform has a limited test broker called "Quickstart" that allows non-secure connections to exercise the component. The example is located in `/examples/wiot/`. Works with MQTT v5 support enabled.
**NOTE** The WIOT Platform will be disabled DEC2023. The demo may still be useful for users of IBM Watson IOT.
**NOTE** The WIOT Platform will be disabled DEC2023. The demo may still be useful for users of IBM Watson IOT.

### MQTT-SN Example
The Sensor Network client implements the MQTT-SN protocol for low-bandwidth networks. There are several differences from MQTT, including the ability to use a two byte Topic ID instead the full topic during subscribe and publish. The SN client requires an MQTT-SN gateway. The gateway acts as an intermediary between the SN clients and the broker. This client was tested with the Eclipse Paho MQTT-SN Gateway, which connects by default to the public Eclipse broker, much like our wolfMQTT Client example. The address of the gateway must be configured as the host. The example is located in `/examples/sn-client/`.
Expand Down Expand Up @@ -262,7 +262,7 @@ The initially supported version with full specification support for all features

### MQTT v5.0 Specification Support

The wolfMQTT client supports connecting to v5 enabled brokers when configured with the `--enable-v5` option.
The wolfMQTT client supports connecting to v5 enabled brokers when configured with the `--enable-v5` option.
The following v5.0 specification features are supported by the wolfMQTT client:
* AUTH packet
* User properties
Expand Down Expand Up @@ -413,6 +413,78 @@ Note: When stress is enabled, the Multithread Example becomes localhost only
and will not connect to remote servers. Additionally the test `scripts/stress.test`
is added to `make check`, and all other tests are disabled.

## Broker

wolfMQTT includes a lightweight MQTT broker implementation suitable for embedded and resource-constrained environments. It supports both MQTT v3.1.1 and v5.0 clients, with optional TLS via wolfSSL.

### Features

* QoS 0 and QoS 1 publish/subscribe
* Retained messages
* Last Will and Testament (LWT) with v5 Will Delay Interval
* Wildcard subscriptions (`+` and `#`)
* Username/password authentication
* TLS support (requires wolfSSL with `--enable-tls`)
* Clean session handling with subscription persistence
* Keep-alive monitoring with automatic client disconnect
* Unique client ID enforcement (existing session takeover)
* Static memory mode (`WOLFMQTT_STATIC_MEMORY`) for zero-malloc operation

### Building

With autotools:

```
./configure --enable-broker
make
```

With CMake:

```
cmake .. -DWOLFMQTT_BROKER=yes
cmake --build .
```

### Running

```
./src/mqtt_broker -p 1883
```

For TLS:

```
./src/mqtt_broker -p 8883 -t -A ca-cert.pem -K server-key.pem -c server-cert.pem
```

Run `./src/mqtt_broker -?` to see all available options.

### Feature Build Options

All broker features are enabled by default. Individual features can be disabled at build time to reduce code and memory footprint on constrained platforms.

| Feature | Autotools | CMake | Define |
|---|---|---|---|
| Retained messages | `--disable-broker-retained` | `-DWOLFMQTT_BROKER_RETAINED=no` | `WOLFMQTT_BROKER_NO_RETAINED` |
| Last Will and Testament | `--disable-broker-will` | `-DWOLFMQTT_BROKER_WILL=no` | `WOLFMQTT_BROKER_NO_WILL` |
| Wildcard subscriptions | `--disable-broker-wildcards` | `-DWOLFMQTT_BROKER_WILDCARDS=no` | `WOLFMQTT_BROKER_NO_WILDCARDS` |
| Authentication | `--disable-broker-auth` | `-DWOLFMQTT_BROKER_AUTH=no` | `WOLFMQTT_BROKER_NO_AUTH` |

### Static Memory Mode

When built with `WOLFMQTT_STATIC_MEMORY`, the broker uses fixed-size arrays instead of dynamic allocation. Buffer sizes and limits can be tuned via compile-time macros:

| Macro | Default | Description |
|---|---|---|
| `BROKER_MAX_CLIENTS` | 8 | Maximum concurrent client connections |
| `BROKER_MAX_SUBS` | 32 | Maximum total subscriptions |
| `BROKER_MAX_RETAINED` | 16 | Maximum retained messages |
| `BROKER_RX_BUF_SZ` | 4096 | Per-client receive buffer size |
| `BROKER_TX_BUF_SZ` | 4096 | Per-client transmit buffer size |
| `BROKER_MAX_PAYLOAD_LEN` | 4096 | Maximum retained message payload |
| `BROKER_MAX_WILL_PAYLOAD_LEN` | 256 | Maximum LWT payload size |

## WebSocket Support

wolfMQTT supports MQTT over WebSockets, allowing clients to connect to MQTT brokers through WebSocket endpoints. This is useful for environments where traditional MQTT ports might be blocked or when integrating with web applications.
Expand Down Expand Up @@ -544,11 +616,11 @@ You can test the wolfMQTT client against public brokers supporting websockets:
* Mosquitto unencrypted

`./examples/websocket/websocket_client -h test.mosquitto.org -p8080`

* Mosquitto secure websocket

`./examples/websocket/websocket_client -h test.mosquitto.org -p8081 -t`

* HiveMQ unencrypted

`./examples/websocket/websocket_client -h broker.hivemq.com -p8000`
Expand Down
59 changes: 58 additions & 1 deletion configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ then
[ ENABLED_PROPCB=$enableval ],
[ ENABLED_PROPCB=yes ]
)

if test "x$ENABLED_PROPCB" = "xyes"
then
AM_CFLAGS="$AM_CFLAGS -DWOLFMQTT_PROPERTY_CB"
Expand Down Expand Up @@ -405,6 +405,60 @@ then
[AC_MSG_ERROR([libwebsockets not found. Install libwebsockets or use --disable-websocket])])
fi

# Broker
AC_ARG_ENABLE([broker],
[AS_HELP_STRING([--enable-broker],[Enable lightweight broker support (default: disabled)])],
[ ENABLED_BROKER=$enableval ],
[ ENABLED_BROKER=no ]
)

if test "x$ENABLED_BROKER" = "xyes"
then
AM_CFLAGS="$AM_CFLAGS -DWOLFMQTT_BROKER"
fi

# Broker feature toggles (only relevant when --enable-broker)
AC_ARG_ENABLE([broker-retained],
[AS_HELP_STRING([--disable-broker-retained],[Disable broker retained message support])],
[ ENABLED_BROKER_RETAINED=$enableval ],
[ ENABLED_BROKER_RETAINED=yes ]
)
if test "x$ENABLED_BROKER_RETAINED" = "xno"
then
AM_CFLAGS="$AM_CFLAGS -DWOLFMQTT_BROKER_NO_RETAINED"
fi

AC_ARG_ENABLE([broker-will],
[AS_HELP_STRING([--disable-broker-will],[Disable broker Last Will and Testament support])],
[ ENABLED_BROKER_WILL=$enableval ],
[ ENABLED_BROKER_WILL=yes ]
)
if test "x$ENABLED_BROKER_WILL" = "xno"
then
AM_CFLAGS="$AM_CFLAGS -DWOLFMQTT_BROKER_NO_WILL"
fi

AC_ARG_ENABLE([broker-wildcards],
[AS_HELP_STRING([--disable-broker-wildcards],[Disable broker wildcard subscription support])],
[ ENABLED_BROKER_WILDCARDS=$enableval ],
[ ENABLED_BROKER_WILDCARDS=yes ]
)
if test "x$ENABLED_BROKER_WILDCARDS" = "xno"
then
AM_CFLAGS="$AM_CFLAGS -DWOLFMQTT_BROKER_NO_WILDCARDS"
fi

AC_ARG_ENABLE([broker-auth],
[AS_HELP_STRING([--disable-broker-auth],[Disable broker username/password authentication])],
[ ENABLED_BROKER_AUTH=$enableval ],
[ ENABLED_BROKER_AUTH=yes ]
)
if test "x$ENABLED_BROKER_AUTH" = "xno"
then
AM_CFLAGS="$AM_CFLAGS -DWOLFMQTT_BROKER_NO_AUTH"
fi


AM_CONDITIONAL([HAVE_LIBWOLFSSL], [test "x$ENABLED_TLS" = "xyes"])
AM_CONDITIONAL([HAVE_LIBCURL], [test "x$ENABLED_CURL" = "xyes"])
AM_CONDITIONAL([BUILD_STRESS], [test "x$ENABLED_STRESS" != "xno"])
Expand All @@ -415,6 +469,8 @@ AM_CONDITIONAL([BUILD_MQTT5], [test "x$ENABLED_MQTTV50" = "xyes"])
AM_CONDITIONAL([BUILD_NONBLOCK], [test "x$ENABLED_NONBLOCK" = "xyes"])
AM_CONDITIONAL([BUILD_MULTITHREAD], [test "x$ENABLED_MULTITHREAD" = "xyes"])
AM_CONDITIONAL([BUILD_WEBSOCKET], [test "x$ENABLED_WEBSOCKET" = "xyes"])
AM_CONDITIONAL([BUILD_BROKER], [test "x$ENABLED_BROKER" = "xyes"])



# HARDEN FLAGS
Expand Down Expand Up @@ -542,3 +598,4 @@ echo " * CURL: $ENABLED_CURL"
echo " * Multi-thread: $ENABLED_MULTITHREAD"
echo " * Stress: $ENABLED_STRESS"
echo " * WebSocket: $ENABLED_WEBSOCKET"
echo " * Broker: $ENABLED_BROKER"
11 changes: 8 additions & 3 deletions examples/mqttclient/mqttclient.c
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,7 @@ int mqttclient_test(MQTTCtx *mqttCtx)

/* Publish Topic */
XMEMSET(&mqttCtx->publish, 0, sizeof(MqttPublish));
mqttCtx->publish.retain = 0;
mqttCtx->publish.retain = mqttCtx->retain;
mqttCtx->publish.qos = mqttCtx->qos;
mqttCtx->publish.duplicate = 0;
mqttCtx->publish.topic_name = mqttCtx->topic_name;
Expand Down Expand Up @@ -567,7 +567,7 @@ int mqttclient_test(MQTTCtx *mqttCtx)
/* Publish Topic */
mqttCtx->stat = WMQ_PUB;
XMEMSET(&mqttCtx->publish, 0, sizeof(MqttPublish));
mqttCtx->publish.retain = 0;
mqttCtx->publish.retain = mqttCtx->retain;
mqttCtx->publish.qos = mqttCtx->qos;
mqttCtx->publish.duplicate = 0;
mqttCtx->publish.topic_name = mqttCtx->topic_name;
Expand Down Expand Up @@ -625,6 +625,11 @@ int mqttclient_test(MQTTCtx *mqttCtx)
mqttCtx->return_code = rc;

disconn:
/* Preserve error code through disconnect */
if (rc != MQTT_CODE_SUCCESS) {
mqttCtx->return_code = rc;
}

/* Disconnect */
XMEMSET(&mqttCtx->disconnect, 0, sizeof(mqttCtx->disconnect));
#ifdef WOLFMQTT_V5
Expand Down Expand Up @@ -668,7 +673,7 @@ int mqttclient_test(MQTTCtx *mqttCtx)

MqttClient_DeInit(&mqttCtx->client);

return rc;
return mqttCtx->return_code;
}


Expand Down
Loading
Loading