Skip to content

Commit 7c2fe8a

Browse files
committed
Peer review fixes (thank you embhorn)
1 parent 2fb1f65 commit 7c2fe8a

6 files changed

Lines changed: 169 additions & 83 deletions

File tree

.github/workflows/broker-check.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ jobs:
7979
cflags: ""
8080
wolfmqtt_opts: "--enable-broker --enable-v5 --enable-broker-persist --enable-tls"
8181
- name: "Broker with persist + AES-GCM encryption"
82-
cflags: ""
82+
cflags: "-DWOLFMQTT_BROKER_PERSIST_ENCRYPT_DEV_KEY"
8383
wolfmqtt_opts: "--enable-broker --enable-v5 --enable-broker-persist --enable-broker-persist-encrypt"
8484
- name: "Broker with persist + static memory"
8585
cflags: "-DWOLFMQTT_STATIC_MEMORY"

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,3 +128,5 @@ src/mqtt_broker
128128

129129
tests/fuzz/broker_fuzz
130130
tests/fuzz/corpus/
131+
tests/test_broker_connect
132+
tests/unit_tests

BROKER.md

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
# wolfMQTT Broker
2+
3+
wolfMQTT includes a lightweight MQTT broker suitable for embedded and resource-constrained environments. It serves both MQTT v3.1.1 and v5.0 clients, with optional TLS via wolfSSL, optional WebSocket transport, and optional encrypted persistence. The broker uses non-blocking sockets driven by a single `select()` loop, so it runs without threads.
4+
5+
## Features
6+
7+
* QoS 0, QoS 1, and QoS 2 publish/subscribe (full QoS 2 flow with PUBREC/PUBREL/PUBCOMP)
8+
* Retained messages
9+
* Last Will and Testament (LWT), including v5 Will Delay Interval
10+
* Wildcard subscriptions (`+` and `#`)
11+
* Username/password authentication
12+
* MQTT v5 ordering and Receive Maximum (per-subscriber inflight shaping)
13+
* TLS support (requires wolfSSL with `--enable-tls`)
14+
* WebSocket / secure WebSocket transport (requires libwebsockets; see the WebSocket section of the main [README.md](README.md))
15+
* Clean session handling with subscription persistence
16+
* Keep-alive monitoring with automatic client disconnect
17+
* Unique client ID enforcement (existing session takeover)
18+
* Optional on-disk persistence of sessions, subscriptions, retained messages, and offline queues, with optional AES-GCM encryption-at-rest
19+
* Static memory mode (`WOLFMQTT_STATIC_MEMORY`) for zero-malloc operation
20+
21+
## Quick start
22+
23+
With autotools:
24+
25+
```sh
26+
./configure --enable-broker
27+
make
28+
./src/mqtt_broker -p 1883
29+
```
30+
31+
With CMake:
32+
33+
```sh
34+
cmake .. -DWOLFMQTT_BROKER=yes
35+
cmake --build .
36+
```
37+
38+
For TLS:
39+
40+
```sh
41+
./configure --enable-broker --enable-tls
42+
make
43+
./src/mqtt_broker -p 8883 -t -A ca-cert.pem -K server-key.pem -c server-cert.pem
44+
```
45+
46+
Run `./src/mqtt_broker -h` to see the options compiled into your build.
47+
48+
## Command-line options
49+
50+
```
51+
usage: mqtt_broker [-p port] [-v level] [-u user] [-P pass]
52+
[-t] [-s port] [-V ver] [-c cert] [-K key] [-A ca]
53+
[-w port] [-D dir] [-E source]
54+
```
55+
56+
| Option | Available when | Description |
57+
|---|---|---|
58+
| `-p <port>` | always | Plain (non-TLS) port (default: 1883) |
59+
| `-v <level>` | always | Log level: 1=error, 2=info (default), 3=debug |
60+
| `-u <user>` | auth build | Username for authentication |
61+
| `-P <pass>` | auth build | Password for authentication |
62+
| `-t` | TLS build | Enable the TLS listener |
63+
| `-s <port>` | TLS build | TLS port (default: 8883) |
64+
| `-V <ver>` | TLS build | TLS version: 12=TLS 1.2, 13=TLS 1.3 (default: auto) |
65+
| `-c <file>` | TLS build | Server certificate file (PEM) |
66+
| `-K <file>` | TLS build | Server private key file (PEM) |
67+
| `-A <file>` | TLS build | CA certificate for mutual TLS (PEM) |
68+
| `-w <port>` | WebSocket build | WebSocket listen port (enables WebSocket) |
69+
| `-D <dir>` | persist build | Persistent storage directory (enables persistence; default `/var/lib/wolfmqtt`) |
70+
| `-E <source>` | encrypt + dev-key build | Encryption key source. Only `dev` is recognized, selecting the development hard-coded key. NOT FOR PRODUCTION. |
71+
72+
## Build options
73+
74+
All broker features are enabled by default and can be disabled at build time to reduce code and memory footprint on constrained platforms.
75+
76+
| Feature | Autotools | CMake | Define |
77+
|---|---|---|---|
78+
| Broker support | `--enable-broker` | `-DWOLFMQTT_BROKER=yes` | `WOLFMQTT_BROKER` |
79+
| Retained messages | `--disable-broker-retained` | `-DWOLFMQTT_BROKER_RETAINED=no` | `WOLFMQTT_BROKER_NO_RETAINED` |
80+
| Last Will and Testament | `--disable-broker-will` | `-DWOLFMQTT_BROKER_WILL=no` | `WOLFMQTT_BROKER_NO_WILL` |
81+
| Wildcard subscriptions | `--disable-broker-wildcards` | `-DWOLFMQTT_BROKER_WILDCARDS=no` | `WOLFMQTT_BROKER_NO_WILDCARDS` |
82+
| Authentication | `--disable-broker-auth` | `-DWOLFMQTT_BROKER_AUTH=no` | `WOLFMQTT_BROKER_NO_AUTH` |
83+
| Logging | `--disable-broker-log` | `-DWOLFMQTT_BROKER_LOG=no` | `WOLFMQTT_BROKER_NO_LOG` |
84+
| Plain-text listener | `--disable-broker-insecure` | `-DWOLFMQTT_BROKER_INSECURE=no` | `WOLFMQTT_BROKER_NO_INSECURE` |
85+
86+
The maximum QoS the broker negotiates is capped by `--enable-max-qos=<0,1,2>` (default 2). Setting it to 1 or 0 compiles out the QoS 2 state machine and shrinks the broker.
87+
88+
## Static memory tuning
89+
90+
When built with `WOLFMQTT_STATIC_MEMORY`, the broker uses fixed-size arrays instead of dynamic allocation. The limits below can be overridden via CFLAGS at build time.
91+
92+
| Macro | Default | Description |
93+
|---|---|---|
94+
| `BROKER_MAX_CLIENTS` | 8 | Maximum concurrent client connections |
95+
| `BROKER_MAX_SUBS` | 32 | Maximum total subscriptions across all clients |
96+
| `BROKER_MAX_RETAINED` | 16 | Maximum retained messages |
97+
| `BROKER_MAX_CLIENT_ID_LEN` | 64 | Maximum client ID length |
98+
| `BROKER_MAX_USERNAME_LEN` | 64 | Maximum username length |
99+
| `BROKER_MAX_PASSWORD_LEN` | 64 | Maximum password length |
100+
| `BROKER_MAX_FILTER_LEN` | 128 | Maximum subscription filter length |
101+
| `BROKER_MAX_TOPIC_LEN` | 128 | Maximum topic name length |
102+
| `BROKER_MAX_PAYLOAD_LEN` | 4096 | Maximum retained message payload |
103+
| `BROKER_MAX_WILL_PAYLOAD_LEN` | 256 | Maximum LWT payload |
104+
| `BROKER_MAX_PENDING_WILLS` | 4 | Maximum queued pending wills |
105+
| `BROKER_MAX_INBOUND_QOS2` | 16 | Concurrent inbound QoS 2 packet IDs per client |
106+
| `BROKER_RX_BUF_SZ` | 4096 | Per-client receive buffer size |
107+
| `BROKER_TX_BUF_SZ` | 4096 | Per-client transmit buffer size |
108+
| `BROKER_TIMEOUT_MS` | 1000 | `select()` timeout |
109+
| `BROKER_LISTEN_BACKLOG` | 128 | Listen queue depth |
110+
111+
With dynamic memory the per-subscriber inflight window is derived at runtime, bounded by `BROKER_MIN_INFLIGHT_PER_SUB` (default 8) and `BROKER_MAX_INFLIGHT_PER_SUB`. Define `BROKER_MAX_INFLIGHT_PER_SUB=1` to force strict serial delivery (one inflight QoS 1/2 message per subscriber).
112+
113+
## Persistence
114+
115+
Build with `--enable-broker-persist` to persist sessions, subscriptions, retained messages, and offline queues across restarts. The persistence layer is hook-based: a default POSIX backend stores records as files under the directory given with `-D` (default `/var/lib/wolfmqtt`). Embedded targets can supply their own storage backend through `MqttBroker_SetPersistHooks()`.
116+
117+
| Macro | Default | Description |
118+
|---|---|---|
119+
| `BROKER_MAX_PERSIST_SESSIONS` | 64 | Persistent sessions retained across restarts |
120+
| `BROKER_MAX_OFFLINE_MSGS_PER_SUB` | 32 | Offline queue depth per session |
121+
| `WOLFMQTT_BROKER_PERSIST_SCHEMA_VER` | 3 | On-disk record schema version |
122+
123+
### Encryption at rest
124+
125+
Add `--enable-broker-persist-encrypt` (requires `--enable-broker-persist`) to wrap persisted records with wolfCrypt AES-GCM. The key is provided by a `derive_key` callback that real deployments install via `MqttBroker_SetPersistHooks()` before starting the broker.
126+
127+
For development and CI only, the CLI can link a fixed-pattern `derive_key` hook so the AES-GCM round-trip can be exercised without external key management. This is NOT a configure option -- define the macro through CFLAGS:
128+
129+
```sh
130+
CFLAGS="-DWOLFMQTT_BROKER_PERSIST_ENCRYPT_DEV_KEY" \
131+
./configure --enable-broker --enable-broker-persist --enable-broker-persist-encrypt
132+
make
133+
./src/mqtt_broker -p 1883 -D ./state -E dev
134+
```
135+
136+
The dev key is a trivially-recoverable hard-coded pattern. Never define `WOLFMQTT_BROKER_PERSIST_ENCRYPT_DEV_KEY` in a production build, and never pass `-E dev` in production -- doing so substitutes the fixed key for real key management. Production builds omit the macro entirely, so the `-E` option and the dev hook are not present in the binary.
137+
138+
## Testing
139+
140+
The repository ships an end-to-end broker test harness:
141+
142+
```sh
143+
./scripts/broker.test
144+
```
145+
146+
It builds the client examples (`examples/pub-sub/mqtt-pub`, `examples/pub-sub/mqtt-sub`) and `mosquitto`-based checks against the wolfMQTT broker, covering QoS flows, retained messages, wildcards, persistence round-trips, and AES-GCM encryption (when the dev-key hook is linked). Tests that depend on features not present in the current build are reported as `SKIP`.
147+
148+
The CONNECT-handler unit test (`tests/test_broker_connect`) is part of `make check` and exercises the broker packet path with a mock network layer.
149+
150+
## Limitations
151+
152+
The wolfMQTT broker targets embedded and edge use cases. It is intentionally smaller in scope than full-featured server brokers such as Mosquitto or EMQX: there is no clustering, no bridging, no plugin/ACL framework, and no dynamic configuration reload. For large-scale or feature-rich deployments use a dedicated server broker; for a small, auditable, optionally-TLS broker that runs without threads or a heap, wolfMQTT is a good fit.

README.md

Lines changed: 2 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -418,75 +418,15 @@ is added to `make check`, and all other tests are disabled.
418418

419419
## Broker
420420

421-
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.
422-
423-
### Features
424-
425-
* QoS 0, QoS 1, and QoS 2 publish/subscribe (full QoS 2 flow with PUBREC/PUBREL/PUBCOMP)
426-
* Retained messages
427-
* Last Will and Testament (LWT) with v5 Will Delay Interval
428-
* Wildcard subscriptions (`+` and `#`)
429-
* Username/password authentication
430-
* TLS support (requires wolfSSL with `--enable-tls`)
431-
* Clean session handling with subscription persistence
432-
* Keep-alive monitoring with automatic client disconnect
433-
* Unique client ID enforcement (existing session takeover)
434-
* Static memory mode (`WOLFMQTT_STATIC_MEMORY`) for zero-malloc operation
435-
436-
### Building
437-
438-
With autotools:
421+
wolfMQTT includes a lightweight MQTT broker suitable for embedded and resource-constrained environments. It serves both MQTT v3.1.1 and v5.0 clients, with optional TLS, WebSocket transport, and encrypted persistence.
439422

440423
```
441424
./configure --enable-broker
442425
make
443-
```
444-
445-
With CMake:
446-
447-
```
448-
cmake .. -DWOLFMQTT_BROKER=yes
449-
cmake --build .
450-
```
451-
452-
### Running
453-
454-
```
455426
./src/mqtt_broker -p 1883
456427
```
457428

458-
For TLS:
459-
460-
```
461-
./src/mqtt_broker -p 8883 -t -A ca-cert.pem -K server-key.pem -c server-cert.pem
462-
```
463-
464-
Run `./src/mqtt_broker -h` to see all available options.
465-
466-
### Feature Build Options
467-
468-
All broker features are enabled by default. Individual features can be disabled at build time to reduce code and memory footprint on constrained platforms.
469-
470-
| Feature | Autotools | CMake | Define |
471-
|---|---|---|---|
472-
| Retained messages | `--disable-broker-retained` | `-DWOLFMQTT_BROKER_RETAINED=no` | `WOLFMQTT_BROKER_NO_RETAINED` |
473-
| Last Will and Testament | `--disable-broker-will` | `-DWOLFMQTT_BROKER_WILL=no` | `WOLFMQTT_BROKER_NO_WILL` |
474-
| Wildcard subscriptions | `--disable-broker-wildcards` | `-DWOLFMQTT_BROKER_WILDCARDS=no` | `WOLFMQTT_BROKER_NO_WILDCARDS` |
475-
| Authentication | `--disable-broker-auth` | `-DWOLFMQTT_BROKER_AUTH=no` | `WOLFMQTT_BROKER_NO_AUTH` |
476-
477-
### Static Memory Mode
478-
479-
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:
480-
481-
| Macro | Default | Description |
482-
|---|---|---|
483-
| `BROKER_MAX_CLIENTS` | 8 | Maximum concurrent client connections |
484-
| `BROKER_MAX_SUBS` | 32 | Maximum total subscriptions |
485-
| `BROKER_MAX_RETAINED` | 16 | Maximum retained messages |
486-
| `BROKER_RX_BUF_SZ` | 4096 | Per-client receive buffer size |
487-
| `BROKER_TX_BUF_SZ` | 4096 | Per-client transmit buffer size |
488-
| `BROKER_MAX_PAYLOAD_LEN` | 4096 | Maximum retained message payload |
489-
| `BROKER_MAX_WILL_PAYLOAD_LEN` | 256 | Maximum LWT payload size |
429+
See [BROKER.md](BROKER.md) for the full feature list, command-line options, build options, static-memory tuning, and persistence/encryption details.
490430

491431
## WebSocket Support
492432

configure.ac

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -531,24 +531,12 @@ then
531531
AM_CFLAGS="$AM_CFLAGS -DWOLFMQTT_BROKER_PERSIST_ENCRYPT"
532532
fi
533533

534-
# Development-only fixed-pattern derive_key hook for the CLI broker.
535-
# Off by default; required to use "-E dev" on encrypt builds. Never
536-
# enable in a production build - the resulting binary contains a
537-
# trivially-recoverable AES-GCM key generator that any flip of the
538-
# CLI argument would substitute for real key management.
539-
AC_ARG_ENABLE([broker-persist-encrypt-dev-key],
540-
[AS_HELP_STRING([--enable-broker-persist-encrypt-dev-key],[Link the CLI broker's fixed-pattern "dev" derive_key hook (default: disabled; requires --enable-broker-persist-encrypt; NOT FOR PRODUCTION)])],
541-
[ ENABLED_BROKER_PERSIST_ENCRYPT_DEV_KEY=$enableval ],
542-
[ ENABLED_BROKER_PERSIST_ENCRYPT_DEV_KEY=no ]
543-
)
544-
if test "x$ENABLED_BROKER_PERSIST_ENCRYPT_DEV_KEY" = "xyes"
545-
then
546-
if test "x$ENABLED_BROKER_PERSIST_ENCRYPT" != "xyes"
547-
then
548-
AC_MSG_ERROR([--enable-broker-persist-encrypt-dev-key requires --enable-broker-persist-encrypt])
549-
fi
550-
AM_CFLAGS="$AM_CFLAGS -DWOLFMQTT_BROKER_PERSIST_ENCRYPT_DEV_KEY"
551-
fi
534+
# Note: the development-only fixed-pattern derive_key hook for the CLI
535+
# broker (required to use "-E dev" on encrypt builds) is not a configure
536+
# option. Define WOLFMQTT_BROKER_PERSIST_ENCRYPT_DEV_KEY via CFLAGS to
537+
# link it. Never enable it in a production build - the resulting binary
538+
# contains a trivially-recoverable AES-GCM key generator. Real
539+
# deployments install a derive_key hook via MqttBroker_SetPersistHooks.
552540

553541

554542
AM_CONDITIONAL([HAVE_LIBWOLFSSL], [test "x$ENABLED_TLS" = "xyes"])

tests/include.am

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ if BUILD_BROKER
2222
check_PROGRAMS += tests/test_broker_connect
2323
tests_test_broker_connect_SOURCES = \
2424
tests/test_broker_connect.c \
25-
src/mqtt_broker.c
25+
src/mqtt_broker.c \
26+
src/mqtt_broker_persist.c \
27+
src/mqtt_broker_persist_posix.c
2628
tests_test_broker_connect_CFLAGS = -DWOLFMQTT_BROKER -DWOLFMQTT_BROKER_CUSTOM_NET \
2729
-DWOLFMQTT_BROKER_NO_LOG -DNO_MAIN_DRIVER \
2830
'-DWOLFMQTT_BROKER_GET_TIME_S()=((WOLFMQTT_BROKER_TIME_T)0)' \
@@ -38,7 +40,9 @@ if BUILD_BROKER
3840
noinst_PROGRAMS += tests/fuzz/broker_fuzz
3941
tests_fuzz_broker_fuzz_SOURCES = \
4042
tests/fuzz/broker_fuzz.c \
41-
src/mqtt_broker.c
43+
src/mqtt_broker.c \
44+
src/mqtt_broker_persist.c \
45+
src/mqtt_broker_persist_posix.c
4246
tests_fuzz_broker_fuzz_CFLAGS = -DWOLFMQTT_BROKER -DWOLFMQTT_BROKER_CUSTOM_NET \
4347
-DWOLFMQTT_BROKER_NO_LOG -DNO_MAIN_DRIVER \
4448
'-DWOLFMQTT_BROKER_GET_TIME_S()=((WOLFMQTT_BROKER_TIME_T)0)' \

0 commit comments

Comments
 (0)