diff --git a/lib/handle_pubackcomp.c b/lib/handle_pubackcomp.c index d03ea594..b9fc2c5d 100644 --- a/lib/handle_pubackcomp.c +++ b/lib/handle_pubackcomp.c @@ -52,12 +52,14 @@ int handle__pubackcomp(struct mosquitto *mosq, const char *type) if(rc) return rc; if(mid == 0) return MOSQ_ERR_PROTOCOL; - if(mosq->protocol == mosq_p_mqtt5){ + if(mosq->protocol == mosq_p_mqtt5 && mosq->in_packet.remaining_length > 2){ rc = packet__read_byte(&mosq->in_packet, &reason_code); if(rc) return rc; - rc = property__read_all(CMD_PUBACK, &mosq->in_packet, &properties); - if(rc) return rc; + if(mosq->in_packet.remaining_length > 3){ + rc = property__read_all(CMD_PUBACK, &mosq->in_packet, &properties); + if(rc) return rc; + } } #ifdef WITH_BROKER diff --git a/lib/handle_pubrec.c b/lib/handle_pubrec.c index 589b405a..feb41e92 100644 --- a/lib/handle_pubrec.c +++ b/lib/handle_pubrec.c @@ -47,14 +47,16 @@ int handle__pubrec(struct mosquitto_db *db, struct mosquitto *mosq) if(rc) return rc; if(mid == 0) return MOSQ_ERR_PROTOCOL; - if(mosq->protocol == mosq_p_mqtt5){ + if(mosq->protocol == mosq_p_mqtt5 && mosq->in_packet.remaining_length > 2){ rc = packet__read_byte(&mosq->in_packet, &reason_code); if(rc) return rc; - rc = property__read_all(CMD_PUBREC, &mosq->in_packet, &properties); - if(rc) return rc; - /* Immediately free, we don't do anything with Reason String or User Property at the moment */ - mosquitto_property_free_all(&properties); + if(mosq->in_packet.remaining_length > 3){ + rc = property__read_all(CMD_PUBREC, &mosq->in_packet, &properties); + if(rc) return rc; + /* Immediately free, we don't do anything with Reason String or User Property at the moment */ + mosquitto_property_free_all(&properties); + } } #ifdef WITH_BROKER @@ -71,7 +73,16 @@ int handle__pubrec(struct mosquitto_db *db, struct mosquitto *mosq) if(reason_code < 0x80){ rc = message__out_update(mosq, mid, mosq_ms_wait_for_pubcomp); }else{ - message__delete(mosq, mid, mosq_md_out); + if(!message__delete(mosq, mid, mosq_md_out)){ + /* Only inform the client the message has been sent once. */ + pthread_mutex_lock(&mosq->callback_mutex); + if(mosq->on_publish_v5){ + mosq->in_callback = true; + mosq->on_publish_v5(mosq, mosq->userdata, mid, reason_code, properties); + mosq->in_callback = false; + } + pthread_mutex_unlock(&mosq->callback_mutex); + } return MOSQ_ERR_SUCCESS; } #endif diff --git a/lib/handle_pubrel.c b/lib/handle_pubrel.c index 2ec6dc8b..e046c40b 100644 --- a/lib/handle_pubrel.c +++ b/lib/handle_pubrel.c @@ -56,12 +56,14 @@ int handle__pubrel(struct mosquitto_db *db, struct mosquitto *mosq) if(rc) return rc; if(mid == 0) return MOSQ_ERR_PROTOCOL; - if(mosq->protocol == mosq_p_mqtt5){ + if(mosq->protocol == mosq_p_mqtt5 && mosq->in_packet.remaining_length > 2){ rc = packet__read_byte(&mosq->in_packet, &reason_code); if(rc) return rc; - rc = property__read_all(CMD_PUBREL, &mosq->in_packet, &properties); - if(rc) return rc; + if(mosq->in_packet.remaining_length > 3){ + rc = property__read_all(CMD_PUBREL, &mosq->in_packet, &properties); + if(rc) return rc; + } } #ifdef WITH_BROKER @@ -75,9 +77,18 @@ int handle__pubrel(struct mosquitto_db *db, struct mosquitto *mosq) * due to a repeated PUBREL after a client has reconnected. */ log__printf(mosq, MOSQ_LOG_WARNING, "Warning: Received PUBREL from %s for an unknown packet identifier %d.", mosq->id, mid); } + + rc = send__pubcomp(mosq, mid); + if(rc) return rc; #else log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received PUBREL (Mid: %d)", mosq->id, mid); + rc = send__pubcomp(mosq, mid); + if(rc){ + message__remove(mosq, mid, mosq_md_in, &message); + return rc; + } + if(!message__remove(mosq, mid, mosq_md_in, &message)){ /* Only pass the message on if we have removed it from the queue - this * prevents multiple callbacks for the same message. */ @@ -97,8 +108,6 @@ int handle__pubrel(struct mosquitto_db *db, struct mosquitto *mosq) message__cleanup(&message); } #endif - rc = send__pubcomp(mosq, mid); - if(rc) return rc; return MOSQ_ERR_SUCCESS; } diff --git a/lib/mosquitto.h b/lib/mosquitto.h index 394d1a3a..8174eca0 100644 --- a/lib/mosquitto.h +++ b/lib/mosquitto.h @@ -1816,7 +1816,9 @@ libmosq_EXPORT void mosquitto_publish_callback_set(struct mosquitto *mosq, void * Function: mosquitto_publish_v5_callback_set * * Set the publish callback. This is called when a message initiated with - * has been sent to the broker successfully. + * has been sent to the broker. This callback will be + * called both if the message is sent successfully, or if the broker responded + * with an error, which will be reflected in the reason_code parameter. * * Parameters: * mosq - a valid mosquitto instance. diff --git a/lib/send_mosq.c b/lib/send_mosq.c index 5366fd1e..6f39b36d 100644 --- a/lib/send_mosq.c +++ b/lib/send_mosq.c @@ -135,10 +135,15 @@ int send__command_with_mid(struct mosquitto *mosq, uint8_t command, uint16_t mid packet->remaining_length = 2; if(mosq->protocol == mosq_p_mqtt5){ - proplen = property__get_length_all(properties); - varbytes = packet__varint_bytes(proplen); - /* 1 here is sizeof(reason_code) */ - packet->remaining_length += 1 + varbytes + proplen; + if(reason_code != 0 || properties){ + packet->remaining_length += 1; + } + + if(properties){ + proplen = property__get_length_all(properties); + varbytes = packet__varint_bytes(proplen); + packet->remaining_length += varbytes + proplen; + } } rc = packet__alloc(packet); @@ -150,8 +155,12 @@ int send__command_with_mid(struct mosquitto *mosq, uint8_t command, uint16_t mid packet__write_uint16(packet, mid); if(mosq->protocol == mosq_p_mqtt5){ - packet__write_byte(packet, reason_code); - property__write_all(packet, properties, true); + if(reason_code != 0 || properties){ + packet__write_byte(packet, reason_code); + } + if(properties){ + property__write_all(packet, properties, true); + } } return packet__queue(mosq, packet); diff --git a/test/broker/03-publish-b2c-qos1-len-helper.py b/test/broker/03-publish-b2c-qos1-len-helper.py new file mode 100755 index 00000000..3c491c4e --- /dev/null +++ b/test/broker/03-publish-b2c-qos1-len-helper.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python + +from mosq_test_helper import * + +port = mosq_test.get_port() + +rc = 1 +keepalive = 60 +connect_packet = mosq_test.gen_connect("test-helper", keepalive=keepalive) +connack_packet = mosq_test.gen_connack(rc=0) + +mid = 1 +publish_packet = mosq_test.gen_publish("qos1/len/test", qos=1, mid=mid, payload="len-message") +puback_packet = mosq_test.gen_puback(mid) + +sock = mosq_test.do_client_connect(connect_packet, connack_packet, connack_error="helper connack", port=port) + +mosq_test.do_send_receive(sock, publish_packet, puback_packet, "helper puback") + +rc = 0 + +sock.close() + +exit(rc) diff --git a/test/broker/03-publish-b2c-qos1-len.py b/test/broker/03-publish-b2c-qos1-len.py new file mode 100755 index 00000000..4eb82ae5 --- /dev/null +++ b/test/broker/03-publish-b2c-qos1-len.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python + +# Check whether the broker handles a v5 PUBACK with all combinations +# of with/without reason code and properties. + +from mosq_test_helper import * + +def len_test(test, puback_packet): + port = mosq_test.get_port() + + rc = 1 + mid = 3265 + keepalive = 60 + connect_packet = mosq_test.gen_connect("pub-qos1-test", keepalive=keepalive, clean_session=False, proto_ver=5) + connack_packet = mosq_test.gen_connack(resv=0, rc=0, proto_ver=5) + + subscribe_packet = mosq_test.gen_subscribe(mid, "qos1/len/test", 1, proto_ver=5) + suback_packet = mosq_test.gen_suback(mid, 1, proto_ver=5) + + mid = 1 + publish_packet = mosq_test.gen_publish("qos1/len/test", qos=1, mid=mid, payload="len-message", proto_ver=5) + + pingreq_packet = mosq_test.gen_pingreq() + pingresp_packet = mosq_test.gen_pingresp() + + 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, subscribe_packet, suback_packet, "suback") + + pub = subprocess.Popen(['./03-publish-b2c-qos1-len-helper.py', str(port)], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + pub.wait() + (stdo, stde) = pub.communicate() + # Should have now received a publish command + + if mosq_test.expect_packet(sock, "publish", publish_packet): + sock.send(puback_packet) + + mosq_test.do_send_receive(sock, pingreq_packet, pingresp_packet, "pingreq") + rc = 0 + + sock.close() + finally: + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde) + + if rc != 0: + print(test) + exit(rc) + + +# No reason code, no properties +puback_packet = mosq_test.gen_puback(1) +len_test("qos1 len 2", puback_packet) + +# Reason code, no properties +puback_packet = mosq_test.gen_puback(1, proto_ver=5, reason_code=0x00) +len_test("qos1 len 3", puback_packet) + +# Reason code, empty properties +puback_packet = mosq_test.gen_puback(1, proto_ver=5, reason_code=0x00, properties="") +len_test("qos1 len 4", puback_packet) + +# Reason code, one property +props = mqtt5_props.gen_string_pair_prop(mqtt5_props.PROP_USER_PROPERTY, "key", "value") +puback_packet = mosq_test.gen_puback(1, proto_ver=5, reason_code=0x00, properties=props) +len_test("qos1 len >5", puback_packet) diff --git a/test/broker/03-publish-b2c-qos2-len-helper.py b/test/broker/03-publish-b2c-qos2-len-helper.py new file mode 100755 index 00000000..e2a9c456 --- /dev/null +++ b/test/broker/03-publish-b2c-qos2-len-helper.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python + +from mosq_test_helper import * + +port = mosq_test.get_port() + +rc = 1 +keepalive = 60 +connect_packet = mosq_test.gen_connect("test-helper", keepalive=keepalive) +connack_packet = mosq_test.gen_connack(rc=0) + +mid = 1 +publish_packet = mosq_test.gen_publish("qos2/len/test", qos=2, mid=mid, payload="len-message") +pubrec_packet = mosq_test.gen_pubrec(mid) +pubrel_packet = mosq_test.gen_pubrel(mid) +pubcomp_packet = mosq_test.gen_pubcomp(mid) + +sock = mosq_test.do_client_connect(connect_packet, connack_packet, connack_error="helper connack", port=port) + +mosq_test.do_send_receive(sock, publish_packet, pubrec_packet, "helper pubrec") +mosq_test.do_send_receive(sock, pubrel_packet, pubcomp_packet, "helper pubcomp") + +rc = 0 + +sock.close() + +exit(rc) diff --git a/test/broker/03-publish-b2c-qos2-len.py b/test/broker/03-publish-b2c-qos2-len.py new file mode 100755 index 00000000..b3f0e765 --- /dev/null +++ b/test/broker/03-publish-b2c-qos2-len.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python + +# Check whether the broker handles a v5 PUBREC, PUBCOMP with all combinations +# of with/without reason code and properties. + +from mosq_test_helper import * + +def len_test(test, pubrec_packet, pubcomp_packet): + port = mosq_test.get_port() + + rc = 1 + mid = 3265 + keepalive = 60 + connect_packet = mosq_test.gen_connect("pub-test", keepalive=keepalive, clean_session=False, proto_ver=5) + connack_packet = mosq_test.gen_connack(resv=0, rc=0, proto_ver=5) + + subscribe_packet = mosq_test.gen_subscribe(mid, "qos2/len/test", 2, proto_ver=5) + suback_packet = mosq_test.gen_suback(mid, 2, proto_ver=5) + + mid = 1 + publish_packet = mosq_test.gen_publish("qos2/len/test", qos=2, mid=mid, payload="len-message", proto_ver=5) + pubrel_packet = mosq_test.gen_pubrel(mid) + + pingreq_packet = mosq_test.gen_pingreq() + pingresp_packet = mosq_test.gen_pingresp() + + 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, subscribe_packet, suback_packet, "suback") + + pub = subprocess.Popen(['./03-publish-b2c-qos2-len-helper.py', str(port)], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + pub.wait() + (stdo, stde) = pub.communicate() + # Should have now received a publish command + + if mosq_test.expect_packet(sock, "publish", publish_packet): + mosq_test.do_send_receive(sock, pubrec_packet, pubrel_packet, "pubrel") + sock.send(pubcomp_packet) + + mosq_test.do_send_receive(sock, pingreq_packet, pingresp_packet, "pingresp") + rc = 0 + + sock.close() + finally: + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde) + + if rc != 0: + print(test) + exit(rc) + + +# No reason code, no properties +pubrec_packet = mosq_test.gen_pubrec(1) +pubcomp_packet = mosq_test.gen_pubcomp(1) +len_test("qos2 len 2", pubrec_packet, pubcomp_packet) + +# Reason code, no properties +pubrec_packet = mosq_test.gen_pubrec(1, proto_ver=5, reason_code=0x00) +pubcomp_packet = mosq_test.gen_pubcomp(1, proto_ver=5, reason_code=0x00) +len_test("qos2 len 3", pubrec_packet, pubcomp_packet) + +# Reason code, empty properties +pubrec_packet = mosq_test.gen_pubrec(1, proto_ver=5, reason_code=0x00, properties="") +pubcomp_packet = mosq_test.gen_pubcomp(1, proto_ver=5, reason_code=0x00, properties="") +len_test("qos2 len 4", pubrec_packet, pubcomp_packet) + +# Reason code, one property +props = mqtt5_props.gen_string_pair_prop(mqtt5_props.PROP_USER_PROPERTY, "key", "value") +pubrec_packet = mosq_test.gen_pubrec(1, proto_ver=5, reason_code=0x00, properties=props) +props = mqtt5_props.gen_string_pair_prop(mqtt5_props.PROP_USER_PROPERTY, "key", "value") +pubcomp_packet = mosq_test.gen_pubcomp(1, proto_ver=5, reason_code=0x00, properties=props) +len_test("qos2 len >5", pubrec_packet, pubcomp_packet) diff --git a/test/broker/03-publish-c2b-qos2-len.py b/test/broker/03-publish-c2b-qos2-len.py new file mode 100755 index 00000000..753c571d --- /dev/null +++ b/test/broker/03-publish-c2b-qos2-len.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python + +# Check whether the broker handles a v5 PUBREL with all combinations +# of with/without reason code and properties. + +from mosq_test_helper import * + +def len_test(test, pubrel_packet): + port = mosq_test.get_port() + + rc = 1 + mid = 3265 + keepalive = 60 + connect_packet = mosq_test.gen_connect("pub-test", keepalive=keepalive, clean_session=False, proto_ver=5) + connack_packet = mosq_test.gen_connack(resv=0, rc=0, proto_ver=5) + + mid = 1 + publish_packet = mosq_test.gen_publish("qos2/len/test", qos=2, mid=mid, payload="len-message", proto_ver=5) + pubrec_packet = mosq_test.gen_pubrec(mid) + pubcomp_packet = mosq_test.gen_pubcomp(mid) + + pingreq_packet = mosq_test.gen_pingreq() + pingresp_packet = mosq_test.gen_pingresp() + + 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, publish_packet, pubrec_packet, "pubrec") + mosq_test.do_send_receive(sock, pubrel_packet, pubcomp_packet, "pubcomp") + + mosq_test.do_send_receive(sock, pingreq_packet, pingresp_packet, "pingresp") + rc = 0 + + sock.close() + finally: + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde) + + if rc != 0: + print(test) + exit(rc) + + +# No reason code, no properties +pubrel_packet = mosq_test.gen_pubrel(1) +len_test("qos2 len 2", pubrel_packet) + +# Reason code, no properties +pubrel_packet = mosq_test.gen_pubrel(1, proto_ver=5, reason_code=0x00) +len_test("qos2 len 3", pubrel_packet) + +# Reason code, empty properties +pubrel_packet = mosq_test.gen_pubrel(1, proto_ver=5, reason_code=0x00, properties="") +len_test("qos2 len 4", pubrel_packet) + +# Reason code, one property +props = mqtt5_props.gen_string_pair_prop(mqtt5_props.PROP_USER_PROPERTY, "key", "value") +pubrel_packet = mosq_test.gen_pubrel(1, proto_ver=5, reason_code=0x00, properties=props) +len_test("qos2 len >5", pubrel_packet) diff --git a/test/broker/Makefile b/test/broker/Makefile index 7583026f..6295d729 100644 --- a/test/broker/Makefile +++ b/test/broker/Makefile @@ -74,6 +74,9 @@ endif ./03-publish-b2c-disconnect-qos1.py ./03-publish-c2b-disconnect-qos2.py ./03-publish-b2c-disconnect-qos2.py + ./03-publish-b2c-qos1-len.py + ./03-publish-b2c-qos2-len.py + ./03-publish-c2b-qos2-len.py ./03-pattern-matching.py #./03-publish-qos1-queued-bytes.py ./03-publish-invalid-utf8.py diff --git a/test/broker/ptest.py b/test/broker/ptest.py index fbaf48f8..3f96794c 100755 --- a/test/broker/ptest.py +++ b/test/broker/ptest.py @@ -56,6 +56,9 @@ tests = [ (1, './03-publish-b2c-disconnect-qos1.py'), (1, './03-publish-c2b-disconnect-qos2.py'), (1, './03-publish-b2c-disconnect-qos2.py'), + (1, './03-publish-b2c-qos1-len.py'), + (1, './03-publish-b2c-qos2-len.py'), + (1, './03-publish-c2b-qos2-len.py'), (1, './03-pattern-matching.py'), #(1, './03-publish-qos1-queued-bytes.py'), (1, './03-publish-invalid-utf8.py'), diff --git a/test/lib/03-publish-b2c-qos2-len.py b/test/lib/03-publish-b2c-qos2-len.py new file mode 100755 index 00000000..775669cc --- /dev/null +++ b/test/lib/03-publish-b2c-qos2-len.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python + +# Check whether a v5 client handles a v5 PUBREL with all combinations +# of with/without reason code and properties. + +from mosq_test_helper import * + +mid = 56 + +def len_test(test, pubrel_packet): + port = mosq_test.get_lib_port() + + rc = 1 + keepalive = 60 + connect_packet = mosq_test.gen_connect("publish-qos2-test", keepalive=keepalive, proto_ver=5) + connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) + + disconnect_packet = mosq_test.gen_disconnect(proto_ver=5) + + publish_packet = mosq_test.gen_publish("len/qos2/test", qos=2, mid=mid, payload="message", proto_ver=5) + pubrec_packet = mosq_test.gen_pubrec(mid, proto_ver=5) + pubcomp_packet = mosq_test.gen_pubcomp(mid, proto_ver=5) + + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + sock.settimeout(10) + sock.bind(('', port)) + sock.listen(5) + + client_args = sys.argv[1:] + env = dict(os.environ) + env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' + try: + pp = env['PYTHONPATH'] + except KeyError: + pp = '' + env['PYTHONPATH'] = '../../lib/python:'+pp + + client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env, port=port) + + try: + (conn, address) = sock.accept() + conn.settimeout(15) + + if mosq_test.expect_packet(conn, "connect", connect_packet): + conn.send(connack_packet) + + mosq_test.do_send_receive(conn, publish_packet, pubrec_packet, "pubrec") + mosq_test.do_send_receive(conn, pubrel_packet, pubcomp_packet, "pubcomp") + if mosq_test.expect_packet(conn, "disconnect", disconnect_packet): + rc = 0 + + conn.close() + finally: + client.terminate() + client.wait() + sock.close() + + if rc != 0: + print(test) + exit(rc) + + +# No reason code, no properties +pubrel_packet = mosq_test.gen_pubrel(mid) +len_test("qos2 len 2", pubrel_packet) + +# Reason code, no properties +pubrel_packet = mosq_test.gen_pubrel(mid, proto_ver=5, reason_code=0x00) +len_test("qos2 len 3", pubrel_packet) + +# Reason code, empty properties +pubrel_packet = mosq_test.gen_pubrel(mid, proto_ver=5, reason_code=0x00, properties="") +len_test("qos2 len 4", pubrel_packet) + +# Reason code, one property +props = mqtt5_props.gen_string_pair_prop(mqtt5_props.PROP_USER_PROPERTY, "key", "value") +pubrel_packet = mosq_test.gen_pubrel(mid, proto_ver=5, reason_code=0x00, properties=props) +len_test("qos2 len >5", pubrel_packet) diff --git a/test/lib/03-publish-c2b-qos1-len.py b/test/lib/03-publish-c2b-qos1-len.py new file mode 100755 index 00000000..30e65337 --- /dev/null +++ b/test/lib/03-publish-c2b-qos1-len.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python + +# Check whether a v5 client handles a v5 PUBACK with all combinations +# of with/without reason code and properties. + +from mosq_test_helper import * + +def len_test(test, puback_packet): + port = mosq_test.get_lib_port() + + rc = 1 + keepalive = 60 + connect_packet = mosq_test.gen_connect("publish-qos1-test", keepalive=keepalive, proto_ver=5) + connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) + + disconnect_packet = mosq_test.gen_disconnect(proto_ver=5) + + mid = 1 + publish_packet = mosq_test.gen_publish("pub/qos1/test", qos=1, mid=mid, payload="message", proto_ver=5) + + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + sock.settimeout(10) + sock.bind(('', port)) + sock.listen(5) + + client_args = sys.argv[1:] + env = dict(os.environ) + env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' + try: + pp = env['PYTHONPATH'] + except KeyError: + pp = '' + env['PYTHONPATH'] = '../../lib/python:'+pp + + client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env, port=port) + + try: + (conn, address) = sock.accept() + conn.settimeout(15) + + if mosq_test.expect_packet(conn, "connect", connect_packet): + conn.send(connack_packet) + + if mosq_test.expect_packet(conn, "publish", publish_packet): + conn.send(puback_packet) + + if mosq_test.expect_packet(conn, "disconnect", disconnect_packet): + rc = 0 + + conn.close() + finally: + client.terminate() + client.wait() + sock.close() + + if rc != 0: + print(test) + exit(rc) + + +# No reason code, no properties +puback_packet = mosq_test.gen_puback(1) +len_test("qos1 len 2", puback_packet) + +# Reason code, no properties +puback_packet = mosq_test.gen_puback(1, proto_ver=5, reason_code=0x00) +len_test("qos1 len 3", puback_packet) + +# Reason code, empty properties +puback_packet = mosq_test.gen_puback(1, proto_ver=5, reason_code=0x00, properties="") +len_test("qos1 len 4", puback_packet) + +# Reason code, one property +props = mqtt5_props.gen_string_pair_prop(mqtt5_props.PROP_USER_PROPERTY, "key", "value") +puback_packet = mosq_test.gen_puback(1, proto_ver=5, reason_code=0x00, properties=props) +len_test("qos1 len >5", puback_packet) diff --git a/test/lib/03-publish-c2b-qos2-len.py b/test/lib/03-publish-c2b-qos2-len.py new file mode 100755 index 00000000..fc444167 --- /dev/null +++ b/test/lib/03-publish-c2b-qos2-len.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python + +# Check whether a v5 client handles a v5 PUBREC, PUBCOMP with all combinations +# of with/without reason code and properties. + +from mosq_test_helper import * + +def len_test(test, pubrec_packet, pubcomp_packet): + port = mosq_test.get_lib_port() + + rc = 1 + keepalive = 60 + connect_packet = mosq_test.gen_connect("publish-qos2-test", keepalive=keepalive, proto_ver=5) + connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) + + disconnect_packet = mosq_test.gen_disconnect(proto_ver=5) + + mid = 1 + publish_packet = mosq_test.gen_publish("pub/qos2/test", qos=2, mid=mid, payload="message", proto_ver=5) + pubrel_packet = mosq_test.gen_pubrel(mid, proto_ver=5) + + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + sock.settimeout(10) + sock.bind(('', port)) + sock.listen(5) + + client_args = sys.argv[1:] + env = dict(os.environ) + env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' + try: + pp = env['PYTHONPATH'] + except KeyError: + pp = '' + env['PYTHONPATH'] = '../../lib/python:'+pp + + client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env, port=port) + + try: + (conn, address) = sock.accept() + conn.settimeout(15) + + if mosq_test.expect_packet(conn, "connect", connect_packet): + conn.send(connack_packet) + + if mosq_test.expect_packet(conn, "publish", publish_packet): + conn.send(pubrec_packet) + + if mosq_test.expect_packet(conn, "pubrel", pubrel_packet): + conn.send(pubcomp_packet) + + if mosq_test.expect_packet(conn, "disconnect", disconnect_packet): + rc = 0 + + conn.close() + finally: + client.terminate() + client.wait() + sock.close() + + if rc != 0: + print(test) + exit(rc) + + +# No reason code, no properties +pubrec_packet = mosq_test.gen_pubrec(1) +pubcomp_packet = mosq_test.gen_pubcomp(1) +len_test("qos2 len 2", pubrec_packet, pubcomp_packet) + +# Reason code, no properties +pubrec_packet = mosq_test.gen_pubrec(1, proto_ver=5, reason_code=0x00) +pubcomp_packet = mosq_test.gen_pubcomp(1, proto_ver=5, reason_code=0x00) +len_test("qos2 len 3", pubrec_packet, pubcomp_packet) + +# Reason code, empty properties +pubrec_packet = mosq_test.gen_pubrec(1, proto_ver=5, reason_code=0x00, properties="") +pubcomp_packet = mosq_test.gen_pubcomp(1, proto_ver=5, reason_code=0x00, properties="") +len_test("qos2 len 4", pubrec_packet, pubcomp_packet) + +# Reason code, one property +props = mqtt5_props.gen_string_pair_prop(mqtt5_props.PROP_USER_PROPERTY, "key", "value") +pubrec_packet = mosq_test.gen_pubrec(1, proto_ver=5, reason_code=0x00, properties=props) +props = mqtt5_props.gen_string_pair_prop(mqtt5_props.PROP_USER_PROPERTY, "key", "value") +pubcomp_packet = mosq_test.gen_pubcomp(1, proto_ver=5, reason_code=0x00, properties=props) +len_test("qos2 len >5", pubrec_packet, pubcomp_packet) diff --git a/test/lib/Makefile b/test/lib/Makefile index 7fa08a0d..cb71a4e7 100644 --- a/test/lib/Makefile +++ b/test/lib/Makefile @@ -34,6 +34,9 @@ c : test-compile ./03-publish-qos0.py $@/03-publish-qos0.test ./03-publish-qos0-no-payload.py $@/03-publish-qos0-no-payload.test ./03-publish-c2b-qos1-disconnect.py $@/03-publish-c2b-qos1-disconnect.test + ./03-publish-c2b-qos1-len.py $@/03-publish-c2b-qos1-len.test + ./03-publish-c2b-qos2-len.py $@/03-publish-c2b-qos2-len.test + ./03-publish-b2c-qos2-len.py $@/03-publish-b2c-qos2-len.test ./03-publish-c2b-qos2.py $@/03-publish-c2b-qos2.test ./03-publish-c2b-qos2-disconnect.py $@/03-publish-c2b-qos2-disconnect.test ./03-publish-c2b-qos1-receive-maximum.py $@/03-publish-c2b-qos1-receive-maximum.test diff --git a/test/lib/c/03-publish-b2c-qos2-len.c b/test/lib/c/03-publish-b2c-qos2-len.c new file mode 100644 index 00000000..3975d914 --- /dev/null +++ b/test/lib/c/03-publish-b2c-qos2-len.c @@ -0,0 +1,74 @@ +#include +#include +#include +#include +#include + +static int run = -1; + +void on_connect(struct mosquitto *mosq, void *obj, int rc) +{ + if(rc){ + exit(1); + } +} + +void on_message(struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg) +{ + if(msg->mid != 56){ + printf("Invalid mid (%d)\n", msg->mid); + exit(1); + } + if(msg->qos != 2){ + printf("Invalid qos (%d)\n", msg->qos); + exit(1); + } + if(strcmp(msg->topic, "len/qos2/test")){ + printf("Invalid topic (%s)\n", msg->topic); + exit(1); + } + if(strcmp(msg->payload, "message")){ + printf("Invalid payload (%s)\n", (char *)msg->payload); + exit(1); + } + if(msg->payloadlen != 7){ + printf("Invalid payloadlen (%d)\n", msg->payloadlen); + exit(1); + } + if(msg->retain != false){ + printf("Invalid retain (%d)\n", msg->retain); + exit(1); + } + + mosquitto_disconnect(mosq); +} + +void on_disconnect(struct mosquitto *mosq, void *obj, int rc) +{ + run = 0; +} + +int main(int argc, char *argv[]) +{ + int rc; + struct mosquitto *mosq; + + int port = atoi(argv[1]); + + mosquitto_lib_init(); + + mosq = mosquitto_new("publish-qos2-test", true, &run); + mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5); + mosquitto_connect_callback_set(mosq, on_connect); + mosquitto_disconnect_callback_set(mosq, on_disconnect); + mosquitto_message_callback_set(mosq, on_message); + + rc = mosquitto_connect(mosq, "localhost", port, 60); + + while(run == -1){ + mosquitto_loop(mosq, 100, 1); + } + + mosquitto_lib_cleanup(); + return run; +} diff --git a/test/lib/c/03-publish-c2b-qos1-len.c b/test/lib/c/03-publish-c2b-qos1-len.c new file mode 100644 index 00000000..66fe76e6 --- /dev/null +++ b/test/lib/c/03-publish-c2b-qos1-len.c @@ -0,0 +1,52 @@ +#include +#include +#include +#include +#include + +static int run = -1; + +void on_connect(struct mosquitto *mosq, void *obj, int rc) +{ + if(rc){ + exit(1); + }else{ + mosquitto_publish(mosq, NULL, "pub/qos1/test", strlen("message"), "message", 1, false); + } +} + +void on_publish(struct mosquitto *mosq, void *obj, int mid) +{ + mosquitto_disconnect(mosq); +} + +void on_disconnect(struct mosquitto *mosq, void *obj, int rc) +{ + run = 0; +} + +int main(int argc, char *argv[]) +{ + int rc; + struct mosquitto *mosq; + + int port = atoi(argv[1]); + + mosquitto_lib_init(); + + mosq = mosquitto_new("publish-qos1-test", true, NULL); + mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5); + mosquitto_connect_callback_set(mosq, on_connect); + mosquitto_disconnect_callback_set(mosq, on_disconnect); + mosquitto_publish_callback_set(mosq, on_publish); + mosquitto_message_retry_set(mosq, 3); + + rc = mosquitto_connect(mosq, "localhost", port, 60); + + while(run == -1){ + mosquitto_loop(mosq, 300, 1); + } + + mosquitto_lib_cleanup(); + return run; +} diff --git a/test/lib/c/03-publish-c2b-qos2-len.c b/test/lib/c/03-publish-c2b-qos2-len.c new file mode 100644 index 00000000..ab59adb7 --- /dev/null +++ b/test/lib/c/03-publish-c2b-qos2-len.c @@ -0,0 +1,52 @@ +#include +#include +#include +#include +#include + +static int run = -1; + +void on_connect(struct mosquitto *mosq, void *obj, int rc) +{ + if(rc){ + exit(1); + }else{ + mosquitto_publish(mosq, NULL, "pub/qos2/test", strlen("message"), "message", 2, false); + } +} + +void on_publish(struct mosquitto *mosq, void *obj, int mid, int reason_code, const mosquitto_property *properties) +{ + mosquitto_disconnect(mosq); +} + +void on_disconnect(struct mosquitto *mosq, void *obj, int rc) +{ + run = 0; +} + +int main(int argc, char *argv[]) +{ + int rc; + struct mosquitto *mosq; + + int port = atoi(argv[1]); + + mosquitto_lib_init(); + + mosq = mosquitto_new("publish-qos2-test", true, NULL); + mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5); + mosquitto_connect_callback_set(mosq, on_connect); + mosquitto_disconnect_callback_set(mosq, on_disconnect); + mosquitto_publish_v5_callback_set(mosq, on_publish); + mosquitto_message_retry_set(mosq, 3); + + rc = mosquitto_connect(mosq, "localhost", port, 60); + + while(run == -1){ + mosquitto_loop(mosq, 300, 1); + } + + mosquitto_lib_cleanup(); + return run; +} diff --git a/test/lib/c/Makefile b/test/lib/c/Makefile index cf05d72b..2df4221c 100644 --- a/test/lib/c/Makefile +++ b/test/lib/c/Makefile @@ -17,8 +17,11 @@ SRC = \ 03-publish-qos0.c \ 03-publish-qos0-no-payload.c \ 03-publish-c2b-qos1-disconnect.c \ + 03-publish-c2b-qos1-len.c \ 03-publish-c2b-qos2.c \ 03-publish-c2b-qos2-disconnect.c \ + 03-publish-c2b-qos2-len.c \ + 03-publish-b2c-qos2-len.c \ 03-publish-c2b-qos1-receive-maximum.c \ 03-publish-c2b-qos2-receive-maximum-1.c \ 03-publish-c2b-qos2-receive-maximum-2.c \ diff --git a/test/lib/ptest.py b/test/lib/ptest.py index 78ebcd0e..b94e6b40 100755 --- a/test/lib/ptest.py +++ b/test/lib/ptest.py @@ -18,6 +18,9 @@ tests = [ ('./02-unsubscribe.py', 'c/02-unsubscribe.test'), ('./03-publish-b2c-qos1.py', 'c/03-publish-b2c-qos1.test'), ('./03-publish-b2c-qos2.py', 'c/03-publish-b2c-qos2.test'), + ('./03-publish-c2b-qos1-len.py', 'c/03-publish-c2b-qos1-len.test'), + ('./03-publish-c2b-qos2-len.py', 'c/03-publish-c2b-qos2-len.test'), + ('./03-publish-b2c-qos2-len.py', 'c/03-publish-b2c-qos2-len.test'), ('./03-publish-c2b-qos1-disconnect.py', 'c/03-publish-c2b-qos1-disconnect.test'), ('./03-publish-c2b-qos2-disconnect.py', 'c/03-publish-c2b-qos2-disconnect.test'), ('./03-publish-c2b-qos2.py', 'c/03-publish-c2b-qos2.test'), diff --git a/test/mosq_test.py b/test/mosq_test.py index 21c702bd..a09367cc 100644 --- a/test/mosq_test.py +++ b/test/mosq_test.py @@ -415,27 +415,37 @@ def gen_publish(topic, qos, payload=None, retain=False, dup=False, mid=0, proto_ else: return struct.pack("!B" + str(len(rlpacked))+"s" + pack_format, cmd, rlpacked, len(topic), topic, payload) -def _gen_command_with_mid(cmd, mid, proto_ver=4, reason_code=0): - if proto_ver == 5: - return struct.pack('!BBHBB', cmd, 4, mid, reason_code, 0) +def _gen_command_with_mid(cmd, mid, proto_ver=4, reason_code=-1, properties=None): + if proto_ver == 5 and (reason_code != -1 or properties is not None): + if reason_code == -1: + reason_code = 0 + + if properties is None: + return struct.pack('!BBHB', cmd, 3, mid, reason_code) + elif properties == "": + return struct.pack('!BBHBB', cmd, 4, mid, reason_code, 0) + else: + properties = mqtt5_props.prop_finalise(properties) + pack_format = "!BBHB"+str(len(properties))+"s" + return struct.pack(pack_format, cmd, 2+1+len(properties), mid, reason_code, properties) else: return struct.pack('!BBH', cmd, 2, mid) -def gen_puback(mid, proto_ver=4, reason_code=0): - return _gen_command_with_mid(64, mid, proto_ver, reason_code) +def gen_puback(mid, proto_ver=4, reason_code=-1, properties=None): + return _gen_command_with_mid(64, mid, proto_ver, reason_code, properties) -def gen_pubrec(mid, proto_ver=4, reason_code=0): - return _gen_command_with_mid(80, mid, proto_ver, reason_code) +def gen_pubrec(mid, proto_ver=4, reason_code=-1, properties=None): + return _gen_command_with_mid(80, mid, proto_ver, reason_code, properties) -def gen_pubrel(mid, dup=False, proto_ver=4, reason_code=0): +def gen_pubrel(mid, dup=False, proto_ver=4, reason_code=-1, properties=None): if dup: cmd = 96+8+2 else: cmd = 96+2 - return _gen_command_with_mid(cmd, mid, proto_ver, reason_code) + return _gen_command_with_mid(cmd, mid, proto_ver, reason_code, properties) -def gen_pubcomp(mid, proto_ver=4, reason_code=0): - return _gen_command_with_mid(112, mid, proto_ver, reason_code) +def gen_pubcomp(mid, proto_ver=4, reason_code=-1, properties=None): + return _gen_command_with_mid(112, mid, proto_ver, reason_code, properties) def gen_subscribe(mid, topic, qos, proto_ver=4, properties=""): if proto_ver == 5: