From 75c2a39d2cb3b5fba962908bac60d9f774af2988 Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Fri, 15 Mar 2019 20:39:19 +0000 Subject: [PATCH] Send topic alias maximum from the broker. --- man/mosquitto.conf.5.xml | 10 ++++++ src/conf.c | 12 +++++++ src/handle_connect.c | 6 ++++ src/handle_publish.c | 27 ++++++++-------- src/mosquitto_broker_internal.h | 1 + test/broker/09-auth-bad-method.py | 2 +- test/broker/12-prop-topic-alias-invalid.py | 37 ++++++++++++++++++++++ test/broker/Makefile | 1 + test/broker/test.py | 1 + test/mosq_test.py | 4 +++ 10 files changed, 87 insertions(+), 14 deletions(-) create mode 100755 test/broker/12-prop-topic-alias-invalid.py diff --git a/man/mosquitto.conf.5.xml b/man/mosquitto.conf.5.xml index 25005cb7..ea32659c 100644 --- a/man/mosquitto.conf.5.xml +++ b/man/mosquitto.conf.5.xml @@ -952,6 +952,16 @@ log_timestamp_format %Y-%m-%dT%H:%M:%S Not reloaded on reload signal. + + number + + This option sets the maximum number topic aliases + that an MQTT v5 client is allowed to create. It + applies per listener. Defaults to 10. Set to 0 to + disallow topic aliases. + Not reloaded on reload signal. + + topic prefix diff --git a/src/conf.c b/src/conf.c index d53fab0c..e9e0a410 100644 --- a/src/conf.c +++ b/src/conf.c @@ -272,6 +272,7 @@ void config__init(struct mosquitto_db *db, struct mosquitto__config *config) config->default_listener.protocol = mp_mqtt; config->default_listener.security_options.allow_anonymous = -1; config->default_listener.maximum_qos = 2; + config->default_listener.max_topic_alias = 10; } void config__cleanup(struct mosquitto__config *config) @@ -507,6 +508,7 @@ int config__parse_args(struct mosquitto_db *db, struct mosquitto__config *config config->listeners[config->listener_count-1].tls_engine = config->default_listener.tls_engine; config->listeners[config->listener_count-1].tls_keyform = config->default_listener.tls_keyform; config->listeners[config->listener_count-1].tls_engine_kpass_sha1 = config->default_listener.tls_engine_kpass_sha1; + config->listeners[config->listener_count-1].max_topic_alias = config->default_listener.max_topic_alias; config->listeners[config->listener_count-1].cafile = config->default_listener.cafile; config->listeners[config->listener_count-1].capath = config->default_listener.capath; config->listeners[config->listener_count-1].certfile = config->default_listener.certfile; @@ -1344,6 +1346,7 @@ int config__read_file_core(struct mosquitto__config *config, bool reload, struct cur_listener->protocol = mp_mqtt; cur_listener->port = tmp_int; cur_listener->maximum_qos = 2; + cur_listener->max_topic_alias = 10; token = strtok_r(NULL, " ", &saveptr); if (token != NULL && token[0] == '#'){ token = NULL; @@ -2072,6 +2075,15 @@ int config__read_file_core(struct mosquitto__config *config, bool reload, struct #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge support not available."); #endif + }else if(!strcmp(token, "max_topic_alias")){ + if(reload) continue; // Listeners not valid for reloading. + token = strtok_r(NULL, " ", &saveptr); + if(token){ + cur_listener->max_topic_alias = atoi(token); + if(cur_listener->max_topic_alias < 0) cur_listener->max_topic_alias = -1; + }else{ + log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty max_topic_alias value in configuration."); + } }else if(!strcmp(token, "try_private")){ #ifdef WITH_BRIDGE if(reload) continue; // FIXME diff --git a/src/handle_connect.c b/src/handle_connect.c index d5aa8ab0..c65f4148 100644 --- a/src/handle_connect.c +++ b/src/handle_connect.c @@ -300,6 +300,12 @@ int handle__connect(struct mosquitto_db *db, struct mosquitto *context) goto handle_connect_error; } } + if(protocol_version == PROTOCOL_VERSION_v5 && context->listener->max_topic_alias > 0){ + if(mosquitto_property_add_int16(&connack_props, MQTT_PROP_TOPIC_ALIAS_MAXIMUM, context->listener->max_topic_alias)){ + rc = MOSQ_ERR_NOMEM; + goto handle_connect_error; + } + } if(packet__read_byte(&context->in_packet, &connect_flags)){ rc = 1; diff --git a/src/handle_publish.c b/src/handle_publish.c index 5905fd3c..babef977 100644 --- a/src/handle_publish.c +++ b/src/handle_publish.c @@ -51,7 +51,7 @@ int handle__publish(struct mosquitto_db *db, struct mosquitto *context) mosquitto_property *p, *p_prev; mosquitto_property *msg_properties = NULL, *msg_properties_last; uint32_t message_expiry_interval = 0; - uint16_t topic_alias = 0; + int topic_alias = -1; uint8_t reason_code = 0; #ifdef WITH_BRIDGE @@ -160,19 +160,20 @@ int handle__publish(struct mosquitto_db *db, struct mosquitto *context) } mosquitto_property_free_all(&properties); - if(topic && topic_alias){ - rc = alias__add(context, topic, topic_alias); - if(rc) return rc; - }else if(topic == NULL && topic_alias){ - rc = alias__find(context, &topic, topic_alias); - if(rc){ - if(context->protocol == mosq_p_mqtt5){ - send__disconnect(context, MQTT_RC_TOPIC_ALIAS_INVALID, NULL); - } - return rc; - } - }else if(topic == NULL && topic_alias == 0){ + if(topic_alias == 0 || topic_alias > context->listener->max_topic_alias){ + send__disconnect(context, MQTT_RC_TOPIC_ALIAS_INVALID, NULL); return MOSQ_ERR_PROTOCOL; + }else if(topic_alias > 0){ + if(topic){ + rc = alias__add(context, topic, topic_alias); + if(rc) return rc; + }else{ + rc = alias__find(context, &topic, topic_alias); + if(rc){ + send__disconnect(context, MQTT_RC_TOPIC_ALIAS_INVALID, NULL); + return rc; + } + } } if(mosquitto_validate_utf8(topic, slen) != MOSQ_ERR_SUCCESS){ log__printf(NULL, MOSQ_LOG_INFO, "Client %s sent topic with invalid UTF-8, disconnecting.", context->id); diff --git a/src/mosquitto_broker_internal.h b/src/mosquitto_broker_internal.h index dedcf61b..5574463b 100644 --- a/src/mosquitto_broker_internal.h +++ b/src/mosquitto_broker_internal.h @@ -212,6 +212,7 @@ struct mosquitto__listener { int socket_domain; bool use_username_as_clientid; uint8_t maximum_qos; + uint16_t max_topic_alias; #ifdef WITH_TLS char *cafile; char *capath; diff --git a/test/broker/09-auth-bad-method.py b/test/broker/09-auth-bad-method.py index 254ebf6d..e83178ac 100755 --- a/test/broker/09-auth-bad-method.py +++ b/test/broker/09-auth-bad-method.py @@ -9,7 +9,7 @@ rc = 1 keepalive = 10 props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_METHOD, "basic") connect_packet = mosq_test.gen_connect("connect-test", proto_ver=5, keepalive=keepalive, properties=props) -connack_packet = mosq_test.gen_connack(rc=mqtt5_rc.MQTT_RC_BAD_AUTHENTICATION_METHOD, proto_ver=5) +connack_packet = mosq_test.gen_connack(rc=mqtt5_rc.MQTT_RC_BAD_AUTHENTICATION_METHOD, proto_ver=5, properties=None) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) diff --git a/test/broker/12-prop-topic-alias-invalid.py b/test/broker/12-prop-topic-alias-invalid.py new file mode 100755 index 00000000..e45bb548 --- /dev/null +++ b/test/broker/12-prop-topic-alias-invalid.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python + +# Test whether the broker handles a topic alias of >max_topic_alias correctly. +# MQTTv5 + +from mosq_test_helper import * + +def do_test(value): + rc = 1 + + keepalive = 10 + connect_packet = mosq_test.gen_connect("test", proto_ver=5, keepalive=keepalive) + connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) + + props = mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_TOPIC_ALIAS, value) + publish_packet = mosq_test.gen_publish(topic="test/topic", qos=0, payload="12345678901234567890", proto_ver=5, properties=props) + + disconnect_packet = mosq_test.gen_disconnect(reason_code=mqtt5_rc.MQTT_RC_TOPIC_ALIAS_INVALID, proto_ver=5) + port = mosq_test.get_port() + broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) + + try: + sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) + sock.send(publish_packet) + if mosq_test.expect_packet(sock, "disconnect", disconnect_packet): + rc = 0 + finally: + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde) + exit(rc) + + +do_test(11) + diff --git a/test/broker/Makefile b/test/broker/Makefile index 1fd1df8e..fcbdcf8f 100644 --- a/test/broker/Makefile +++ b/test/broker/Makefile @@ -198,3 +198,4 @@ endif ./12-prop-session-expiry-invalid.py ./12-prop-subpub-content-type.py ./12-prop-subpub-payload-format.py + ./12-prop-topic-alias-invalid.py diff --git a/test/broker/test.py b/test/broker/test.py index 0c86329b..f435654b 100755 --- a/test/broker/test.py +++ b/test/broker/test.py @@ -165,6 +165,7 @@ tests = [ (1, './12-prop-session-expiry-invalid.py'), (1, './12-prop-subpub-content-type.py'), (1, './12-prop-subpub-payload-format.py'), + (1, './12-prop-topic-alias-invalid.py'), ] ptest.run_tests(tests) diff --git a/test/mosq_test.py b/test/mosq_test.py index 9142c2c9..6c039619 100644 --- a/test/mosq_test.py +++ b/test/mosq_test.py @@ -389,6 +389,10 @@ def gen_connect(client_id, clean_session=True, keepalive=60, username=None, pass def gen_connack(flags=0, rc=0, proto_ver=4, properties=""): if proto_ver == 5: + if properties is not None: + properties = mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_TOPIC_ALIAS_MAXIMUM, 10) + properties + else: + properties = "" properties = mqtt5_props.prop_finalise(properties) packet = struct.pack('!BBBB', 32, 2+len(properties), flags, rc) + properties