Skip to content

Commit

Permalink
Fix missing reason_code on v5 UNSUBACK.
Browse files Browse the repository at this point in the history
Closes #1167. Thanks to Christoph Krey.
  • Loading branch information
ralight committed Feb 17, 2019
1 parent 458a984 commit 1ec0cea
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 25 deletions.
62 changes: 42 additions & 20 deletions src/handle_unsubscribe.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ int handle__unsubscribe(struct mosquitto_db *db, struct mosquitto *context)
char *sub;
int slen;
int rc;
int reason_code_count = 0;
int reason_code_max;
uint8_t *reason_codes = NULL, *reason_tmp;
mosquitto_property *properties = NULL;

if(!context) return MOSQ_ERR_INVAL;
Expand All @@ -57,32 +60,49 @@ int handle__unsubscribe(struct mosquitto_db *db, struct mosquitto *context)
return MOSQ_ERR_PROTOCOL;
}
}

reason_code_max = 10;
reason_codes = mosquitto__malloc(reason_code_max);
if(!reason_codes){
return MOSQ_ERR_NOMEM;
}

while(context->in_packet.pos < context->in_packet.remaining_length){
sub = NULL;
if(packet__read_string(&context->in_packet, &sub, &slen)){
return 1;
}

if(sub){
if(!slen){
log__printf(NULL, MOSQ_LOG_INFO,
"Empty unsubscription string from %s, disconnecting.",
context->id);
mosquitto__free(sub);
return 1;
}
if(mosquitto_sub_topic_check(sub)){
log__printf(NULL, MOSQ_LOG_INFO,
"Invalid unsubscription string from %s, disconnecting.",
context->id);
mosquitto__free(sub);
return 1;
}

log__printf(NULL, MOSQ_LOG_DEBUG, "\t%s", sub);
sub__remove(db, context, sub, db->subs);
log__printf(NULL, MOSQ_LOG_UNSUBSCRIBE, "%s %s", context->id, sub);
if(!slen){
log__printf(NULL, MOSQ_LOG_INFO,
"Empty unsubscription string from %s, disconnecting.",
context->id);
mosquitto__free(sub);
return 1;
}
if(mosquitto_sub_topic_check(sub)){
log__printf(NULL, MOSQ_LOG_INFO,
"Invalid unsubscription string from %s, disconnecting.",
context->id);
mosquitto__free(sub);
return 1;
}

log__printf(NULL, MOSQ_LOG_DEBUG, "\t%s", sub);
sub__remove(db, context, sub, db->subs);
log__printf(NULL, MOSQ_LOG_UNSUBSCRIBE, "%s %s", context->id, sub);
mosquitto__free(sub);

reason_codes[reason_code_count] = 0;
reason_code_count++;
if(reason_code_count == reason_code_max){
reason_tmp = mosquitto__realloc(reason_codes, reason_code_max*2);
if(!reason_tmp){
mosquitto__free(reason_codes);
return MOSQ_ERR_NOMEM;
}
reason_codes = reason_tmp;
reason_code_max *= 2;
}
}
#ifdef WITH_PERSISTENCE
Expand All @@ -92,6 +112,8 @@ int handle__unsubscribe(struct mosquitto_db *db, struct mosquitto *context)
log__printf(NULL, MOSQ_LOG_DEBUG, "Sending UNSUBACK to %s", context->id);

/* We don't use Reason String or User Property yet. */
return send__unsuback(context, mid, NULL);
rc = send__unsuback(context, mid, reason_code_count, reason_codes, NULL);
mosquitto__free(reason_codes);
return rc;
}

2 changes: 1 addition & 1 deletion src/mosquitto_broker_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -525,7 +525,7 @@ int restore_privileges(void);
* ============================================================ */
int send__connack(struct mosquitto_db *db, struct mosquitto *context, int ack, int reason_code, const mosquitto_property *properties);
int send__suback(struct mosquitto *context, uint16_t mid, uint32_t payloadlen, const void *payload);
int send__unsuback(struct mosquitto *context, uint16_t mid, const mosquitto_property *properties);
int send__unsuback(struct mosquitto *context, uint16_t mid, int reason_code_count, uint8_t *reason_codes, const mosquitto_property *properties);

/* ============================================================
* Network functions
Expand Down
5 changes: 3 additions & 2 deletions src/send_unsuback.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ and the Eclipse Distribution License is available at
#include "property_mosq.h"


int send__unsuback(struct mosquitto *mosq, uint16_t mid, const mosquitto_property *properties)
int send__unsuback(struct mosquitto *mosq, uint16_t mid, int reason_code_count, uint8_t *reason_codes, const mosquitto_property *properties)
{
struct mosquitto__packet *packet = NULL;
int rc;
Expand All @@ -41,7 +41,7 @@ int send__unsuback(struct mosquitto *mosq, uint16_t mid, const mosquitto_propert
if(mosq->protocol == mosq_p_mqtt5){
proplen = property__get_length_all(properties);
varbytes = packet__varint_bytes(proplen);
packet->remaining_length += varbytes + proplen;
packet->remaining_length += varbytes + proplen + reason_code_count;
}

rc = packet__alloc(packet);
Expand All @@ -54,6 +54,7 @@ int send__unsuback(struct mosquitto *mosq, uint16_t mid, const mosquitto_propert

if(mosq->protocol == mosq_p_mqtt5){
property__write_all(packet, properties, true);
packet__write_bytes(packet, reason_codes, reason_code_count);
}

return packet__queue(mosq, packet);
Expand Down
34 changes: 34 additions & 0 deletions test/broker/02-unsubscribe-qos2-multiple-v5.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#!/usr/bin/env python

# Test whether a v5 UNSUBSCRIBE to multiple topics with QoS 2 results in the correct UNSUBACK packet.

from mosq_test_helper import *

rc = 1
mid = 3
keepalive = 60
connect_packet = mosq_test.gen_connect("unsubscribe-qos2-test", keepalive=keepalive, proto_ver=5)
connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)

unsubscribe_packet = mosq_test.gen_unsubscribe_multiple(mid, ["qos2/one", "qos2/two"], proto_ver=5)
unsuback_packet = mosq_test.gen_unsuback(mid, proto_ver=5, reason_code=[0, 0])

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)
mosq_test.do_send_receive(sock, unsubscribe_packet, unsuback_packet, "unsuback")

rc = 0

sock.close()
finally:
broker.terminate()
broker.wait()
(stdo, stde) = broker.communicate()
if rc:
print(stde)

exit(rc)

1 change: 1 addition & 0 deletions test/broker/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ endif
./02-unsubscribe-qos2-v5.py
./02-unsubscribe-invalid-no-topic.py
./02-unsubscribe-qos2-multiple.py
./02-unsubscribe-qos2-multiple-v5.py
./02-subscribe-invalid-utf8.py
./02-subscribe-persistence-flipflop.py
./02-subhier-crash.py
Expand Down
1 change: 1 addition & 0 deletions test/broker/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
(1, './02-unsubscribe-qos2-v5.py'),
(1, './02-unsubscribe-invalid-no-topic.py'),
(1, './02-unsubscribe-qos2-multiple.py'),
(1, './02-unsubscribe-qos2-multiple-v5.py'),
(1, './02-subscribe-invalid-utf8.py'),
(1, './02-subscribe-persistence-flipflop.py'),
(1, './02-subhier-crash.py'),
Expand Down
11 changes: 9 additions & 2 deletions test/mosq_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -498,9 +498,16 @@ def gen_unsubscribe_multiple(mid, topics, proto_ver=4):

return struct.pack("!BBH", 162, remaining_length, mid) + packet

def gen_unsuback(mid, proto_ver=4):
def gen_unsuback(mid, proto_ver=4, reason_code=0):
if proto_ver == 5:
return struct.pack('!BBHB', 176, 3, mid, 0)
if isinstance(reason_code, list):
reason_code_count = len(reason_code)
p = struct.pack('!BBHB', 176, 3+reason_code_count, mid, 0)
for r in reason_code:
p += struct.pack('B', r)
return p
else:
return struct.pack('!BBHBB', 176, 4, mid, 0, reason_code)
else:
return struct.pack('!BBH', 176, 2, mid)

Expand Down

0 comments on commit 1ec0cea

Please sign in to comment.