From b6db71e77a57d763e07bfea0cf07b320b54da343 Mon Sep 17 00:00:00 2001 From: Artem Ryzhov Date: Mon, 1 Jun 2026 19:10:35 +0200 Subject: [PATCH 1/2] Enforce Wildcard Subscription Available gate on client side MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #3636. When the server sends Wildcard Subscription Available = 0 in the CONNACK properties, the client library must not send SUBSCRIBE with '+' or '#' topic filters. Previously the property was stored but never enforced, so wildcard subscribes were sent regardless. - Add wildcard_sub_available field to struct mosquitto - Default to 1 (supported) — MQTT 5 spec: absent means supported - Reset to 1 on every (re)connect to avoid stale state from prior broker - Read MQTT_PROP_WILDCARD_SUB_AVAILABLE from CONNACK properties - Return MOSQ_ERR_WILDCARD_SUBS_NOT_SUPPORTED from subscribe API when a wildcard filter is used and the server declared unavailable Signed-off-by: Artem Ryzhov --- lib/actions_subscribe.c | 5 +++++ lib/connect.c | 1 + lib/handle_connack.c | 1 + lib/libmosquitto.c | 1 + lib/mosquitto_internal.h | 1 + 5 files changed, 9 insertions(+) diff --git a/lib/actions_subscribe.c b/lib/actions_subscribe.c index aec454d041..03a5858ad3 100644 --- a/lib/actions_subscribe.c +++ b/lib/actions_subscribe.c @@ -90,6 +90,11 @@ int mosquitto_subscribe_multiple(struct mosquitto *mosq, int *mid, int sub_count if(mosquitto_validate_utf8(sub[i], slen)){ return MOSQ_ERR_MALFORMED_UTF8; } + if(!mosq->wildcard_sub_available){ + if(strchr(sub[i], '+') || strchr(sub[i], '#')){ + return MOSQ_ERR_WILDCARD_SUBS_NOT_SUPPORTED; + } + } remaining_length += 2+(uint32_t)slen + 1; } diff --git a/lib/connect.c b/lib/connect.c index 2e714b22fe..d22289a663 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -94,6 +94,7 @@ static int mosquitto__connect_init(struct mosquitto *mosq, const char *host, int mosq->msgs_in.inflight_quota = mosq->msgs_in.inflight_maximum; mosq->msgs_out.inflight_quota = mosq->msgs_out.inflight_maximum; mosq->retain_available = 1; + mosq->wildcard_sub_available = 1; mosquitto__set_request_disconnect(mosq, false); return MOSQ_ERR_SUCCESS; diff --git a/lib/handle_connack.c b/lib/handle_connack.c index 93ee81c6f1..d8aeabda10 100644 --- a/lib/handle_connack.c +++ b/lib/handle_connack.c @@ -102,6 +102,7 @@ int handle__connack(struct mosquitto *mosq) } mosquitto_property_read_byte(properties, MQTT_PROP_RETAIN_AVAILABLE, &mosq->retain_available, false); + mosquitto_property_read_byte(properties, MQTT_PROP_WILDCARD_SUB_AVAILABLE, &mosq->wildcard_sub_available, false); mosquitto_property_read_byte(properties, MQTT_PROP_MAXIMUM_QOS, &mosq->max_qos, false); mosquitto_property_read_int16(properties, MQTT_PROP_RECEIVE_MAXIMUM, &mosq->msgs_out.inflight_maximum, false); mosquitto_property_read_int16(properties, MQTT_PROP_SERVER_KEEP_ALIVE, &mosq->keepalive, false); diff --git a/lib/libmosquitto.c b/lib/libmosquitto.c index 559ac99cfc..91a53bab93 100644 --- a/lib/libmosquitto.c +++ b/lib/libmosquitto.c @@ -206,6 +206,7 @@ int mosquitto_reinitialise(struct mosquitto *mosq, const char *id, bool clean_st mosq->last_mid = 0; mosq->state = mosq_cs_new; mosq->max_qos = 2; + mosq->wildcard_sub_available = 1; mosq->msgs_in.inflight_maximum = 20; mosq->msgs_out.inflight_maximum = 20; mosq->msgs_in.inflight_quota = 20; diff --git a/lib/mosquitto_internal.h b/lib/mosquitto_internal.h index 0dd27b3158..fdfb579d80 100644 --- a/lib/mosquitto_internal.h +++ b/lib/mosquitto_internal.h @@ -451,6 +451,7 @@ struct mosquitto { #endif uint8_t max_qos; uint8_t retain_available; + uint8_t wildcard_sub_available; bool tcp_nodelay; #if defined(WITH_WEBSOCKETS) && WITH_WEBSOCKETS == WS_IS_BUILTIN char *http_request; From 2c9637d13f8143eb7c6fecf5f899cc31fe53a2a4 Mon Sep 17 00:00:00 2001 From: Artem Ryzhov Date: Mon, 1 Jun 2026 19:13:32 +0200 Subject: [PATCH 2/2] Add C test for wildcard subscription unavailable (MQTT v5) Signed-off-by: Artem Ryzhov --- .../c/02-subscribe-wildcard-unavailable-v5.c | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 test/lib/c/02-subscribe-wildcard-unavailable-v5.c diff --git a/test/lib/c/02-subscribe-wildcard-unavailable-v5.c b/test/lib/c/02-subscribe-wildcard-unavailable-v5.c new file mode 100644 index 0000000000..981a6a236a --- /dev/null +++ b/test/lib/c/02-subscribe-wildcard-unavailable-v5.c @@ -0,0 +1,92 @@ +#include +#include +#include +#include +#include + +static int run = -1; + + +static void on_connect(struct mosquitto *mosq, void *obj, int rc) +{ + (void)obj; + + if(rc){ + exit(1); + }else{ + /* Wildcard subscribe must be rejected client-side: CONNACK said = 0 */ + rc = mosquitto_subscribe(mosq, NULL, "wildcard/+/rejected", 0); + if(rc != MOSQ_ERR_WILDCARD_SUBS_NOT_SUPPORTED){ + run = 1; + return; + } + rc = mosquitto_subscribe(mosq, NULL, "wildcard/#", 0); + if(rc != MOSQ_ERR_WILDCARD_SUBS_NOT_SUPPORTED){ + run = 1; + return; + } + /* Plain topic must succeed */ + rc = mosquitto_subscribe(mosq, NULL, "plain/topic", 0); + if(rc != MOSQ_ERR_SUCCESS){ + run = 1; + } + } +} + + +static void on_subscribe(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos) +{ + (void)obj; + (void)mid; + (void)qos_count; + (void)granted_qos; + + mosquitto_disconnect(mosq); +} + + +static void on_disconnect(struct mosquitto *mosq, void *obj, int rc) +{ + (void)mosq; + (void)obj; + (void)rc; + + run = 0; +} + + +int main(int argc, char *argv[]) +{ + int rc; + struct mosquitto *mosq; + int port; + + if(argc < 2){ + return 1; + } + port = atoi(argv[1]); + + mosquitto_lib_init(); + + mosq = mosquitto_new("subscribe-wildcard-unavailable-test", true, NULL); + if(mosq == NULL){ + return 1; + } + mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5); + mosquitto_connect_callback_set(mosq, on_connect); + mosquitto_subscribe_callback_set(mosq, on_subscribe); + mosquitto_disconnect_callback_set(mosq, on_disconnect); + + rc = mosquitto_connect(mosq, "localhost", port, 60); + if(rc != MOSQ_ERR_SUCCESS){ + return rc; + } + + while(run == -1){ + mosquitto_loop(mosq, 50, 1); + } + + mosquitto_destroy(mosq); + mosquitto_lib_cleanup(); + return run; +}