Send topic alias maximum from the broker.

This commit is contained in:
Roger A. Light 2019-03-15 20:39:19 +00:00
parent e4db8707a6
commit 75c2a39d2c
10 changed files with 87 additions and 14 deletions

View File

@ -952,6 +952,16 @@ log_timestamp_format %Y-%m-%dT%H:%M:%S
<para>Not reloaded on reload signal.</para> <para>Not reloaded on reload signal.</para>
</listitem> </listitem>
</varlistentry> </varlistentry>
<varlistentry>
<term><option>max_topic_alias</option> <replaceable>number</replaceable></term>
<listitem>
<para>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.</para>
<para>Not reloaded on reload signal.</para>
</listitem>
</varlistentry>
<varlistentry> <varlistentry>
<term><option>mount_point</option> <replaceable>topic prefix</replaceable></term> <term><option>mount_point</option> <replaceable>topic prefix</replaceable></term>
<listitem> <listitem>

View File

@ -272,6 +272,7 @@ void config__init(struct mosquitto_db *db, struct mosquitto__config *config)
config->default_listener.protocol = mp_mqtt; config->default_listener.protocol = mp_mqtt;
config->default_listener.security_options.allow_anonymous = -1; config->default_listener.security_options.allow_anonymous = -1;
config->default_listener.maximum_qos = 2; config->default_listener.maximum_qos = 2;
config->default_listener.max_topic_alias = 10;
} }
void config__cleanup(struct mosquitto__config *config) 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_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_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].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].cafile = config->default_listener.cafile;
config->listeners[config->listener_count-1].capath = config->default_listener.capath; config->listeners[config->listener_count-1].capath = config->default_listener.capath;
config->listeners[config->listener_count-1].certfile = config->default_listener.certfile; 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->protocol = mp_mqtt;
cur_listener->port = tmp_int; cur_listener->port = tmp_int;
cur_listener->maximum_qos = 2; cur_listener->maximum_qos = 2;
cur_listener->max_topic_alias = 10;
token = strtok_r(NULL, " ", &saveptr); token = strtok_r(NULL, " ", &saveptr);
if (token != NULL && token[0] == '#'){ if (token != NULL && token[0] == '#'){
token = NULL; token = NULL;
@ -2072,6 +2075,15 @@ int config__read_file_core(struct mosquitto__config *config, bool reload, struct
#else #else
log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge support not available."); log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge support not available.");
#endif #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")){ }else if(!strcmp(token, "try_private")){
#ifdef WITH_BRIDGE #ifdef WITH_BRIDGE
if(reload) continue; // FIXME if(reload) continue; // FIXME

View File

@ -300,6 +300,12 @@ int handle__connect(struct mosquitto_db *db, struct mosquitto *context)
goto handle_connect_error; 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)){ if(packet__read_byte(&context->in_packet, &connect_flags)){
rc = 1; rc = 1;

View File

@ -51,7 +51,7 @@ int handle__publish(struct mosquitto_db *db, struct mosquitto *context)
mosquitto_property *p, *p_prev; mosquitto_property *p, *p_prev;
mosquitto_property *msg_properties = NULL, *msg_properties_last; mosquitto_property *msg_properties = NULL, *msg_properties_last;
uint32_t message_expiry_interval = 0; uint32_t message_expiry_interval = 0;
uint16_t topic_alias = 0; int topic_alias = -1;
uint8_t reason_code = 0; uint8_t reason_code = 0;
#ifdef WITH_BRIDGE #ifdef WITH_BRIDGE
@ -160,19 +160,20 @@ int handle__publish(struct mosquitto_db *db, struct mosquitto *context)
} }
mosquitto_property_free_all(&properties); mosquitto_property_free_all(&properties);
if(topic && topic_alias){ 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); rc = alias__add(context, topic, topic_alias);
if(rc) return rc; if(rc) return rc;
}else if(topic == NULL && topic_alias){ }else{
rc = alias__find(context, &topic, topic_alias); rc = alias__find(context, &topic, topic_alias);
if(rc){ if(rc){
if(context->protocol == mosq_p_mqtt5){
send__disconnect(context, MQTT_RC_TOPIC_ALIAS_INVALID, NULL); send__disconnect(context, MQTT_RC_TOPIC_ALIAS_INVALID, NULL);
}
return rc; return rc;
} }
}else if(topic == NULL && topic_alias == 0){ }
return MOSQ_ERR_PROTOCOL;
} }
if(mosquitto_validate_utf8(topic, slen) != MOSQ_ERR_SUCCESS){ 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); log__printf(NULL, MOSQ_LOG_INFO, "Client %s sent topic with invalid UTF-8, disconnecting.", context->id);

View File

@ -212,6 +212,7 @@ struct mosquitto__listener {
int socket_domain; int socket_domain;
bool use_username_as_clientid; bool use_username_as_clientid;
uint8_t maximum_qos; uint8_t maximum_qos;
uint16_t max_topic_alias;
#ifdef WITH_TLS #ifdef WITH_TLS
char *cafile; char *cafile;
char *capath; char *capath;

View File

@ -9,7 +9,7 @@ rc = 1
keepalive = 10 keepalive = 10
props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_METHOD, "basic") 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) 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() port = mosq_test.get_port()
broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)

View File

@ -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)

View File

@ -198,3 +198,4 @@ endif
./12-prop-session-expiry-invalid.py ./12-prop-session-expiry-invalid.py
./12-prop-subpub-content-type.py ./12-prop-subpub-content-type.py
./12-prop-subpub-payload-format.py ./12-prop-subpub-payload-format.py
./12-prop-topic-alias-invalid.py

View File

@ -165,6 +165,7 @@ tests = [
(1, './12-prop-session-expiry-invalid.py'), (1, './12-prop-session-expiry-invalid.py'),
(1, './12-prop-subpub-content-type.py'), (1, './12-prop-subpub-content-type.py'),
(1, './12-prop-subpub-payload-format.py'), (1, './12-prop-subpub-payload-format.py'),
(1, './12-prop-topic-alias-invalid.py'),
] ]
ptest.run_tests(tests) ptest.run_tests(tests)

View File

@ -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=""): def gen_connack(flags=0, rc=0, proto_ver=4, properties=""):
if proto_ver == 5: 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) properties = mqtt5_props.prop_finalise(properties)
packet = struct.pack('!BBBB', 32, 2+len(properties), flags, rc) + properties packet = struct.pack('!BBBB', 32, 2+len(properties), flags, rc) + properties