diff --git a/.gitignore b/.gitignore index 1ae86973..d640d5b8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,17 @@ -*.o -*.so -*.exe *.db +*.gcda +*.gcno +*.exe +*.o +*.old +*.pyc +*.so +*.vglog +massif.out.* +vglog* + c/*.test cpp/*.test -*.pyc -*.vglog build/ @@ -50,5 +56,9 @@ test/ssl/*.csr test/lib/c/*.test test/lib/cpp/*.test +test/unit/coverage.info +test/unit/mosq_test +test/unit/out + www/cache/ __pycache__ diff --git a/CMakeLists.txt b/CMakeLists.txt index 161696da..ea6e0733 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ project(mosquitto) cmake_minimum_required(VERSION 2.8) # Only for version 3 and up. cmake_policy(SET CMP0042 NEW) -set (VERSION 1.5.4) +set (VERSION 1.5.90) add_definitions (-DCMAKE -DVERSION=\"${VERSION}\") @@ -51,7 +51,7 @@ option(WITH_TLS_PSK "Include TLS-PSK support (requires WITH_TLS)?" ON) option(WITH_EC "Include Elliptic Curve support (requires WITH_TLS)?" ON) -if (${WITH_TLS} STREQUAL ON) +if (WITH_TLS) find_package(OpenSSL REQUIRED) add_definitions("-DWITH_TLS") @@ -62,19 +62,19 @@ if (${WITH_TLS} STREQUAL ON) if (${WITH_EC} STREQUAL ON) add_definitions("-DWITH_EC") endif (${WITH_EC} STREQUAL ON) -else (${WITH_TLS} STREQUAL ON) +else (WITH_TLS) set (OPENSSL_INCLUDE_DIR "") -endif (${WITH_TLS} STREQUAL ON) +endif (WITH_TLS) option(WITH_SOCKS "Include SOCKS5 support?" ON) -if (${WITH_SOCKS} STREQUAL ON) +if (WITH_SOCKS) add_definitions("-DWITH_SOCKS") -endif (${WITH_SOCKS} STREQUAL ON) +endif (WITH_SOCKS) option(WITH_SRV "Include SRV lookup support?" OFF) option(WITH_THREADING "Include client library threading support?" ON) -if (${WITH_THREADING} STREQUAL ON) +if (WITH_THREADING) add_definitions("-DWITH_THREADING") if (WIN32) if (CMAKE_CL_64) @@ -92,10 +92,10 @@ if (${WITH_THREADING} STREQUAL ON) endif() set (PTHREAD_INCLUDE_DIR "") endif (WIN32) -else (${WITH_THREADING} STREQUAL ON) +else (WITH_THREADING) set (PTHREAD_LIBRARIES "") set (PTHREAD_INCLUDE_DIR "") -endif (${WITH_THREADING} STREQUAL ON) +endif (WITH_THREADING) option(DOCUMENTATION "Build documentation?" ON) @@ -106,9 +106,9 @@ option(DOCUMENTATION "Build documentation?" ON) add_subdirectory(lib) add_subdirectory(client) add_subdirectory(src) -if (${DOCUMENTATION} STREQUAL ON) +if (DOCUMENTATION) add_subdirectory(man) -endif (${DOCUMENTATION} STREQUAL ON) +endif (DOCUMENTATION) # ======================================== # Install config file diff --git a/ChangeLog.txt b/ChangeLog.txt index 9857808f..14b45d97 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -1,6 +1,13 @@ 1.6 - 2018xxxx ============== +Broker features: +- Improved general support for broker generated client ids. Removed libuuid + dependency. +- auto_id_prefix now defaults to 'auto-'. +- Add max_keepalive option, to allow a maximum keepalive value to be set for + MQTT v5 clients only. + Client library features: - Add mosquitto_subscribe_multiple() for sending subscriptions to multiple topics in one command. @@ -9,6 +16,120 @@ Client features: - Add -E to mosquitto_sub, which causes it to exit immediately after having its subscriptions acknowledged. Use with -c to create a durable client session without requiring a message to be received. +- -V now accepts `5, `311`, `31`, as well as `mqttv5` etc. + +Client fixes: +- mosquitto_pub wouldn't always publish all messages when using `-l` and + QoS>0. This has been fixed. + + +1.5.7 - 201902xx +================ + +Broker: +- Fix build failure when using WITH_ADNS=yes +- Ensure that an error occurs if `per_listener_settings true` is given after + other security options. Closes #1149. +- Fix include_dir not sorting config files before loading. This was partially + fixed in 1.5 previously. + +Library: +- Fix `mosquitto_topic_matches_sub()` not returning MOSQ_ERR_INVAL for + invalid subscriptions like `topic/#abc`. This only affects the return value, + not the match/no match result, which was already correct. + +Build: +- Don't require C99 compiler. + + +1.5.6 - 20190206 +================ + +Security: +- CVE-2018-12551: If Mosquitto is configured to use a password file for + authentication, any malformed data in the password file will be treated as + valid. This typically means that the malformed data becomes a username and no + password. If this occurs, clients can circumvent authentication and get access + to the broker by using the malformed username. In particular, a blank line + will be treated as a valid empty username. Other security measures are + unaffected. Users who have only used the mosquitto_passwd utility to create + and modify their password files are unaffected by this vulnerability. + Affects version 1.0 to 1.5.5 inclusive. +- CVE-2018-12550: If an ACL file is empty, or has only blank lines or + comments, then mosquitto treats the ACL file as not being defined, which + means that no topic access is denied. Although denying access to all topics + is not a useful configuration, this behaviour is unexpected and could lead + to access being incorrectly granted in some circumstances. This is now + fixed. Affects versions 1.0 to 1.5.5 inclusive. +- CVE-2018-12546. If a client publishes a retained message to a topic that + they have access to, and then their access to that topic is revoked, the + retained message will still be delivered to future subscribers. This + behaviour may be undesirable in some applications, so a configuration option + `check_retain_source` has been introduced to enforce checking of the + retained message source on publish. + +Broker: +- Fixed comment handling for config options that have optional arguments. +- Improved documentation around bridge topic remapping. +- Handle mismatched handshakes (e.g. QoS1 PUBLISH with QoS2 reply) properly. +- Fix spaces not being allowed in the bridge remote_username option. Closes + #1131. +- Allow broker to always restart on Windows when using `log_dest file`. Closes + #1080. +- Fix Will not being sent for Websockets clients. Closes #1143. +- Windows: Fix possible crash when client disconnects. Closes #1137. +- Fixed durable clients being unable to receive messages when offline, when + per_listener_settings was set to true. Closes #1081. +- Add log message for the case where a client is disconnected for sending a + topic with invalid UTF-8. Closes #1144. + +Library: +- Fix TLS connections not working over SOCKS. +- Don't clear SSL context when TLS connection is closed, meaning if a user + provided an external SSL_CTX they have less chance of leaking references. + +Build: +- Fix comparison of boolean values in CMake build. Closes #1101. +- Fix compilation when openssl deprecated APIs are not available. + Closes #1094. +- Man pages can now be built on any system. Closes #1139. + + +1.5.5 - 20181211 +================ + +Security: +- If `per_listener_settings` is set to true, then the `acl_file` setting was + ignored for the "default listener" only. This has been fixed. This does not + affect any listeners defined with the `listener` option. Closes #1073. + This is now tracked as CVE-2018-20145. + +Broker: +- Add `socket_domain` option to allow listeners to disable IPv6 support. + This is required to work around a problem in libwebsockets that means + sockets only listen on IPv6 by default if IPv6 support is compiled in. + Closes #1004. +- When using ADNS, don't ask for all network protocols when connecting, + because this can lead to confusing "Protocol not supported" errors if the + network is down. Closes #1062. +- Fix outgoing retained messages not being sent by bridges on initial + connection. Closes #1040. +- Don't reload auth_opt_ options on reload, to match the behaviour of the + other plugin options. Closes #1068. +- Print message on error when installing/uninstalling as a Windows service. +- All non-error connect/disconnect messages are controlled by the + `connection_messages` option. Closes #772. Closes #613. Closes #537. + +Library: +- Fix reconnect delay backoff behaviour. Closes #1027. +- Don't call on_disconnect() twice if keepalive tests fail. Closes #1067. + +Client: +- Always print leading zeros in mosquitto_sub when output format is hex. + Closes #1066. + +Build: +- Fix building where TLS-PSK is not available. Closes #68. 1.5.4 - 20181108 diff --git a/Makefile b/Makefile index c2c2bcf8..9a9f5562 100644 --- a/Makefile +++ b/Makefile @@ -81,3 +81,6 @@ copy : sign cd dist; scp *.html mosquitto:site/mosquitto.org/man/ scp ChangeLog.txt mosquitto:site/mosquitto.org/ +coverage : + lcov --capture --directory . --output-file coverage.info + genhtml coverage.info --output-directory out diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 660bf1ac..c36b06e8 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -1,14 +1,15 @@ include_directories(${mosquitto_SOURCE_DIR} ${mosquitto_SOURCE_DIR}/lib - ${STDBOOL_H_PATH} ${STDINT_H_PATH} ${PTHREAD_INCLUDE_DIR}) + ${STDBOOL_H_PATH} ${STDINT_H_PATH} ${PTHREAD_INCLUDE_DIR} + ${OPENSSL_INCLUDE_DIR}) link_directories(${mosquitto_BINARY_DIR}/lib) -set(shared_src client_shared.c client_shared.h) +set(shared_src client_shared.c client_shared.h client_props.c) -if (${WITH_SRV} STREQUAL ON) +if (WITH_SRV) add_definitions("-DWITH_SRV") -endif (${WITH_SRV} STREQUAL ON) +endif (WITH_SRV) -add_executable(mosquitto_pub pub_client.c ${shared_src}) +add_executable(mosquitto_pub pub_client.c pub_shared.c ${shared_src}) add_executable(mosquitto_sub sub_client.c sub_client_output.c ${shared_src}) diff --git a/client/Makefile b/client/Makefile index 824d703c..d19dd44a 100644 --- a/client/Makefile +++ b/client/Makefile @@ -20,21 +20,24 @@ static : static_pub static_sub # This makes mosquitto_pub/sub versions that are statically linked with # libmosquitto only. -static_pub : pub_client.o client_shared.o ../lib/libmosquitto.a +static_pub : pub_client.o pub_shared.o client_props.o client_shared.o ../lib/libmosquitto.a ${CROSS_COMPILE}${CC} $^ -o mosquitto_pub ${CLIENT_LDFLAGS} -lssl -lcrypto -lpthread -static_sub : sub_client.o sub_client_output.o client_shared.o ../lib/libmosquitto.a +static_sub : sub_client.o sub_client_output.o client_props.o client_shared.o ../lib/libmosquitto.a ${CROSS_COMPILE}${CC} $^ -o mosquitto_sub ${CLIENT_LDFLAGS} -lssl -lcrypto -lpthread -mosquitto_pub : pub_client.o client_shared.o +mosquitto_pub : pub_client.o pub_shared.o client_shared.o client_props.o ${CROSS_COMPILE}${CC} $^ -o $@ ${CLIENT_LDFLAGS} -mosquitto_sub : sub_client.o sub_client_output.o client_shared.o +mosquitto_sub : sub_client.o sub_client_output.o client_shared.o client_props.o ${CROSS_COMPILE}${CC} $^ -o $@ ${CLIENT_LDFLAGS} pub_client.o : pub_client.c ${SHARED_DEP} ${CROSS_COMPILE}${CC} -c $< -o $@ ${CLIENT_CFLAGS} +pub_shared.o : pub_shared.c ${SHARED_DEP} + ${CROSS_COMPILE}${CC} -c $< -o $@ ${CLIENT_CFLAGS} + sub_client.o : sub_client.c ${SHARED_DEP} ${CROSS_COMPILE}${CC} -c $< -o $@ ${CLIENT_CFLAGS} @@ -44,6 +47,9 @@ sub_client_output.o : sub_client_output.c ${SHARED_DEP} client_shared.o : client_shared.c client_shared.h ${CROSS_COMPILE}${CC} -c $< -o $@ ${CLIENT_CFLAGS} +client_props.o : client_props.c client_shared.h + ${CROSS_COMPILE}${CC} -c $< -o $@ ${CLIENT_CFLAGS} + ../lib/libmosquitto.so.${SOVERSION} : $(MAKE) -C ../lib @@ -62,4 +68,4 @@ uninstall : reallyclean : clean clean : - -rm -f *.o mosquitto_pub mosquitto_sub + -rm -f *.o mosquitto_pub mosquitto_sub *.gcda *.gcno diff --git a/client/client_props.c b/client/client_props.c new file mode 100644 index 00000000..7415bc06 --- /dev/null +++ b/client/client_props.c @@ -0,0 +1,193 @@ +/* +Copyright (c) 2018 Roger Light + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#include +#include +#include +#include +#include +#ifndef WIN32 +#include +#include +#else +#include +#include +#define snprintf sprintf_s +#define strncasecmp _strnicmp +#endif + +#include "mosquitto.h" +#include "mqtt_protocol.h" +#include "client_shared.h" + +enum prop_type +{ + PROP_TYPE_BYTE, + PROP_TYPE_INT16, + PROP_TYPE_INT32, + PROP_TYPE_BINARY, + PROP_TYPE_STRING, + PROP_TYPE_STRING_PAIR +}; + +/* This parses property inputs. It should work for any command type, but is limited at the moment. + * + * Format: + * + * command property value + * command property key value + * + * Example: + * + * publish message-expiry-interval 32 + * connect user-property key value + */ + +int cfg_parse_property(struct mosq_config *cfg, int argc, char *argv[], int *idx) +{ + char *cmdname = NULL, *propname = NULL; + char *key = NULL, *value = NULL; + int cmd, identifier, type; + mosquitto_property **proplist; + int rc; + + /* idx now points to "command" */ + if((*idx)+2 > argc-1){ + /* Not enough args */ + fprintf(stderr, "Error: --property argument given but not enough arguments specified.\n\n"); + return MOSQ_ERR_INVAL; + } + + cmdname = argv[*idx]; + if(mosquitto_string_to_command(cmdname, &cmd)){ + fprintf(stderr, "Error: Invalid command given in --property argument.\n\n"); + return MOSQ_ERR_INVAL; + } + + propname = argv[(*idx)+1]; + if(mosquitto_string_to_property_info(propname, &identifier, &type)){ + fprintf(stderr, "Error: Invalid property name given in --property argument.\n\n"); + return MOSQ_ERR_INVAL; + } + + if(mosquitto_property_check_command(cmd, identifier)){ + fprintf(stderr, "Error: %s property not allow for %s in --property argument.\n\n", propname, cmdname); + return MOSQ_ERR_INVAL; + } + + if(identifier == MQTT_PROP_USER_PROPERTY){ + if((*idx)+3 > argc-1){ + /* Not enough args */ + fprintf(stderr, "Error: --property argument given but not enough arguments specified.\n\n"); + return MOSQ_ERR_INVAL; + } + + key = argv[(*idx)+2]; + value = argv[(*idx)+3]; + (*idx) += 3; + }else{ + value = argv[(*idx)+2]; + (*idx) += 2; + } + + + switch(cmd){ + case CMD_CONNECT: + proplist = &cfg->connect_props; + break; + + case CMD_PUBLISH: + if(identifier == MQTT_PROP_TOPIC_ALIAS){ + cfg->have_topic_alias = true; + } + if(identifier == MQTT_PROP_SUBSCRIPTION_IDENTIFIER){ + fprintf(stderr, "Error: %s property not supported for %s in --property argument.\n\n", propname, cmdname); + return MOSQ_ERR_INVAL; + } + proplist = &cfg->publish_props; + break; + + case CMD_SUBSCRIBE: + if(identifier != MQTT_PROP_SUBSCRIPTION_IDENTIFIER && identifier != MQTT_PROP_USER_PROPERTY){ + fprintf(stderr, "Error: %s property not supported for %s in --property argument.\n\n", propname, cmdname); + return MOSQ_ERR_NOT_SUPPORTED; + } + proplist = &cfg->subscribe_props; + break; + + case CMD_UNSUBSCRIBE: + proplist = &cfg->unsubscribe_props; + break; + + case CMD_DISCONNECT: + proplist = &cfg->disconnect_props; + break; + + case CMD_AUTH: + fprintf(stderr, "Error: %s property not supported for %s in --property argument.\n\n", propname, cmdname); + return MOSQ_ERR_NOT_SUPPORTED; + + case CMD_WILL: + proplist = &cfg->will_props; + break; + + case CMD_PUBACK: + case CMD_PUBREC: + case CMD_PUBREL: + case CMD_PUBCOMP: + case CMD_SUBACK: + case CMD_UNSUBACK: + fprintf(stderr, "Error: %s property not supported for %s in --property argument.\n\n", propname, cmdname); + return MOSQ_ERR_NOT_SUPPORTED; + + default: + return MOSQ_ERR_INVAL; + } + + switch(type){ + case MQTT_PROP_TYPE_BYTE: + rc = mosquitto_property_add_byte(proplist, identifier, atoi(value)); + break; + case MQTT_PROP_TYPE_INT16: + rc = mosquitto_property_add_int16(proplist, identifier, atoi(value)); + break; + case MQTT_PROP_TYPE_INT32: + rc = mosquitto_property_add_int32(proplist, identifier, atoi(value)); + break; + case MQTT_PROP_TYPE_VARINT: + rc = mosquitto_property_add_varint(proplist, identifier, atoi(value)); + break; + case MQTT_PROP_TYPE_BINARY: + rc = mosquitto_property_add_binary(proplist, identifier, value, strlen(value)); + break; + case MQTT_PROP_TYPE_STRING: + rc = mosquitto_property_add_string(proplist, identifier, value); + break; + case MQTT_PROP_TYPE_STRING_PAIR: + rc = mosquitto_property_add_string_pair(proplist, identifier, key, value); + break; + default: + return MOSQ_ERR_INVAL; + } + if(rc){ + fprintf(stderr, "Error adding property %s %d\n", propname, type); + return rc; + } + return MOSQ_ERR_SUCCESS; +} + diff --git a/client/client_shared.c b/client/client_shared.c index f74e06c0..53ac1f9a 100644 --- a/client/client_shared.c +++ b/client/client_shared.c @@ -32,9 +32,12 @@ Contributors: #endif #include +#include #include "client_shared.h" +#ifdef WITH_SOCKS static int mosquitto__parse_socks_url(struct mosq_config *cfg, char *url); +#endif static int client_config_line_proc(struct mosq_config *cfg, int pub_or_sub, int argc, char *argv[]); @@ -154,7 +157,7 @@ void client_config_cleanup(struct mosq_config *cfg) free(cfg->tls_engine); free(cfg->tls_engine_kpass_sha1); free(cfg->keyform); -# ifdef WITH_TLS_PSK +# ifdef FINAL_WITH_TLS_PSK free(cfg->psk); free(cfg->psk_identity); # endif @@ -182,6 +185,12 @@ void client_config_cleanup(struct mosq_config *cfg) free(cfg->socks5_username); free(cfg->socks5_password); #endif + mosquitto_property_free_all(&cfg->connect_props); + mosquitto_property_free_all(&cfg->publish_props); + mosquitto_property_free_all(&cfg->subscribe_props); + mosquitto_property_free_all(&cfg->unsubscribe_props); + mosquitto_property_free_all(&cfg->disconnect_props); + mosquitto_property_free_all(&cfg->will_props); } int client_config_load(struct mosq_config *cfg, int pub_or_sub, int argc, char *argv[]) @@ -320,7 +329,7 @@ int client_config_load(struct mosq_config *cfg, int pub_or_sub, int argc, char * return 1; } #endif -#ifdef WITH_TLS_PSK +#ifdef FINAL_WITH_TLS_PSK if((cfg->cafile || cfg->capath) && cfg->psk){ if(!cfg->quiet) fprintf(stderr, "Error: Only one of --psk or --cafile/--capath may be used at once.\n"); return 1; @@ -344,8 +353,44 @@ int client_config_load(struct mosq_config *cfg, int pub_or_sub, int argc, char * } if(!cfg->host){ - cfg->host = "localhost"; + cfg->host = strdup("localhost"); + if(!cfg->host){ + if(!cfg->quiet) fprintf(stderr, "Error: Out of memory.\n"); + return 1; + } } + + rc = mosquitto_property_check_all(CMD_CONNECT, cfg->connect_props); + if(rc){ + if(!cfg->quiet) fprintf(stderr, "Error in CONNECT properties: %s\n", mosquitto_strerror(rc)); + return 1; + } + rc = mosquitto_property_check_all(CMD_PUBLISH, cfg->publish_props); + if(rc){ + if(!cfg->quiet) fprintf(stderr, "Error in PUBLISH properties: %s\n", mosquitto_strerror(rc)); + return 1; + } + rc = mosquitto_property_check_all(CMD_SUBSCRIBE, cfg->subscribe_props); + if(rc){ + if(!cfg->quiet) fprintf(stderr, "Error in SUBSCRIBE properties: %s\n", mosquitto_strerror(rc)); + return 1; + } + rc = mosquitto_property_check_all(CMD_UNSUBSCRIBE, cfg->unsubscribe_props); + if(rc){ + if(!cfg->quiet) fprintf(stderr, "Error in UNSUBSCRIBE properties: %s\n", mosquitto_strerror(rc)); + return 1; + } + rc = mosquitto_property_check_all(CMD_DISCONNECT, cfg->disconnect_props); + if(rc){ + if(!cfg->quiet) fprintf(stderr, "Error in DISCONNECT properties: %s\n", mosquitto_strerror(rc)); + return 1; + } + rc = mosquitto_property_check_all(CMD_WILL, cfg->will_props); + if(rc){ + if(!cfg->quiet) fprintf(stderr, "Error in Will properties: %s\n", mosquitto_strerror(rc)); + return 1; + } + return MOSQ_ERR_SUCCESS; } @@ -487,6 +532,12 @@ int client_config_line_proc(struct mosq_config *cfg, int pub_or_sub, int argc, c } }else if(!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")){ cfg->debug = true; + }else if(!strcmp(argv[i], "-D") || !strcmp(argv[i], "--property")){ + i++; + if(cfg_parse_property(cfg, argc, argv, &i)){ + return 1; + } + cfg->protocol_version = MQTT_PROTOCOL_V5; }else if(!strcmp(argv[i], "-f") || !strcmp(argv[i], "--file")){ if(pub_or_sub == CLIENT_SUB){ goto unknown_option; @@ -686,15 +737,22 @@ int client_config_line_proc(struct mosq_config *cfg, int pub_or_sub, int argc, c }else{ cfg->pub_mode = MSGMODE_NULL; } + }else if(!strcmp(argv[i], "--retain-as-published")){ + if(pub_or_sub == CLIENT_PUB){ + goto unknown_option; + } + cfg->sub_opts |= MQTT_SUB_OPT_RETAIN_AS_PUBLISHED; }else if(!strcmp(argv[i], "-V") || !strcmp(argv[i], "--protocol-version")){ if(i==argc-1){ fprintf(stderr, "Error: --protocol-version argument given but no version specified.\n\n"); return 1; }else{ - if(!strcmp(argv[i+1], "mqttv31")){ + if(!strcmp(argv[i+1], "mqttv31") || !strcmp(argv[i+1], "31")){ cfg->protocol_version = MQTT_PROTOCOL_V31; - }else if(!strcmp(argv[i+1], "mqttv311")){ + }else if(!strcmp(argv[i+1], "mqttv311") || !strcmp(argv[i+1], "311")){ cfg->protocol_version = MQTT_PROTOCOL_V311; + }else if(!strcmp(argv[i+1], "mqttv5") || !strcmp(argv[i+1], "5")){ + cfg->protocol_version = MQTT_PROTOCOL_V5; }else{ fprintf(stderr, "Error: Invalid protocol version argument given.\n\n"); return 1; @@ -713,7 +771,7 @@ int client_config_line_proc(struct mosq_config *cfg, int pub_or_sub, int argc, c i++; } #endif -#ifdef WITH_TLS_PSK +#ifdef FINAL_WITH_TLS_PSK }else if(!strcmp(argv[i], "--psk")){ if(i==argc-1){ fprintf(stderr, "Error: --psk argument given but no key specified.\n\n"); @@ -924,16 +982,22 @@ unknown_option: int client_opts_set(struct mosquitto *mosq, struct mosq_config *cfg) { +#ifdef WITH_SOCKS int rc; +#endif - if(cfg->will_topic && mosquitto_will_set(mosq, cfg->will_topic, + mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, cfg->protocol_version); + + if(cfg->will_topic && mosquitto_will_set_v5(mosq, cfg->will_topic, cfg->will_payloadlen, cfg->will_payload, cfg->will_qos, - cfg->will_retain)){ + cfg->will_retain, cfg->will_props)){ if(!cfg->quiet) fprintf(stderr, "Error: Problem setting will.\n"); mosquitto_lib_cleanup(); return 1; } + cfg->will_props = NULL; + if(cfg->username && mosquitto_username_pw_set(mosq, cfg->username, cfg->password)){ if(!cfg->quiet) fprintf(stderr, "Error: Problem setting username and password.\n"); mosquitto_lib_cleanup(); @@ -967,7 +1031,7 @@ int client_opts_set(struct mosquitto *mosq, struct mosq_config *cfg) mosquitto_lib_cleanup(); return 1; } -# ifdef WITH_TLS_PSK +# ifdef FINAL_WITH_TLS_PSK if(cfg->psk && mosquitto_tls_psk_set(mosq, cfg->psk, cfg->psk_identity, NULL)){ if(!cfg->quiet) fprintf(stderr, "Error: Problem setting TLS-PSK options.\n"); mosquitto_lib_cleanup(); @@ -990,7 +1054,6 @@ int client_opts_set(struct mosquitto *mosq, struct mosq_config *cfg) } } #endif - mosquitto_opts_set(mosq, MOSQ_OPT_PROTOCOL_VERSION, &(cfg->protocol_version)); return MOSQ_ERR_SUCCESS; } @@ -1007,7 +1070,7 @@ int client_id_generate(struct mosq_config *cfg, const char *id_base) return 1; } snprintf(cfg->id, strlen(cfg->id_prefix)+10, "%s%d", cfg->id_prefix, getpid()); - }else if(!cfg->id){ + }else if(!cfg->id && (cfg->protocol_version == MQTT_PROTOCOL_V31 || cfg->protocol_version == MQTT_PROTOCOL_V311)){ hostname[0] = '\0'; gethostname(hostname, 256); hostname[255] = '\0'; @@ -1040,7 +1103,7 @@ int client_connect(struct mosquitto *mosq, struct mosq_config *cfg) if(cfg->port < 0){ #ifdef WITH_TLS if(cfg->cafile || cfg->capath -# ifdef WITH_TLS_PSK +# ifdef FINAL_WITH_TLS_PSK || cfg->psk # endif ){ @@ -1058,10 +1121,10 @@ int client_connect(struct mosquitto *mosq, struct mosq_config *cfg) if(cfg->use_srv){ rc = mosquitto_connect_srv(mosq, cfg->host, cfg->keepalive, cfg->bind_address); }else{ - rc = mosquitto_connect_bind(mosq, cfg->host, port, cfg->keepalive, cfg->bind_address); + rc = mosquitto_connect_bind_v5(mosq, cfg->host, port, cfg->keepalive, cfg->bind_address, cfg->connect_props); } #else - rc = mosquitto_connect_bind(mosq, cfg->host, port, cfg->keepalive, cfg->bind_address); + rc = mosquitto_connect_bind_v5(mosq, cfg->host, port, cfg->keepalive, cfg->bind_address, cfg->connect_props); #endif if(rc>0){ if(!cfg->quiet){ diff --git a/client/client_shared.h b/client/client_shared.h index 254458eb..f44097cd 100644 --- a/client/client_shared.h +++ b/client/client_shared.h @@ -69,7 +69,7 @@ struct mosq_config { char *tls_engine; char *tls_engine_kpass_sha1; char *keyform; -# ifdef WITH_TLS_PSK +# ifdef FINAL_WITH_TLS_PSK char *psk; char *psk_identity; # endif @@ -89,12 +89,20 @@ struct mosq_config { int msg_count; /* sub */ char *format; /* sub */ int timeout; /* sub */ + int sub_opts; /* sub */ #ifdef WITH_SOCKS char *socks5_host; int socks5_port; char *socks5_username; char *socks5_password; #endif + mosquitto_property *connect_props; + mosquitto_property *publish_props; + mosquitto_property *subscribe_props; + mosquitto_property *unsubscribe_props; + mosquitto_property *disconnect_props; + mosquitto_property *will_props; + bool have_topic_alias; /* pub */ }; int client_config_load(struct mosq_config *config, int pub_or_sub, int argc, char *argv[]); @@ -103,4 +111,6 @@ int client_opts_set(struct mosquitto *mosq, struct mosq_config *cfg); int client_id_generate(struct mosq_config *cfg, const char *id_base); int client_connect(struct mosquitto *mosq, struct mosq_config *cfg); +int cfg_parse_property(struct mosq_config *cfg, int argc, char *argv[], int *idx); + #endif diff --git a/client/pub_client.c b/client/pub_client.c index 4957adb8..404f7885 100644 --- a/client/pub_client.c +++ b/client/pub_client.c @@ -31,50 +31,43 @@ Contributors: #include #include "client_shared.h" - -#define STATUS_CONNECTING 0 -#define STATUS_CONNACK_RECVD 1 -#define STATUS_WAITING 2 -#define STATUS_DISCONNECTING 3 +#include "pub_shared.h" /* Global variables for use in callbacks. See sub_client.c for an example of * using a struct to hold variables for use in callbacks. */ -static char *topic = NULL; -static char *message = NULL; -static long msglen = 0; -static int qos = 0; -static int retain = 0; -static int mode = MSGMODE_NONE; -static int status = STATUS_CONNECTING; -static int mid_sent = 0; -static int last_mid = -1; -static int last_mid_sent = -1; -static bool connected = true; -static char *username = NULL; -static char *password = NULL; -static bool disconnect_sent = false; -static bool quiet = false; +static bool first_publish = true; -void my_connect_callback(struct mosquitto *mosq, void *obj, int result) +int my_publish(struct mosquitto *mosq, int *mid, const char *topic, int payloadlen, void *payload, int qos, bool retain) +{ + if(cfg.protocol_version == MQTT_PROTOCOL_V5 && cfg.have_topic_alias && first_publish == false){ + return mosquitto_publish_v5(mosq, mid, NULL, payloadlen, payload, qos, retain, cfg.publish_props); + }else{ + first_publish = false; + return mosquitto_publish_v5(mosq, mid, topic, payloadlen, payload, qos, retain, cfg.publish_props); + } +} + + +void my_connect_callback(struct mosquitto *mosq, void *obj, int result, int flags, const mosquitto_property *properties) { int rc = MOSQ_ERR_SUCCESS; if(!result){ - switch(mode){ + switch(cfg.pub_mode){ case MSGMODE_CMD: case MSGMODE_FILE: case MSGMODE_STDIN_FILE: - rc = mosquitto_publish(mosq, &mid_sent, topic, msglen, message, qos, retain); + rc = my_publish(mosq, &mid_sent, cfg.topic, cfg.msglen, cfg.message, cfg.qos, cfg.retain); break; case MSGMODE_NULL: - rc = mosquitto_publish(mosq, &mid_sent, topic, 0, NULL, qos, retain); + rc = my_publish(mosq, &mid_sent, cfg.topic, 0, NULL, cfg.qos, cfg.retain); break; case MSGMODE_STDIN_LINE: status = STATUS_CONNACK_RECVD; break; } if(rc){ - if(!quiet){ + if(!cfg.quiet){ switch(rc){ case MOSQ_ERR_INVAL: fprintf(stderr, "Error: Invalid input. Does your topic contain '+' or '#'?\n"); @@ -91,114 +84,24 @@ void my_connect_callback(struct mosquitto *mosq, void *obj, int result) case MOSQ_ERR_PAYLOAD_SIZE: fprintf(stderr, "Error: Message payload is too large.\n"); break; + case MOSQ_ERR_QOS_NOT_SUPPORTED: + fprintf(stderr, "Error: Message QoS not supported on broker, try a lower QoS.\n"); + break; } } - mosquitto_disconnect(mosq); + mosquitto_disconnect_v5(mosq, 0, cfg.disconnect_props); } }else{ - if(result && !quiet){ - fprintf(stderr, "%s\n", mosquitto_connack_string(result)); + if(result && !cfg.quiet){ + if(cfg.protocol_version == MQTT_PROTOCOL_V5){ + fprintf(stderr, "%s\n", mosquitto_reason_string(result)); + }else{ + fprintf(stderr, "%s\n", mosquitto_connack_string(result)); + } } } } -void my_disconnect_callback(struct mosquitto *mosq, void *obj, int rc) -{ - connected = false; -} - -void my_publish_callback(struct mosquitto *mosq, void *obj, int mid) -{ - last_mid_sent = mid; - if(mode == MSGMODE_STDIN_LINE){ - if(mid == last_mid){ - mosquitto_disconnect(mosq); - disconnect_sent = true; - } - }else if(disconnect_sent == false){ - mosquitto_disconnect(mosq); - disconnect_sent = true; - } -} - -void my_log_callback(struct mosquitto *mosq, void *obj, int level, const char *str) -{ - printf("%s\n", str); -} - -int load_stdin(void) -{ - long pos = 0, rlen; - char buf[1024]; - char *aux_message = NULL; - - mode = MSGMODE_STDIN_FILE; - - while(!feof(stdin)){ - rlen = fread(buf, 1, 1024, stdin); - aux_message = realloc(message, pos+rlen); - if(!aux_message){ - if(!quiet) fprintf(stderr, "Error: Out of memory.\n"); - free(message); - return 1; - } else - { - message = aux_message; - } - memcpy(&(message[pos]), buf, rlen); - pos += rlen; - } - msglen = pos; - - if(!msglen){ - if(!quiet) fprintf(stderr, "Error: Zero length input.\n"); - return 1; - } - - return 0; -} - -int load_file(const char *filename) -{ - long pos, rlen; - FILE *fptr = NULL; - - fptr = fopen(filename, "rb"); - if(!fptr){ - if(!quiet) fprintf(stderr, "Error: Unable to open file \"%s\".\n", filename); - return 1; - } - mode = MSGMODE_FILE; - fseek(fptr, 0, SEEK_END); - msglen = ftell(fptr); - if(msglen > 268435455){ - fclose(fptr); - if(!quiet) fprintf(stderr, "Error: File \"%s\" is too large (>268,435,455 bytes).\n", filename); - return 1; - }else if(msglen == 0){ - fclose(fptr); - if(!quiet) fprintf(stderr, "Error: File \"%s\" is empty.\n", filename); - return 1; - }else if(msglen < 0){ - fclose(fptr); - if(!quiet) fprintf(stderr, "Error: Unable to determine size of file \"%s\".\n", filename); - return 1; - } - fseek(fptr, 0, SEEK_SET); - message = malloc(msglen); - if(!message){ - fclose(fptr); - if(!quiet) fprintf(stderr, "Error: Out of memory.\n"); - return 1; - } - pos = 0; - while(pos < msglen){ - rlen = fread(&(message[pos]), sizeof(char), msglen-pos, fptr); - pos += rlen; - } - fclose(fptr); - return 0; -} void print_usage(void) { @@ -224,17 +127,20 @@ void print_usage(void) printf(" [{--cafile file | --capath dir} [--cert file] [--key file]\n"); printf(" [--ciphers ciphers] [--insecure]\n"); printf(" [--tls-engine engine] [--keyform keyform] [--tls-engine-kpass-sha1]]\n"); -#ifdef WITH_TLS_PSK +#ifdef FINAL_WITH_TLS_PSK printf(" [--psk hex-key --psk-identity identity [--ciphers ciphers]]\n"); #endif #endif #ifdef WITH_SOCKS printf(" [--proxy socks-url]\n"); #endif + printf(" [--property command identifier value]\n"); + printf(" [-D command identifier value]\n"); printf(" mosquitto_pub --help\n\n"); printf(" -A : bind the outgoing socket to this host/ip address. Use to control which interface\n"); printf(" the client communicates over.\n"); printf(" -d : enable debug messages.\n"); + printf(" -D : Define MQTT v5 properties. See the documentation for more details.\n"); printf(" -f : send the contents of a file as the message.\n"); printf(" -h : mqtt host to connect to. Defaults to localhost.\n"); printf(" -i : id to use for this client. Defaults to mosquitto_pub_ appended with the process id.\n"); @@ -258,7 +164,7 @@ void print_usage(void) printf(" -t : mqtt topic to publish to.\n"); printf(" -u : provide a username\n"); printf(" -V : specify the version of the MQTT protocol to use when connecting.\n"); - printf(" Can be mqttv31 or mqttv311. Defaults to mqttv311.\n"); + printf(" Can be mqttv5, mqttv311 or mqttv31. Defaults to mqttv311.\n"); printf(" --help : display this message.\n"); printf(" --quiet : don't print error messages.\n"); printf(" --will-payload : payload for the client Will, which is sent by the broker in case of\n"); @@ -282,9 +188,9 @@ void print_usage(void) printf(" hostname. Using this option means that you cannot be sure that the\n"); printf(" remote host is the server you wish to connect to and so is insecure.\n"); printf(" Do not use this option in a production environment.\n"); - printf(" --tls-engine : If set, enables the use of a SSL engine device.\n"); + printf(" --tls-engine : If set, enables the use of a TLS engine device.\n"); printf(" --tls-engine-kpass-sha1 : SHA1 of the key password to be used with the selected SSL engine.\n"); -# ifdef WITH_TLS_PSK +# ifdef FINAL_WITH_TLS_PSK printf(" --psk : pre-shared-key in hexadecimal (no leading 0x) to enable TLS-PSK mode.\n"); printf(" --psk-identity : client identity string for TLS-PSK mode.\n"); # endif @@ -294,191 +200,106 @@ void print_usage(void) printf(" socks5h://[username[:password]@]hostname[:port]\n"); printf(" Only \"none\" and \"username\" authentication is supported.\n"); #endif - printf("\nSee http://mosquitto.org/ for more information.\n\n"); + printf("\nSee https://mosquitto.org/ for more information.\n\n"); } int main(int argc, char *argv[]) { - struct mosq_config cfg; struct mosquitto *mosq = NULL; int rc; - int rc2; - char *buf, *buf2; - int buf_len = 1024; - int buf_len_actual; - int read_len; - int pos; - buf = malloc(buf_len); - if(!buf){ - fprintf(stderr, "Error: Out of memory.\n"); - return 1; - } + mosquitto_lib_init(); + + if(pub_shared_init()) return 1; memset(&cfg, 0, sizeof(struct mosq_config)); rc = client_config_load(&cfg, CLIENT_PUB, argc, argv); if(rc){ - client_config_cleanup(&cfg); if(rc == 2){ /* --help */ print_usage(); }else{ fprintf(stderr, "\nUse 'mosquitto_pub --help' to see usage.\n"); } - free(buf); - return 1; + goto cleanup; } - topic = cfg.topic; - message = cfg.message; - msglen = cfg.msglen; - qos = cfg.qos; - retain = cfg.retain; - mode = cfg.pub_mode; - username = cfg.username; - password = cfg.password; - quiet = cfg.quiet; - #ifndef WITH_THREADING if(cfg.pub_mode == MSGMODE_STDIN_LINE){ fprintf(stderr, "Error: '-l' mode not available, threading support has not been compiled in.\n"); - free(buf); - return 1; + goto cleanup; } #endif if(cfg.pub_mode == MSGMODE_STDIN_FILE){ if(load_stdin()){ fprintf(stderr, "Error loading input from stdin.\n"); - free(buf); - return 1; + goto cleanup; } }else if(cfg.file_input){ if(load_file(cfg.file_input)){ fprintf(stderr, "Error loading input file \"%s\".\n", cfg.file_input); - free(buf); - return 1; + goto cleanup; } } - if(!topic || mode == MSGMODE_NONE){ + if(!cfg.topic || cfg.pub_mode == MSGMODE_NONE){ fprintf(stderr, "Error: Both topic and message must be supplied.\n"); print_usage(); - free(buf); - return 1; + goto cleanup; } - mosquitto_lib_init(); - if(client_id_generate(&cfg, "mosqpub")){ - free(buf); - return 1; + goto cleanup; } mosq = mosquitto_new(cfg.id, true, NULL); if(!mosq){ switch(errno){ case ENOMEM: - if(!quiet) fprintf(stderr, "Error: Out of memory.\n"); + if(!cfg.quiet) fprintf(stderr, "Error: Out of memory.\n"); break; case EINVAL: - if(!quiet) fprintf(stderr, "Error: Invalid id.\n"); + if(!cfg.quiet) fprintf(stderr, "Error: Invalid id.\n"); break; } - mosquitto_lib_cleanup(); - free(buf); - return 1; + goto cleanup; } if(cfg.debug){ mosquitto_log_callback_set(mosq, my_log_callback); } - mosquitto_connect_callback_set(mosq, my_connect_callback); - mosquitto_disconnect_callback_set(mosq, my_disconnect_callback); - mosquitto_publish_callback_set(mosq, my_publish_callback); + mosquitto_connect_v5_callback_set(mosq, my_connect_callback); + mosquitto_disconnect_v5_callback_set(mosq, my_disconnect_callback); + mosquitto_publish_v5_callback_set(mosq, my_publish_callback); if(client_opts_set(mosq, &cfg)){ - free(buf); - return 1; + goto cleanup; } + rc = client_connect(mosq, &cfg); - if(rc) return rc; - - if(mode == MSGMODE_STDIN_LINE){ - mosquitto_loop_start(mosq); + if(rc){ + goto cleanup; } - do{ - if(mode == MSGMODE_STDIN_LINE){ - if(status == STATUS_CONNACK_RECVD){ - pos = 0; - read_len = buf_len; - while(fgets(&buf[pos], read_len, stdin)){ - buf_len_actual = strlen(buf); - if(buf[buf_len_actual-1] == '\n'){ - buf[buf_len_actual-1] = '\0'; - rc2 = mosquitto_publish(mosq, &mid_sent, topic, buf_len_actual-1, buf, qos, retain); - if(rc2){ - if(!quiet) fprintf(stderr, "Error: Publish returned %d, disconnecting.\n", rc2); - mosquitto_disconnect(mosq); - } - break; - }else{ - buf_len += 1024; - pos += 1023; - read_len = 1024; - buf2 = realloc(buf, buf_len); - if(!buf2){ - free(buf); - fprintf(stderr, "Error: Out of memory.\n"); - return 1; - } - buf = buf2; - } - } - if(feof(stdin)){ - if(last_mid == -1){ - /* Empty file */ - mosquitto_disconnect(mosq); - disconnect_sent = true; - status = STATUS_DISCONNECTING; - }else{ - last_mid = mid_sent; - status = STATUS_WAITING; - } - } - }else if(status == STATUS_WAITING){ - if(last_mid_sent == last_mid && disconnect_sent == false){ - mosquitto_disconnect(mosq); - disconnect_sent = true; - } -#ifdef WIN32 - Sleep(100); -#else - struct timespec ts; - ts.tv_sec = 0; - ts.tv_nsec = 100000000; - nanosleep(&ts, NULL); -#endif - } - rc = MOSQ_ERR_SUCCESS; - }else{ - rc = mosquitto_loop(mosq, -1, 1); - } - }while(rc == MOSQ_ERR_SUCCESS && connected); + rc = pub_shared_loop(mosq); - if(mode == MSGMODE_STDIN_LINE){ - mosquitto_loop_stop(mosq, false); - } - - if(message && mode == MSGMODE_FILE){ - free(message); + if(cfg.message && cfg.pub_mode == MSGMODE_FILE){ + free(cfg.message); } mosquitto_destroy(mosq); mosquitto_lib_cleanup(); + client_config_cleanup(&cfg); + pub_shared_cleanup(); if(rc){ fprintf(stderr, "Error: %s\n", mosquitto_strerror(rc)); } return rc; + +cleanup: + mosquitto_lib_cleanup(); + client_config_cleanup(&cfg); + pub_shared_cleanup(); + return 1; } diff --git a/client/pub_shared.c b/client/pub_shared.c new file mode 100644 index 00000000..7f0f22ad --- /dev/null +++ b/client/pub_shared.c @@ -0,0 +1,243 @@ +/* +Copyright (c) 2009-2018 Roger Light + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#include +#include +#include +#include +#include +#ifndef WIN32 +#include +#else +#include +#include +#define snprintf sprintf_s +#endif + +#include +#include +#include "client_shared.h" +#include "pub_shared.h" + +/* Global variables for use in callbacks. See sub_client.c for an example of + * using a struct to hold variables for use in callbacks. */ +int mid_sent = 0; +int status = STATUS_CONNECTING; +struct mosq_config cfg; + +static int last_mid = -1; +static int last_mid_sent = -1; +static bool connected = true; +static bool disconnect_sent = false; +static char *buf = NULL; +static int buf_len = 1024; + +void my_disconnect_callback(struct mosquitto *mosq, void *obj, int rc, const mosquitto_property *properties) +{ + connected = false; +} + +void my_publish_callback(struct mosquitto *mosq, void *obj, int mid, int reason_code, const mosquitto_property *properties) +{ + last_mid_sent = mid; + if(cfg.pub_mode == MSGMODE_STDIN_LINE){ + if(mid == last_mid){ + mosquitto_disconnect_v5(mosq, 0, cfg.disconnect_props); + disconnect_sent = true; + } + }else if(disconnect_sent == false){ + mosquitto_disconnect_v5(mosq, 0, cfg.disconnect_props); + disconnect_sent = true; + } +} + +void my_log_callback(struct mosquitto *mosq, void *obj, int level, const char *str) +{ + printf("%s\n", str); +} + +int load_stdin(void) +{ + long pos = 0, rlen; + char buf[1024]; + char *aux_message = NULL; + + cfg.pub_mode = MSGMODE_STDIN_FILE; + + while(!feof(stdin)){ + rlen = fread(buf, 1, 1024, stdin); + aux_message = realloc(cfg.message, pos+rlen); + if(!aux_message){ + if(!cfg.quiet) fprintf(stderr, "Error: Out of memory.\n"); + free(cfg.message); + return 1; + } else + { + cfg.message = aux_message; + } + memcpy(&(cfg.message[pos]), buf, rlen); + pos += rlen; + } + cfg.msglen = pos; + + if(!cfg.msglen){ + if(!cfg.quiet) fprintf(stderr, "Error: Zero length input.\n"); + return 1; + } + + return 0; +} + +int load_file(const char *filename) +{ + long pos, rlen; + FILE *fptr = NULL; + + fptr = fopen(filename, "rb"); + if(!fptr){ + if(!cfg.quiet) fprintf(stderr, "Error: Unable to open file \"%s\".\n", filename); + return 1; + } + cfg.pub_mode = MSGMODE_FILE; + fseek(fptr, 0, SEEK_END); + cfg.msglen = ftell(fptr); + if(cfg.msglen > 268435455){ + fclose(fptr); + if(!cfg.quiet) fprintf(stderr, "Error: File \"%s\" is too large (>268,435,455 bytes).\n", filename); + return 1; + }else if(cfg.msglen == 0){ + fclose(fptr); + if(!cfg.quiet) fprintf(stderr, "Error: File \"%s\" is empty.\n", filename); + return 1; + }else if(cfg.msglen < 0){ + fclose(fptr); + if(!cfg.quiet) fprintf(stderr, "Error: Unable to determine size of file \"%s\".\n", filename); + return 1; + } + fseek(fptr, 0, SEEK_SET); + cfg.message = malloc(cfg.msglen); + if(!cfg.message){ + fclose(fptr); + if(!cfg.quiet) fprintf(stderr, "Error: Out of memory.\n"); + return 1; + } + pos = 0; + while(pos < cfg.msglen){ + rlen = fread(&(cfg.message[pos]), sizeof(char), cfg.msglen-pos, fptr); + pos += rlen; + } + fclose(fptr); + return 0; +} + + +int pub_shared_init(void) +{ + buf = malloc(buf_len); + if(!buf){ + fprintf(stderr, "Error: Out of memory.\n"); + return 1; + } + return 0; +} + + +int pub_shared_loop(struct mosquitto *mosq) +{ + int read_len; + int pos; + int rc, rc2; + char *buf2; + int buf_len_actual; + int mode; + + mode = cfg.pub_mode; + + if(mode == MSGMODE_STDIN_LINE){ + mosquitto_loop_start(mosq); + } + + do{ + if(mode == MSGMODE_STDIN_LINE){ + if(status == STATUS_CONNACK_RECVD){ + pos = 0; + read_len = buf_len; + while(connected && fgets(&buf[pos], read_len, stdin)){ + buf_len_actual = strlen(buf); + if(buf[buf_len_actual-1] == '\n'){ + buf[buf_len_actual-1] = '\0'; + rc2 = my_publish(mosq, &mid_sent, cfg.topic, buf_len_actual-1, buf, cfg.qos, cfg.retain); + if(rc2){ + if(!cfg.quiet) fprintf(stderr, "Error: Publish returned %d, disconnecting.\n", rc2); + mosquitto_disconnect_v5(mosq, MQTT_RC_DISCONNECT_WITH_WILL_MSG, cfg.disconnect_props); + } + break; + }else{ + buf_len += 1024; + pos += 1023; + read_len = 1024; + buf2 = realloc(buf, buf_len); + if(!buf2){ + fprintf(stderr, "Error: Out of memory.\n"); + return MOSQ_ERR_NOMEM; + } + buf = buf2; + } + } + if(feof(stdin)){ + if(mid_sent == -1){ + /* Empty file */ + mosquitto_disconnect_v5(mosq, 0, cfg.disconnect_props); + disconnect_sent = true; + status = STATUS_DISCONNECTING; + }else{ + last_mid = mid_sent; + status = STATUS_WAITING; + } + } + }else if(status == STATUS_WAITING){ + if(last_mid_sent == last_mid && disconnect_sent == false){ + mosquitto_disconnect_v5(mosq, 0, cfg.disconnect_props); + disconnect_sent = true; + } +#ifdef WIN32 + Sleep(100); +#else + struct timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = 100000000; + nanosleep(&ts, NULL); +#endif + } + rc = MOSQ_ERR_SUCCESS; + }else{ + rc = mosquitto_loop(mosq, -1, 1); + } + }while(rc == MOSQ_ERR_SUCCESS && connected); + + if(mode == MSGMODE_STDIN_LINE){ + mosquitto_loop_stop(mosq, false); + } + return 0; +} + + +void pub_shared_cleanup(void) +{ + free(buf); +} diff --git a/client/pub_shared.h b/client/pub_shared.h new file mode 100644 index 00000000..fd7331f7 --- /dev/null +++ b/client/pub_shared.h @@ -0,0 +1,42 @@ +/* +Copyright (c) 2009-2018 Roger Light + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ +#ifndef PUB_SHARED_H +#define PUB_SHARED_H + +#define STATUS_CONNECTING 0 +#define STATUS_CONNACK_RECVD 1 +#define STATUS_WAITING 2 +#define STATUS_DISCONNECTING 3 + +extern int mid_sent; +extern int status; +extern struct mosq_config cfg; + + +void my_connect_callback(struct mosquitto *mosq, void *obj, int result, int flags, const mosquitto_property *properties); +void my_disconnect_callback(struct mosquitto *mosq, void *obj, int rc, const mosquitto_property *properties); +void my_publish_callback(struct mosquitto *mosq, void *obj, int mid, int reason_code, const mosquitto_property *properties); +void my_log_callback(struct mosquitto *mosq, void *obj, int level, const char *str); +int load_stdin(void); +int load_file(const char *filename); + +int my_publish(struct mosquitto *mosq, int *mid, const char *topic, int payloadlen, void *payload, int qos, bool retain); + +int pub_shared_init(void); +int pub_shared_loop(struct mosquitto *mosq); +void pub_shared_cleanup(void); + +#endif diff --git a/client/pub_test_properties b/client/pub_test_properties new file mode 100755 index 00000000..8e400589 --- /dev/null +++ b/client/pub_test_properties @@ -0,0 +1,26 @@ +LD_LIBRARY_PATH=../lib ./mosquitto_pub \ + \ + -t asdf -V mqttv5 -m '{"key":"value"}' \ + \ + -D connect authentication-data password \ + -D connect authentication-method something \ + -D connect maximum-packet-size 0191 \ + -D connect receive-maximum 1000 \ + -D connect request-problem-information 1 \ + -D connect request-response-information 1 \ + -D connect session-expiry-interval 39 \ + -D connect topic-alias-maximum 123 \ + -D connect user-property connect up \ + \ + -D publish content-type application/json \ + -D publish correlation-data some-data \ + -D publish message-expiry-interval 59 \ + -D publish payload-format-indicator 1 \ + -D publish response-topic /dev/null \ + -D publish topic-alias 4 \ + -D publish user-property publish up \ + \ + -D disconnect reason-string "reason" \ + -D disconnect session-expiry-interval 40 \ + -D disconnect user-property disconnect up + diff --git a/client/sub_client.c b/client/sub_client.c index a12a886b..4e99b10c 100644 --- a/client/sub_client.c +++ b/client/sub_client.c @@ -32,8 +32,10 @@ Contributors: #endif #include +#include #include "client_shared.h" +static struct mosq_config cfg; bool process_messages = true; int msg_count = 0; struct mosquitto *mosq = NULL; @@ -43,7 +45,7 @@ void my_signal_handler(int signum) { if(signum == SIGALRM){ process_messages = false; - mosquitto_disconnect(mosq); + mosquitto_disconnect_v5(mosq, MQTT_RC_DISCONNECT_WITH_WILL_MSG, cfg.disconnect_props); } } #endif @@ -51,80 +53,72 @@ void my_signal_handler(int signum) void print_message(struct mosq_config *cfg, const struct mosquitto_message *message); -void my_message_callback(struct mosquitto *mosq, void *obj, const struct mosquitto_message *message) +void my_message_callback(struct mosquitto *mosq, void *obj, const struct mosquitto_message *message, const mosquitto_property *properties) { - struct mosq_config *cfg; int i; bool res; if(process_messages == false) return; - assert(obj); - cfg = (struct mosq_config *)obj; - - if(cfg->retained_only && !message->retain && process_messages){ + if(cfg.retained_only && !message->retain && process_messages){ process_messages = false; - mosquitto_disconnect(mosq); + mosquitto_disconnect_v5(mosq, 0, cfg.disconnect_props); return; } - if(message->retain && cfg->no_retain) return; - if(cfg->filter_outs){ - for(i=0; ifilter_out_count; i++){ - mosquitto_topic_matches_sub(cfg->filter_outs[i], message->topic, &res); + if(message->retain && cfg.no_retain) return; + if(cfg.filter_outs){ + for(i=0; itopic, &res); if(res) return; } } - print_message(cfg, message); + print_message(&cfg, message); - if(cfg->msg_count>0){ + if(cfg.msg_count>0){ msg_count++; - if(cfg->msg_count == msg_count){ + if(cfg.msg_count == msg_count){ process_messages = false; - mosquitto_disconnect(mosq); + mosquitto_disconnect_v5(mosq, 0, cfg.disconnect_props); } } } -void my_connect_callback(struct mosquitto *mosq, void *obj, int result, int flags) +void my_connect_callback(struct mosquitto *mosq, void *obj, int result, int flags, const mosquitto_property *properties) { int i; - struct mosq_config *cfg; - - assert(obj); - cfg = (struct mosq_config *)obj; if(!result){ - mosquitto_subscribe_multiple(mosq, NULL, cfg->topic_count, cfg->topics, cfg->qos); + mosquitto_subscribe_multiple(mosq, NULL, cfg.topic_count, cfg.topics, cfg.qos, cfg.sub_opts, cfg.subscribe_props); - for(i=0; iunsub_topic_count; i++){ - mosquitto_unsubscribe(mosq, NULL, cfg->unsub_topics[i]); + for(i=0; iquiet){ - fprintf(stderr, "%s\n", mosquitto_connack_string(result)); + if(result && !cfg.quiet){ + if(cfg.protocol_version == MQTT_PROTOCOL_V5){ + fprintf(stderr, "%s\n", mosquitto_reason_string(result)); + }else{ + fprintf(stderr, "%s\n", mosquitto_connack_string(result)); + } } - mosquitto_disconnect(mosq); + mosquitto_disconnect_v5(mosq, 0, cfg.disconnect_props); } } void my_subscribe_callback(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos) { int i; - struct mosq_config *cfg; - assert(obj); - cfg = (struct mosq_config *)obj; - - if(!cfg->quiet) printf("Subscribed (mid: %d): %d", mid, granted_qos[0]); + if(!cfg.quiet) printf("Subscribed (mid: %d): %d", mid, granted_qos[0]); for(i=1; iquiet) printf(", %d", granted_qos[i]); + if(!cfg.quiet) printf(", %d", granted_qos[i]); } - if(!cfg->quiet) printf("\n"); + if(!cfg.quiet) printf("\n"); - if(cfg->exit_after_sub){ - mosquitto_disconnect(mosq); + if(cfg.exit_after_sub){ + mosquitto_disconnect_v5(mosq, 0, cfg.disconnect_props); } } @@ -159,19 +153,21 @@ void print_usage(void) printf(" [{--cafile file | --capath dir} [--cert file] [--key file]\n"); printf(" [--ciphers ciphers] [--insecure] [--tls-engine engine]\n"); printf(" [--keyform keyform] [--tls-engine-kpass-sha1]]\n"); -#ifdef WITH_TLS_PSK +#ifdef FINAL_WITH_TLS_PSK printf(" [--psk hex-key --psk-identity identity [--ciphers ciphers]]\n"); #endif #endif #ifdef WITH_SOCKS printf(" [--proxy socks-url]\n"); #endif + printf(" [-D command identifier value]\n"); printf(" mosquitto_sub --help\n\n"); printf(" -A : bind the outgoing socket to this host/ip address. Use to control which interface\n"); printf(" the client communicates over.\n"); printf(" -c : disable 'clean session' (store subscription and pending messages when client disconnects).\n"); printf(" -C : disconnect and exit after receiving the 'msg_count' messages.\n"); printf(" -d : enable debug messages.\n"); + printf(" -D : Define MQTT v5 properties. See the documentation for more details.\n"); printf(" -E : Exit once all subscriptions have been acknowledged by the broker.\n"); printf(" -F : output format.\n"); printf(" -h : mqtt host to connect to. Defaults to localhost.\n"); @@ -195,7 +191,7 @@ void print_usage(void) printf(" -U : unsubscribe from a topic. May be repeated.\n"); printf(" -v : print published messages verbosely.\n"); printf(" -V : specify the version of the MQTT protocol to use when connecting.\n"); - printf(" Can be mqttv31 or mqttv311. Defaults to mqttv311.\n"); + printf(" Can be mqttv5, mqttv311 or mqttv31. Defaults to mqttv311.\n"); #ifndef WIN32 printf(" -W : Specifies a timeout in seconds how long to process incoming MQTT messages.\n"); #endif @@ -226,7 +222,7 @@ void print_usage(void) printf(" Do not use this option in a production environment.\n"); printf(" --tls-engine : If set, enables the use of a SSL engine device.\n"); printf(" --tls-engine-kpass-sha1 : SHA1 of the key password to be used with the selected SSL engine.\n"); -#ifdef WITH_TLS_PSK +#ifdef FINAL_WITH_TLS_PSK printf(" --psk : pre-shared-key in hexadecimal (no leading 0x) to enable TLS-PSK mode.\n"); printf(" --psk-identity : client identity string for TLS-PSK mode.\n"); #endif @@ -241,7 +237,6 @@ void print_usage(void) int main(int argc, char *argv[]) { - struct mosq_config cfg; int rc; #ifndef WIN32 struct sigaction sigact; @@ -249,27 +244,26 @@ int main(int argc, char *argv[]) memset(&cfg, 0, sizeof(struct mosq_config)); + mosquitto_lib_init(); + rc = client_config_load(&cfg, CLIENT_SUB, argc, argv); if(rc){ - client_config_cleanup(&cfg); if(rc == 2){ /* --help */ print_usage(); }else{ fprintf(stderr, "\nUse 'mosquitto_sub --help' to see usage.\n"); } - return 1; + goto cleanup; } if(cfg.no_retain && cfg.retained_only){ fprintf(stderr, "\nError: Combining '-R' and '--retained-only' makes no sense.\n"); - return 1; + goto cleanup; } - mosquitto_lib_init(); - if(client_id_generate(&cfg, "mosqsub")){ - return 1; + goto cleanup; } mosq = mosquitto_new(cfg.id, cfg.clean_session, &cfg); @@ -282,21 +276,22 @@ int main(int argc, char *argv[]) if(!cfg.quiet) fprintf(stderr, "Error: Invalid id and/or clean_session.\n"); break; } - mosquitto_lib_cleanup(); - return 1; + goto cleanup; } if(client_opts_set(mosq, &cfg)){ - return 1; + goto cleanup; } if(cfg.debug){ mosquitto_log_callback_set(mosq, my_log_callback); mosquitto_subscribe_callback_set(mosq, my_subscribe_callback); } - mosquitto_connect_with_flags_callback_set(mosq, my_connect_callback); - mosquitto_message_callback_set(mosq, my_message_callback); + mosquitto_connect_v5_callback_set(mosq, my_connect_callback); + mosquitto_message_v5_callback_set(mosq, my_message_callback); rc = client_connect(mosq, &cfg); - if(rc) return rc; + if(rc){ + goto cleanup; + } #ifndef WIN32 sigact.sa_handler = my_signal_handler; @@ -305,7 +300,7 @@ int main(int argc, char *argv[]) if(sigaction(SIGALRM, &sigact, NULL) == -1){ perror("sigaction"); - return 1; + goto cleanup; } if(cfg.timeout){ @@ -321,9 +316,15 @@ int main(int argc, char *argv[]) if(cfg.msg_count>0 && rc == MOSQ_ERR_NO_CONN){ rc = 0; } + client_config_cleanup(&cfg); if(rc){ fprintf(stderr, "Error: %s\n", mosquitto_strerror(rc)); } return rc; + +cleanup: + mosquitto_lib_cleanup(); + client_config_cleanup(&cfg); + return 1; } diff --git a/client/sub_client_output.c b/client/sub_client_output.c index ab3f5e96..4aeb8c39 100644 --- a/client/sub_client_output.c +++ b/client/sub_client_output.c @@ -85,11 +85,11 @@ static void write_payload(const unsigned char *payload, int payloadlen, int hex) (void)fwrite(payload, 1, payloadlen, stdout); }else if(hex == 1){ for(i=0; i +# if defined(WITH_TLS_PSK) && !defined(OPENSSL_NO_PSK) +# define FINAL_WITH_TLS_PSK +# endif +#endif + #endif diff --git a/config.mk b/config.mk index d591cd49..cf4bef62 100644 --- a/config.mk +++ b/config.mk @@ -64,10 +64,6 @@ WITH_SYSTEMD:=no # Build with SRV lookup support. WITH_SRV:=no -# Build using libuuid for clientid generation (Linux only - please report if -# supported on your platform). -WITH_UUID:=yes - # Build with websockets support on the broker. WITH_WEBSOCKETS:=no @@ -98,6 +94,9 @@ WITH_EPOLL:=yes # Build with bundled uthash.h WITH_BUNDLED_DEPS:=yes +# Build with coverage options +WITH_COVERAGE:=no + # ============================================================================= # End of user configuration # ============================================================================= @@ -105,13 +104,13 @@ WITH_BUNDLED_DEPS:=yes # Also bump lib/mosquitto.h, CMakeLists.txt, # installer/mosquitto.nsi, installer/mosquitto64.nsi -VERSION=1.5.4 +VERSION=1.5.90 # Client library SO version. Bump if incompatible API/ABI changes are made. SOVERSION=1 # Man page generation requires xsltproc and docbook-xsl -XSLTPROC=xsltproc +XSLTPROC=xsltproc --nonet # For html generation DB_HTML_XSL=man/html.xsl @@ -134,6 +133,7 @@ LIB_CXXFLAGS:=$(CFLAGS) ${CPPFLAGS} -I. -I.. -I../lib LIB_LDFLAGS:=${LDFLAGS} BROKER_CFLAGS:=${LIB_CFLAGS} ${CPPFLAGS} -DVERSION="\"${VERSION}\"" -DWITH_BROKER +BROKER_LDFLAGS:=${LDFLAGS} CLIENT_CFLAGS:=${CFLAGS} ${CPPFLAGS} -I.. -I../lib -DVERSION="\"${VERSION}\"" ifneq ($(or $(findstring $(UNAME),FreeBSD), $(findstring $(UNAME),OpenBSD), $(findstring $(UNAME),NetBSD)),) @@ -211,13 +211,6 @@ ifeq ($(WITH_SOCKS),yes) CLIENT_CFLAGS:=$(CLIENT_CFLAGS) -DWITH_SOCKS endif -ifeq ($(WITH_UUID),yes) - ifeq ($(UNAME),Linux) - BROKER_CFLAGS:=$(BROKER_CFLAGS) -DWITH_UUID - BROKER_LIBS:=$(BROKER_LIBS) -luuid - endif -endif - ifeq ($(WITH_BRIDGE),yes) BROKER_CFLAGS:=$(BROKER_CFLAGS) -DWITH_BRIDGE endif @@ -301,3 +294,12 @@ endif ifeq ($(WITH_BUNDLED_DEPS),yes) BROKER_CFLAGS:=$(BROKER_CFLAGS) -Ideps endif + +ifeq ($(WITH_COVERAGE),yes) + BROKER_CFLAGS:=$(BROKER_CFLAGS) -coverage + BROKER_LDFLAGS:=$(BROKER_LDFLAGS) -coverage + LIB_CFLAGS:=$(LIB_CFLAGS) -coverage + LIB_LDFLAGS:=$(LIB_LDFLAGS) -coverage + CLIENT_CFLAGS:=$(CLIENT_CFLAGS) -coverage + CLIENT_LDFLAGS:=$(CLIENT_LDFLAGS) -coverage +endif diff --git a/docker/1.5/Dockerfile b/docker/1.5/Dockerfile index 0f480eff..0681f4ed 100644 --- a/docker/1.5/Dockerfile +++ b/docker/1.5/Dockerfile @@ -3,8 +3,8 @@ FROM alpine:3.8 LABEL maintainer="Roger Light " \ description="Eclipse Mosquitto MQTT Broker" -ENV VERSION=1.5.4 \ - DOWNLOAD_SHA256=5fd7f3454fd6d286645d032bc07f44a1c8583cec02ef2422c9eb32e0a89a9b2f \ +ENV VERSION=1.5.6 \ + DOWNLOAD_SHA256=d5bdc13cc668350026376d57fc14de10aaee029f6840707677637d15e0751a40 \ GPG_KEYS=A0D6EEA1DCAE49A635A3B2F0779B22DFB3E717B7 \ LWS_VERSION=2.4.2 @@ -67,8 +67,8 @@ RUN set -x && \ WITH_WEBSOCKETS=yes \ prefix=/usr \ binary && \ - addgroup -S mosquitto 2>/dev/null && \ - adduser -S -D -H -h /var/empty -s /sbin/nologin -G mosquitto -g mosquitto mosquitto 2>/dev/null && \ + addgroup -S -g 1883 mosquitto 2>/dev/null && \ + adduser -S -u 1883 -D -H -h /var/empty -s /sbin/nologin -G mosquitto -g mosquitto mosquitto 2>/dev/null && \ mkdir -p /mosquitto/config /mosquitto/data /mosquitto/log && \ install -d /usr/sbin/ && \ install -s -m755 /build/mosq/src/mosquitto /usr/sbin/mosquitto && \ diff --git a/docker/1.5/README.md b/docker/1.5/README.md index 6a8c1794..27b05904 100644 --- a/docker/1.5/README.md +++ b/docker/1.5/README.md @@ -13,6 +13,11 @@ Two docker volumes have been created in the image to be used for persistent stor /mosquitto/log ``` +## User/Group + +The image runs mosqutto under the mosquitto user and group, which are created +with a uid and gid of 1883. + ## Configuration When creating a container from the image, the default configuration values are used. To use a custom configuration file, mount a **local** configuration file to `/mosquitto/config/mosquitto.conf` diff --git a/installer/mosquitto.nsi b/installer/mosquitto.nsi index 34143f85..15c356f0 100644 --- a/installer/mosquitto.nsi +++ b/installer/mosquitto.nsi @@ -9,7 +9,7 @@ !define env_hklm 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"' Name "Eclipse Mosquitto" -!define VERSION 1.5.4 +!define VERSION 1.5.90 OutFile "mosquitto-${VERSION}-install-windows-x86.exe" InstallDir "$PROGRAMFILES\mosquitto" @@ -18,8 +18,7 @@ InstallDir "$PROGRAMFILES\mosquitto" ; Installer pages !insertmacro MUI_PAGE_WELCOME -Page custom DependencyPage -!insertmacro MUI_PAGE_COMPONENTS +;!insertmacro MUI_PAGE_COMPONENTS !insertmacro MUI_PAGE_DIRECTORY !insertmacro MUI_PAGE_INSTFILES !insertmacro MUI_PAGE_FINISH @@ -55,8 +54,8 @@ Section "Files" SecInstall File "..\readme.md" File "..\readme-windows.txt" ;File "C:\pthreads\Pre-built.2\dll\x86\pthreadVC2.dll" - ;File "C:\OpenSSL-Win32\bin\libssl_1-1.dll" - ;File "C:\OpenSSL-Win32\bin\libcrypto_1-1.dll" + File "C:\OpenSSL-Win32\bin\libssl-1_1.dll" + File "C:\OpenSSL-Win32\bin\libcrypto-1_1.dll" File "..\edl-v10" File "..\epl-v10" @@ -96,8 +95,8 @@ Section "Uninstall" Delete "$INSTDIR\readme.txt" Delete "$INSTDIR\readme-windows.txt" ;Delete "$INSTDIR\pthreadVC2.dll" - ;Delete "$INSTDIR\libssl_1-1.dll" - ;Delete "$INSTDIR\libcrypto_1-1.dll" + Delete "$INSTDIR\libssl-1_1.dll" + Delete "$INSTDIR\libcrypto-1_1.dll" Delete "$INSTDIR\edl-v10" Delete "$INSTDIR\epl-v10" @@ -120,28 +119,3 @@ LangString DESC_SecInstall ${LANG_ENGLISH} "The main installation." !insertmacro MUI_DESCRIPTION_TEXT ${SecInstall} $(DESC_SecInstall) !insertmacro MUI_FUNCTION_DESCRIPTION_END -Var Dialog -Var OSSLLink -Var PTHLink - -Function DependencyPage - nsDialogs::Create 1018 - Pop $Dialog - - ${If} $Dialog == error - Abort - ${EndIf} - - ${NSD_CreateLabel} 0 0 100% 12u "OpenSSL - install 'Win32 OpenSSL v1.1.0* Light' then copy libssl_1-1.dll and libcrypto_1-1.dll to the mosquitto directory" - ${NSD_CreateLink} 13u 13u 100% 12u "http://slproweb.com/products/Win32OpenSSL.html" - Pop $OSSLLink - ${NSD_OnClick} $OSSLLink OnClick_OSSL - - !insertmacro MUI_HEADER_TEXT_PAGE "Dependencies" "This page lists packages that must be installed if not already present" - nsDialogs::Show -FunctionEnd - -Function OnClick_OSSL - Pop $0 - ExecShell "open" "http://slproweb.com/products/Win32OpenSSL.html" -FunctionEnd diff --git a/installer/mosquitto64.nsi b/installer/mosquitto64.nsi index 097f4521..2da63198 100644 --- a/installer/mosquitto64.nsi +++ b/installer/mosquitto64.nsi @@ -9,7 +9,7 @@ !define env_hklm 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"' Name "Eclipse Mosquitto" -!define VERSION 1.5.4 +!define VERSION 1.5.90 OutFile "mosquitto-${VERSION}-install-windows-x64.exe" !include "x64.nsh" @@ -19,8 +19,7 @@ InstallDir "$PROGRAMFILES64\mosquitto" ; Installer pages !insertmacro MUI_PAGE_WELCOME -Page custom DependencyPage -!insertmacro MUI_PAGE_COMPONENTS +;!insertmacro MUI_PAGE_COMPONENTS !insertmacro MUI_PAGE_DIRECTORY !insertmacro MUI_PAGE_INSTFILES !insertmacro MUI_PAGE_FINISH @@ -56,8 +55,8 @@ Section "Files" SecInstall File "..\readme.md" File "..\readme-windows.txt" ;File "C:\pthreads\Pre-built.2\dll\x64\pthreadVC2.dll" - ;File "C:\OpenSSL-Win64\bin\libssl_1-1-x64.dll" - ;File "C:\OpenSSL-Win64\bin\libcrypto_1-1-x64.dll" + File "C:\OpenSSL-Win64\bin\libssl-1_1-x64.dll" + File "C:\OpenSSL-Win64\bin\libcrypto-1_1-x64.dll" File "..\edl-v10" File "..\epl-v10" @@ -97,8 +96,8 @@ Section "Uninstall" Delete "$INSTDIR\readme.txt" Delete "$INSTDIR\readme-windows.txt" ;Delete "$INSTDIR\pthreadVC2.dll" - ;Delete "$INSTDIR\libssl_1-1-x64.dll" - ;Delete "$INSTDIR\libcrypto_1-1-x64.dll" + Delete "$INSTDIR\libssl-1_1-x64.dll" + Delete "$INSTDIR\libcrypto-1_1-x64.dll" Delete "$INSTDIR\edl-v10" Delete "$INSTDIR\epl-v10" @@ -121,28 +120,3 @@ LangString DESC_SecInstall ${LANG_ENGLISH} "The main installation." !insertmacro MUI_DESCRIPTION_TEXT ${SecInstall} $(DESC_SecInstall) !insertmacro MUI_FUNCTION_DESCRIPTION_END -Var Dialog -Var OSSLLink -Var PTHLink - -Function DependencyPage - nsDialogs::Create 1018 - Pop $Dialog - - ${If} $Dialog == error - Abort - ${EndIf} - - ${NSD_CreateLabel} 0 0 100% 12u "OpenSSL - install 'Win64 OpenSSL v1.1.0* Light' then copy libssl_1-1-x64.dll and libcrypto_1-1-x64.dll to the mosquitto directory" - ${NSD_CreateLink} 13u 13u 100% 12u "http://slproweb.com/products/Win32OpenSSL.html" - Pop $OSSLLink - ${NSD_OnClick} $OSSLLink OnClick_OSSL - - !insertmacro MUI_HEADER_TEXT_PAGE "Dependencies" "This page lists packages that must be installed if not already present" - nsDialogs::Show -FunctionEnd - -Function OnClick_OSSL - Pop $0 - ExecShell "open" "http://slproweb.com/products/Win32OpenSSL.html" -FunctionEnd diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index c92571aa..de42e11c 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -11,7 +11,9 @@ set(C_SRC actions.c callbacks.c connect.c + handle_auth.c handle_connack.c + handle_disconnect.c handle_ping.c handle_pubackcomp.c handle_publish.c @@ -26,9 +28,10 @@ set(C_SRC messages_mosq.c messages_mosq.h mosquitto.c mosquitto.h mosquitto_internal.h - mqtt3_protocol.h + mqtt_protocol.h net_mosq.c net_mosq.h options.c + packet_datatypes.c packet_mosq.c packet_mosq.h read_handle.c read_handle.h send_connect.c @@ -44,7 +47,7 @@ set(C_SRC time_mosq.c tls_mosq.c utf8_mosq.c - util_mosq.c util_mosq.h + util_mosq.c util_topic.c util_mosq.h will_mosq.c will_mosq.h) set (LIBRARIES ${OPENSSL_LIBRARIES} ${PTHREAD_LIBRARIES}) @@ -60,7 +63,7 @@ if (WIN32) set (LIBRARIES ${LIBRARIES} ws2_32) endif (WIN32) -if (${WITH_SRV} STREQUAL ON) +if (WITH_SRV) # Simple detect c-ares find_path(ARES_HEADER ares.h) if (ARES_HEADER) @@ -69,7 +72,7 @@ if (${WITH_SRV} STREQUAL ON) else (ARES_HEADER) message(WARNING "c-ares library not found.") endif (ARES_HEADER) -endif (${WITH_SRV} STREQUAL ON) +endif (WITH_SRV) add_library(libmosquitto SHARED ${C_SRC}) set_target_properties(libmosquitto PROPERTIES @@ -86,13 +89,13 @@ set_target_properties(libmosquitto PROPERTIES install(TARGETS libmosquitto RUNTIME DESTINATION "${BINDIR}" LIBRARY DESTINATION "${LIBDIR}") -if (${WITH_STATIC_LIBRARIES} STREQUAL ON) +if (WITH_STATIC_LIBRARIES) add_library(libmosquitto_static STATIC ${C_SRC}) - if (${WITH_PIC} STREQUAL ON) + if (WITH_PIC) set_target_properties(libmosquitto_static PROPERTIES POSITION_INDEPENDENT_CODE 1 ) - endif (${WITH_PIC} STREQUAL ON) + endif (WITH_PIC) target_link_libraries(libmosquitto_static ${LIBRARIES}) @@ -103,7 +106,7 @@ if (${WITH_STATIC_LIBRARIES} STREQUAL ON) target_compile_definitions(libmosquitto_static PUBLIC "LIBMOSQUITTO_STATIC") install(TARGETS libmosquitto_static RUNTIME DESTINATION "${BINDIR}" ARCHIVE DESTINATION "${LIBDIR}") -endif (${WITH_STATIC_LIBRARIES} STREQUAL ON) +endif (WITH_STATIC_LIBRARIES) install(FILES mosquitto.h DESTINATION "${INCLUDEDIR}") diff --git a/lib/Makefile b/lib/Makefile index 4c9cac43..efce2c98 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -6,7 +6,9 @@ MOSQ_OBJS=mosquitto.o \ actions.o \ callbacks.o \ connect.o \ + handle_auth.o \ handle_connack.o \ + handle_disconnect.o \ handle_ping.o \ handle_pubackcomp.o \ handle_publish.o \ @@ -21,7 +23,9 @@ MOSQ_OBJS=mosquitto.o \ messages_mosq.o \ net_mosq.o \ options.o \ + packet_datatypes.o \ packet_mosq.o \ + property_mosq.o \ read_handle.o \ send_connect.o \ send_disconnect.o \ @@ -36,6 +40,7 @@ MOSQ_OBJS=mosquitto.o \ tls_mosq.o \ utf8_mosq.o \ util_mosq.o \ + util_topic.o \ will_mosq.o ALL_DEPS:= @@ -80,7 +85,7 @@ uninstall : reallyclean : clean clean : - -rm -f *.o libmosquitto.so.${SOVERSION} libmosquitto.so libmosquitto.a + -rm -f *.o libmosquitto.so.${SOVERSION} libmosquitto.so libmosquitto.a *.gcno *.gcda $(MAKE) -C cpp clean libmosquitto.so.${SOVERSION} : ${MOSQ_OBJS} @@ -101,9 +106,15 @@ callbacks.o : callbacks.c mosquitto.h mosquitto_internal.h connect.o : connect.c mosquitto.h mosquitto_internal.h ${CROSS_COMPILE}$(CC) $(LIB_CFLAGS) -c $< -o $@ +handle_auth.o : handle_auth.c read_handle.h + ${CROSS_COMPILE}$(CC) $(LIB_CFLAGS) -c $< -o $@ + handle_connack.o : handle_connack.c read_handle.h ${CROSS_COMPILE}$(CC) $(LIB_CFLAGS) -c $< -o $@ +handle_disconnect.o : handle_disconnect.c read_handle.h + ${CROSS_COMPILE}$(CC) $(LIB_CFLAGS) -c $< -o $@ + handle_publish.o : handle_publish.c read_handle.h ${CROSS_COMPILE}$(CC) $(LIB_CFLAGS) -c $< -o $@ @@ -146,9 +157,15 @@ net_mosq.o : net_mosq.c net_mosq.h options.o : options.c mosquitto.h mosquitto_internal.h ${CROSS_COMPILE}$(CC) $(LIB_CFLAGS) -c $< -o $@ +packet_datatypes.o : packet_datatypes.c packet_mosq.h + ${CROSS_COMPILE}$(CC) $(LIB_CFLAGS) -c $< -o $@ + packet_mosq.o : packet_mosq.c packet_mosq.h ${CROSS_COMPILE}$(CC) $(LIB_CFLAGS) -c $< -o $@ +property_mosq.o : property_mosq.c property_mosq.h + ${CROSS_COMPILE}$(CC) $(LIB_CFLAGS) -c $< -o $@ + read_handle.o : read_handle.c read_handle.h ${CROSS_COMPILE}$(CC) $(LIB_CFLAGS) -c $< -o $@ @@ -191,6 +208,9 @@ utf8_mosq.o : utf8_mosq.c util_mosq.o : util_mosq.c util_mosq.h ${CROSS_COMPILE}$(CC) $(LIB_CFLAGS) -c $< -o $@ +util_topic.o : util_topic.c util_mosq.h + ${CROSS_COMPILE}$(CC) $(LIB_CFLAGS) -c $< -o $@ + will_mosq.o : will_mosq.c will_mosq.h ${CROSS_COMPILE}$(CC) $(LIB_CFLAGS) -c $< -o $@ diff --git a/lib/actions.c b/lib/actions.c index f994c6a9..ee847e94 100644 --- a/lib/actions.c +++ b/lib/actions.c @@ -22,25 +22,84 @@ Contributors: #include "mosquitto_internal.h" #include "memory_mosq.h" #include "messages_mosq.h" -#include "mqtt3_protocol.h" +#include "mqtt_protocol.h" #include "net_mosq.h" +#include "packet_mosq.h" #include "send_mosq.h" #include "util_mosq.h" int mosquitto_publish(struct mosquitto *mosq, int *mid, const char *topic, int payloadlen, const void *payload, int qos, bool retain) +{ + return mosquitto_publish_v5(mosq, mid, topic, payloadlen, payload, qos, retain, NULL); +} + +int mosquitto_publish_v5(struct mosquitto *mosq, int *mid, const char *topic, int payloadlen, const void *payload, int qos, bool retain, const mosquitto_property *properties) { struct mosquitto_message_all *message; uint16_t local_mid; int queue_status; + const mosquitto_property *p; + const mosquitto_property *outgoing_properties = NULL; + mosquitto_property local_property; + bool have_topic_alias; + int rc; + int tlen = 0; + uint32_t remaining_length; - if(!mosq || !topic || qos<0 || qos>2) return MOSQ_ERR_INVAL; - if(STREMPTY(topic)) return MOSQ_ERR_INVAL; - if(mosquitto_validate_utf8(topic, strlen(topic))) return MOSQ_ERR_MALFORMED_UTF8; - if(payloadlen < 0 || payloadlen > MQTT_MAX_PAYLOAD) return MOSQ_ERR_PAYLOAD_SIZE; + if(!mosq || qos<0 || qos>2) return MOSQ_ERR_INVAL; + if(mosq->protocol != mosq_p_mqtt5 && properties) return MOSQ_ERR_NOT_SUPPORTED; + if(qos > mosq->maximum_qos) return MOSQ_ERR_QOS_NOT_SUPPORTED; - if(mosquitto_pub_topic_check(topic) != MOSQ_ERR_SUCCESS){ - return MOSQ_ERR_INVAL; + if(properties){ + if(properties->client_generated){ + outgoing_properties = properties; + }else{ + memcpy(&local_property, properties, sizeof(mosquitto_property)); + local_property.client_generated = true; + local_property.next = NULL; + outgoing_properties = &local_property; + } + rc = mosquitto_property_check_all(CMD_PUBLISH, outgoing_properties); + if(rc) return rc; + } + + if(!topic || STREMPTY(topic)){ + if(topic) topic = NULL; + + if(mosq->protocol == mosq_p_mqtt5){ + p = outgoing_properties; + have_topic_alias = false; + while(p){ + if(p->identifier == MQTT_PROP_TOPIC_ALIAS){ + have_topic_alias = true; + break; + } + p = p->next; + } + if(have_topic_alias == false){ + return MOSQ_ERR_INVAL; + } + }else{ + return MOSQ_ERR_INVAL; + } + }else{ + tlen = strlen(topic); + if(mosquitto_validate_utf8(topic, tlen)) return MOSQ_ERR_MALFORMED_UTF8; + if(payloadlen < 0 || payloadlen > MQTT_MAX_PAYLOAD) return MOSQ_ERR_PAYLOAD_SIZE; + if(mosquitto_pub_topic_check(topic) != MOSQ_ERR_SUCCESS){ + return MOSQ_ERR_INVAL; + } + } + + if(mosq->maximum_packet_size > 0){ + remaining_length = 1 + 2+tlen + payloadlen + property__get_length_all(outgoing_properties); + if(qos > 0){ + remaining_length++; + } + if(packet__check_oversize(mosq, remaining_length)){ + return MOSQ_ERR_OVERSIZE_PACKET; + } } local_mid = mosquitto__mid_generate(mosq); @@ -49,7 +108,7 @@ int mosquitto_publish(struct mosquitto *mosq, int *mid, const char *topic, int p } if(qos == 0){ - return send__publish(mosq, local_mid, topic, payloadlen, payload, qos, retain, false); + return send__publish(mosq, local_mid, topic, payloadlen, payload, qos, retain, false, outgoing_properties, NULL, 0); }else{ message = mosquitto__calloc(1, sizeof(struct mosquitto_message_all)); if(!message) return MOSQ_ERR_NOMEM; @@ -57,10 +116,12 @@ int mosquitto_publish(struct mosquitto *mosq, int *mid, const char *topic, int p message->next = NULL; message->timestamp = mosquitto_time(); message->msg.mid = local_mid; - message->msg.topic = mosquitto__strdup(topic); - if(!message->msg.topic){ - message__cleanup(&message); - return MOSQ_ERR_NOMEM; + if(topic){ + message->msg.topic = mosquitto__strdup(topic); + if(!message->msg.topic){ + message__cleanup(&message); + return MOSQ_ERR_NOMEM; + } } if(payloadlen){ message->msg.payloadlen = payloadlen; @@ -87,7 +148,7 @@ int mosquitto_publish(struct mosquitto *mosq, int *mid, const char *topic, int p message->state = mosq_ms_wait_for_pubrec; } pthread_mutex_unlock(&mosq->out_message_mutex); - return send__publish(mosq, message->msg.mid, message->msg.topic, message->msg.payloadlen, message->msg.payload, message->msg.qos, message->msg.retain, message->dup); + return send__publish(mosq, message->msg.mid, message->msg.topic, message->msg.payloadlen, message->msg.payload, message->msg.qos, message->msg.retain, message->dup, outgoing_properties, NULL, 0); }else{ message->state = mosq_ms_invalid; pthread_mutex_unlock(&mosq->out_message_mutex); @@ -99,41 +160,112 @@ int mosquitto_publish(struct mosquitto *mosq, int *mid, const char *topic, int p int mosquitto_subscribe(struct mosquitto *mosq, int *mid, const char *sub, int qos) { - if(!mosq) return MOSQ_ERR_INVAL; - if(mosq->sock == INVALID_SOCKET) return MOSQ_ERR_NO_CONN; - - if(mosquitto_sub_topic_check(sub)) return MOSQ_ERR_INVAL; - if(mosquitto_validate_utf8(sub, strlen(sub))) return MOSQ_ERR_MALFORMED_UTF8; - - return send__subscribe(mosq, mid, 1, (char *const *const)&sub, qos); + return mosquitto_subscribe_multiple(mosq, mid, 1, (char *const *const)&sub, qos, 0, NULL); } -int mosquitto_subscribe_multiple(struct mosquitto *mosq, int *mid, int sub_count, char *const *const sub, int qos) +int mosquitto_subscribe_v5(struct mosquitto *mosq, int *mid, const char *sub, int qos, int options, const mosquitto_property *properties) { + return mosquitto_subscribe_multiple(mosq, mid, 1, (char *const *const)&sub, qos, options, properties); +} + + +int mosquitto_subscribe_multiple(struct mosquitto *mosq, int *mid, int sub_count, char *const *const sub, int qos, int options, const mosquitto_property *properties) +{ + const mosquitto_property *outgoing_properties = NULL; + mosquitto_property local_property; int i; + int rc; + uint32_t remaining_length = 0; + int slen; if(!mosq || !sub_count || !sub) return MOSQ_ERR_INVAL; + if(mosq->protocol != mosq_p_mqtt5 && properties) return MOSQ_ERR_NOT_SUPPORTED; if(qos < 0 || qos > 2) return MOSQ_ERR_INVAL; + if((options & 0x30) == 0x30 || (options & 0xC0) != 0) return MOSQ_ERR_INVAL; if(mosq->sock == INVALID_SOCKET) return MOSQ_ERR_NO_CONN; + if(properties){ + if(properties->client_generated){ + outgoing_properties = properties; + }else{ + memcpy(&local_property, properties, sizeof(mosquitto_property)); + local_property.client_generated = true; + local_property.next = NULL; + outgoing_properties = &local_property; + } + rc = mosquitto_property_check_all(CMD_SUBSCRIBE, outgoing_properties); + if(rc) return rc; + } + for(i=0; imaximum_packet_size > 0){ + remaining_length += 2 + property__get_length_all(outgoing_properties); + if(packet__check_oversize(mosq, remaining_length)){ + return MOSQ_ERR_OVERSIZE_PACKET; + } + } + + return send__subscribe(mosq, mid, sub_count, sub, qos|options, outgoing_properties); } int mosquitto_unsubscribe(struct mosquitto *mosq, int *mid, const char *sub) { - if(!mosq) return MOSQ_ERR_INVAL; - if(mosq->sock == INVALID_SOCKET) return MOSQ_ERR_NO_CONN; - - if(mosquitto_sub_topic_check(sub)) return MOSQ_ERR_INVAL; - if(mosquitto_validate_utf8(sub, strlen(sub))) return MOSQ_ERR_MALFORMED_UTF8; - - return send__unsubscribe(mosq, mid, sub); + return mosquitto_unsubscribe_multiple(mosq, mid, 1, (char *const *const)&sub, NULL); +} + +int mosquitto_unsubscribe_v5(struct mosquitto *mosq, int *mid, const char *sub, const mosquitto_property *properties) +{ + return mosquitto_unsubscribe_multiple(mosq, mid, 1, (char *const *const)&sub, properties); +} + +int mosquitto_unsubscribe_multiple(struct mosquitto *mosq, int *mid, int sub_count, char *const *const sub, const mosquitto_property *properties) +{ + const mosquitto_property *outgoing_properties = NULL; + mosquitto_property local_property; + int rc; + int i; + uint32_t remaining_length = 0; + int slen; + + if(!mosq) return MOSQ_ERR_INVAL; + if(mosq->protocol != mosq_p_mqtt5 && properties) return MOSQ_ERR_NOT_SUPPORTED; + if(mosq->sock == INVALID_SOCKET) return MOSQ_ERR_NO_CONN; + + if(properties){ + if(properties->client_generated){ + outgoing_properties = properties; + }else{ + memcpy(&local_property, properties, sizeof(mosquitto_property)); + local_property.client_generated = true; + local_property.next = NULL; + outgoing_properties = &local_property; + } + rc = mosquitto_property_check_all(CMD_UNSUBSCRIBE, outgoing_properties); + if(rc) return rc; + } + + for(i=0; imaximum_packet_size > 0){ + remaining_length += 2 + property__get_length_all(outgoing_properties); + if(packet__check_oversize(mosq, remaining_length)){ + return MOSQ_ERR_OVERSIZE_PACKET; + } + } + + return send__unsubscribe(mosq, mid, sub_count, sub, outgoing_properties); } diff --git a/lib/alias_mosq.c b/lib/alias_mosq.c new file mode 100644 index 00000000..8057bb99 --- /dev/null +++ b/lib/alias_mosq.c @@ -0,0 +1,85 @@ +/* +Copyright (c) 2019 Roger Light + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#include "mosquitto.h" +#include "alias_mosq.h" +#include "memory_mosq.h" + +int alias__add(struct mosquitto *mosq, const char *topic, int alias) +{ + int i; + struct mosquitto__alias *aliases; + + for(i=0; ialias_count; i++){ + if(mosq->aliases[i].alias == alias){ + mosquitto__free(mosq->aliases[i].topic); + mosq->aliases[i].topic = mosquitto__strdup(topic); + if(mosq->aliases[i].topic){ + return MOSQ_ERR_SUCCESS; + }else{ + + return MOSQ_ERR_NOMEM; + } + } + } + + /* New alias */ + aliases = mosquitto__realloc(mosq->aliases, sizeof(struct mosquitto__alias)*(mosq->alias_count+1)); + if(!aliases) return MOSQ_ERR_NOMEM; + + mosq->aliases = aliases; + mosq->aliases[mosq->alias_count].alias = alias; + mosq->aliases[mosq->alias_count].topic = mosquitto__strdup(topic); + if(!mosq->aliases[mosq->alias_count].topic){ + return MOSQ_ERR_NOMEM; + } + mosq->alias_count++; + + return MOSQ_ERR_SUCCESS; +} + + +int alias__find(struct mosquitto *mosq, char **topic, int alias) +{ + int i; + + for(i=0; ialias_count; i++){ + if(mosq->aliases[i].alias == alias){ + *topic = mosquitto__strdup(mosq->aliases[i].topic); + if(*topic){ + return MOSQ_ERR_SUCCESS; + }else{ + return MOSQ_ERR_NOMEM; + } + } + } + return MOSQ_ERR_INVAL; +} + + +void alias__free_all(struct mosquitto *mosq) +{ + int i; + + for(i=0; ialias_count; i++){ + mosquitto__free(mosq->aliases[i].topic); + } + mosquitto__free(mosq->aliases); + mosq->aliases = NULL; + mosq->alias_count = 0; +} diff --git a/lib/alias_mosq.h b/lib/alias_mosq.h new file mode 100644 index 00000000..f892dbf8 --- /dev/null +++ b/lib/alias_mosq.h @@ -0,0 +1,26 @@ +/* +Copyright (c) 2019 Roger Light + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#ifndef ALIAS_MOSQ_H +#define ALIAS_MOSQ_H + +#include "mosquitto_internal.h" + +int alias__add(struct mosquitto *mosq, const char *topic, int alias); +int alias__find(struct mosquitto *mosq, char **topic, int alias); +void alias__free_all(struct mosquitto *mosq); + +#endif diff --git a/lib/callbacks.c b/lib/callbacks.c index 037ee82b..7b424210 100644 --- a/lib/callbacks.c +++ b/lib/callbacks.c @@ -34,6 +34,13 @@ void mosquitto_connect_with_flags_callback_set(struct mosquitto *mosq, void (*on pthread_mutex_unlock(&mosq->callback_mutex); } +void mosquitto_connect_v5_callback_set(struct mosquitto *mosq, void (*on_connect)(struct mosquitto *, void *, int, int, const mosquitto_property *)) +{ + pthread_mutex_lock(&mosq->callback_mutex); + mosq->on_connect_v5 = on_connect; + pthread_mutex_unlock(&mosq->callback_mutex); +} + void mosquitto_disconnect_callback_set(struct mosquitto *mosq, void (*on_disconnect)(struct mosquitto *, void *, int)) { pthread_mutex_lock(&mosq->callback_mutex); @@ -41,6 +48,13 @@ void mosquitto_disconnect_callback_set(struct mosquitto *mosq, void (*on_disconn pthread_mutex_unlock(&mosq->callback_mutex); } +void mosquitto_disconnect_v5_callback_set(struct mosquitto *mosq, void (*on_disconnect)(struct mosquitto *, void *, int, const mosquitto_property *)) +{ + pthread_mutex_lock(&mosq->callback_mutex); + mosq->on_disconnect_v5 = on_disconnect; + pthread_mutex_unlock(&mosq->callback_mutex); +} + void mosquitto_publish_callback_set(struct mosquitto *mosq, void (*on_publish)(struct mosquitto *, void *, int)) { pthread_mutex_lock(&mosq->callback_mutex); @@ -48,6 +62,13 @@ void mosquitto_publish_callback_set(struct mosquitto *mosq, void (*on_publish)(s pthread_mutex_unlock(&mosq->callback_mutex); } +void mosquitto_publish_v5_callback_set(struct mosquitto *mosq, void (*on_publish)(struct mosquitto *, void *, int, int, const mosquitto_property *props)) +{ + pthread_mutex_lock(&mosq->callback_mutex); + mosq->on_publish_v5 = on_publish; + pthread_mutex_unlock(&mosq->callback_mutex); +} + void mosquitto_message_callback_set(struct mosquitto *mosq, void (*on_message)(struct mosquitto *, void *, const struct mosquitto_message *)) { pthread_mutex_lock(&mosq->callback_mutex); @@ -55,6 +76,13 @@ void mosquitto_message_callback_set(struct mosquitto *mosq, void (*on_message)(s pthread_mutex_unlock(&mosq->callback_mutex); } +void mosquitto_message_v5_callback_set(struct mosquitto *mosq, void (*on_message)(struct mosquitto *, void *, const struct mosquitto_message *, const mosquitto_property *props)) +{ + pthread_mutex_lock(&mosq->callback_mutex); + mosq->on_message_v5 = on_message; + pthread_mutex_unlock(&mosq->callback_mutex); +} + void mosquitto_subscribe_callback_set(struct mosquitto *mosq, void (*on_subscribe)(struct mosquitto *, void *, int, int, const int *)) { pthread_mutex_lock(&mosq->callback_mutex); @@ -62,6 +90,13 @@ void mosquitto_subscribe_callback_set(struct mosquitto *mosq, void (*on_subscrib pthread_mutex_unlock(&mosq->callback_mutex); } +void mosquitto_subscribe_v5_callback_set(struct mosquitto *mosq, void (*on_subscribe)(struct mosquitto *, void *, int, int, const int *, const mosquitto_property *props)) +{ + pthread_mutex_lock(&mosq->callback_mutex); + mosq->on_subscribe_v5 = on_subscribe; + pthread_mutex_unlock(&mosq->callback_mutex); +} + void mosquitto_unsubscribe_callback_set(struct mosquitto *mosq, void (*on_unsubscribe)(struct mosquitto *, void *, int)) { pthread_mutex_lock(&mosq->callback_mutex); @@ -69,6 +104,13 @@ void mosquitto_unsubscribe_callback_set(struct mosquitto *mosq, void (*on_unsubs pthread_mutex_unlock(&mosq->callback_mutex); } +void mosquitto_unsubscribe_v5_callback_set(struct mosquitto *mosq, void (*on_unsubscribe)(struct mosquitto *, void *, int, const mosquitto_property *props)) +{ + pthread_mutex_lock(&mosq->callback_mutex); + mosq->on_unsubscribe_v5 = on_unsubscribe; + pthread_mutex_unlock(&mosq->callback_mutex); +} + void mosquitto_log_callback_set(struct mosquitto *mosq, void (*on_log)(struct mosquitto *, void *, int, const char *)) { pthread_mutex_lock(&mosq->log_callback_mutex); diff --git a/lib/connect.c b/lib/connect.c index fe084e31..f2804002 100644 --- a/lib/connect.c +++ b/lib/connect.c @@ -16,25 +16,46 @@ Contributors: #include "config.h" +#include + #include "mosquitto.h" #include "mosquitto_internal.h" #include "logging_mosq.h" #include "messages_mosq.h" #include "memory_mosq.h" #include "packet_mosq.h" +#include "mqtt_protocol.h" #include "net_mosq.h" #include "send_mosq.h" #include "socks_mosq.h" -static int mosquitto__reconnect(struct mosquitto *mosq, bool blocking); +static int mosquitto__reconnect(struct mosquitto *mosq, bool blocking, const mosquitto_property *properties); static int mosquitto__connect_init(struct mosquitto *mosq, const char *host, int port, int keepalive, const char *bind_address); static int mosquitto__connect_init(struct mosquitto *mosq, const char *host, int port, int keepalive, const char *bind_address) { + int i; + if(!mosq) return MOSQ_ERR_INVAL; if(!host || port <= 0) return MOSQ_ERR_INVAL; + if(mosq->id == NULL && (mosq->protocol == mosq_p_mqtt31 || mosq->protocol == mosq_p_mqtt311)){ + mosq->id = (char *)mosquitto__calloc(24, sizeof(char)); + if(!mosq->id){ + return MOSQ_ERR_NOMEM; + } + mosq->id[0] = 'm'; + mosq->id[1] = 'o'; + mosq->id[2] = 's'; + mosq->id[3] = 'q'; + mosq->id[4] = '/'; + + for(i=5; i<23; i++){ + mosq->id[i] = (random()%73)+48; + } + } + mosquitto__free(mosq->host); mosq->host = mosquitto__strdup(host); if(!mosq->host) return MOSQ_ERR_NOMEM; @@ -47,6 +68,8 @@ static int mosquitto__connect_init(struct mosquitto *mosq, const char *host, int } mosq->keepalive = keepalive; + mosq->receive_quota = mosq->receive_maximum; + mosq->send_quota = mosq->send_maximum; if(mosq->sockpairR != INVALID_SOCKET){ COMPAT_CLOSE(mosq->sockpairR); @@ -73,8 +96,19 @@ int mosquitto_connect(struct mosquitto *mosq, const char *host, int port, int ke int mosquitto_connect_bind(struct mosquitto *mosq, const char *host, int port, int keepalive, const char *bind_address) +{ + return mosquitto_connect_bind_v5(mosq, host, port, keepalive, bind_address, NULL); +} + +int mosquitto_connect_bind_v5(struct mosquitto *mosq, const char *host, int port, int keepalive, const char *bind_address, const mosquitto_property *properties) { int rc; + + if(properties){ + rc = mosquitto_property_check_all(CMD_CONNECT, properties); + if(rc) return rc; + } + rc = mosquitto__connect_init(mosq, host, port, keepalive, bind_address); if(rc) return rc; @@ -82,7 +116,7 @@ int mosquitto_connect_bind(struct mosquitto *mosq, const char *host, int port, i mosq->state = mosq_cs_new; pthread_mutex_unlock(&mosq->state_mutex); - return mosquitto__reconnect(mosq, true); + return mosquitto__reconnect(mosq, true, properties); } @@ -101,28 +135,44 @@ int mosquitto_connect_bind_async(struct mosquitto *mosq, const char *host, int p mosq->state = mosq_cs_connect_async; pthread_mutex_unlock(&mosq->state_mutex); - return mosquitto__reconnect(mosq, false); + return mosquitto__reconnect(mosq, false, NULL); } int mosquitto_reconnect_async(struct mosquitto *mosq) { - return mosquitto__reconnect(mosq, false); + return mosquitto__reconnect(mosq, false, NULL); } int mosquitto_reconnect(struct mosquitto *mosq) { - return mosquitto__reconnect(mosq, true); + return mosquitto__reconnect(mosq, true, NULL); } -static int mosquitto__reconnect(struct mosquitto *mosq, bool blocking) +static int mosquitto__reconnect(struct mosquitto *mosq, bool blocking, const mosquitto_property *properties) { + const mosquitto_property *outgoing_properties = NULL; + mosquitto_property local_property; int rc; struct mosquitto__packet *packet; if(!mosq) return MOSQ_ERR_INVAL; if(!mosq->host || mosq->port <= 0) return MOSQ_ERR_INVAL; + if(mosq->protocol != mosq_p_mqtt5 && properties) return MOSQ_ERR_NOT_SUPPORTED; + + if(properties){ + if(properties->client_generated){ + outgoing_properties = properties; + }else{ + memcpy(&local_property, properties, sizeof(mosquitto_property)); + local_property.client_generated = true; + local_property.next = NULL; + outgoing_properties = &local_property; + } + rc = mosquitto_property_check_all(CMD_CONNECT, outgoing_properties); + if(rc) return rc; + } pthread_mutex_lock(&mosq->state_mutex); #ifdef WITH_SOCKS @@ -143,7 +193,7 @@ static int mosquitto__reconnect(struct mosquitto *mosq, bool blocking) mosq->ping_t = 0; packet__cleanup(&mosq->in_packet); - + pthread_mutex_lock(&mosq->current_out_packet_mutex); pthread_mutex_lock(&mosq->out_packet_mutex); @@ -193,20 +243,81 @@ static int mosquitto__reconnect(struct mosquitto *mosq, bool blocking) }else #endif { - return send__connect(mosq, mosq->keepalive, mosq->clean_session); + return send__connect(mosq, mosq->keepalive, mosq->clean_start, outgoing_properties); } } int mosquitto_disconnect(struct mosquitto *mosq) { + return mosquitto_disconnect_v5(mosq, 0, NULL); +} + +int mosquitto_disconnect_v5(struct mosquitto *mosq, int reason_code, const mosquitto_property *properties) +{ + const mosquitto_property *outgoing_properties = NULL; + mosquitto_property local_property; + int rc; if(!mosq) return MOSQ_ERR_INVAL; + if(mosq->protocol != mosq_p_mqtt5 && properties) return MOSQ_ERR_NOT_SUPPORTED; + + if(properties){ + if(properties->client_generated){ + outgoing_properties = properties; + }else{ + memcpy(&local_property, properties, sizeof(mosquitto_property)); + local_property.client_generated = true; + local_property.next = NULL; + outgoing_properties = &local_property; + } + rc = mosquitto_property_check_all(CMD_DISCONNECT, outgoing_properties); + if(rc) return rc; + } pthread_mutex_lock(&mosq->state_mutex); mosq->state = mosq_cs_disconnecting; pthread_mutex_unlock(&mosq->state_mutex); if(mosq->sock == INVALID_SOCKET) return MOSQ_ERR_NO_CONN; - return send__disconnect(mosq); + return send__disconnect(mosq, reason_code, outgoing_properties); +} + + +void do_client_disconnect(struct mosquitto *mosq, int reason_code, const mosquitto_property *properties) +{ + pthread_mutex_lock(&mosq->state_mutex); + mosq->state = mosq_cs_disconnecting; + pthread_mutex_unlock(&mosq->state_mutex); + + net__socket_close(mosq); + + /* Free data and reset values */ + pthread_mutex_lock(&mosq->out_packet_mutex); + mosq->current_out_packet = mosq->out_packet; + if(mosq->out_packet){ + mosq->out_packet = mosq->out_packet->next; + if(!mosq->out_packet){ + mosq->out_packet_last = NULL; + } + } + pthread_mutex_unlock(&mosq->out_packet_mutex); + + pthread_mutex_lock(&mosq->msgtime_mutex); + mosq->next_msg_out = mosquitto_time() + mosq->keepalive; + pthread_mutex_unlock(&mosq->msgtime_mutex); + + pthread_mutex_lock(&mosq->callback_mutex); + if(mosq->on_disconnect){ + mosq->in_callback = true; + mosq->on_disconnect(mosq, mosq->userdata, reason_code); + mosq->in_callback = false; + } + if(mosq->on_disconnect_v5){ + mosq->in_callback = true; + mosq->on_disconnect_v5(mosq, mosq->userdata, reason_code, properties); + mosq->in_callback = false; + } + pthread_mutex_unlock(&mosq->callback_mutex); + pthread_mutex_unlock(&mosq->current_out_packet_mutex); } diff --git a/lib/cpp/CMakeLists.txt b/lib/cpp/CMakeLists.txt index 5e4f08ea..b2d86216 100644 --- a/lib/cpp/CMakeLists.txt +++ b/lib/cpp/CMakeLists.txt @@ -15,16 +15,16 @@ set_target_properties(mosquittopp PROPERTIES ) install(TARGETS mosquittopp RUNTIME DESTINATION "${BINDIR}" LIBRARY DESTINATION "${LIBDIR}") -if (${WITH_STATIC_LIBRARIES} STREQUAL ON) +if (WITH_STATIC_LIBRARIES) add_library(mosquittopp_static STATIC ${C_SRC} ${CPP_SRC} ) - if (${WITH_PIC} STREQUAL ON) + if (WITH_PIC) set_target_properties(mosquittopp_static PROPERTIES POSITION_INDEPENDENT_CODE 1 ) - endif (${WITH_PIC} STREQUAL ON) + endif (WITH_PIC) target_link_libraries(mosquittopp_static ${LIBRARIES}) @@ -35,7 +35,7 @@ if (${WITH_STATIC_LIBRARIES} STREQUAL ON) target_compile_definitions(mosquittopp_static PUBLIC "LIBMOSQUITTO_STATIC") install(TARGETS mosquittopp_static RUNTIME DESTINATION "${BINDIR}" ARCHIVE DESTINATION "${LIBDIR}") -endif (${WITH_STATIC_LIBRARIES} STREQUAL ON) +endif (WITH_STATIC_LIBRARIES) install(FILES mosquittopp.h DESTINATION "${INCLUDEDIR}") diff --git a/lib/handle_auth.c b/lib/handle_auth.c new file mode 100644 index 00000000..5b84d3de --- /dev/null +++ b/lib/handle_auth.c @@ -0,0 +1,49 @@ +/* +Copyright (c) 2018 Roger Light + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#include +#include + +#include "logging_mosq.h" +#include "mosquitto_internal.h" +#include "mqtt_protocol.h" +#include "packet_mosq.h" +#include "property_mosq.h" + + +int handle__auth(struct mosquitto *mosq) +{ + int rc = 0; + uint8_t reason_code; + mosquitto_property *properties = NULL; + + if(!mosq) return MOSQ_ERR_INVAL; + log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received AUTH", mosq->id); + + if(mosq->protocol != mosq_p_mqtt5){ + return MOSQ_ERR_PROTOCOL; + } + + if(packet__read_byte(&mosq->in_packet, &reason_code)) return 1; + + rc = property__read_all(CMD_AUTH, &mosq->in_packet, &properties); + if(rc) return rc; + mosquitto_property_free_all(&properties); /* FIXME - TEMPORARY UNTIL PROPERTIES PROCESSED */ + + return MOSQ_ERR_SUCCESS; +} diff --git a/lib/handle_connack.c b/lib/handle_connack.c index 9d0a01cd..1f8a9400 100644 --- a/lib/handle_connack.c +++ b/lib/handle_connack.c @@ -22,35 +22,73 @@ Contributors: #include "logging_mosq.h" #include "memory_mosq.h" #include "messages_mosq.h" +#include "mqtt_protocol.h" #include "net_mosq.h" #include "packet_mosq.h" +#include "property_mosq.h" #include "read_handle.h" int handle__connack(struct mosquitto *mosq) { uint8_t connect_flags; - uint8_t result; + uint8_t reason_code; int rc; + mosquitto_property *properties = NULL; + char *clientid = NULL; assert(mosq); rc = packet__read_byte(&mosq->in_packet, &connect_flags); if(rc) return rc; - rc = packet__read_byte(&mosq->in_packet, &result); + rc = packet__read_byte(&mosq->in_packet, &reason_code); if(rc) return rc; - log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received CONNACK (%d)", mosq->id, result); + + if(mosq->protocol == mosq_p_mqtt5){ + rc = property__read_all(CMD_CONNACK, &mosq->in_packet, &properties); + if(rc) return rc; + } + + mosquitto_property_read_string(properties, MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER, &clientid, false); + if(clientid){ + if(mosq->id){ + /* We've been sent a client identifier but already have one. This + * shouldn't happen. */ + free(clientid); + mosquitto_property_free_all(&properties); + return MOSQ_ERR_PROTOCOL; + }else{ + mosq->id = clientid; + clientid = NULL; + } + } + + mosquitto_property_read_byte(properties, MQTT_PROP_MAXIMUM_QOS, &mosq->maximum_qos, false); + mosquitto_property_read_int16(properties, MQTT_PROP_RECEIVE_MAXIMUM, &mosq->send_maximum, false); + mosquitto_property_read_int16(properties, MQTT_PROP_SERVER_KEEP_ALIVE, &mosq->keepalive, false); + mosquitto_property_read_int32(properties, MQTT_PROP_MAXIMUM_PACKET_SIZE, &mosq->maximum_packet_size, false); + + mosq->send_quota = mosq->send_maximum; + + log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received CONNACK (%d)", mosq->id, reason_code); pthread_mutex_lock(&mosq->callback_mutex); if(mosq->on_connect){ mosq->in_callback = true; - mosq->on_connect(mosq, mosq->userdata, result); + mosq->on_connect(mosq, mosq->userdata, reason_code); mosq->in_callback = false; } if(mosq->on_connect_with_flags){ mosq->in_callback = true; - mosq->on_connect_with_flags(mosq, mosq->userdata, result, connect_flags); + mosq->on_connect_with_flags(mosq, mosq->userdata, reason_code, connect_flags); + mosq->in_callback = false; + } + if(mosq->on_connect_v5){ + mosq->in_callback = true; + mosq->on_connect_v5(mosq, mosq->userdata, reason_code, connect_flags, properties); mosq->in_callback = false; } pthread_mutex_unlock(&mosq->callback_mutex); - switch(result){ + mosquitto_property_free_all(&properties); + + switch(reason_code){ case 0: if(mosq->state != mosq_cs_disconnecting){ mosq->state = mosq_cs_connected; diff --git a/lib/handle_disconnect.c b/lib/handle_disconnect.c new file mode 100644 index 00000000..0ec1c8b3 --- /dev/null +++ b/lib/handle_disconnect.c @@ -0,0 +1,62 @@ +/* +Copyright (c) 2009-2018 Roger Light + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#include +#include + +#include "logging_mosq.h" +#include "mqtt_protocol.h" +#include "memory_mosq.h" +#include "net_mosq.h" +#include "packet_mosq.h" +#include "property_mosq.h" +#include "send_mosq.h" +#include "util_mosq.h" + +int handle__disconnect(struct mosquitto *mosq) +{ + int rc; + uint8_t reason_code; + mosquitto_property *properties = NULL; + + if(!mosq){ + return MOSQ_ERR_INVAL; + } + + if(mosq->protocol != mosq_p_mqtt5){ + return MOSQ_ERR_PROTOCOL; + } + + rc = packet__read_byte(&mosq->in_packet, &reason_code); + if(rc) return rc; + + if(mosq->in_packet.remaining_length > 2){ + rc = property__read_all(CMD_DISCONNECT, &mosq->in_packet, &properties); + if(rc) return rc; + mosquitto_property_free_all(&properties); + } + + log__printf(mosq, MOSQ_LOG_DEBUG, "Received DISCONNECT (%d)", reason_code); + + do_client_disconnect(mosq, reason_code, properties); + + mosquitto_property_free_all(&properties); + + return MOSQ_ERR_SUCCESS; +} + diff --git a/lib/handle_ping.c b/lib/handle_ping.c index 17b53be0..ad56dda0 100644 --- a/lib/handle_ping.c +++ b/lib/handle_ping.c @@ -28,7 +28,7 @@ Contributors: #include "logging_mosq.h" #include "memory_mosq.h" #include "messages_mosq.h" -#include "mqtt3_protocol.h" +#include "mqtt_protocol.h" #include "net_mosq.h" #include "packet_mosq.h" #include "read_handle.h" diff --git a/lib/handle_pubackcomp.c b/lib/handle_pubackcomp.c index 37788456..6c062bb4 100644 --- a/lib/handle_pubackcomp.c +++ b/lib/handle_pubackcomp.c @@ -28,7 +28,7 @@ Contributors: #include "logging_mosq.h" #include "memory_mosq.h" #include "messages_mosq.h" -#include "mqtt3_protocol.h" +#include "mqtt_protocol.h" #include "net_mosq.h" #include "packet_mosq.h" #include "read_handle.h" @@ -42,17 +42,36 @@ int handle__pubackcomp(struct mosquitto_db *db, struct mosquitto *mosq, const ch int handle__pubackcomp(struct mosquitto *mosq, const char *type) #endif { + uint8_t reason_code = 0; uint16_t mid; int rc; + mosquitto_property *properties = NULL; + int qos; assert(mosq); rc = packet__read_uint16(&mosq->in_packet, &mid); if(rc) return rc; + qos = type[3] == 'A'?1:2; /* pubAck or pubComp */ + if(mid == 0) return MOSQ_ERR_PROTOCOL; + + 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; + + if(mosq->in_packet.remaining_length > 3){ + rc = property__read_all(CMD_PUBACK, &mosq->in_packet, &properties); + if(rc) return rc; + } + } + #ifdef WITH_BROKER log__printf(NULL, MOSQ_LOG_DEBUG, "Received %s from %s (Mid: %d)", type, mosq->id, mid); + /* Immediately free, we don't do anything with Reason String or User Property at the moment */ + mosquitto_property_free_all(&properties); + if(mid){ - rc = db__message_delete(db, mosq, mid, mosq_md_out); + rc = db__message_delete(db, mosq, mid, mosq_md_out, mosq_ms_wait_for_pubcomp, qos); if(rc == MOSQ_ERR_NOT_FOUND){ log__printf(mosq, MOSQ_LOG_WARNING, "Warning: Received %s from %s for an unknown packet identifier %d.", type, mosq->id, mid); return MOSQ_ERR_SUCCESS; @@ -63,7 +82,10 @@ int handle__pubackcomp(struct mosquitto *mosq, const char *type) #else log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received %s (Mid: %d)", mosq->id, type, mid); - if(!message__delete(mosq, mid, mosq_md_out)){ + rc = message__delete(mosq, mid, mosq_md_out, qos); + if(rc){ + return rc; + }else{ /* Only inform the client the message has been sent once. */ pthread_mutex_lock(&mosq->callback_mutex); if(mosq->on_publish){ @@ -71,7 +93,13 @@ int handle__pubackcomp(struct mosquitto *mosq, const char *type) mosq->on_publish(mosq, mosq->userdata, mid); mosq->in_callback = false; } + 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); + mosquitto_property_free_all(&properties); } #endif diff --git a/lib/handle_publish.c b/lib/handle_publish.c index f8092f89..acebd90a 100644 --- a/lib/handle_publish.c +++ b/lib/handle_publish.c @@ -23,8 +23,10 @@ Contributors: #include "mosquitto_internal.h" #include "logging_mosq.h" #include "memory_mosq.h" +#include "mqtt_protocol.h" #include "messages_mosq.h" #include "packet_mosq.h" +#include "property_mosq.h" #include "send_mosq.h" #include "time_mosq.h" @@ -36,6 +38,7 @@ int handle__publish(struct mosquitto *mosq) int rc = 0; uint16_t mid; int slen; + mosquitto_property *properties = NULL; assert(mosq); @@ -59,24 +62,44 @@ int handle__publish(struct mosquitto *mosq) } if(message->msg.qos > 0){ + if(mosq->protocol == mosq_p_mqtt5){ + if(mosq->receive_quota == 0){ + message__cleanup(&message); + /* FIXME - should send a DISCONNECT here */ + return MOSQ_ERR_PROTOCOL; + } + mosq->receive_quota--; + } + rc = packet__read_uint16(&mosq->in_packet, &mid); if(rc){ message__cleanup(&message); return rc; } + if(mid == 0){ + message__cleanup(&message); + return MOSQ_ERR_PROTOCOL; + } message->msg.mid = (int)mid; } + if(mosq->protocol == mosq_p_mqtt5){ + rc = property__read_all(CMD_PUBLISH, &mosq->in_packet, &properties); + if(rc) return rc; + } + message->msg.payloadlen = mosq->in_packet.remaining_length - mosq->in_packet.pos; if(message->msg.payloadlen){ message->msg.payload = mosquitto__calloc(message->msg.payloadlen+1, sizeof(uint8_t)); if(!message->msg.payload){ message__cleanup(&message); + mosquitto_property_free_all(&properties); return MOSQ_ERR_NOMEM; } rc = packet__read_bytes(&mosq->in_packet, message->msg.payload, message->msg.payloadlen); if(rc){ message__cleanup(&message); + mosquitto_property_free_all(&properties); return rc; } } @@ -95,8 +118,14 @@ int handle__publish(struct mosquitto *mosq) mosq->on_message(mosq, mosq->userdata, &message->msg); mosq->in_callback = false; } + if(mosq->on_message_v5){ + mosq->in_callback = true; + mosq->on_message_v5(mosq, mosq->userdata, &message->msg, properties); + mosq->in_callback = false; + } pthread_mutex_unlock(&mosq->callback_mutex); message__cleanup(&message); + mosquitto_property_free_all(&properties); return MOSQ_ERR_SUCCESS; case 1: rc = send__puback(mosq, message->msg.mid); @@ -106,8 +135,14 @@ int handle__publish(struct mosquitto *mosq) mosq->on_message(mosq, mosq->userdata, &message->msg); mosq->in_callback = false; } + if(mosq->on_message_v5){ + mosq->in_callback = true; + mosq->on_message_v5(mosq, mosq->userdata, &message->msg, properties); + mosq->in_callback = false; + } pthread_mutex_unlock(&mosq->callback_mutex); message__cleanup(&message); + mosquitto_property_free_all(&properties); return rc; case 2: rc = send__pubrec(mosq, message->msg.mid); @@ -115,9 +150,11 @@ int handle__publish(struct mosquitto *mosq) message->state = mosq_ms_wait_for_pubrel; message__queue(mosq, message, mosq_md_in); pthread_mutex_unlock(&mosq->in_message_mutex); + mosquitto_property_free_all(&properties); return rc; default: message__cleanup(&message); + mosquitto_property_free_all(&properties); return MOSQ_ERR_PROTOCOL; } } diff --git a/lib/handle_pubrec.c b/lib/handle_pubrec.c index 59eea1b7..9dd6d19c 100644 --- a/lib/handle_pubrec.c +++ b/lib/handle_pubrec.c @@ -28,29 +28,63 @@ Contributors: #include "logging_mosq.h" #include "memory_mosq.h" #include "messages_mosq.h" -#include "mqtt3_protocol.h" +#include "mqtt_protocol.h" #include "net_mosq.h" #include "packet_mosq.h" #include "read_handle.h" #include "send_mosq.h" #include "util_mosq.h" -int handle__pubrec(struct mosquitto *mosq) +int handle__pubrec(struct mosquitto_db *db, struct mosquitto *mosq) { + uint8_t reason_code = 0; uint16_t mid; int rc; + mosquitto_property *properties = NULL; assert(mosq); rc = packet__read_uint16(&mosq->in_packet, &mid); if(rc) return rc; + if(mid == 0) return MOSQ_ERR_PROTOCOL; + + 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; + + 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 log__printf(NULL, MOSQ_LOG_DEBUG, "Received PUBREC from %s (Mid: %d)", mosq->id, mid); - rc = db__message_update(mosq, mid, mosq_md_out, mosq_ms_wait_for_pubcomp); + if(reason_code < 0x80){ + rc = db__message_update(mosq, mid, mosq_md_out, mosq_ms_wait_for_pubcomp, 2); + }else{ + return db__message_delete(db, mosq, mid, mosq_md_out, mosq_ms_wait_for_pubrec, 2); + } #else log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received PUBREC (Mid: %d)", mosq->id, mid); - rc = message__out_update(mosq, mid, mosq_ms_wait_for_pubcomp); + if(reason_code < 0x80){ + rc = message__out_update(mosq, mid, mosq_ms_wait_for_pubcomp, 2); + }else{ + if(!message__delete(mosq, mid, mosq_md_out, 2)){ + /* 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 if(rc == MOSQ_ERR_NOT_FOUND){ log__printf(mosq, MOSQ_LOG_WARNING, "Warning: Received PUBREC from %s for an unknown packet identifier %d.", mosq->id, mid); diff --git a/lib/handle_pubrel.c b/lib/handle_pubrel.c index 56660987..110bf4ae 100644 --- a/lib/handle_pubrel.c +++ b/lib/handle_pubrel.c @@ -28,7 +28,7 @@ Contributors: #include "logging_mosq.h" #include "memory_mosq.h" #include "messages_mosq.h" -#include "mqtt3_protocol.h" +#include "mqtt_protocol.h" #include "net_mosq.h" #include "packet_mosq.h" #include "read_handle.h" @@ -38,32 +38,64 @@ Contributors: int handle__pubrel(struct mosquitto_db *db, struct mosquitto *mosq) { + uint8_t reason_code; uint16_t mid; #ifndef WITH_BROKER struct mosquitto_message_all *message = NULL; #endif int rc; + mosquitto_property *properties = NULL; assert(mosq); - if(mosq->protocol == mosq_p_mqtt311){ + if(mosq->protocol != mosq_p_mqtt31){ if((mosq->in_packet.command&0x0F) != 0x02){ return MOSQ_ERR_PROTOCOL; } } rc = packet__read_uint16(&mosq->in_packet, &mid); if(rc) return rc; + if(mid == 0) return MOSQ_ERR_PROTOCOL; + + 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; + + if(mosq->in_packet.remaining_length > 3){ + rc = property__read_all(CMD_PUBREL, &mosq->in_packet, &properties); + if(rc) return rc; + } + } + #ifdef WITH_BROKER log__printf(NULL, MOSQ_LOG_DEBUG, "Received PUBREL from %s (Mid: %d)", mosq->id, mid); - if(db__message_release(db, mosq, mid, mosq_md_in)){ + /* Immediately free, we don't do anything with Reason String or User Property at the moment */ + mosquitto_property_free_all(&properties); + + rc = db__message_release(db, mosq, mid, mosq_md_in); + if(rc == MOSQ_ERR_PROTOCOL){ + return rc; + }else if(rc != MOSQ_ERR_SUCCESS){ /* Message not found. Still send a PUBCOMP anyway because this could be * 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); - if(!message__remove(mosq, mid, mosq_md_in, &message)){ + rc = send__pubcomp(mosq, mid); + if(rc){ + message__remove(mosq, mid, mosq_md_in, &message, 2); + return rc; + } + + rc = message__remove(mosq, mid, mosq_md_in, &message, 2); + if(rc){ + return rc; + }else{ /* Only pass the message on if we have removed it from the queue - this * prevents multiple callbacks for the same message. */ pthread_mutex_lock(&mosq->callback_mutex); @@ -72,12 +104,16 @@ int handle__pubrel(struct mosquitto_db *db, struct mosquitto *mosq) mosq->on_message(mosq, mosq->userdata, &message->msg); mosq->in_callback = false; } + if(mosq->on_message_v5){ + mosq->in_callback = true; + mosq->on_message_v5(mosq, mosq->userdata, &message->msg, properties); + mosq->in_callback = false; + } pthread_mutex_unlock(&mosq->callback_mutex); + mosquitto_property_free_all(&properties); message__cleanup(&message); } #endif - rc = send__pubcomp(mosq, mid); - if(rc) return rc; return MOSQ_ERR_SUCCESS; } diff --git a/lib/handle_suback.c b/lib/handle_suback.c index d6e3d19c..5f142ac5 100644 --- a/lib/handle_suback.c +++ b/lib/handle_suback.c @@ -26,7 +26,9 @@ Contributors: #include "mosquitto_internal.h" #include "logging_mosq.h" #include "memory_mosq.h" +#include "mqtt_protocol.h" #include "packet_mosq.h" +#include "property_mosq.h" int handle__suback(struct mosquitto *mosq) @@ -37,6 +39,7 @@ int handle__suback(struct mosquitto *mosq) int qos_count; int i = 0; int rc; + mosquitto_property *properties = NULL; assert(mosq); #ifdef WITH_BROKER @@ -46,6 +49,12 @@ int handle__suback(struct mosquitto *mosq) #endif rc = packet__read_uint16(&mosq->in_packet, &mid); if(rc) return rc; + if(mid == 0) return MOSQ_ERR_PROTOCOL; + + if(mosq->protocol == mosq_p_mqtt5){ + rc = property__read_all(CMD_SUBACK, &mosq->in_packet, &properties); + if(rc) return rc; + } qos_count = mosq->in_packet.remaining_length - mosq->in_packet.pos; granted_qos = mosquitto__malloc(qos_count*sizeof(int)); @@ -59,14 +68,23 @@ int handle__suback(struct mosquitto *mosq) granted_qos[i] = (int)qos; i++; } -#ifndef WITH_BROKER +#ifdef WITH_BROKER + /* Immediately free, we don't do anything with Reason String or User Property at the moment */ + mosquitto_property_free_all(&properties); +#else pthread_mutex_lock(&mosq->callback_mutex); if(mosq->on_subscribe){ mosq->in_callback = true; mosq->on_subscribe(mosq, mosq->userdata, mid, qos_count, granted_qos); mosq->in_callback = false; } + if(mosq->on_subscribe_v5){ + mosq->in_callback = true; + mosq->on_subscribe_v5(mosq, mosq->userdata, mid, qos_count, granted_qos, properties); + mosq->in_callback = false; + } pthread_mutex_unlock(&mosq->callback_mutex); + mosquitto_property_free_all(&properties); #endif mosquitto__free(granted_qos); diff --git a/lib/handle_unsuback.c b/lib/handle_unsuback.c index 6297bb10..bce4d369 100644 --- a/lib/handle_unsuback.c +++ b/lib/handle_unsuback.c @@ -28,9 +28,10 @@ Contributors: #include "logging_mosq.h" #include "memory_mosq.h" #include "messages_mosq.h" -#include "mqtt3_protocol.h" +#include "mqtt_protocol.h" #include "net_mosq.h" #include "packet_mosq.h" +#include "property_mosq.h" #include "read_handle.h" #include "send_mosq.h" #include "util_mosq.h" @@ -40,6 +41,7 @@ int handle__unsuback(struct mosquitto *mosq) { uint16_t mid; int rc; + mosquitto_property *properties = NULL; assert(mosq); #ifdef WITH_BROKER @@ -49,14 +51,30 @@ int handle__unsuback(struct mosquitto *mosq) #endif rc = packet__read_uint16(&mosq->in_packet, &mid); if(rc) return rc; -#ifndef WITH_BROKER + if(mid == 0) return MOSQ_ERR_PROTOCOL; + + if(mosq->protocol == mosq_p_mqtt5){ + rc = property__read_all(CMD_UNSUBACK, &mosq->in_packet, &properties); + if(rc) return rc; + } + +#ifdef WITH_BROKER + /* Immediately free, we don't do anything with Reason String or User Property at the moment */ + mosquitto_property_free_all(&properties); +#else pthread_mutex_lock(&mosq->callback_mutex); if(mosq->on_unsubscribe){ mosq->in_callback = true; mosq->on_unsubscribe(mosq, mosq->userdata, mid); mosq->in_callback = false; } + if(mosq->on_unsubscribe_v5){ + mosq->in_callback = true; + mosq->on_unsubscribe_v5(mosq, mosq->userdata, mid, properties); + mosq->in_callback = false; + } pthread_mutex_unlock(&mosq->callback_mutex); + mosquitto_property_free_all(&properties); #endif return MOSQ_ERR_SUCCESS; diff --git a/lib/linker.version b/lib/linker.version index f92acc10..017d5a5e 100644 --- a/lib/linker.version +++ b/lib/linker.version @@ -94,6 +94,41 @@ MOSQ_1.5 { MOSQ_1.6 { global: + mosquitto_connect_bind_v5; + mosquitto_connect_v5_callback_set; + mosquitto_disconnect_v5; + mosquitto_disconnect_v5_callback_set; + mosquitto_int_option; + mosquitto_message_v5_callback_set; + mosquitto_property_add_binary; + mosquitto_property_add_byte; + mosquitto_property_add_int16; + mosquitto_property_add_int32; + mosquitto_property_add_string; + mosquitto_property_add_string_pair; + mosquitto_property_add_varint; + mosquitto_property_check_all; + mosquitto_property_check_command; + mosquitto_property_free_all; + mosquitto_property_read_binary; + mosquitto_property_read_byte; + mosquitto_property_read_int16; + mosquitto_property_read_int32; + mosquitto_property_read_string; + mosquitto_property_read_string_pair; + mosquitto_property_read_varint; + mosquitto_publish_v5; + mosquitto_publish_v5_callback_set; + mosquitto_reason_string; mosquitto_string_option; + mosquitto_string_to_command; + mosquitto_string_to_property_info; mosquitto_subscribe_multiple; + mosquitto_subscribe_v5; + mosquitto_subscribe_v5_callback_set; + mosquitto_unsubscribe_multiple; + mosquitto_unsubscribe_v5; + mosquitto_unsubscribe_v5_callback_set; + mosquitto_void_option; + mosquitto_will_set_v5; } MOSQ_1.5; diff --git a/lib/loop.c b/lib/loop.c index 23e60825..d3ee0c52 100644 --- a/lib/loop.c +++ b/lib/loop.c @@ -147,12 +147,10 @@ int mosquitto_loop(struct mosquitto *mosq, int timeout, int max_packets) }else{ if(mosq->sock != INVALID_SOCKET){ if(FD_ISSET(mosq->sock, &readfds)){ - do{ - rc = mosquitto_loop_read(mosq, max_packets); - if(rc || mosq->sock == INVALID_SOCKET){ - return rc; - } - }while(SSL_DATA_PENDING(mosq)); + rc = mosquitto_loop_read(mosq, max_packets); + if(rc || mosq->sock == INVALID_SOCKET){ + return rc; + } } if(mosq->sockpairR != INVALID_SOCKET && FD_ISSET(mosq->sockpairR, &readfds)){ #ifndef WIN32 @@ -245,8 +243,12 @@ int mosquitto_loop_forever(struct mosquitto *mosq, int timeout, int max_packets) }else{ pthread_mutex_unlock(&mosq->state_mutex); - if(mosq->reconnect_delay > 0 && mosq->reconnect_exponential_backoff){ - reconnect_delay = mosq->reconnect_delay*reconnects*reconnects; + if(mosq->reconnect_delay_max > mosq->reconnect_delay){ + if(mosq->reconnect_exponential_backoff){ + reconnect_delay = mosq->reconnect_delay*(reconnects+1)*(reconnects+1); + }else{ + reconnect_delay = mosq->reconnect_delay*(reconnects+1); + } }else{ reconnect_delay = mosq->reconnect_delay; } @@ -284,37 +286,10 @@ int mosquitto_loop_forever(struct mosquitto *mosq, int timeout, int max_packets) int mosquitto_loop_misc(struct mosquitto *mosq) { - time_t now; - int rc; - if(!mosq) return MOSQ_ERR_INVAL; if(mosq->sock == INVALID_SOCKET) return MOSQ_ERR_NO_CONN; - mosquitto__check_keepalive(mosq); - now = mosquitto_time(); - - if(mosq->ping_t && now - mosq->ping_t >= mosq->keepalive){ - /* mosq->ping_t != 0 means we are waiting for a pingresp. - * This hasn't happened in the keepalive time so we should disconnect. - */ - net__socket_close(mosq); - pthread_mutex_lock(&mosq->state_mutex); - if(mosq->state == mosq_cs_disconnecting){ - rc = MOSQ_ERR_SUCCESS; - }else{ - rc = MOSQ_ERR_KEEPALIVE; - } - pthread_mutex_unlock(&mosq->state_mutex); - pthread_mutex_lock(&mosq->callback_mutex); - if(mosq->on_disconnect){ - mosq->in_callback = true; - mosq->on_disconnect(mosq, mosq->userdata, rc); - mosq->in_callback = false; - } - pthread_mutex_unlock(&mosq->callback_mutex); - return MOSQ_ERR_CONN_LOST; - } - return MOSQ_ERR_SUCCESS; + return mosquitto__check_keepalive(mosq); } @@ -333,6 +308,11 @@ static int mosquitto__loop_rc_handle(struct mosquitto *mosq, int rc) mosq->on_disconnect(mosq, mosq->userdata, rc); mosq->in_callback = false; } + if(mosq->on_disconnect_v5){ + mosq->in_callback = true; + mosq->on_disconnect_v5(mosq, mosq->userdata, rc, NULL); + mosq->in_callback = false; + } pthread_mutex_unlock(&mosq->callback_mutex); return rc; } @@ -364,7 +344,7 @@ int mosquitto_loop_read(struct mosquitto *mosq, int max_packets) /* Queue len here tells us how many messages are awaiting processing and * have QoS > 0. We should try to deal with that many in this loop in order * to keep up. */ - for(i=0; isocks5_host){ rc = socks5__read(mosq); diff --git a/lib/messages_mosq.c b/lib/messages_mosq.c index f6c50b8b..87ad08d4 100644 --- a/lib/messages_mosq.c +++ b/lib/messages_mosq.c @@ -26,6 +26,7 @@ Contributors: #include "messages_mosq.h" #include "send_mosq.h" #include "time_mosq.h" +#include "util_mosq.h" void message__cleanup(struct mosquitto_message_all **message) { @@ -82,13 +83,13 @@ int mosquitto_message_copy(struct mosquitto_message *dst, const struct mosquitto return MOSQ_ERR_SUCCESS; } -int message__delete(struct mosquitto *mosq, uint16_t mid, enum mosquitto_msg_direction dir) +int message__delete(struct mosquitto *mosq, uint16_t mid, enum mosquitto_msg_direction dir, int qos) { struct mosquitto_message_all *message; int rc; assert(mosq); - rc = message__remove(mosq, mid, dir, &message); + rc = message__remove(mosq, mid, dir, &message, qos); if(rc == MOSQ_ERR_SUCCESS){ message__cleanup(&message); } @@ -123,6 +124,7 @@ int message__queue(struct mosquitto *mosq, struct mosquitto_message_all *message /* mosq->*_message_mutex should be locked before entering this function */ assert(mosq); assert(message); + assert(message->msg.qos != 0); if(dir == mosq_md_out){ mosq->out_queue_len++; @@ -133,12 +135,10 @@ int message__queue(struct mosquitto *mosq, struct mosquitto_message_all *message mosq->out_messages = message; } mosq->out_messages_last = message; - if(message->msg.qos > 0){ - if(mosq->max_inflight_messages == 0 || mosq->inflight_messages < mosq->max_inflight_messages){ - mosq->inflight_messages++; - }else{ - rc = 1; - } + if(mosq->send_quota > 0){ + mosq->send_quota--; + }else{ + rc = 1; } }else{ mosq->in_queue_len++; @@ -187,17 +187,15 @@ void message__reconnect_reset(struct mosquitto *mosq) pthread_mutex_lock(&mosq->out_message_mutex); - mosq->inflight_messages = 0; + mosq->send_quota = mosq->send_maximum; message = mosq->out_messages; mosq->out_queue_len = 0; while(message){ mosq->out_queue_len++; message->timestamp = 0; - if(mosq->max_inflight_messages == 0 || mosq->inflight_messages < mosq->max_inflight_messages){ - if(message->msg.qos > 0){ - mosq->inflight_messages++; - } + if(mosq->send_quota > 0){ + mosq->send_quota--; if(message->msg.qos == 1){ message->state = mosq_ms_publish_qos1; }else if(message->msg.qos == 2){ @@ -218,7 +216,7 @@ void message__reconnect_reset(struct mosquitto *mosq) pthread_mutex_unlock(&mosq->out_message_mutex); } -int message__remove(struct mosquitto *mosq, uint16_t mid, enum mosquitto_msg_direction dir, struct mosquitto_message_all **message) +int message__remove(struct mosquitto *mosq, uint16_t mid, enum mosquitto_msg_direction dir, struct mosquitto_message_all **message, int qos) { struct mosquitto_message_all *cur, *prev = NULL; bool found = false; @@ -231,6 +229,9 @@ int message__remove(struct mosquitto *mosq, uint16_t mid, enum mosquitto_msg_dir cur = mosq->out_messages; while(cur){ if(cur->msg.mid == mid){ + if(cur->msg.qos != qos){ + return MOSQ_ERR_PROTOCOL; + } if(prev){ prev->next = cur->next; }else{ @@ -243,9 +244,7 @@ int message__remove(struct mosquitto *mosq, uint16_t mid, enum mosquitto_msg_dir }else if(!mosq->out_messages){ mosq->out_messages_last = NULL; } - if(cur->msg.qos > 0){ - mosq->inflight_messages--; - } + util__increment_send_quota(mosq); found = true; break; } @@ -256,15 +255,15 @@ int message__remove(struct mosquitto *mosq, uint16_t mid, enum mosquitto_msg_dir if(found){ cur = mosq->out_messages; while(cur){ - if(mosq->max_inflight_messages == 0 || mosq->inflight_messages < mosq->max_inflight_messages){ + if(mosq->send_quota > 0){ if(cur->msg.qos > 0 && cur->state == mosq_ms_invalid){ - mosq->inflight_messages++; + mosq->send_quota--; if(cur->msg.qos == 1){ cur->state = mosq_ms_wait_for_puback; }else if(cur->msg.qos == 2){ cur->state = mosq_ms_wait_for_pubrec; } - rc = send__publish(mosq, cur->msg.mid, cur->msg.topic, cur->msg.payloadlen, cur->msg.payload, cur->msg.qos, cur->msg.retain, cur->dup); + rc = send__publish(mosq, cur->msg.mid, cur->msg.topic, cur->msg.payloadlen, cur->msg.payload, cur->msg.qos, cur->msg.retain, cur->dup, NULL, NULL, 0); if(rc){ pthread_mutex_unlock(&mosq->out_message_mutex); return rc; @@ -287,6 +286,9 @@ int message__remove(struct mosquitto *mosq, uint16_t mid, enum mosquitto_msg_dir cur = mosq->in_messages; while(cur){ if(cur->msg.mid == mid){ + if(cur->msg.qos != qos){ + return MOSQ_ERR_PROTOCOL; + } if(prev){ prev->next = cur->next; }else{ @@ -334,7 +336,7 @@ void message__retry_check_actual(struct mosquitto *mosq, struct mosquitto_messag case mosq_ms_publish_qos2: messages->timestamp = now; messages->dup = true; - send__publish(mosq, messages->msg.mid, messages->msg.topic, messages->msg.payloadlen, messages->msg.payload, messages->msg.qos, messages->msg.retain, messages->dup); + send__publish(mosq, messages->msg.mid, messages->msg.topic, messages->msg.payloadlen, messages->msg.payload, messages->msg.qos, messages->msg.retain, messages->dup, NULL, NULL, 0); break; case mosq_ms_wait_for_pubrel: messages->timestamp = now; @@ -370,7 +372,7 @@ void mosquitto_message_retry_set(struct mosquitto *mosq, unsigned int message_re { } -int message__out_update(struct mosquitto *mosq, uint16_t mid, enum mosquitto_msg_state state) +int message__out_update(struct mosquitto *mosq, uint16_t mid, enum mosquitto_msg_state state, int qos) { struct mosquitto_message_all *message; assert(mosq); @@ -379,6 +381,9 @@ int message__out_update(struct mosquitto *mosq, uint16_t mid, enum mosquitto_msg message = mosq->out_messages; while(message){ if(message->msg.mid == mid){ + if(message->msg.qos != qos){ + return MOSQ_ERR_PROTOCOL; + } message->state = state; message->timestamp = mosquitto_time(); pthread_mutex_unlock(&mosq->out_message_mutex); @@ -394,7 +399,7 @@ int mosquitto_max_inflight_messages_set(struct mosquitto *mosq, unsigned int max { if(!mosq) return MOSQ_ERR_INVAL; - mosq->max_inflight_messages = max_inflight_messages; + mosq->send_maximum = max_inflight_messages; return MOSQ_ERR_SUCCESS; } diff --git a/lib/messages_mosq.h b/lib/messages_mosq.h index 85b82185..42385003 100644 --- a/lib/messages_mosq.h +++ b/lib/messages_mosq.h @@ -21,11 +21,11 @@ Contributors: void message__cleanup_all(struct mosquitto *mosq); void message__cleanup(struct mosquitto_message_all **message); -int message__delete(struct mosquitto *mosq, uint16_t mid, enum mosquitto_msg_direction dir); +int message__delete(struct mosquitto *mosq, uint16_t mid, enum mosquitto_msg_direction dir, int qos); int message__queue(struct mosquitto *mosq, struct mosquitto_message_all *message, enum mosquitto_msg_direction dir); void message__reconnect_reset(struct mosquitto *mosq); -int message__remove(struct mosquitto *mosq, uint16_t mid, enum mosquitto_msg_direction dir, struct mosquitto_message_all **message); +int message__remove(struct mosquitto *mosq, uint16_t mid, enum mosquitto_msg_direction dir, struct mosquitto_message_all **message, int qos); void message__retry_check(struct mosquitto *mosq); -int message__out_update(struct mosquitto *mosq, uint16_t mid, enum mosquitto_msg_state state); +int message__out_update(struct mosquitto *mosq, uint16_t mid, enum mosquitto_msg_state state, int qos); #endif diff --git a/lib/mosquitto.c b/lib/mosquitto.c index eab1d99a..45690d65 100644 --- a/lib/mosquitto.c +++ b/lib/mosquitto.c @@ -27,6 +27,7 @@ Contributors: #include "mosquitto_internal.h" #include "memory_mosq.h" #include "messages_mosq.h" +#include "mqtt_protocol.h" #include "net_mosq.h" #include "packet_mosq.h" #include "will_mosq.h" @@ -63,12 +64,12 @@ int mosquitto_lib_cleanup(void) return MOSQ_ERR_SUCCESS; } -struct mosquitto *mosquitto_new(const char *id, bool clean_session, void *userdata) +struct mosquitto *mosquitto_new(const char *id, bool clean_start, void *userdata) { struct mosquitto *mosq = NULL; int rc; - if(clean_session == false && id == NULL){ + if(clean_start == false && id == NULL){ errno = EINVAL; return NULL; } @@ -85,7 +86,7 @@ struct mosquitto *mosquitto_new(const char *id, bool clean_session, void *userda #ifdef WITH_THREADING mosq->thread_id = pthread_self(); #endif - rc = mosquitto_reinitialise(mosq, id, clean_session, userdata); + rc = mosquitto_reinitialise(mosq, id, clean_start, userdata); if(rc){ mosquitto_destroy(mosq); if(rc == MOSQ_ERR_INVAL){ @@ -101,13 +102,11 @@ struct mosquitto *mosquitto_new(const char *id, bool clean_session, void *userda return mosq; } -int mosquitto_reinitialise(struct mosquitto *mosq, const char *id, bool clean_session, void *userdata) +int mosquitto_reinitialise(struct mosquitto *mosq, const char *id, bool clean_start, void *userdata) { - int i; - if(!mosq) return MOSQ_ERR_INVAL; - if(clean_session == false && id == NULL){ + if(clean_start == false && id == NULL){ return MOSQ_ERR_INVAL; } @@ -124,7 +123,7 @@ int mosquitto_reinitialise(struct mosquitto *mosq, const char *id, bool clean_se mosq->sockpairR = INVALID_SOCKET; mosq->sockpairW = INVALID_SOCKET; mosq->keepalive = 60; - mosq->clean_session = clean_session; + mosq->clean_start = clean_start; if(id){ if(STREMPTY(id)){ return MOSQ_ERR_INVAL; @@ -133,20 +132,6 @@ int mosquitto_reinitialise(struct mosquitto *mosq, const char *id, bool clean_se return MOSQ_ERR_MALFORMED_UTF8; } mosq->id = mosquitto__strdup(id); - }else{ - mosq->id = (char *)mosquitto__calloc(24, sizeof(char)); - if(!mosq->id){ - return MOSQ_ERR_NOMEM; - } - mosq->id[0] = 'm'; - mosq->id[1] = 'o'; - mosq->id[2] = 's'; - mosq->id[3] = 'q'; - mosq->id[4] = '/'; - - for(i=5; i<23; i++){ - mosq->id[i] = (rand()%73)+48; - } } mosq->in_packet.payload = NULL; packet__cleanup(&mosq->in_packet); @@ -161,7 +146,9 @@ int mosquitto_reinitialise(struct mosquitto *mosq, const char *id, bool clean_se mosq->in_messages_last = NULL; mosq->out_messages = NULL; mosq->out_messages_last = NULL; - mosq->max_inflight_messages = 20; + mosq->maximum_qos = 2; + mosq->receive_maximum = 20; + mosq->send_maximum = 20; mosq->will = NULL; mosq->on_connect = NULL; mosq->on_publish = NULL; @@ -372,6 +359,14 @@ const char *mosquitto_strerror(int mosq_errno) return "Proxy error."; case MOSQ_ERR_MALFORMED_UTF8: return "Malformed UTF-8"; + case MOSQ_ERR_DUPLICATE_PROPERTY: + return "Duplicate property in property list"; + case MOSQ_ERR_TLS_HANDSHAKE: + return "TLS handshake failed."; + case MOSQ_ERR_QOS_NOT_SUPPORTED: + return "Requested QoS not supported on server."; + case MOSQ_ERR_OVERSIZE_PACKET: + return "Packet larger than supported by the server."; default: return "Unknown error."; } @@ -397,6 +392,135 @@ const char *mosquitto_connack_string(int connack_code) } } +const char *mosquitto_reason_string(int reason_code) +{ + switch(reason_code){ + case MQTT_RC_SUCCESS: + return "Success"; + case MQTT_RC_GRANTED_QOS1: + return "Granted QoS 1"; + case MQTT_RC_GRANTED_QOS2: + return "Granted QoS 2"; + case MQTT_RC_DISCONNECT_WITH_WILL_MSG: + return "Disconnect with Will Message"; + case MQTT_RC_NO_MATCHING_SUBSCRIBERS: + return "No matching subscribers"; + case MQTT_RC_NO_SUBSCRIPTION_EXISTED: + return "No subscription existed"; + case MQTT_RC_CONTINUE_AUTHENTICATION: + return "Continue authentication"; + case MQTT_RC_REAUTHENTICATE: + return "Re-authenticate"; + + case MQTT_RC_UNSPECIFIED: + return "Unspecified error"; + case MQTT_RC_MALFORMED_PACKET: + return "Malformed Packet"; + case MQTT_RC_PROTOCOL_ERROR: + return "Protocol Error"; + case MQTT_RC_IMPLEMENTATION_SPECIFIC: + return "Implementation specific error"; + case MQTT_RC_UNSUPPORTED_PROTOCOL_VERSION: + return "Unsupported Protocol Version"; + case MQTT_RC_CLIENTID_NOT_VALID: + return "Client Identifier not valid"; + case MQTT_RC_BAD_USERNAME_OR_PASSWORD: + return "Bad User Name or Password"; + case MQTT_RC_NOT_AUTHORIZED: + return "Not authorized"; + case MQTT_RC_SERVER_UNAVAILABLE: + return "Server unavailable"; + case MQTT_RC_SERVER_BUSY: + return "Server busy"; + case MQTT_RC_BANNED: + return "Banned"; + case MQTT_RC_SERVER_SHUTTING_DOWN: + return "Server shutting down"; + case MQTT_RC_BAD_AUTHENTICATION_METHOD: + return "Bad authentication method"; + case MQTT_RC_KEEP_ALIVE_TIMEOUT: + return "Keep Alive timeout"; + case MQTT_RC_SESSION_TAKEN_OVER: + return "Session taken over"; + case MQTT_RC_TOPIC_FILTER_INVALID: + return "Topic Filter invalid"; + case MQTT_RC_TOPIC_NAME_INVALID: + return "Topic Name invalid"; + case MQTT_RC_PACKET_ID_IN_USE: + return "Packet Identifier in use"; + case MQTT_RC_PACKET_ID_NOT_FOUND: + return "Packet Identifier not found"; + case MQTT_RC_RECEIVE_MAXIMUM_EXCEEDED: + return "Receive Maximum exceeded"; + case MQTT_RC_TOPIC_ALIAS_INVALID: + return "Topic Alias invalid"; + case MQTT_RC_PACKET_TOO_LARGE: + return "Packet too large"; + case MQTT_RC_MESSAGE_RATE_TOO_HIGH: + return "Message rate too high"; + case MQTT_RC_QUOTA_EXCEEDED: + return "Quota exceeded"; + case MQTT_RC_ADMINISTRATIVE_ACTION: + return "Administrative action"; + case MQTT_RC_PAYLOAD_FORMAT_INVALID: + return "Payload format invalid"; + case MQTT_RC_RETAIN_NOT_SUPPORTED: + return "Retain not supported"; + case MQTT_RC_QOS_NOT_SUPPORTED: + return "QoS not supported"; + case MQTT_RC_USE_ANOTHER_SERVER: + return "Use another server"; + case MQTT_RC_SERVER_MOVED: + return "Server moved"; + case MQTT_RC_SHARED_SUBS_NOT_SUPPORTED: + return "Shared Subscriptions not supported"; + case MQTT_RC_CONNECTION_RATE_EXCEEDED: + return "Connection rate exceeded"; + case MQTT_RC_MAXIMUM_CONNECT_TIME: + return "Maximum connect time"; + case MQTT_RC_SUBSCRIPTION_IDS_NOT_SUPPORTED: + return "Subscription identifiers not supported"; + case MQTT_RC_WILDCARD_SUBS_NOT_SUPPORTED: + return "Wildcard Subscriptions not supported"; + default: + return "Unknown reason"; + } +} + + +int mosquitto_string_to_command(const char *str, int *cmd) +{ + if(!strcasecmp(str, "connect")){ + *cmd = CMD_CONNECT; + }else if(!strcasecmp(str, "connack")){ + *cmd = CMD_CONNACK; + }else if(!strcasecmp(str, "publish")){ + *cmd = CMD_PUBLISH; + }else if(!strcasecmp(str, "puback")){ + *cmd = CMD_PUBACK; + }else if(!strcasecmp(str, "pubrec")){ + *cmd = CMD_PUBREC; + }else if(!strcasecmp(str, "pubrel")){ + *cmd = CMD_PUBREL; + }else if(!strcasecmp(str, "pubcomp")){ + *cmd = CMD_PUBCOMP; + }else if(!strcasecmp(str, "subscribe")){ + *cmd = CMD_SUBSCRIBE; + }else if(!strcasecmp(str, "unsubscribe")){ + *cmd = CMD_UNSUBSCRIBE; + }else if(!strcasecmp(str, "disconnect")){ + *cmd = CMD_DISCONNECT; + }else if(!strcasecmp(str, "auth")){ + *cmd = CMD_AUTH; + }else if(!strcasecmp(str, "will")){ + *cmd = CMD_WILL; + }else{ + return MOSQ_ERR_INVAL; + } + return MOSQ_ERR_SUCCESS; +} + + int mosquitto_sub_topic_tokenise(const char *subtopic, char ***topics, int *count) { int len; diff --git a/lib/mosquitto.h b/lib/mosquitto.h index c3f148d5..4f0903f7 100644 --- a/lib/mosquitto.h +++ b/lib/mosquitto.h @@ -44,10 +44,11 @@ extern "C" { #endif #include +#include #define LIBMOSQUITTO_MAJOR 1 #define LIBMOSQUITTO_MINOR 5 -#define LIBMOSQUITTO_REVISION 4 +#define LIBMOSQUITTO_REVISION 90 /* LIBMOSQUITTO_VERSION_NUMBER looks like 1002001 for e.g. version 1.2.1. */ #define LIBMOSQUITTO_VERSION_NUMBER (LIBMOSQUITTO_MAJOR*1000000+LIBMOSQUITTO_MINOR*1000+LIBMOSQUITTO_REVISION) @@ -65,6 +66,7 @@ extern "C" { /* Error values */ enum mosq_err_t { + MOSQ_ERR_SUB_EXISTS = -2, MOSQ_ERR_CONN_PENDING = -1, MOSQ_ERR_SUCCESS = 0, MOSQ_ERR_NOMEM = 1, @@ -87,6 +89,11 @@ enum mosq_err_t { MOSQ_ERR_MALFORMED_UTF8 = 18, MOSQ_ERR_KEEPALIVE = 19, MOSQ_ERR_LOOKUP = 20, + MOSQ_ERR_MALFORMED_PACKET = 21, + MOSQ_ERR_DUPLICATE_PROPERTY = 22, + MOSQ_ERR_TLS_HANDSHAKE = 23, + MOSQ_ERR_QOS_NOT_SUPPORTED = 24, + MOSQ_ERR_OVERSIZE_PACKET = 25, }; /* Error values */ @@ -94,7 +101,8 @@ enum mosq_opt_t { MOSQ_OPT_PROTOCOL_VERSION = 1, MOSQ_OPT_SSL_CTX = 2, MOSQ_OPT_SSL_CTX_WITH_DEFAULTS = 3, - + MOSQ_OPT_RECEIVE_MAXIMUM = 4, + MOSQ_OPT_SEND_MAXIMUM = 5, MOSQ_OPT_TLS_KEYFORM = 6, MOSQ_OPT_TLS_ENGINE = 7, MOSQ_OPT_TLS_ENGINE_KPASS_SHA1 = 8, @@ -106,6 +114,7 @@ enum mosq_opt_t { #define MQTT_PROTOCOL_V31 3 #define MQTT_PROTOCOL_V311 4 +#define MQTT_PROTOCOL_V5 5 struct mosquitto_message{ int mid; @@ -117,6 +126,7 @@ struct mosquitto_message{ }; struct mosquitto; +typedef struct mqtt5__property mosquitto_property; /* * Topic: Threads @@ -148,6 +158,12 @@ struct mosquitto; * mosquitto_publish() ***************************************************/ + +/* ====================================================================== + * + * Section: Library version, init, and cleanup + * + * ====================================================================== */ /* * Function: mosquitto_lib_version * @@ -200,6 +216,12 @@ libmosq_EXPORT int mosquitto_lib_init(void); */ libmosq_EXPORT int mosquitto_lib_cleanup(void); + +/* ====================================================================== + * + * Section: Client creation, destruction, and reinitialisation + * + * ====================================================================== */ /* * Function: mosquitto_new * @@ -273,6 +295,12 @@ libmosq_EXPORT void mosquitto_destroy(struct mosquitto *mosq); */ libmosq_EXPORT int mosquitto_reinitialise(struct mosquitto *mosq, const char *id, bool clean_session, void *obj); + +/* ====================================================================== + * + * Section: Will + * + * ====================================================================== */ /* * Function: mosquitto_will_set * @@ -299,6 +327,41 @@ libmosq_EXPORT int mosquitto_reinitialise(struct mosquitto *mosq, const char *id */ libmosq_EXPORT int mosquitto_will_set(struct mosquitto *mosq, const char *topic, int payloadlen, const void *payload, int qos, bool retain); +/* + * Function: mosquitto_will_set_v5 + * + * Configure will information for a mosquitto instance, with attached + * properties. By default, clients do not have a will. This must be called + * before calling . + * + * Parameters: + * mosq - a valid mosquitto instance. + * topic - the topic on which to publish the will. + * payloadlen - the size of the payload (bytes). Valid values are between 0 and + * 268,435,455. + * payload - pointer to the data to send. If payloadlen > 0 this must be a + * valid memory location. + * qos - integer value 0, 1 or 2 indicating the Quality of Service to be + * used for the will. + * retain - set to true to make the will a retained message. + * properties - list of MQTT 5 properties. Can be NULL. On success only, the + * property list becomes the property of libmosquitto once this + * function is called and will be freed by the library. The + * property list must be freed by the application on error. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * MOSQ_ERR_PAYLOAD_SIZE - if payloadlen is too large. + * MOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8. + * MOSQ_ERR_NOT_SUPPORTED - if properties is not NULL and the client is not + * using MQTT v5 + * MOSQ_ERR_PROTOCOL - if a property is invalid for use with wills. + * MOSQ_ERR_DUPLICATE_PROPERTY - if a property is duplicated where it is forbidden. + */ +libmosq_EXPORT int mosquitto_will_set_v5(struct mosquitto *mosq, const char *topic, int payloadlen, const void *payload, int qos, bool retain, mosquitto_property *properties); + /* * Function: mosquitto_will_clear * @@ -314,6 +377,12 @@ libmosq_EXPORT int mosquitto_will_set(struct mosquitto *mosq, const char *topic, */ libmosq_EXPORT int mosquitto_will_clear(struct mosquitto *mosq); + +/* ====================================================================== + * + * Section: Username and password + * + * ====================================================================== */ /* * Function: mosquitto_username_pw_set * @@ -339,6 +408,12 @@ libmosq_EXPORT int mosquitto_will_clear(struct mosquitto *mosq); */ libmosq_EXPORT int mosquitto_username_pw_set(struct mosquitto *mosq, const char *username, const char *password); + +/* ====================================================================== + * + * Section: Connecting, reconnecting, disconnecting + * + * ====================================================================== */ /* * Function: mosquitto_connect * @@ -395,6 +470,39 @@ libmosq_EXPORT int mosquitto_connect(struct mosquitto *mosq, const char *host, i */ libmosq_EXPORT int mosquitto_connect_bind(struct mosquitto *mosq, const char *host, int port, int keepalive, const char *bind_address); +/* + * Function: mosquitto_connect_bind_v5 + * + * Connect to an MQTT broker. This extends the functionality of + * by adding the bind_address parameter. Use this function + * if you need to restrict network communication over a particular interface. + * + * Parameters: + * mosq - a valid mosquitto instance. + * host - the hostname or ip address of the broker to connect to. + * port - the network port to connect to. Usually 1883. + * keepalive - the number of seconds after which the broker should send a PING + * message to the client if no other messages have been exchanged + * in that time. + * bind_address - the hostname or ip address of the local network interface to + * bind to. + * properties - the MQTT 5 properties for the connect (not for the Will). + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_ERRNO - if a system call returned an error. The variable errno + * contains the error code, even on Windows. + * Use strerror_r() where available or FormatMessage() on + * Windows. + * MOSQ_ERR_DUPLICATE_PROPERTY - if a property is duplicated where it is forbidden. + * MOSQ_ERR_PROTOCOL - if any property is invalid for use with CONNECT. + * + * See Also: + * , , + */ +libmosq_EXPORT int mosquitto_connect_bind_v5(struct mosquitto *mosq, const char *host, int port, int keepalive, const char *bind_address, const mosquitto_property *properties); + /* * Function: mosquitto_connect_async * @@ -576,6 +684,35 @@ libmosq_EXPORT int mosquitto_reconnect_async(struct mosquitto *mosq); */ libmosq_EXPORT int mosquitto_disconnect(struct mosquitto *mosq); +/* + * Function: mosquitto_disconnect_v5 + * + * Disconnect from the broker, with attached MQTT properties. + * + * Use to create a list of properties, then attach + * them to this publish. Properties need freeing with + * . + * + * Parameters: + * mosq - a valid mosquitto instance. + * reason_code - the disconnect reason code. + * properties - a valid mosquitto_property list, or NULL. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. + * MOSQ_ERR_DUPLICATE_PROPERTY - if a property is duplicated where it is forbidden. + * MOSQ_ERR_PROTOCOL - if any property is invalid for use with DISCONNECT. + */ +libmosq_EXPORT int mosquitto_disconnect_v5(struct mosquitto *mosq, int reason_code, const mosquitto_property *properties); + + +/* ====================================================================== + * + * Section: Publishing, subscribing, unsubscribing + * + * ====================================================================== */ /* * Function: mosquitto_publish * @@ -608,12 +745,74 @@ libmosq_EXPORT int mosquitto_disconnect(struct mosquitto *mosq); * broker. * MOSQ_ERR_PAYLOAD_SIZE - if payloadlen is too large. * MOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8 + * MOSQ_ERR_QOS_NOT_SUPPORTED - if the QoS is greater than that supported by + * the broker. + * MOSQ_ERR_OVERSIZE_PACKET - if the resulting packet would be larger than + * supported by the broker. * * See Also: * */ libmosq_EXPORT int mosquitto_publish(struct mosquitto *mosq, int *mid, const char *topic, int payloadlen, const void *payload, int qos, bool retain); + +/* + * Function: mosquitto_publish_v5 + * + * Publish a message on a given topic, with attached MQTT properties. + * + * Use to create a list of properties, then attach + * them to this publish. Properties need freeing with + * . + * + * Requires the mosquitto instance to be connected with MQTT 5. + * + * Parameters: + * mosq - a valid mosquitto instance. + * mid - pointer to an int. If not NULL, the function will set this + * to the message id of this particular message. This can be then + * used with the publish callback to determine when the message + * has been sent. + * Note that although the MQTT protocol doesn't use message ids + * for messages with QoS=0, libmosquitto assigns them message ids + * so they can be tracked with this parameter. + * topic - null terminated string of the topic to publish to. + * payloadlen - the size of the payload (bytes). Valid values are between 0 and + * 268,435,455. + * payload - pointer to the data to send. If payloadlen > 0 this must be a + * valid memory location. + * qos - integer value 0, 1 or 2 indicating the Quality of Service to be + * used for the message. + * retain - set to true to make the message retained. + * properties - a valid mosquitto_property list, or NULL. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. + * MOSQ_ERR_PROTOCOL - if there is a protocol error communicating with the + * broker. + * MOSQ_ERR_PAYLOAD_SIZE - if payloadlen is too large. + * MOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8 + * MOSQ_ERR_DUPLICATE_PROPERTY - if a property is duplicated where it is forbidden. + * MOSQ_ERR_PROTOCOL - if any property is invalid for use with PUBLISH. + * MOSQ_ERR_QOS_NOT_SUPPORTED - if the QoS is greater than that supported by + * the broker. + * MOSQ_ERR_OVERSIZE_PACKET - if the resulting packet would be larger than + * supported by the broker. + */ +libmosq_EXPORT int mosquitto_publish_v5( + struct mosquitto *mosq, + int *mid, + const char *topic, + int payloadlen, + const void *payload, + int qos, + bool retain, + const mosquitto_property *properties); + + /* * Function: mosquitto_subscribe * @@ -634,9 +833,70 @@ libmosq_EXPORT int mosquitto_publish(struct mosquitto *mosq, int *mid, const cha * MOSQ_ERR_NOMEM - if an out of memory condition occurred. * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. * MOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8 + * MOSQ_ERR_OVERSIZE_PACKET - if the resulting packet would be larger than + * supported by the broker. */ libmosq_EXPORT int mosquitto_subscribe(struct mosquitto *mosq, int *mid, const char *sub, int qos); +/* + * Function: mosquitto_subscribe_v5 + * + * Subscribe to a topic, with attached MQTT properties. + * + * Use to create a list of properties, then attach + * them to this subscribe. Properties need freeing with + * . + * + * Requires the mosquitto instance to be connected with MQTT 5. + * + * + * Parameters: + * mosq - a valid mosquitto instance. + * mid - a pointer to an int. If not NULL, the function will set this to + * the message id of this particular message. This can be then used + * with the subscribe callback to determine when the message has been + * sent. + * sub - the subscription pattern. + * qos - the requested Quality of Service for this subscription. + * options - options to apply to this subscription, OR'd together. Set to 0 to + * use the default options, otherwise choose from the list: + * MQTT_SUB_OPT_NO_LOCAL - with this option set, if this client + * publishes to a topic to which it is subscribed, the + * broker will not publish the message back to the + * client. + * MQTT_SUB_OPT_RETAIN_AS_PUBLISHED - with this option set, messages + * published for this subscription will keep the + * retain flag as was set by the publishing client. + * The default behaviour without this option set has + * the retain flag indicating whether a message is + * fresh/stale. + * MQTT_SUB_OPT_SEND_RETAIN_ALWAYS - with this option set, + * pre-existing retained messages are sent as soon as + * the subscription is made, even if the subscription + * already exists. This is the default behaviour, so + * it is not necessary to set this option. + * MQTT_SUB_OPT_SEND_RETAIN_NEW - with this option set, pre-existing + * retained messages for this subscription will be + * sent when the subscription is made, but only if the + * subscription does not already exist. + * MQTT_SUB_OPT_SEND_RETAIN_NEVER - with this option set, + * pre-existing retained messages will never be sent + * for this subscription. + * properties - a valid mosquitto_property list, or NULL. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. + * MOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8 + * MOSQ_ERR_DUPLICATE_PROPERTY - if a property is duplicated where it is forbidden. + * MOSQ_ERR_PROTOCOL - if any property is invalid for use with SUBSCRIBE. + * MOSQ_ERR_OVERSIZE_PACKET - if the resulting packet would be larger than + * supported by the broker. + */ +libmosq_EXPORT int mosquitto_subscribe_v5(struct mosquitto *mosq, int *mid, const char *sub, int qos, int options, const mosquitto_property *properties); + /* * Function: mosquitto_subscribe_multiple * @@ -655,6 +915,32 @@ libmosq_EXPORT int mosquitto_subscribe(struct mosquitto *mosq, int *mid, const c * familiar with this, just think of it as a safer "char **", * equivalent to "const char *" for a simple string pointer. * qos - the requested Quality of Service for each subscription. + * options - options to apply to this subscription, OR'd together. Set to 0 to + * use the default options, otherwise choose from the list: + * MQTT_SUB_OPT_NO_LOCAL - with this option set, if this client + * publishes to a topic to which it is subscribed, the + * broker will not publish the message back to the + * client. + * MQTT_SUB_OPT_RETAIN_AS_PUBLISHED - with this option set, messages + * published for this subscription will keep the + * retain flag as was set by the publishing client. + * The default behaviour without this option set has + * the retain flag indicating whether a message is + * fresh/stale. + * MQTT_SUB_OPT_SEND_RETAIN_ALWAYS - with this option set, + * pre-existing retained messages are sent as soon as + * the subscription is made, even if the subscription + * already exists. This is the default behaviour, so + * it is not necessary to set this option. + * MQTT_SUB_OPT_SEND_RETAIN_NEW - with this option set, pre-existing + * retained messages for this subscription will be + * sent when the subscription is made, but only if the + * subscription does not already exist. + * MQTT_SUB_OPT_SEND_RETAIN_NEVER - with this option set, + * pre-existing retained messages will never be sent + * for this subscription. + * properties - a valid mosquitto_property list, or NULL. Only used with MQTT + * v5 clients. * * Returns: * MOSQ_ERR_SUCCESS - on success. @@ -662,8 +948,10 @@ libmosq_EXPORT int mosquitto_subscribe(struct mosquitto *mosq, int *mid, const c * MOSQ_ERR_NOMEM - if an out of memory condition occurred. * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. * MOSQ_ERR_MALFORMED_UTF8 - if a topic is not valid UTF-8 + * MOSQ_ERR_OVERSIZE_PACKET - if the resulting packet would be larger than + * supported by the broker. */ -int mosquitto_subscribe_multiple(struct mosquitto *mosq, int *mid, int sub_count, char *const *const sub, int qos); +int mosquitto_subscribe_multiple(struct mosquitto *mosq, int *mid, int sub_count, char *const *const sub, int qos, int options, const mosquitto_property *properties); /* * Function: mosquitto_unsubscribe @@ -684,9 +972,76 @@ int mosquitto_subscribe_multiple(struct mosquitto *mosq, int *mid, int sub_count * MOSQ_ERR_NOMEM - if an out of memory condition occurred. * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. * MOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8 + * MOSQ_ERR_OVERSIZE_PACKET - if the resulting packet would be larger than + * supported by the broker. */ libmosq_EXPORT int mosquitto_unsubscribe(struct mosquitto *mosq, int *mid, const char *sub); +/* + * Function: mosquitto_unsubscribe_v5 + * + * Unsubscribe from a topic, with attached MQTT properties. + * + * Parameters: + * mosq - a valid mosquitto instance. + * mid - a pointer to an int. If not NULL, the function will set this to + * the message id of this particular message. This can be then used + * with the unsubscribe callback to determine when the message has been + * sent. + * sub - the unsubscription pattern. + * properties - a valid mosquitto_property list, or NULL. Only used with MQTT + * v5 clients. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. + * MOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8 + * MOSQ_ERR_DUPLICATE_PROPERTY - if a property is duplicated where it is forbidden. + * MOSQ_ERR_PROTOCOL - if any property is invalid for use with UNSUBSCRIBE. + * MOSQ_ERR_OVERSIZE_PACKET - if the resulting packet would be larger than + * supported by the broker. + */ +libmosq_EXPORT int mosquitto_unsubscribe_v5(struct mosquitto *mosq, int *mid, const char *sub, const mosquitto_property *properties); + +/* + * Function: mosquitto_unsubscribe_multiple + * + * Unsubscribe from multiple topics. + * + * Parameters: + * mosq - a valid mosquitto instance. + * mid - a pointer to an int. If not NULL, the function will set this to + * the message id of this particular message. This can be then used + * with the subscribe callback to determine when the message has been + * sent. + * sub_count - the count of unsubscriptions to be made + * sub - array of sub_count pointers, each pointing to an unsubscription string. + * The "char *const *const" datatype ensures that neither the array of + * pointers nor the strings that they point to are mutable. If you aren't + * familiar with this, just think of it as a safer "char **", + * equivalent to "const char *" for a simple string pointer. + * properties - a valid mosquitto_property list, or NULL. Only used with MQTT + * v5 clients. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * MOSQ_ERR_NOMEM - if an out of memory condition occurred. + * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. + * MOSQ_ERR_MALFORMED_UTF8 - if a topic is not valid UTF-8 + * MOSQ_ERR_OVERSIZE_PACKET - if the resulting packet would be larger than + * supported by the broker. + */ +int mosquitto_unsubscribe_multiple(struct mosquitto *mosq, int *mid, int sub_count, char *const *const sub, const mosquitto_property *properties); + + +/* ====================================================================== + * + * Section: Struct mosquitto_message helper functions + * + * ====================================================================== */ /* * Function: mosquitto_message_copy * @@ -733,6 +1088,12 @@ libmosq_EXPORT void mosquitto_message_free(struct mosquitto_message **message); */ libmosq_EXPORT void mosquitto_message_free_contents(struct mosquitto_message *message); + +/* ====================================================================== + * + * Section: Network loop (managed by libmosquitto) + * + * ====================================================================== */ /* * Function: mosquitto_loop * @@ -859,20 +1220,12 @@ libmosq_EXPORT int mosquitto_loop_start(struct mosquitto *mosq); */ libmosq_EXPORT int mosquitto_loop_stop(struct mosquitto *mosq, bool force); -/* - * Function: mosquitto_socket - * - * Return the socket handle for a mosquitto instance. Useful if you want to - * include a mosquitto client in your own select() calls. - * - * Parameters: - * mosq - a valid mosquitto instance. - * - * Returns: - * The socket for the mosquitto client or -1 on failure. - */ -libmosq_EXPORT int mosquitto_socket(struct mosquitto *mosq); +/* ====================================================================== + * + * Section: Network loop (for use in other event loops) + * + * ====================================================================== */ /* * Function: mosquitto_loop_read * @@ -956,6 +1309,26 @@ libmosq_EXPORT int mosquitto_loop_write(struct mosquitto *mosq, int max_packets) */ libmosq_EXPORT int mosquitto_loop_misc(struct mosquitto *mosq); + +/* ====================================================================== + * + * Section: Network loop (helper functions) + * + * ====================================================================== */ +/* + * Function: mosquitto_socket + * + * Return the socket handle for a mosquitto instance. Useful if you want to + * include a mosquitto client in your own select() calls. + * + * Parameters: + * mosq - a valid mosquitto instance. + * + * Returns: + * The socket for the mosquitto client or -1 on failure. + */ +libmosq_EXPORT int mosquitto_socket(struct mosquitto *mosq); + /* * Function: mosquitto_want_write * @@ -986,11 +1359,20 @@ libmosq_EXPORT bool mosquitto_want_write(struct mosquitto *mosq); */ libmosq_EXPORT int mosquitto_threaded_set(struct mosquitto *mosq, bool threaded); + +/* ====================================================================== + * + * Section: Client options + * + * ====================================================================== */ /* * Function: mosquitto_opts_set * * Used to set options for the client. * + * This function is deprecated, the replacement and + * functions should be used instead. + * * Parameters: * mosq - a valid mosquitto instance. * option - the option to set. @@ -1023,7 +1405,182 @@ libmosq_EXPORT int mosquitto_threaded_set(struct mosquitto *mosq, bool threaded) */ libmosq_EXPORT int mosquitto_opts_set(struct mosquitto *mosq, enum mosq_opt_t option, void *value); +/* + * Function: mosquitto_int_option + * + * Used to set integer options for the client. + * + * Parameters: + * mosq - a valid mosquitto instance. + * option - the option to set. + * value - the option specific value. + * + * Options: + * MOSQ_OPT_PROTOCOL_VERSION + * Value must be set to either MQTT_PROTOCOL_V31, + * MQTT_PROTOCOL_V311, or MQTT_PROTOCOL_V5. Must be set before the + * client connects. Defaults to MQTT_PROTOCOL_V311. + * + * MOSQ_OPT_RECEIVE_MAXIMUM + * Value can be set between 1 and 65535 inclusive, and represents + * the maximum number of incoming QoS 1 and QoS 2 messages that this + * client wants to process at once. Defaults to 20. This option is + * not valid for MQTT v3.1 or v3.1.1 clients. + * Note that if the MQTT_PROP_RECEIVE_MAXIMUM property is in the + * proplist passed to mosquitto_connect_v5(), then that property + * will override this option. Using this option is the recommended + * method however. + * + * MOSQ_OPT_SEND_MAXIMUM + * Value can be set between 1 and 65535 inclusive, and represents + * the maximum number of outgoing QoS 1 and QoS 2 messages that this + * client will attempt to have "in flight" at once. Defaults to 20. + * This option is not valid for MQTT v3.1 or v3.1.1 clients. + * Note that if the broker being connected to sends a + * MQTT_PROP_RECEIVE_MAXIMUM property that has a lower value than + * this option, then the broker provided value will be used. + * + * MOSQ_OPT_SSL_CTX_WITH_DEFAULTS + * If value is set to a non zero value, then the user specified + * SSL_CTX passed in using MOSQ_OPT_SSL_CTX will have the default + * options applied to it. This means that you only need to change + * the values that are relevant to you. If you use this option then + * you must configure the TLS options as normal, i.e. you should + * use to configure the cafile/capath as a + * minimum. + * This option is only available for openssl 1.1.0 and higher. + */ +libmosq_EXPORT int mosquitto_int_option(struct mosquitto *mosq, enum mosq_opt_t option, int value); +/* + * Function: mosquitto_void_option + * + * Used to set void* options for the client. + * + * Parameters: + * mosq - a valid mosquitto instance. + * option - the option to set. + * value - the option specific value. + * + * Options: + * MOSQ_OPT_SSL_CTX + * Pass an openssl SSL_CTX to be used when creating TLS connections + * rather than libmosquitto creating its own. This must be called + * before connecting to have any effect. If you use this option, the + * onus is on you to ensure that you are using secure settings. + * Setting to NULL means that libmosquitto will use its own SSL_CTX + * if TLS is to be used. + * This option is only available for openssl 1.1.0 and higher. + */ +libmosq_EXPORT int mosquitto_void_option(struct mosquitto *mosq, enum mosq_opt_t option, void *value); + + +/* + * Function: mosquitto_reconnect_delay_set + * + * Control the behaviour of the client when it has unexpectedly disconnected in + * or after . The default + * behaviour if this function is not used is to repeatedly attempt to reconnect + * with a delay of 1 second until the connection succeeds. + * + * Use reconnect_delay parameter to change the delay between successive + * reconnection attempts. You may also enable exponential backoff of the time + * between reconnections by setting reconnect_exponential_backoff to true and + * set an upper bound on the delay with reconnect_delay_max. + * + * Example 1: + * delay=2, delay_max=10, exponential_backoff=False + * Delays would be: 2, 4, 6, 8, 10, 10, ... + * + * Example 2: + * delay=3, delay_max=30, exponential_backoff=True + * Delays would be: 3, 6, 12, 24, 30, 30, ... + * + * Parameters: + * mosq - a valid mosquitto instance. + * reconnect_delay - the number of seconds to wait between + * reconnects. + * reconnect_delay_max - the maximum number of seconds to wait + * between reconnects. + * reconnect_exponential_backoff - use exponential backoff between + * reconnect attempts. Set to true to enable + * exponential backoff. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + */ +libmosq_EXPORT int mosquitto_reconnect_delay_set(struct mosquitto *mosq, unsigned int reconnect_delay, unsigned int reconnect_delay_max, bool reconnect_exponential_backoff); + +/* + * Function: mosquitto_max_inflight_messages_set + * + * This function is deprected. Use the function with the + * MOSQ_OPT_SEND_MAXIMUM option instead. + * + * Set the number of QoS 1 and 2 messages that can be "in flight" at one time. + * An in flight message is part way through its delivery flow. Attempts to send + * further messages with will result in the messages being + * queued until the number of in flight messages reduces. + * + * A higher number here results in greater message throughput, but if set + * higher than the maximum in flight messages on the broker may lead to + * delays in the messages being acknowledged. + * + * Set to 0 for no maximum. + * + * Parameters: + * mosq - a valid mosquitto instance. + * max_inflight_messages - the maximum number of inflight messages. Defaults + * to 20. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + */ +libmosq_EXPORT int mosquitto_max_inflight_messages_set(struct mosquitto *mosq, unsigned int max_inflight_messages); + +/* + * Function: mosquitto_message_retry_set + * + * This function now has no effect. + */ +libmosq_EXPORT void mosquitto_message_retry_set(struct mosquitto *mosq, unsigned int message_retry); + +/* + * Function: mosquitto_user_data_set + * + * When is called, the pointer given as the "obj" parameter + * will be passed to the callbacks as user data. The + * function allows this obj parameter to be updated at any time. This function + * will not modify the memory pointed to by the current user data pointer. If + * it is dynamically allocated memory you must free it yourself. + * + * Parameters: + * mosq - a valid mosquitto instance. + * obj - A user pointer that will be passed as an argument to any callbacks + * that are specified. + */ +libmosq_EXPORT void mosquitto_user_data_set(struct mosquitto *mosq, void *obj); + +/* Function: mosquitto_userdata + * + * Retrieve the "userdata" variable for a mosquitto client. + * + * Parameters: + * mosq - a valid mosquitto instance. + * + * Returns: + * A pointer to the userdata member variable. + */ +libmosq_EXPORT void *mosquitto_userdata(struct mosquitto *mosq); + + +/* ====================================================================== + * + * Section: TLS support + * + * ====================================================================== */ /* * Function: mosquitto_tls_set * @@ -1165,6 +1722,12 @@ libmosq_EXPORT int mosquitto_tls_opts_set(struct mosquitto *mosq, int cert_reqs, */ libmosq_EXPORT int mosquitto_tls_psk_set(struct mosquitto *mosq, const char *psk, const char *identity, const char *ciphers); + +/* ====================================================================== + * + * Section: Callbacks + * + * ====================================================================== */ /* * Function: mosquitto_connect_callback_set * @@ -1214,6 +1777,32 @@ libmosq_EXPORT void mosquitto_connect_callback_set(struct mosquitto *mosq, void */ libmosq_EXPORT void mosquitto_connect_with_flags_callback_set(struct mosquitto *mosq, void (*on_connect)(struct mosquitto *, void *, int, int)); +/* + * Function: mosquitto_connect_v5_callback_set + * + * Set the connect callback. This is called when the broker sends a CONNACK + * message in response to a connection. + * + * Parameters: + * mosq - a valid mosquitto instance. + * on_connect - a callback function in the following form: + * void callback(struct mosquitto *mosq, void *obj, int rc) + * + * Callback Parameters: + * mosq - the mosquitto instance making the callback. + * obj - the user data provided in + * rc - the return code of the connection response, one of: + * * 0 - success + * * 1 - connection refused (unacceptable protocol version) + * * 2 - connection refused (identifier rejected) + * * 3 - connection refused (broker unavailable) + * * 4-255 - reserved for future use + * flags - the connect flags. + * props - list of MQTT 5 properties, or NULL + * + */ +libmosq_EXPORT void mosquitto_connect_v5_callback_set(struct mosquitto *mosq, void (*on_connect)(struct mosquitto *, void *, int, int, const mosquitto_property *props)); + /* * Function: mosquitto_disconnect_callback_set * @@ -1234,6 +1823,27 @@ libmosq_EXPORT void mosquitto_connect_with_flags_callback_set(struct mosquitto * */ libmosq_EXPORT void mosquitto_disconnect_callback_set(struct mosquitto *mosq, void (*on_disconnect)(struct mosquitto *, void *, int)); +/* + * Function: mosquitto_disconnect_v5_callback_set + * + * Set the disconnect callback. This is called when the broker has received the + * DISCONNECT command and has disconnected the client. + * + * Parameters: + * mosq - a valid mosquitto instance. + * on_disconnect - a callback function in the following form: + * void callback(struct mosquitto *mosq, void *obj) + * + * Callback Parameters: + * mosq - the mosquitto instance making the callback. + * obj - the user data provided in + * rc - integer value indicating the reason for the disconnect. A value of 0 + * means the client has called . Any other value + * indicates that the disconnect is unexpected. + * props - list of MQTT 5 properties, or NULL + */ +libmosq_EXPORT void mosquitto_disconnect_v5_callback_set(struct mosquitto *mosq, void (*on_disconnect)(struct mosquitto *, void *, int, const mosquitto_property *)); + /* * Function: mosquitto_publish_callback_set * @@ -1252,6 +1862,28 @@ libmosq_EXPORT void mosquitto_disconnect_callback_set(struct mosquitto *mosq, vo */ libmosq_EXPORT void mosquitto_publish_callback_set(struct mosquitto *mosq, void (*on_publish)(struct mosquitto *, void *, int)); +/* + * Function: mosquitto_publish_v5_callback_set + * + * Set the publish callback. This is called when a message initiated with + * 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. + * on_publish - a callback function in the following form: + * void callback(struct mosquitto *mosq, void *obj, int mid) + * + * Callback Parameters: + * mosq - the mosquitto instance making the callback. + * obj - the user data provided in + * mid - the message id of the sent message. + * reason_code - the MQTT 5 reason code + * props - list of MQTT 5 properties, or NULL + */ +libmosq_EXPORT void mosquitto_publish_v5_callback_set(struct mosquitto *mosq, void (*on_publish)(struct mosquitto *, void *, int, int, const mosquitto_property *)); + /* * Function: mosquitto_message_callback_set * @@ -1275,6 +1907,30 @@ libmosq_EXPORT void mosquitto_publish_callback_set(struct mosquitto *mosq, void */ libmosq_EXPORT void mosquitto_message_callback_set(struct mosquitto *mosq, void (*on_message)(struct mosquitto *, void *, const struct mosquitto_message *)); +/* + * Function: mosquitto_message_v5_callback_set + * + * Set the message callback. This is called when a message is received from the + * broker. + * + * Parameters: + * mosq - a valid mosquitto instance. + * on_message - a callback function in the following form: + * void callback(struct mosquitto *mosq, void *obj, const struct mosquitto_message *message) + * + * Callback Parameters: + * mosq - the mosquitto instance making the callback. + * obj - the user data provided in + * message - the message data. This variable and associated memory will be + * freed by the library after the callback completes. The client + * should make copies of any of the data it requires. + * props - list of MQTT 5 properties, or NULL + * + * See Also: + * + */ +libmosq_EXPORT void mosquitto_message_v5_callback_set(struct mosquitto *mosq, void (*on_message)(struct mosquitto *, void *, const struct mosquitto_message *, const mosquitto_property *)); + /* * Function: mosquitto_subscribe_callback_set * @@ -1296,6 +1952,28 @@ libmosq_EXPORT void mosquitto_message_callback_set(struct mosquitto *mosq, void */ libmosq_EXPORT void mosquitto_subscribe_callback_set(struct mosquitto *mosq, void (*on_subscribe)(struct mosquitto *, void *, int, int, const int *)); +/* + * Function: mosquitto_subscribe_v5_callback_set + * + * Set the subscribe callback. This is called when the broker responds to a + * subscription request. + * + * Parameters: + * mosq - a valid mosquitto instance. + * on_subscribe - a callback function in the following form: + * void callback(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos) + * + * Callback Parameters: + * mosq - the mosquitto instance making the callback. + * obj - the user data provided in + * mid - the message id of the subscribe message. + * qos_count - the number of granted subscriptions (size of granted_qos). + * granted_qos - an array of integers indicating the granted QoS for each of + * the subscriptions. + * props - list of MQTT 5 properties, or NULL + */ +libmosq_EXPORT void mosquitto_subscribe_v5_callback_set(struct mosquitto *mosq, void (*on_subscribe)(struct mosquitto *, void *, int, int, const int *, const mosquitto_property *)); + /* * Function: mosquitto_unsubscribe_callback_set * @@ -1314,6 +1992,25 @@ libmosq_EXPORT void mosquitto_subscribe_callback_set(struct mosquitto *mosq, voi */ libmosq_EXPORT void mosquitto_unsubscribe_callback_set(struct mosquitto *mosq, void (*on_unsubscribe)(struct mosquitto *, void *, int)); +/* + * Function: mosquitto_unsubscribe_v5_callback_set + * + * Set the unsubscribe callback. This is called when the broker responds to a + * unsubscription request. + * + * Parameters: + * mosq - a valid mosquitto instance. + * on_unsubscribe - a callback function in the following form: + * void callback(struct mosquitto *mosq, void *obj, int mid) + * + * Callback Parameters: + * mosq - the mosquitto instance making the callback. + * obj - the user data provided in + * mid - the message id of the unsubscribe message. + * props - list of MQTT 5 properties, or NULL + */ +libmosq_EXPORT void mosquitto_unsubscribe_v5_callback_set(struct mosquitto *mosq, void (*on_unsubscribe)(struct mosquitto *, void *, int, const mosquitto_property *)); + /* * Function: mosquitto_log_callback_set * @@ -1337,7 +2034,6 @@ libmosq_EXPORT void mosquitto_unsubscribe_callback_set(struct mosquitto *mosq, v */ libmosq_EXPORT void mosquitto_log_callback_set(struct mosquitto *mosq, void (*on_log)(struct mosquitto *, void *, int, const char *)); - /* * Function: mosquitto_string_option * @@ -1405,53 +2101,6 @@ libmosq_EXPORT int mosquitto_string_option(struct mosquitto *mosq, enum mosq_opt */ libmosq_EXPORT int mosquitto_reconnect_delay_set(struct mosquitto *mosq, unsigned int reconnect_delay, unsigned int reconnect_delay_max, bool reconnect_exponential_backoff); -/* - * Function: mosquitto_max_inflight_messages_set - * - * Set the number of QoS 1 and 2 messages that can be "in flight" at one time. - * An in flight message is part way through its delivery flow. Attempts to send - * further messages with will result in the messages being - * queued until the number of in flight messages reduces. - * - * A higher number here results in greater message throughput, but if set - * higher than the maximum in flight messages on the broker may lead to - * delays in the messages being acknowledged. - * - * Set to 0 for no maximum. - * - * Parameters: - * mosq - a valid mosquitto instance. - * max_inflight_messages - the maximum number of inflight messages. Defaults - * to 20. - * - * Returns: - * MOSQ_ERR_SUCCESS - on success. - * MOSQ_ERR_INVAL - if the input parameters were invalid. - */ -libmosq_EXPORT int mosquitto_max_inflight_messages_set(struct mosquitto *mosq, unsigned int max_inflight_messages); - -/* - * Function: mosquitto_message_retry_set - * - * This function now has no effect. - */ -libmosq_EXPORT void mosquitto_message_retry_set(struct mosquitto *mosq, unsigned int message_retry); - -/* - * Function: mosquitto_user_data_set - * - * When is called, the pointer given as the "obj" parameter - * will be passed to the callbacks as user data. The - * function allows this obj parameter to be updated at any time. This function - * will not modify the memory pointed to by the current user data pointer. If - * it is dynamically allocated memory you must free it yourself. - * - * Parameters: - * mosq - a valid mosquitto instance. - * obj - A user pointer that will be passed as an argument to any callbacks - * that are specified. - */ -libmosq_EXPORT void mosquitto_user_data_set(struct mosquitto *mosq, void *obj); /* ============================================================================= * @@ -1477,6 +2126,7 @@ libmosq_EXPORT void mosquitto_user_data_set(struct mosquitto *mosq, void *obj); */ libmosq_EXPORT int mosquitto_socks5_set(struct mosquitto *mosq, const char *host, int port, const char *username, const char *password); + /* ============================================================================= * * Section: Utility functions @@ -1510,6 +2160,38 @@ libmosq_EXPORT const char *mosquitto_strerror(int mosq_errno); */ libmosq_EXPORT const char *mosquitto_connack_string(int connack_code); +/* + * Function: mosquitto_reason_string + * + * Call to obtain a const string description of an MQTT reason code. + * + * Parameters: + * reason_code - an MQTT reason code. + * + * Returns: + * A constant string describing the reason. + */ +libmosq_EXPORT const char *mosquitto_reason_string(int reason_code); + +/* Function: mosquitto_string_to_command + * + * Take a string input representing an MQTT command and convert it to the + * libmosquitto integer representation. + * + * Parameters: + * str - the string to parse. + * cmd - pointer to an int, for the result. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - on an invalid input. + * + * Example: + * mosquitto_string_to_command("CONNECT", &cmd); + * // cmd == CMD_CONNECT + */ +libmosq_EXPORT int mosquitto_string_to_command(const char *str, int *cmd); + /* * Function: mosquitto_sub_topic_tokenise * @@ -1817,17 +2499,478 @@ libmosq_EXPORT int mosquitto_subscribe_callback( libmosq_EXPORT int mosquitto_validate_utf8(const char *str, int len); -/* Function: mosquitto_userdata +/* ============================================================================= * - * Retrieve the "userdata" variable for a mosquitto client. + * Section: Properties + * + * ============================================================================= + */ + + +/* + * Function: mosquitto_property_add_byte + * + * Add a new byte property to a property list. + * + * If *proplist == NULL, a new list will be created, otherwise the new property + * will be appended to the list. * * Parameters: - * mosq - a valid mosquitto instance. + * proplist - pointer to mosquitto_property pointer, the list of properties + * identifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR) + * value - integer value for the new property * * Returns: - * A pointer to the userdata member variable. + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if identifier is invalid, or if proplist is NULL + * MOSQ_ERR_NOMEM - on out of memory + * + * Example: + * mosquitto_property *proplist = NULL; + * mosquitto_property_add_byte(&proplist, MQTT_PROP_PAYLOAD_FORMAT_IDENTIFIER, 1); */ -libmosq_EXPORT void *mosquitto_userdata(struct mosquitto *mosq); +libmosq_EXPORT int mosquitto_property_add_byte(mosquitto_property **proplist, int identifier, uint8_t value); + +/* + * Function: mosquitto_property_add_int16 + * + * Add a new int16 property to a property list. + * + * If *proplist == NULL, a new list will be created, otherwise the new property + * will be appended to the list. + * + * Parameters: + * proplist - pointer to mosquitto_property pointer, the list of properties + * identifier - property identifier (e.g. MQTT_PROP_RECEIVE_MAXIMUM) + * value - integer value for the new property + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if identifier is invalid, or if proplist is NULL + * MOSQ_ERR_NOMEM - on out of memory + * + * Example: + * mosquitto_property *proplist = NULL; + * mosquitto_property_add_int16(&proplist, MQTT_PROP_RECEIVE_MAXIMUM, 1000); + */ +libmosq_EXPORT int mosquitto_property_add_int16(mosquitto_property **proplist, int identifier, uint16_t value); + +/* + * Function: mosquitto_property_add_int32 + * + * Add a new int32 property to a property list. + * + * If *proplist == NULL, a new list will be created, otherwise the new property + * will be appended to the list. + * + * Parameters: + * proplist - pointer to mosquitto_property pointer, the list of properties + * identifier - property identifier (e.g. MQTT_PROP_MESSAGE_EXPIRY_INTERVAL) + * value - integer value for the new property + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if identifier is invalid, or if proplist is NULL + * MOSQ_ERR_NOMEM - on out of memory + * + * Example: + * mosquitto_property *proplist = NULL; + * mosquitto_property_add_int32(&proplist, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, 86400); + */ +libmosq_EXPORT int mosquitto_property_add_int32(mosquitto_property **proplist, int identifier, uint32_t value); + +/* + * Function: mosquitto_property_add_varint + * + * Add a new varint property to a property list. + * + * If *proplist == NULL, a new list will be created, otherwise the new property + * will be appended to the list. + * + * Parameters: + * proplist - pointer to mosquitto_property pointer, the list of properties + * identifier - property identifier (e.g. MQTT_PROP_SUBSCRIPTION_IDENTIFIER) + * value - integer value for the new property + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if identifier is invalid, or if proplist is NULL + * MOSQ_ERR_NOMEM - on out of memory + * + * Example: + * mosquitto_property *proplist = NULL; + * mosquitto_property_add_varint(&proplist, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 1); + */ +libmosq_EXPORT int mosquitto_property_add_varint(mosquitto_property **proplist, int identifier, uint32_t value); + +/* + * Function: mosquitto_property_add_binary + * + * Add a new binary property to a property list. + * + * If *proplist == NULL, a new list will be created, otherwise the new property + * will be appended to the list. + * + * Parameters: + * proplist - pointer to mosquitto_property pointer, the list of properties + * identifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR) + * value - pointer to the property data + * len - length of property data in bytes + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if identifier is invalid, or if proplist is NULL + * MOSQ_ERR_NOMEM - on out of memory + * + * Example: + * mosquitto_property *proplist = NULL; + * mosquitto_property_add_binary(&proplist, MQTT_PROP_AUTHENTICATION_DATA, auth_data, auth_data_len); + */ +libmosq_EXPORT int mosquitto_property_add_binary(mosquitto_property **proplist, int identifier, const void *value, uint16_t len); + +/* + * Function: mosquitto_property_add_string + * + * Add a new string property to a property list. + * + * If *proplist == NULL, a new list will be created, otherwise the new property + * will be appended to the list. + * + * Parameters: + * proplist - pointer to mosquitto_property pointer, the list of properties + * identifier - property identifier (e.g. MQTT_PROP_CONTENT_TYPE) + * value - string value for the new property, must be UTF-8 and zero terminated + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if identifier is invalid, if value is NULL, or if proplist is NULL + * MOSQ_ERR_NOMEM - on out of memory + * MOSQ_ERR_MALFORMED_UTF8 - value is not valid UTF-8. + * + * Example: + * mosquitto_property *proplist = NULL; + * mosquitto_property_add_string(&proplist, MQTT_PROP_CONTENT_TYPE, "application/json"); + */ +libmosq_EXPORT int mosquitto_property_add_string(mosquitto_property **proplist, int identifier, const char *value); + +/* + * Function: mosquitto_property_add_string_pair + * + * Add a new string pair property to a property list. + * + * If *proplist == NULL, a new list will be created, otherwise the new property + * will be appended to the list. + * + * Parameters: + * proplist - pointer to mosquitto_property pointer, the list of properties + * identifier - property identifier (e.g. MQTT_PROP_USER_PROPERTY) + * name - string name for the new property, must be UTF-8 and zero terminated + * value - string value for the new property, must be UTF-8 and zero terminated + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if identifier is invalid, if name or value is NULL, or if proplist is NULL + * MOSQ_ERR_NOMEM - on out of memory + * MOSQ_ERR_MALFORMED_UTF8 - if name or value are not valid UTF-8. + * + * Example: + * mosquitto_property *proplist = NULL; + * mosquitto_property_add_string_pair(&proplist, MQTT_PROP_USER_PROPERTY, "client", "mosquitto_pub"); + */ +libmosq_EXPORT int mosquitto_property_add_string_pair(mosquitto_property **proplist, int identifier, const char *name, const char *value); + +/* + * Function: mosquitto_property_read_byte + * + * Attempt to read a byte property matching an identifier, from a property list + * or single property. This function can search for multiple entries of the + * same identifier by using the returned value and skip_first. Note however + * that it is forbidden for most properties to be duplicated. + * + * If the property is not found, *value will not be modified, so it is safe to + * pass a variable with a default value to be potentially overwritten: + * + * uint16_t keepalive = 60; // default value + * // Get value from property list, or keep default if not found. + * mosquitto_property_read_int16(proplist, MQTT_PROP_SERVER_KEEP_ALIVE, &keepalive, false); + * + * Parameters: + * proplist - mosquitto_property pointer, the list of properties or single property + * identifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR) + * value - pointer to store the value, or NULL if the value is not required. + * skip_first - boolean that indicates whether the first item in the list + * should be ignored or not. Should usually be set to false. + * + * Returns: + * A valid property pointer if the property is found + * NULL, if the property is not found, or proplist is NULL. + * + * Example: + * // proplist is obtained from a callback + * mosquitto_property *prop; + * prop = mosquitto_property_read_byte(proplist, identifier, &value, false); + * while(prop){ + * printf("value: %s\n", value); + * prop = mosquitto_property_read_byte(prop, identifier, &value); + * } + */ +libmosq_EXPORT const mosquitto_property *mosquitto_property_read_byte( + const mosquitto_property *proplist, + int identifier, + uint8_t *value, + bool skip_first); + +/* + * Function: mosquitto_property_read_int16 + * + * Read an int16 property value from a property. + * + * Parameters: + * property - property to read + * identifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR) + * value - pointer to store the value, or NULL if the value is not required. + * skip_first - boolean that indicates whether the first item in the list + * should be ignored or not. Should usually be set to false. + * + * Returns: + * A valid property pointer if the property is found + * NULL, if the property is not found, or proplist is NULL. + * + * Example: + * See + */ +libmosq_EXPORT const mosquitto_property *mosquitto_property_read_int16( + const mosquitto_property *proplist, + int identifier, + uint16_t *value, + bool skip_first); + +/* + * Function: mosquitto_property_read_int32 + * + * Read an int32 property value from a property. + * + * Parameters: + * property - pointer to mosquitto_property pointer, the list of properties + * identifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR) + * value - pointer to store the value, or NULL if the value is not required. + * skip_first - boolean that indicates whether the first item in the list + * should be ignored or not. Should usually be set to false. + * + * Returns: + * A valid property pointer if the property is found + * NULL, if the property is not found, or proplist is NULL. + * + * Example: + * See + */ +libmosq_EXPORT const mosquitto_property *mosquitto_property_read_int32( + const mosquitto_property *proplist, + int identifier, + uint32_t *value, + bool skip_first); + +/* + * Function: mosquitto_property_read_varint + * + * Read a varint property value from a property. + * + * Parameters: + * property - property to read + * identifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR) + * value - pointer to store the value, or NULL if the value is not required. + * skip_first - boolean that indicates whether the first item in the list + * should be ignored or not. Should usually be set to false. + * + * Returns: + * A valid property pointer if the property is found + * NULL, if the property is not found, or proplist is NULL. + * + * Example: + * See + */ +libmosq_EXPORT const mosquitto_property *mosquitto_property_read_varint( + const mosquitto_property *proplist, + int identifier, + uint32_t *value, + bool skip_first); + +/* + * Function: mosquitto_property_read_binary + * + * Read a binary property value from a property. + * + * On success, value must be free()'d by the application. + * + * Parameters: + * property - property to read + * identifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR) + * value - pointer to store the value, or NULL if the value is not required. + * skip_first - boolean that indicates whether the first item in the list + * should be ignored or not. Should usually be set to false. + * + * Returns: + * A valid property pointer if the property is found + * NULL, if the property is not found, or proplist is NULL, or if an out of memory condition occurred. + * + * Example: + * See + */ +libmosq_EXPORT const mosquitto_property *mosquitto_property_read_binary( + const mosquitto_property *proplist, + int identifier, + void **value, + int *len, + bool skip_first); + +/* + * Function: mosquitto_property_read_string + * + * Read a string property value from a property. + * + * On success, value must be free()'d by the application. + * + * Parameters: + * property - property to read + * identifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR) + * value - pointer to char*, for the property data to be stored in, or NULL if + * the value is not required. + * skip_first - boolean that indicates whether the first item in the list + * should be ignored or not. Should usually be set to false. + * + * Returns: + * A valid property pointer if the property is found + * NULL, if the property is not found, or proplist is NULL, or if an out of memory condition occurred. + * + * Example: + * See + */ +libmosq_EXPORT const mosquitto_property *mosquitto_property_read_string( + const mosquitto_property *proplist, + int identifier, + char **value, + bool skip_first); + +/* + * Function: mosquitto_property_read_string_pair + * + * Read a string pair property value pair from a property. + * + * On success, name and value must be free()'d by the application. + * + * Parameters: + * property - property to read + * identifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR) + * name - pointer to char* for the name property data to be stored in, or NULL + * if the name is not required. + * value - pointer to char*, for the property data to be stored in, or NULL if + * the value is not required. + * skip_first - boolean that indicates whether the first item in the list + * should be ignored or not. Should usually be set to false. + * + * Returns: + * A valid property pointer if the property is found + * NULL, if the property is not found, or proplist is NULL, or if an out of memory condition occurred. + * + * Example: + * See + */ +libmosq_EXPORT const mosquitto_property *mosquitto_property_read_string_pair( + const mosquitto_property *proplist, + int identifier, + char **name, + char **value, + bool skip_first); + +/* + * Function: mosquitto_property_free_all + * + * Free all properties from a list of properties. Frees the list and sets *properties to NULL. + * + * Parameters: + * properties - list of properties to free + * + * Example: + * mosquitto_properties *properties = NULL; + * // Add properties + * mosquitto_property_free_all(&properties); + */ +libmosq_EXPORT void mosquitto_property_free_all(mosquitto_property **properties); + +/* + * Function: mosquitto_property_copy_all + * + * Parameters: + * dest : pointer for new property list + * src : property list + * + * Returns: + * MOSQ_ERR_SUCCESS - on successful copy + * MOSQ_ERR_INVAL - if dest is NULL + * MOSQ_ERR_NOMEM - on out of memory (dest will be set to NULL) + */ +libmosq_EXPORT int mosquitto_property_copy_all(mosquitto_property **dest, const mosquitto_property *src); + +/* + * Function: mosquitto_property_check_command + * + * Check whether a property identifier is valid for the given command. + * + * Parameters: + * command - MQTT command (e.g. CMD_CONNECT) + * identifier - MQTT property (e.g. MQTT_PROP_USER_PROPERTY) + * + * Returns: + * MOSQ_ERR_SUCCESS - if the identifier is valid for command + * MOSQ_ERR_PROTOCOL - if the identifier is not valid for use with command. + */ +libmosq_EXPORT int mosquitto_property_check_command(int command, int identifier); + + +/* + * Function: mosquitto_property_check_all + * + * Check whether a list of properties are valid for a particular command, + * whether there are duplicates, and whether the values are valid where + * possible. + * + * Note that this function is used internally in the library whenever + * properties are passed to it, so in basic use this is not needed, but should + * be helpful to check property lists *before* the point of using them. + * + * Parameters: + * command - MQTT command (e.g. CMD_CONNECT) + * properties - list of MQTT properties to check. + * + * Returns: + * MOSQ_ERR_SUCCESS - if all properties are valid + * MOSQ_ERR_DUPLICATE_PROPERTY - if a property is duplicated where it is forbidden. + * MOSQ_ERR_PROTOCOL - if any property is invalid + */ +libmosq_EXPORT int mosquitto_property_check_all(int command, const mosquitto_property *properties); + +/* Function: mosquitto_string_to_property_info + * + * Parse a property name string and convert to a property identifier and data type. + * The property name is as defined in the MQTT specification, with - as a + * separator, for example: payload-format-indicator. + * + * Parameters: + * propname - the string to parse + * identifier - pointer to an int to receive the property identifier + * type - pointer to an int to receive the property type + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if the string does not match a property + * + * Example: + * mosquitto_string_to_property_info("response-topic", &id, &type); + * // id == MQTT_PROP_RESPONSE_TOPIC + * // type == MQTT_PROP_TYPE_STRING + */ +libmosq_EXPORT int mosquitto_string_to_property_info(const char *propname, int *identifier, int *type); + #ifdef __cplusplus } diff --git a/lib/mosquitto_internal.h b/lib/mosquitto_internal.h index dcba8659..41d6a896 100644 --- a/lib/mosquitto_internal.h +++ b/lib/mosquitto_internal.h @@ -115,7 +115,8 @@ enum mosquitto__protocol { mosq_p_invalid = 0, mosq_p_mqtt31 = 1, mosq_p_mqtt311 = 2, - mosq_p_mqtts = 3 + mosq_p_mqtts = 3, + mosq_p_mqtt5 = 5, }; enum mosquitto__threaded_state { @@ -131,6 +132,12 @@ enum mosquitto__transport { mosq_t_sctp = 3 }; + +struct mosquitto__alias{ + char *topic; + uint16_t alias; +}; + struct mosquitto__packet{ uint8_t *payload; struct mosquitto__packet *next; @@ -146,11 +153,13 @@ struct mosquitto__packet{ struct mosquitto_message_all{ struct mosquitto_message_all *next; + mosquitto_property *properties; time_t timestamp; //enum mosquitto_msg_direction direction; enum mosquitto_msg_state state; bool dup; struct mosquitto_message msg; + uint32_t expiry_interval; }; #ifdef WITH_TLS @@ -182,7 +191,10 @@ struct mosquitto { struct mosquitto__packet in_packet; struct mosquitto__packet *current_out_packet; struct mosquitto__packet *out_packet; - struct mosquitto_message *will; + struct mosquitto_message_all *will; + struct mosquitto__alias *aliases; + uint32_t maximum_packet_size; + int alias_count; #ifdef WITH_TLS SSL *ssl; SSL_CTX *ssl_ctx; @@ -216,10 +228,10 @@ struct mosquitto { pthread_mutex_t mid_mutex; pthread_t thread_id; #endif - bool clean_session; + bool clean_start; + uint32_t session_expiry_interval; #ifdef WITH_BROKER - char *old_id; /* for when a duplicate client connects, but we still want to - know what the id was */ + bool removed_from_by_id; /* True if removed from by_id hash */ bool is_dropping; bool is_bridge; struct mosquitto__bridge *bridge; @@ -262,11 +274,17 @@ struct mosquitto { struct mosquitto_message_all *out_messages_last; void (*on_connect)(struct mosquitto *, void *userdata, int rc); void (*on_connect_with_flags)(struct mosquitto *, void *userdata, int rc, int flags); + void (*on_connect_v5)(struct mosquitto *, void *userdata, int rc, int flags, const mosquitto_property *props); void (*on_disconnect)(struct mosquitto *, void *userdata, int rc); + void (*on_disconnect_v5)(struct mosquitto *, void *userdata, int rc, const mosquitto_property *props); void (*on_publish)(struct mosquitto *, void *userdata, int mid); + void (*on_publish_v5)(struct mosquitto *, void *userdata, int mid, int reason_code, const mosquitto_property *props); void (*on_message)(struct mosquitto *, void *userdata, const struct mosquitto_message *message); + void (*on_message_v5)(struct mosquitto *, void *userdata, const struct mosquitto_message *message, const mosquitto_property *props); void (*on_subscribe)(struct mosquitto *, void *userdata, int mid, int qos_count, const int *granted_qos); + void (*on_subscribe_v5)(struct mosquitto *, void *userdata, int mid, int qos_count, const int *granted_qos, const mosquitto_property *props); void (*on_unsubscribe)(struct mosquitto *, void *userdata, int mid); + void (*on_unsubscribe_v5)(struct mosquitto *, void *userdata, int mid, const mosquitto_property *props); void (*on_log)(struct mosquitto *, void *userdata, int level, const char *str); //void (*on_error)(); char *host; @@ -279,12 +297,15 @@ struct mosquitto { bool reconnect_exponential_backoff; char threaded; struct mosquitto__packet *out_packet_last; - int inflight_messages; - int max_inflight_messages; # ifdef WITH_SRV ares_channel achan; # endif #endif + int send_quota; + int receive_quota; + uint16_t send_maximum; + uint16_t receive_maximum; + uint8_t maximum_qos; #ifdef WITH_BROKER UT_hash_handle hh_id; @@ -298,5 +319,7 @@ struct mosquitto { #define STREMPTY(str) (str[0] == '\0') +void do_client_disconnect(struct mosquitto *mosq, int reason_code, const mosquitto_property *properties); + #endif diff --git a/lib/mqtt3_protocol.h b/lib/mqtt3_protocol.h deleted file mode 100644 index 4884bacb..00000000 --- a/lib/mqtt3_protocol.h +++ /dev/null @@ -1,53 +0,0 @@ -/* -Copyright (c) 2009-2018 Roger Light - -All rights reserved. This program and the accompanying materials -are made available under the terms of the Eclipse Public License v1.0 -and Eclipse Distribution License v1.0 which accompany this distribution. - -The Eclipse Public License is available at - http://www.eclipse.org/legal/epl-v10.html -and the Eclipse Distribution License is available at - http://www.eclipse.org/org/documents/edl-v10.php. - -Contributors: - Roger Light - initial implementation and documentation. -*/ - -#ifndef MQTT3_PROTOCOL_H -#define MQTT3_PROTOCOL_H - -/* For version 3 of the MQTT protocol */ - -#define PROTOCOL_NAME_v31 "MQIsdp" -#define PROTOCOL_VERSION_v31 3 - -#define PROTOCOL_NAME_v311 "MQTT" -#define PROTOCOL_VERSION_v311 4 - -/* Message types */ -#define CONNECT 0x10 -#define CONNACK 0x20 -#define PUBLISH 0x30 -#define PUBACK 0x40 -#define PUBREC 0x50 -#define PUBREL 0x60 -#define PUBCOMP 0x70 -#define SUBSCRIBE 0x80 -#define SUBACK 0x90 -#define UNSUBSCRIBE 0xA0 -#define UNSUBACK 0xB0 -#define PINGREQ 0xC0 -#define PINGRESP 0xD0 -#define DISCONNECT 0xE0 - -#define CONNACK_ACCEPTED 0 -#define CONNACK_REFUSED_PROTOCOL_VERSION 1 -#define CONNACK_REFUSED_IDENTIFIER_REJECTED 2 -#define CONNACK_REFUSED_SERVER_UNAVAILABLE 3 -#define CONNACK_REFUSED_BAD_USERNAME_PASSWORD 4 -#define CONNACK_REFUSED_NOT_AUTHORIZED 5 - -#define MQTT_MAX_PAYLOAD 268435455 - -#endif diff --git a/lib/mqtt_protocol.h b/lib/mqtt_protocol.h new file mode 100644 index 00000000..672d50b7 --- /dev/null +++ b/lib/mqtt_protocol.h @@ -0,0 +1,158 @@ +/* +Copyright (c) 2009-2018 Roger Light + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#ifndef MQTT_PROTOCOL_H +#define MQTT_PROTOCOL_H + +#define PROTOCOL_NAME_v31 "MQIsdp" +#define PROTOCOL_VERSION_v31 3 + +#define PROTOCOL_NAME "MQTT" + +#define PROTOCOL_VERSION_v311 4 +#define PROTOCOL_VERSION_v5 5 + + +/* Message types */ +#define CMD_CONNECT 0x10 +#define CMD_CONNACK 0x20 +#define CMD_PUBLISH 0x30 +#define CMD_PUBACK 0x40 +#define CMD_PUBREC 0x50 +#define CMD_PUBREL 0x60 +#define CMD_PUBCOMP 0x70 +#define CMD_SUBSCRIBE 0x80 +#define CMD_SUBACK 0x90 +#define CMD_UNSUBSCRIBE 0xA0 +#define CMD_UNSUBACK 0xB0 +#define CMD_PINGREQ 0xC0 +#define CMD_PINGRESP 0xD0 +#define CMD_DISCONNECT 0xE0 +#define CMD_AUTH 0xF0 + +/* Mosquitto only: for distinguishing CONNECT and WILL properties */ +#define CMD_WILL 0x100 + +enum mqtt311_connack_codes { + CONNACK_ACCEPTED = 0, + CONNACK_REFUSED_PROTOCOL_VERSION = 1, + CONNACK_REFUSED_IDENTIFIER_REJECTED = 2, + CONNACK_REFUSED_SERVER_UNAVAILABLE = 3, + CONNACK_REFUSED_BAD_USERNAME_PASSWORD = 4, + CONNACK_REFUSED_NOT_AUTHORIZED = 5, +}; + + +enum mqtt5_return_codes { + MQTT_RC_SUCCESS = 0, /* CONNACK, PUBACK, PUBREC, PUBREL, PUBCOMP, UNSUBACK, AUTH */ + MQTT_RC_NORMAL_DISCONNECTION = 0, /* DISCONNECT */ + MQTT_RC_GRANTED_QOS0 = 0, /* SUBACK */ + MQTT_RC_GRANTED_QOS1 = 1, /* SUBACK */ + MQTT_RC_GRANTED_QOS2 = 2, /* SUBACK */ + MQTT_RC_DISCONNECT_WITH_WILL_MSG = 4, /* DISCONNECT */ + MQTT_RC_NO_MATCHING_SUBSCRIBERS = 16, /* PUBACK, PUBREC */ + MQTT_RC_NO_SUBSCRIPTION_EXISTED = 17, /* UNSUBACK */ + MQTT_RC_CONTINUE_AUTHENTICATION = 24, /* AUTH */ + MQTT_RC_REAUTHENTICATE = 25, /* AUTH */ + + MQTT_RC_UNSPECIFIED = 128, /* CONNACK, PUBACK, PUBREC, SUBACK, UNSUBACK, DISCONNECT */ + MQTT_RC_MALFORMED_PACKET = 129, /* CONNACK, DISCONNECT */ + MQTT_RC_PROTOCOL_ERROR = 130, /* DISCONNECT */ + MQTT_RC_IMPLEMENTATION_SPECIFIC = 131, /* CONNACK, PUBACK, PUBREC, SUBACK, UNSUBACK, DISCONNECT */ + MQTT_RC_UNSUPPORTED_PROTOCOL_VERSION = 132, /* CONNACK */ + MQTT_RC_CLIENTID_NOT_VALID = 133, /* CONNACK */ + MQTT_RC_BAD_USERNAME_OR_PASSWORD = 134, /* CONNACK */ + MQTT_RC_NOT_AUTHORIZED = 135, /* CONNACK, PUBACK, PUBREC, SUBACK, UNSUBACK, DISCONNECT */ + MQTT_RC_SERVER_UNAVAILABLE = 136, /* CONNACK */ + MQTT_RC_SERVER_BUSY = 137, /* CONNACK, DISCONNECT */ + MQTT_RC_BANNED = 138, /* CONNACK */ + MQTT_RC_SERVER_SHUTTING_DOWN = 139, /* DISCONNECT */ + MQTT_RC_BAD_AUTHENTICATION_METHOD = 140, /* CONNACK */ + MQTT_RC_KEEP_ALIVE_TIMEOUT = 141, /* DISCONNECT */ + MQTT_RC_SESSION_TAKEN_OVER = 142, /* DISCONNECT */ + MQTT_RC_TOPIC_FILTER_INVALID = 143, /* SUBACK, UNSUBACK, DISCONNECT */ + MQTT_RC_TOPIC_NAME_INVALID = 144, /* CONNACK, PUBACK, PUBREC, DISCONNECT */ + MQTT_RC_PACKET_ID_IN_USE = 145, /* PUBACK, SUBACK, UNSUBACK */ + MQTT_RC_PACKET_ID_NOT_FOUND = 146, /* PUBREL, PUBCOMP */ + MQTT_RC_RECEIVE_MAXIMUM_EXCEEDED = 147, /* DISCONNECT */ + MQTT_RC_TOPIC_ALIAS_INVALID = 148, /* DISCONNECT */ + MQTT_RC_PACKET_TOO_LARGE = 149, /* CONNACK, PUBACK, PUBREC, DISCONNECT */ + MQTT_RC_MESSAGE_RATE_TOO_HIGH = 150, /* DISCONNECT */ + MQTT_RC_QUOTA_EXCEEDED = 151, /* PUBACK, PUBREC, SUBACK, DISCONNECT */ + MQTT_RC_ADMINISTRATIVE_ACTION = 152, /* DISCONNECT */ + MQTT_RC_PAYLOAD_FORMAT_INVALID = 153, /* CONNACK, DISCONNECT */ + MQTT_RC_RETAIN_NOT_SUPPORTED = 154, /* CONNACK, DISCONNECT */ + MQTT_RC_QOS_NOT_SUPPORTED = 155, /* CONNACK, DISCONNECT */ + MQTT_RC_USE_ANOTHER_SERVER = 156, /* CONNACK, DISCONNECT */ + MQTT_RC_SERVER_MOVED = 157, /* CONNACK, DISCONNECT */ + MQTT_RC_SHARED_SUBS_NOT_SUPPORTED = 158, /* SUBACK, DISCONNECT */ + MQTT_RC_CONNECTION_RATE_EXCEEDED = 159, /* CONNACK, DISCONNECT */ + MQTT_RC_MAXIMUM_CONNECT_TIME = 160, /* DISCONNECT */ + MQTT_RC_SUBSCRIPTION_IDS_NOT_SUPPORTED = 161, /* SUBACK, DISCONNECT */ + MQTT_RC_WILDCARD_SUBS_NOT_SUPPORTED = 162, /* SUBACK, DISCONNECT */ +}; + +enum mqtt5_property { + MQTT_PROP_PAYLOAD_FORMAT_INDICATOR = 1, /* Byte : PUBLISH, Will Properties */ + MQTT_PROP_MESSAGE_EXPIRY_INTERVAL = 2, /* 4 byte int : PUBLISH, Will Properties */ + MQTT_PROP_CONTENT_TYPE = 3, /* UTF-8 string : PUBLISH, Will Properties */ + MQTT_PROP_RESPONSE_TOPIC = 8, /* UTF-8 string : PUBLISH, Will Properties */ + MQTT_PROP_CORRELATION_DATA = 9, /* Binary Data : PUBLISH, Will Properties */ + MQTT_PROP_SUBSCRIPTION_IDENTIFIER = 11, /* Variable byte int : PUBLISH, SUBSCRIBE */ + MQTT_PROP_SESSION_EXPIRY_INTERVAL = 17, /* 4 byte int : CONNECT, CONNACK, DISCONNECT */ + MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER = 18, /* UTF-8 string : CONNACK */ + MQTT_PROP_SERVER_KEEP_ALIVE = 19, /* 2 byte int : CONNACK */ + MQTT_PROP_AUTHENTICATION_METHOD = 21, /* UTF-8 string : CONNECT, CONNACK, AUTH */ + MQTT_PROP_AUTHENTICATION_DATA = 22, /* Binary Data : CONNECT, CONNACK, AUTH */ + MQTT_PROP_REQUEST_PROBLEM_INFORMATION = 23, /* Byte : CONNECT */ + MQTT_PROP_WILL_DELAY_INTERVAL = 24, /* 4 byte int : Will properties */ + MQTT_PROP_REQUEST_RESPONSE_INFORMATION = 25,/* Byte : CONNECT */ + MQTT_PROP_RESPONSE_INFORMATION = 26, /* UTF-8 string : CONNACK */ + MQTT_PROP_SERVER_REFERENCE = 28, /* UTF-8 string : CONNACK, DISCONNECT */ + MQTT_PROP_REASON_STRING = 31, /* UTF-8 string : All except Will properties */ + MQTT_PROP_RECEIVE_MAXIMUM = 33, /* 2 byte int : CONNECT, CONNACK */ + MQTT_PROP_TOPIC_ALIAS_MAXIMUM = 34, /* 2 byte int : CONNECT, CONNACK */ + MQTT_PROP_TOPIC_ALIAS = 35, /* 2 byte int : PUBLISH */ + MQTT_PROP_MAXIMUM_QOS = 36, /* Byte : CONNACK */ + MQTT_PROP_RETAIN_AVAILABLE = 37, /* Byte : CONNACK */ + MQTT_PROP_USER_PROPERTY = 38, /* UTF-8 string pair : All */ + MQTT_PROP_MAXIMUM_PACKET_SIZE = 39, /* 4 byte int : CONNECT, CONNACK */ + MQTT_PROP_WILDCARD_SUB_AVAILABLE = 40, /* Byte : CONNACK */ + MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE = 41, /* Byte : CONNACK */ + MQTT_PROP_SHARED_SUB_AVAILABLE = 42, /* Byte : CONNACK */ +}; + +enum mqtt5_property_type { + MQTT_PROP_TYPE_BYTE = 1, + MQTT_PROP_TYPE_INT16 = 2, + MQTT_PROP_TYPE_INT32 = 3, + MQTT_PROP_TYPE_VARINT = 4, + MQTT_PROP_TYPE_BINARY = 5, + MQTT_PROP_TYPE_STRING = 6, + MQTT_PROP_TYPE_STRING_PAIR = 7 +}; + +enum mqtt5_sub_options { + MQTT_SUB_OPT_NO_LOCAL = 0x04, + MQTT_SUB_OPT_RETAIN_AS_PUBLISHED = 0x08, + MQTT_SUB_OPT_SEND_RETAIN_ALWAYS = 0x00, + MQTT_SUB_OPT_SEND_RETAIN_NEW = 0x10, + MQTT_SUB_OPT_SEND_RETAIN_NEVER = 0x20, +}; + +#define MQTT_MAX_PAYLOAD 268435455 + +#endif diff --git a/lib/net_mosq.c b/lib/net_mosq.c index 5e679e1a..35bbf466 100644 --- a/lib/net_mosq.c +++ b/lib/net_mosq.c @@ -64,7 +64,7 @@ Contributors: #include "logging_mosq.h" #include "memory_mosq.h" -#include "mqtt3_protocol.h" +#include "mqtt_protocol.h" #include "net_mosq.h" #include "time_mosq.h" #include "util_mosq.h" @@ -123,11 +123,17 @@ int net__init(void) #endif #ifdef WITH_TLS +# if OPENSSL_VERSION_NUMBER < 0x10100000L || OPENSSL_API_COMPAT < 0x10100000L SSL_load_error_strings(); SSL_library_init(); OpenSSL_add_all_algorithms(); ENGINE_load_builtin_engines(); setup_ui_method(); +# else + OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS \ + | OPENSSL_INIT_ADD_ALL_DIGESTS \ + | OPENSSL_INIT_LOAD_CONFIG, NULL); +# endif if(tls_ex_index_mosq == -1){ tls_ex_index_mosq = SSL_get_ex_new_index(0, "client context", NULL, NULL, NULL); } @@ -138,16 +144,18 @@ int net__init(void) void net__cleanup(void) { #ifdef WITH_TLS - #if OPENSSL_VERSION_NUMBER < 0x10100000L - ERR_remove_state(0); - #endif - #ifndef OPENSSL_NO_ENGINE - ENGINE_cleanup(); - #endif - CONF_modules_unload(1); - ERR_free_strings(); - EVP_cleanup(); +# if OPENSSL_VERSION_NUMBER < 0x10100000L || OPENSSL_API_COMPAT < 0x10100000L CRYPTO_cleanup_all_ex_data(); + ERR_free_strings(); + ERR_remove_state(0); + EVP_cleanup(); + +# if !defined(OPENSSL_NO_ENGINE) + ENGINE_cleanup(); +# endif +# endif + + CONF_modules_unload(1); #endif #ifdef WITH_SRV @@ -183,10 +191,6 @@ int net__socket_close(struct mosquitto *mosq) SSL_free(mosq->ssl); mosq->ssl = NULL; } - if(mosq->ssl_ctx){ - SSL_CTX_free(mosq->ssl_ctx); - mosq->ssl_ctx = NULL; - } } #endif @@ -200,7 +204,7 @@ int net__socket_close(struct mosquitto *mosq) }else #endif { - if((int)mosq->sock >= 0){ + if(mosq->sock != INVALID_SOCKET){ #ifdef WITH_BROKER HASH_DELETE(hh_sock, db->contexts_by_sock, mosq); #endif @@ -212,8 +216,6 @@ int net__socket_close(struct mosquitto *mosq) #ifdef WITH_BROKER if(mosq->listener){ mosq->listener->client_count--; - assert(mosq->listener->client_count >= 0); - mosq->listener = NULL; } #endif @@ -221,7 +223,7 @@ int net__socket_close(struct mosquitto *mosq) } -#ifdef WITH_TLS_PSK +#ifdef FINAL_WITH_TLS_PSK static unsigned int psk_client_callback(SSL *ssl, const char *hint, char *identity, unsigned int max_identity_len, unsigned char *psk, unsigned int max_psk_len) @@ -246,21 +248,39 @@ int net__try_connect_step1(struct mosquitto *mosq, const char *host) { int s; void *sevp = NULL; + struct addrinfo *hints; if(mosq->adns){ + gai_cancel(mosq->adns); + mosquitto__free((struct addrinfo *)mosq->adns->ar_request); mosquitto__free(mosq->adns); } mosq->adns = mosquitto__calloc(1, sizeof(struct gaicb)); if(!mosq->adns){ return MOSQ_ERR_NOMEM; } + + hints = mosquitto__calloc(1, sizeof(struct addrinfo)); + if(!hints){ + mosquitto__free(mosq->adns); + mosq->adns = NULL; + return MOSQ_ERR_NOMEM; + } + + hints->ai_family = AF_UNSPEC; + hints->ai_socktype = SOCK_STREAM; + mosq->adns->ar_name = host; + mosq->adns->ar_request = hints; s = getaddrinfo_a(GAI_NOWAIT, &mosq->adns, 1, sevp); if(s){ errno = s; - mosquitto__free(mosq->adns); - mosq->adns = NULL; + if(mosq->adns){ + mosquitto__free((struct addrinfo *)mosq->adns->ar_request); + mosquitto__free(mosq->adns); + mosq->adns = NULL; + } return MOSQ_ERR_EAI; } @@ -316,6 +336,7 @@ int net__try_connect_step2(struct mosquitto *mosq, uint16_t port, mosq_sock_t *s freeaddrinfo(mosq->adns->ar_result); mosq->adns->ar_result = NULL; + mosquitto__free((struct addrinfo *)mosq->adns->ar_request); mosquitto__free(mosq->adns); mosq->adns = NULL; @@ -672,7 +693,7 @@ static int net__init_ssl_ctx(struct mosquitto *mosq) return MOSQ_ERR_TLS; } } -#ifdef WITH_TLS_PSK +#ifdef FINAL_WITH_TLS_PSK }else if(mosq->tls_psk){ SSL_CTX_set_psk_client_callback(mosq->ssl_ctx, psk_client_callback); #endif @@ -684,7 +705,7 @@ static int net__init_ssl_ctx(struct mosquitto *mosq) #endif -int net__socket_connect_step3(struct mosquitto *mosq, const char *host, uint16_t port, const char *bind_address, bool blocking) +int net__socket_connect_step3(struct mosquitto *mosq, const char *host) { #ifdef WITH_TLS BIO *bio; @@ -745,8 +766,13 @@ int net__socket_connect(struct mosquitto *mosq, const char *host, uint16_t port, mosq->sock = sock; - rc = net__socket_connect_step3(mosq, host, port, bind_address, blocking); - if(rc) return rc; +#if defined(WITH_SOCKS) && !defined(WITH_BROKER) + if(!mosq->socks5_host) +#endif + { + rc = net__socket_connect_step3(mosq, host); + if(rc) return rc; + } return MOSQ_ERR_SUCCESS; } diff --git a/lib/net_mosq.h b/lib/net_mosq.h index a5676032..e0c118dc 100644 --- a/lib/net_mosq.h +++ b/lib/net_mosq.h @@ -61,7 +61,7 @@ int net__socket_close(struct mosquitto *mosq); int net__try_connect(struct mosquitto *mosq, const char *host, uint16_t port, mosq_sock_t *sock, const char *bind_address, bool blocking); int net__try_connect_step1(struct mosquitto *mosq, const char *host); int net__try_connect_step2(struct mosquitto *mosq, uint16_t port, mosq_sock_t *sock); -int net__socket_connect_step3(struct mosquitto *mosq, const char *host, uint16_t port, const char *bind_address, bool blocking); +int net__socket_connect_step3(struct mosquitto *mosq, const char *host); int net__socket_nonblock(mosq_sock_t *sock); int net__socketpair(mosq_sock_t *sp1, mosq_sock_t *sp2); diff --git a/lib/options.c b/lib/options.c index f1e737e3..5346066b 100644 --- a/lib/options.c +++ b/lib/options.c @@ -29,14 +29,29 @@ Contributors: #include "mosquitto.h" #include "mosquitto_internal.h" #include "memory_mosq.h" +#include "mqtt_protocol.h" #include "util_mosq.h" #include "will_mosq.h" int mosquitto_will_set(struct mosquitto *mosq, const char *topic, int payloadlen, const void *payload, int qos, bool retain) { + return mosquitto_will_set_v5(mosq, topic, payloadlen, payload, qos, retain, NULL); +} + + +int mosquitto_will_set_v5(struct mosquitto *mosq, const char *topic, int payloadlen, const void *payload, int qos, bool retain, mosquitto_property *properties) +{ + int rc; + if(!mosq) return MOSQ_ERR_INVAL; - return will__set(mosq, topic, payloadlen, payload, qos, retain); + + if(properties){ + rc = mosquitto_property_check_all(CMD_WILL, properties); + if(rc) return rc; + } + + return will__set(mosq, topic, payloadlen, payload, qos, retain, properties); } @@ -80,6 +95,8 @@ int mosquitto_reconnect_delay_set(struct mosquitto *mosq, unsigned int reconnect { if(!mosq) return MOSQ_ERR_INVAL; + if(reconnect_delay == 0) reconnect_delay = 1; + mosq->reconnect_delay = reconnect_delay; mosq->reconnect_delay_max = reconnect_delay_max; mosq->reconnect_exponential_backoff = reconnect_exponential_backoff; @@ -288,7 +305,7 @@ int mosquitto_string_option(struct mosquitto *mosq, enum mosq_opt_t option, cons int mosquitto_tls_psk_set(struct mosquitto *mosq, const char *psk, const char *identity, const char *ciphers) { -#ifdef WITH_TLS_PSK +#ifdef FINAL_WITH_TLS_PSK if(!mosq || !psk || !identity) return MOSQ_ERR_INVAL; /* Check for hex only digits */ @@ -326,14 +343,7 @@ int mosquitto_opts_set(struct mosquitto *mosq, enum mosq_opt_t option, void *val switch(option){ case MOSQ_OPT_PROTOCOL_VERSION: ival = *((int *)value); - if(ival == MQTT_PROTOCOL_V31){ - mosq->protocol = mosq_p_mqtt31; - }else if(ival == MQTT_PROTOCOL_V311){ - mosq->protocol = mosq_p_mqtt311; - }else{ - return MOSQ_ERR_INVAL; - } - break; + return mosquitto_int_option(mosq, option, ival); case MOSQ_OPT_SSL_CTX: #ifdef WITH_TLS mosq->ssl_ctx = (SSL_CTX *)value; @@ -348,9 +358,78 @@ int mosquitto_opts_set(struct mosquitto *mosq, enum mosq_opt_t option, void *val #else return MOSQ_ERR_NOT_SUPPORTED; #endif + default: + return MOSQ_ERR_INVAL; + } + return MOSQ_ERR_SUCCESS; +} + + +int mosquitto_int_option(struct mosquitto *mosq, enum mosq_opt_t option, int value) +{ + if(!mosq) return MOSQ_ERR_INVAL; + + switch(option){ + case MOSQ_OPT_PROTOCOL_VERSION: + if(value == MQTT_PROTOCOL_V31){ + mosq->protocol = mosq_p_mqtt31; + }else if(value == MQTT_PROTOCOL_V311){ + mosq->protocol = mosq_p_mqtt311; + }else if(value == MQTT_PROTOCOL_V5){ + mosq->protocol = mosq_p_mqtt5; + }else{ + return MOSQ_ERR_INVAL; + } + break; + + case MOSQ_OPT_RECEIVE_MAXIMUM: + if(value < 0 || value > 65535){ + return MOSQ_ERR_INVAL; + } + mosq->receive_maximum = value; + break; + + case MOSQ_OPT_SEND_MAXIMUM: + if(value < 0 || value > 65535){ + return MOSQ_ERR_INVAL; + } + mosq->send_maximum = value; + break; + case MOSQ_OPT_SSL_CTX_WITH_DEFAULTS: #if defined(WITH_TLS) && OPENSSL_VERSION_NUMBER >= 0x10100000L - mosq->ssl_ctx_defaults = true; + if(value){ + mosq->ssl_ctx_defaults = true; + }else{ + mosq->ssl_ctx_defaults = false; + } + break; +#else + return MOSQ_ERR_NOT_SUPPORTED; +#endif + + default: + return MOSQ_ERR_INVAL; + } + return MOSQ_ERR_SUCCESS; +} + + +int mosquitto_void_option(struct mosquitto *mosq, enum mosq_opt_t option, void *value) +{ + if(!mosq || !value) return MOSQ_ERR_INVAL; + + switch(option){ + case MOSQ_OPT_SSL_CTX: +#ifdef WITH_TLS + mosq->ssl_ctx = (SSL_CTX *)value; + if(mosq->ssl_ctx){ +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(LIBRESSL_VERSION_NUMBER) + SSL_CTX_up_ref(mosq->ssl_ctx); +#else + CRYPTO_add(&(mosq->ssl_ctx)->references, 1, CRYPTO_LOCK_SSL_CTX); +#endif + } break; #else return MOSQ_ERR_NOT_SUPPORTED; diff --git a/lib/packet_datatypes.c b/lib/packet_datatypes.c new file mode 100644 index 00000000..335cb9f0 --- /dev/null +++ b/lib/packet_datatypes.c @@ -0,0 +1,271 @@ +/* +Copyright (c) 2009-2018 Roger Light + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#include +#include +#include + +#ifdef WITH_BROKER +# include "mosquitto_broker_internal.h" +# ifdef WITH_WEBSOCKETS +# include +# endif +#else +# include "read_handle.h" +#endif + +#include "memory_mosq.h" +#include "mqtt_protocol.h" +#include "net_mosq.h" +#include "packet_mosq.h" +#include "read_handle.h" +#ifdef WITH_BROKER +# include "sys_tree.h" +#else +# define G_BYTES_RECEIVED_INC(A) +# define G_BYTES_SENT_INC(A) +# define G_MSGS_SENT_INC(A) +# define G_PUB_MSGS_SENT_INC(A) +#endif + + +int packet__read_byte(struct mosquitto__packet *packet, uint8_t *byte) +{ + assert(packet); + if(packet->pos+1 > packet->remaining_length) return MOSQ_ERR_PROTOCOL; + + *byte = packet->payload[packet->pos]; + packet->pos++; + + return MOSQ_ERR_SUCCESS; +} + + +void packet__write_byte(struct mosquitto__packet *packet, uint8_t byte) +{ + assert(packet); + assert(packet->pos+1 <= packet->packet_length); + + packet->payload[packet->pos] = byte; + packet->pos++; +} + + +int packet__read_bytes(struct mosquitto__packet *packet, void *bytes, uint32_t count) +{ + assert(packet); + if(packet->pos+count > packet->remaining_length) return MOSQ_ERR_PROTOCOL; + + memcpy(bytes, &(packet->payload[packet->pos]), count); + packet->pos += count; + + return MOSQ_ERR_SUCCESS; +} + + +void packet__write_bytes(struct mosquitto__packet *packet, const void *bytes, uint32_t count) +{ + assert(packet); + assert(packet->pos+count <= packet->packet_length); + + memcpy(&(packet->payload[packet->pos]), bytes, count); + packet->pos += count; +} + + +int packet__read_binary(struct mosquitto__packet *packet, uint8_t **data, int *length) +{ + uint16_t slen; + int rc; + + assert(packet); + rc = packet__read_uint16(packet, &slen); + if(rc) return rc; + + if(slen == 0){ + *data = NULL; + *length = 0; + return MOSQ_ERR_SUCCESS; + } + + if(packet->pos+slen > packet->remaining_length) return MOSQ_ERR_PROTOCOL; + + *data = mosquitto__malloc(slen+1); + if(*data){ + memcpy(*data, &(packet->payload[packet->pos]), slen); + ((uint8_t *)(*data))[slen] = '\0'; + packet->pos += slen; + }else{ + return MOSQ_ERR_NOMEM; + } + + *length = slen; + return MOSQ_ERR_SUCCESS; +} + + +int packet__read_string(struct mosquitto__packet *packet, char **str, int *length) +{ + int rc; + + rc = packet__read_binary(packet, (uint8_t **)str, length); + if(rc) return rc; + if(*length == 0) return MOSQ_ERR_SUCCESS; + + if(mosquitto_validate_utf8(*str, *length)){ + mosquitto__free(*str); + *str = NULL; + *length = -1; + return MOSQ_ERR_MALFORMED_UTF8; + } + + return MOSQ_ERR_SUCCESS; +} + + +void packet__write_string(struct mosquitto__packet *packet, const char *str, uint16_t length) +{ + assert(packet); + packet__write_uint16(packet, length); + packet__write_bytes(packet, str, length); +} + + +int packet__read_uint16(struct mosquitto__packet *packet, uint16_t *word) +{ + uint8_t msb, lsb; + + assert(packet); + if(packet->pos+2 > packet->remaining_length) return MOSQ_ERR_PROTOCOL; + + msb = packet->payload[packet->pos]; + packet->pos++; + lsb = packet->payload[packet->pos]; + packet->pos++; + + *word = (msb<<8) + lsb; + + return MOSQ_ERR_SUCCESS; +} + + +void packet__write_uint16(struct mosquitto__packet *packet, uint16_t word) +{ + packet__write_byte(packet, MOSQ_MSB(word)); + packet__write_byte(packet, MOSQ_LSB(word)); +} + + +int packet__read_uint32(struct mosquitto__packet *packet, uint32_t *word) +{ + uint32_t val = 0; + int i; + + assert(packet); + if(packet->pos+4 > packet->remaining_length) return MOSQ_ERR_PROTOCOL; + + for(i=0; i<4; i++){ + val = (val << 8) + packet->payload[packet->pos]; + packet->pos++; + } + + *word = val; + + return MOSQ_ERR_SUCCESS; +} + + +void packet__write_uint32(struct mosquitto__packet *packet, uint32_t word) +{ + packet__write_byte(packet, (word & 0xFF000000) >> 24); + packet__write_byte(packet, (word & 0x00FF0000) >> 16); + packet__write_byte(packet, (word & 0x0000FF00) >> 8); + packet__write_byte(packet, (word & 0x000000FF)); +} + + +int packet__read_varint(struct mosquitto__packet *packet, int32_t *word, int8_t *bytes) +{ + int i; + uint8_t byte; + int remaining_mult = 1; + int32_t lword = 0; + uint8_t lbytes = 0; + + for(i=0; i<4; i++){ + if(packet->pos < packet->remaining_length){ + lbytes++; + byte = packet->payload[packet->pos]; + lword += (byte & 127) * remaining_mult; + remaining_mult *= 128; + packet->pos++; + if((byte & 128) == 0){ + if(lbytes > 1 && byte == 0){ + /* Catch overlong encodings */ + return MOSQ_ERR_PROTOCOL; + }else{ + *word = lword; + if(bytes) (*bytes) = lbytes; + return MOSQ_ERR_SUCCESS; + } + } + }else{ + return MOSQ_ERR_PROTOCOL; + } + } + return MOSQ_ERR_PROTOCOL; +} + + +int packet__write_varint(struct mosquitto__packet *packet, int32_t word) +{ + uint8_t byte; + int count = 0; + + do{ + byte = word % 128; + word = word / 128; + /* If there are more digits to encode, set the top bit of this digit */ + if(word > 0){ + byte = byte | 0x80; + } + packet__write_byte(packet, byte); + count++; + }while(word > 0 && count < 5); + + if(count == 5){ + return MOSQ_ERR_PROTOCOL; + } + return MOSQ_ERR_SUCCESS; +} + + +int packet__varint_bytes(int32_t word) +{ + if(word < 128){ + return 1; + }else if(word < 16384){ + return 2; + }else if(word < 2097152){ + return 3; + }else if(word < 268435456){ + return 4; + }else{ + return 5; + } +} diff --git a/lib/packet_mosq.c b/lib/packet_mosq.c index 686006fc..abc03922 100644 --- a/lib/packet_mosq.c +++ b/lib/packet_mosq.c @@ -30,12 +30,13 @@ Contributors: #endif #include "memory_mosq.h" -#include "mqtt3_protocol.h" +#include "mqtt_protocol.h" #include "net_mosq.h" #include "packet_mosq.h" #include "read_handle.h" #ifdef WITH_BROKER # include "sys_tree.h" +# include "send_mosq.h" #else # define G_BYTES_RECEIVED_INC(A) # define G_BYTES_SENT_INC(A) @@ -141,7 +142,7 @@ int packet__queue(struct mosquitto *mosq, struct mosquitto__packet *packet) #endif } - if(mosq->in_callback == false && mosq->threaded == false){ + if(mosq->in_callback == false && mosq->threaded == mosq_ts_none){ return packet__write(mosq); }else{ return MOSQ_ERR_SUCCESS; @@ -150,105 +151,18 @@ int packet__queue(struct mosquitto *mosq, struct mosquitto__packet *packet) } -int packet__read_byte(struct mosquitto__packet *packet, uint8_t *byte) +int packet__check_oversize(struct mosquitto *mosq, uint32_t remaining_length) { - assert(packet); - if(packet->pos+1 > packet->remaining_length) return MOSQ_ERR_PROTOCOL; + int len; - *byte = packet->payload[packet->pos]; - packet->pos++; + if(mosq->maximum_packet_size == 0) return MOSQ_ERR_SUCCESS; - return MOSQ_ERR_SUCCESS; -} - - -void packet__write_byte(struct mosquitto__packet *packet, uint8_t byte) -{ - assert(packet); - assert(packet->pos+1 <= packet->packet_length); - - packet->payload[packet->pos] = byte; - packet->pos++; -} - - -int packet__read_bytes(struct mosquitto__packet *packet, void *bytes, uint32_t count) -{ - assert(packet); - if(packet->pos+count > packet->remaining_length) return MOSQ_ERR_PROTOCOL; - - memcpy(bytes, &(packet->payload[packet->pos]), count); - packet->pos += count; - - return MOSQ_ERR_SUCCESS; -} - - -void packet__write_bytes(struct mosquitto__packet *packet, const void *bytes, uint32_t count) -{ - assert(packet); - assert(packet->pos+count <= packet->packet_length); - - memcpy(&(packet->payload[packet->pos]), bytes, count); - packet->pos += count; -} - - -int packet__read_string(struct mosquitto__packet *packet, char **str, int *length) -{ - uint16_t slen; - int rc; - - assert(packet); - rc = packet__read_uint16(packet, &slen); - if(rc) return rc; - - if(packet->pos+slen > packet->remaining_length) return MOSQ_ERR_PROTOCOL; - - *str = mosquitto__malloc(slen+1); - if(*str){ - memcpy(*str, &(packet->payload[packet->pos]), slen); - (*str)[slen] = '\0'; - packet->pos += slen; + len = remaining_length + packet__varint_bytes(remaining_length); + if(len > mosq->maximum_packet_size){ + return MOSQ_ERR_OVERSIZE_PACKET; }else{ - return MOSQ_ERR_NOMEM; + return MOSQ_ERR_SUCCESS; } - - *length = slen; - return MOSQ_ERR_SUCCESS; -} - - -void packet__write_string(struct mosquitto__packet *packet, const char *str, uint16_t length) -{ - assert(packet); - packet__write_uint16(packet, length); - packet__write_bytes(packet, str, length); -} - - -int packet__read_uint16(struct mosquitto__packet *packet, uint16_t *word) -{ - uint8_t msb, lsb; - - assert(packet); - if(packet->pos+2 > packet->remaining_length) return MOSQ_ERR_PROTOCOL; - - msb = packet->payload[packet->pos]; - packet->pos++; - lsb = packet->payload[packet->pos]; - packet->pos++; - - *word = (msb<<8) + lsb; - - return MOSQ_ERR_SUCCESS; -} - - -void packet__write_uint16(struct mosquitto__packet *packet, uint16_t word) -{ - packet__write_byte(packet, MOSQ_MSB(word)); - packet__write_byte(packet, MOSQ_LSB(word)); } @@ -309,7 +223,7 @@ int packet__write(struct mosquitto *mosq) } G_MSGS_SENT_INC(1); - if(((packet->command)&0xF6) == PUBLISH){ + if(((packet->command)&0xF6) == CMD_PUBLISH){ G_PUB_MSGS_SENT_INC(1); #ifndef WITH_BROKER pthread_mutex_lock(&mosq->callback_mutex); @@ -319,41 +233,17 @@ int packet__write(struct mosquitto *mosq) mosq->on_publish(mosq, mosq->userdata, packet->mid); mosq->in_callback = false; } - pthread_mutex_unlock(&mosq->callback_mutex); - }else if(((packet->command)&0xF0) == DISCONNECT){ - /* FIXME what cleanup needs doing here? - * incoming/outgoing messages? */ - net__socket_close(mosq); - - /* Start of duplicate, possibly unnecessary code. - * This does leave things in a consistent state at least. */ - /* Free data and reset values */ - pthread_mutex_lock(&mosq->out_packet_mutex); - mosq->current_out_packet = mosq->out_packet; - if(mosq->out_packet){ - mosq->out_packet = mosq->out_packet->next; - if(!mosq->out_packet){ - mosq->out_packet_last = NULL; - } - } - pthread_mutex_unlock(&mosq->out_packet_mutex); - - packet__cleanup(packet); - mosquitto__free(packet); - - pthread_mutex_lock(&mosq->msgtime_mutex); - mosq->next_msg_out = mosquitto_time() + mosq->keepalive; - pthread_mutex_unlock(&mosq->msgtime_mutex); - /* End of duplicate, possibly unnecessary code */ - - pthread_mutex_lock(&mosq->callback_mutex); - if(mosq->on_disconnect){ + if(mosq->on_publish_v5){ + /* This is a QoS=0 message */ mosq->in_callback = true; - mosq->on_disconnect(mosq, mosq->userdata, MOSQ_ERR_SUCCESS); + mosq->on_publish_v5(mosq, mosq->userdata, packet->mid, 0, NULL); mosq->in_callback = false; } pthread_mutex_unlock(&mosq->callback_mutex); - pthread_mutex_unlock(&mosq->current_out_packet_mutex); + }else if(((packet->command)&0xF0) == CMD_DISCONNECT){ + do_client_disconnect(mosq, MOSQ_ERR_SUCCESS, NULL); + packet__cleanup(packet); + mosquitto__free(packet); return MOSQ_ERR_SUCCESS; #endif } @@ -391,8 +281,12 @@ int packet__read(struct mosquitto *mosq) ssize_t read_length; int rc = 0; - if(!mosq) return MOSQ_ERR_INVAL; - if(mosq->sock == INVALID_SOCKET) return MOSQ_ERR_NO_CONN; + if(!mosq){ + return MOSQ_ERR_INVAL; + } + if(mosq->sock == INVALID_SOCKET){ + return MOSQ_ERR_NO_CONN; + } if(mosq->state == mosq_cs_connect_pending){ return MOSQ_ERR_SUCCESS; } @@ -418,10 +312,14 @@ int packet__read(struct mosquitto *mosq) #ifdef WITH_BROKER G_BYTES_RECEIVED_INC(1); /* Clients must send CONNECT as their first command. */ - if(!(mosq->bridge) && mosq->state == mosq_cs_new && (byte&0xF0) != CONNECT) return MOSQ_ERR_PROTOCOL; + if(!(mosq->bridge) && mosq->state == mosq_cs_new && (byte&0xF0) != CMD_CONNECT){ + return MOSQ_ERR_PROTOCOL; + } #endif }else{ - if(read_length == 0) return MOSQ_ERR_CONN_LOST; /* EOF */ + if(read_length == 0){ + return MOSQ_ERR_CONN_LOST; /* EOF */ + } #ifdef WIN32 errno = WSAGetLastError(); #endif @@ -454,13 +352,17 @@ int packet__read(struct mosquitto *mosq) /* Max 4 bytes length for remaining length as defined by protocol. * Anything more likely means a broken/malicious client. */ - if(mosq->in_packet.remaining_count < -4) return MOSQ_ERR_PROTOCOL; + if(mosq->in_packet.remaining_count < -4){ + return MOSQ_ERR_PROTOCOL; + } G_BYTES_RECEIVED_INC(1); mosq->in_packet.remaining_length += (byte & 127) * mosq->in_packet.remaining_mult; mosq->in_packet.remaining_mult *= 128; }else{ - if(read_length == 0) return MOSQ_ERR_CONN_LOST; /* EOF */ + if(read_length == 0){ + return MOSQ_ERR_CONN_LOST; /* EOF */ + } #ifdef WIN32 errno = WSAGetLastError(); #endif @@ -480,9 +382,22 @@ int packet__read(struct mosquitto *mosq) * positive. */ mosq->in_packet.remaining_count *= -1; +#ifdef WITH_BROKER + if(db->config->max_packet_size > 0 && mosq->in_packet.remaining_length+1 > db->config->max_packet_size){ + log__printf(NULL, MOSQ_LOG_INFO, "Client %s sent too large packet %d, disconnecting.", mosq->id, mosq->in_packet.remaining_length+1); + if(mosq->protocol == mosq_p_mqtt5){ + send__disconnect(mosq, MQTT_RC_PACKET_TOO_LARGE, NULL); + } + return MOSQ_ERR_OVERSIZE_PACKET; + } +#else + // FIXME - client case for incoming message received from broker too large +#endif if(mosq->in_packet.remaining_length > 0){ mosq->in_packet.payload = mosquitto__malloc(mosq->in_packet.remaining_length*sizeof(uint8_t)); - if(!mosq->in_packet.payload) return MOSQ_ERR_NOMEM; + if(!mosq->in_packet.payload){ + return MOSQ_ERR_NOMEM; + } mosq->in_packet.to_process = mosq->in_packet.remaining_length; } } @@ -523,7 +438,7 @@ int packet__read(struct mosquitto *mosq) mosq->in_packet.pos = 0; #ifdef WITH_BROKER G_MSGS_RECEIVED_INC(1); - if(((mosq->in_packet.command)&0xF5) == PUBLISH){ + if(((mosq->in_packet.command)&0xF5) == CMD_PUBLISH){ G_PUB_MSGS_RECEIVED_INC(1); } rc = handle__packet(db, mosq); diff --git a/lib/packet_mosq.h b/lib/packet_mosq.h index bc6eebfe..503fb523 100644 --- a/lib/packet_mosq.h +++ b/lib/packet_mosq.h @@ -27,15 +27,24 @@ int packet__alloc(struct mosquitto__packet *packet); void packet__cleanup(struct mosquitto__packet *packet); int packet__queue(struct mosquitto *mosq, struct mosquitto__packet *packet); +int packet__check_oversize(struct mosquitto *mosq, uint32_t remaining_length); + int packet__read_byte(struct mosquitto__packet *packet, uint8_t *byte); int packet__read_bytes(struct mosquitto__packet *packet, void *bytes, uint32_t count); +int packet__read_binary(struct mosquitto__packet *packet, uint8_t **data, int *length); int packet__read_string(struct mosquitto__packet *packet, char **str, int *length); int packet__read_uint16(struct mosquitto__packet *packet, uint16_t *word); +int packet__read_uint32(struct mosquitto__packet *packet, uint32_t *word); +int packet__read_varint(struct mosquitto__packet *packet, int32_t *word, int8_t *bytes); void packet__write_byte(struct mosquitto__packet *packet, uint8_t byte); void packet__write_bytes(struct mosquitto__packet *packet, const void *bytes, uint32_t count); void packet__write_string(struct mosquitto__packet *packet, const char *str, uint16_t length); void packet__write_uint16(struct mosquitto__packet *packet, uint16_t word); +void packet__write_uint32(struct mosquitto__packet *packet, uint32_t word); +int packet__write_varint(struct mosquitto__packet *packet, int32_t word); + +int packet__varint_bytes(int32_t word); int packet__write(struct mosquitto *mosq); #ifdef WITH_BROKER diff --git a/lib/property_mosq.c b/lib/property_mosq.c new file mode 100644 index 00000000..e9399fd5 --- /dev/null +++ b/lib/property_mosq.c @@ -0,0 +1,1176 @@ +/* +Copyright (c) 2018 Roger Light + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#include +#include +#include + +#include "logging_mosq.h" +#include "memory_mosq.h" +#include "mqtt_protocol.h" +#include "packet_mosq.h" +#include "property_mosq.h" + + +int property__read(struct mosquitto__packet *packet, int32_t *len, mosquitto_property *property) +{ + int rc; + int32_t property_identifier; + uint8_t byte; + int8_t byte_count; + uint16_t uint16; + uint32_t uint32; + int32_t varint; + char *str1, *str2; + int slen1, slen2; + + rc = packet__read_varint(packet, &property_identifier, NULL); + if(rc) return rc; + *len -= 1; + + memset(property, 0, sizeof(mosquitto_property)); + + property->identifier = property_identifier; + + switch(property_identifier){ + case MQTT_PROP_PAYLOAD_FORMAT_INDICATOR: + case MQTT_PROP_REQUEST_PROBLEM_INFORMATION: + case MQTT_PROP_REQUEST_RESPONSE_INFORMATION: + case MQTT_PROP_MAXIMUM_QOS: + case MQTT_PROP_RETAIN_AVAILABLE: + case MQTT_PROP_WILDCARD_SUB_AVAILABLE: + case MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE: + case MQTT_PROP_SHARED_SUB_AVAILABLE: + rc = packet__read_byte(packet, &byte); + if(rc) return rc; + *len -= 1; /* byte */ + property->value.i8 = byte; + break; + + case MQTT_PROP_SERVER_KEEP_ALIVE: + case MQTT_PROP_RECEIVE_MAXIMUM: + case MQTT_PROP_TOPIC_ALIAS_MAXIMUM: + case MQTT_PROP_TOPIC_ALIAS: + rc = packet__read_uint16(packet, &uint16); + if(rc) return rc; + *len -= 2; /* uint16 */ + property->value.i16 = uint16; + break; + + case MQTT_PROP_MESSAGE_EXPIRY_INTERVAL: + case MQTT_PROP_SESSION_EXPIRY_INTERVAL: + case MQTT_PROP_WILL_DELAY_INTERVAL: + case MQTT_PROP_MAXIMUM_PACKET_SIZE: + rc = packet__read_uint32(packet, &uint32); + if(rc) return rc; + *len -= 4; /* uint32 */ + property->value.i32 = uint32; + break; + + case MQTT_PROP_SUBSCRIPTION_IDENTIFIER: + rc = packet__read_varint(packet, &varint, &byte_count); + if(rc) return rc; + *len -= byte_count; + property->value.varint = varint; + break; + + case MQTT_PROP_CONTENT_TYPE: + case MQTT_PROP_RESPONSE_TOPIC: + case MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER: + case MQTT_PROP_AUTHENTICATION_METHOD: + case MQTT_PROP_RESPONSE_INFORMATION: + case MQTT_PROP_SERVER_REFERENCE: + case MQTT_PROP_REASON_STRING: + rc = packet__read_string(packet, &str1, &slen1); + if(rc) return rc; + *len = (*len) - 2 - slen1; /* uint16, string len */ + property->value.s.v = str1; + property->value.s.len = slen1; + break; + + case MQTT_PROP_AUTHENTICATION_DATA: + case MQTT_PROP_CORRELATION_DATA: + rc = packet__read_binary(packet, (uint8_t **)&str1, &slen1); + if(rc) return rc; + *len = (*len) - 2 - slen1; /* uint16, binary len */ + property->value.bin.v = str1; + property->value.bin.len = slen1; + break; + + case MQTT_PROP_USER_PROPERTY: + rc = packet__read_string(packet, &str1, &slen1); + if(rc) return rc; + *len = (*len) - 2 - slen1; /* uint16, string len */ + + rc = packet__read_string(packet, &str2, &slen2); + if(rc){ + mosquitto__free(str1); + return rc; + } + *len = (*len) - 2 - slen2; /* uint16, string len */ + + property->name.v = str1; + property->name.len = slen1; + property->value.s.v = str2; + property->value.s.len = slen2; + break; + + default: + log__printf(NULL, MOSQ_LOG_DEBUG, "Unsupported property type: %d", byte); + return MOSQ_ERR_MALFORMED_PACKET; + } + + return MOSQ_ERR_SUCCESS; +} + + +int property__read_all(int command, struct mosquitto__packet *packet, mosquitto_property **properties) +{ + int rc; + int32_t proplen; + mosquitto_property *p, *tail = NULL; + + rc = packet__read_varint(packet, &proplen, NULL); + if(rc) return rc; + + *properties = NULL; + + /* The order of properties must be preserved for some types, so keep the + * same order for all */ + while(proplen > 0){ + p = mosquitto__calloc(1, sizeof(mosquitto_property)); + + rc = property__read(packet, &proplen, p); + if(rc){ + mosquitto__free(p); + mosquitto_property_free_all(properties); + return rc; + } + + if(!(*properties)){ + *properties = p; + }else{ + tail->next = p; + } + tail = p; + + } + + rc = mosquitto_property_check_all(command, *properties); + if(rc){ + mosquitto_property_free_all(properties); + return rc; + } + return MOSQ_ERR_SUCCESS; +} + + +void property__free(mosquitto_property **property) +{ + if(!property || !(*property)) return; + + switch((*property)->identifier){ + case MQTT_PROP_CONTENT_TYPE: + case MQTT_PROP_RESPONSE_TOPIC: + case MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER: + case MQTT_PROP_AUTHENTICATION_METHOD: + case MQTT_PROP_RESPONSE_INFORMATION: + case MQTT_PROP_SERVER_REFERENCE: + case MQTT_PROP_REASON_STRING: + mosquitto__free((*property)->value.s.v); + break; + + case MQTT_PROP_AUTHENTICATION_DATA: + case MQTT_PROP_CORRELATION_DATA: + mosquitto__free((*property)->value.bin.v); + break; + + case MQTT_PROP_USER_PROPERTY: + mosquitto__free((*property)->name.v); + mosquitto__free((*property)->value.s.v); + break; + + case MQTT_PROP_PAYLOAD_FORMAT_INDICATOR: + case MQTT_PROP_MESSAGE_EXPIRY_INTERVAL: + case MQTT_PROP_SUBSCRIPTION_IDENTIFIER: + case MQTT_PROP_SESSION_EXPIRY_INTERVAL: + case MQTT_PROP_SERVER_KEEP_ALIVE: + case MQTT_PROP_REQUEST_PROBLEM_INFORMATION: + case MQTT_PROP_WILL_DELAY_INTERVAL: + case MQTT_PROP_REQUEST_RESPONSE_INFORMATION: + case MQTT_PROP_RECEIVE_MAXIMUM: + case MQTT_PROP_TOPIC_ALIAS_MAXIMUM: + case MQTT_PROP_TOPIC_ALIAS: + case MQTT_PROP_MAXIMUM_QOS: + case MQTT_PROP_RETAIN_AVAILABLE: + case MQTT_PROP_MAXIMUM_PACKET_SIZE: + case MQTT_PROP_WILDCARD_SUB_AVAILABLE: + case MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE: + case MQTT_PROP_SHARED_SUB_AVAILABLE: + /* Nothing to free */ + break; + } + + free(*property); + *property = NULL; +} + + +void mosquitto_property_free_all(mosquitto_property **property) +{ + mosquitto_property *p, *next; + + if(!property) return; + + p = *property; + while(p){ + next = p->next; + property__free(&p); + p = next; + } + *property = NULL; +} + + +int property__get_length(const mosquitto_property *property) +{ + if(!property) return 0; + + switch(property->identifier){ + /* Byte */ + case MQTT_PROP_PAYLOAD_FORMAT_INDICATOR: + case MQTT_PROP_REQUEST_PROBLEM_INFORMATION: + case MQTT_PROP_REQUEST_RESPONSE_INFORMATION: + case MQTT_PROP_MAXIMUM_QOS: + case MQTT_PROP_RETAIN_AVAILABLE: + case MQTT_PROP_WILDCARD_SUB_AVAILABLE: + case MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE: + case MQTT_PROP_SHARED_SUB_AVAILABLE: + return 2; /* 1 (identifier) + 1 byte */ + + /* uint16 */ + case MQTT_PROP_SERVER_KEEP_ALIVE: + case MQTT_PROP_RECEIVE_MAXIMUM: + case MQTT_PROP_TOPIC_ALIAS_MAXIMUM: + case MQTT_PROP_TOPIC_ALIAS: + return 3; /* 1 (identifier) + 2 bytes */ + + /* uint32 */ + case MQTT_PROP_MESSAGE_EXPIRY_INTERVAL: + case MQTT_PROP_WILL_DELAY_INTERVAL: + case MQTT_PROP_MAXIMUM_PACKET_SIZE: + case MQTT_PROP_SESSION_EXPIRY_INTERVAL: + return 5; /* 1 (identifier) + 4 bytes */ + + /* varint */ + case MQTT_PROP_SUBSCRIPTION_IDENTIFIER: + if(property->value.varint < 128){ + return 2; + }else if(property->value.varint < 16384){ + return 3; + }else if(property->value.varint < 2097152){ + return 4; + }else if(property->value.varint < 268435456){ + return 5; + }else{ + return 0; + } + + /* binary */ + case MQTT_PROP_CORRELATION_DATA: + case MQTT_PROP_AUTHENTICATION_DATA: + return 3 + property->value.bin.len; /* 1 + 2 bytes (len) + X bytes (payload) */ + + /* string */ + case MQTT_PROP_CONTENT_TYPE: + case MQTT_PROP_RESPONSE_TOPIC: + case MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER: + case MQTT_PROP_AUTHENTICATION_METHOD: + case MQTT_PROP_RESPONSE_INFORMATION: + case MQTT_PROP_SERVER_REFERENCE: + case MQTT_PROP_REASON_STRING: + return 3 + property->value.s.len; /* 1 + 2 bytes (len) + X bytes (string) */ + + /* string pair */ + case MQTT_PROP_USER_PROPERTY: + return 5 + property->value.s.len + property->name.len; /* 1 + 2*(2 bytes (len) + X bytes (string))*/ + + default: + return 0; + } + return 0; +} + + +int property__get_length_all(const mosquitto_property *property) +{ + const mosquitto_property *p; + int len = 0; + + p = property; + while(p){ + len += property__get_length(p); + p = p->next; + } + return len; +} + + +int property__write(struct mosquitto__packet *packet, const mosquitto_property *property) +{ + int rc; + + rc = packet__write_varint(packet, property->identifier); + if(rc) return rc; + + switch(property->identifier){ + case MQTT_PROP_PAYLOAD_FORMAT_INDICATOR: + case MQTT_PROP_REQUEST_PROBLEM_INFORMATION: + case MQTT_PROP_REQUEST_RESPONSE_INFORMATION: + case MQTT_PROP_MAXIMUM_QOS: + case MQTT_PROP_RETAIN_AVAILABLE: + case MQTT_PROP_WILDCARD_SUB_AVAILABLE: + case MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE: + case MQTT_PROP_SHARED_SUB_AVAILABLE: + packet__write_byte(packet, property->value.i8); + break; + + case MQTT_PROP_SERVER_KEEP_ALIVE: + case MQTT_PROP_RECEIVE_MAXIMUM: + case MQTT_PROP_TOPIC_ALIAS_MAXIMUM: + case MQTT_PROP_TOPIC_ALIAS: + packet__write_uint16(packet, property->value.i16); + break; + + case MQTT_PROP_MESSAGE_EXPIRY_INTERVAL: + case MQTT_PROP_SESSION_EXPIRY_INTERVAL: + case MQTT_PROP_WILL_DELAY_INTERVAL: + case MQTT_PROP_MAXIMUM_PACKET_SIZE: + packet__write_uint32(packet, property->value.i32); + break; + + case MQTT_PROP_SUBSCRIPTION_IDENTIFIER: + return packet__write_varint(packet, property->value.varint); + + case MQTT_PROP_CONTENT_TYPE: + case MQTT_PROP_RESPONSE_TOPIC: + case MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER: + case MQTT_PROP_AUTHENTICATION_METHOD: + case MQTT_PROP_RESPONSE_INFORMATION: + case MQTT_PROP_SERVER_REFERENCE: + case MQTT_PROP_REASON_STRING: + packet__write_string(packet, property->value.s.v, property->value.s.len); + break; + + case MQTT_PROP_AUTHENTICATION_DATA: + case MQTT_PROP_CORRELATION_DATA: + packet__write_uint16(packet, property->value.bin.len); + packet__write_bytes(packet, property->value.bin.v, property->value.bin.len); + break; + + case MQTT_PROP_USER_PROPERTY: + packet__write_string(packet, property->name.v, property->name.len); + packet__write_string(packet, property->value.s.v, property->value.s.len); + break; + + default: + log__printf(NULL, MOSQ_LOG_DEBUG, "Unsupported property type: %d", property->identifier); + return MOSQ_ERR_INVAL; + } + + return MOSQ_ERR_SUCCESS; +} + + +int property__write_all(struct mosquitto__packet *packet, const mosquitto_property *properties, bool write_len) +{ + int rc; + const mosquitto_property *p; + + if(write_len){ + rc = packet__write_varint(packet, property__get_length_all(properties)); + if(rc) return rc; + } + + p = properties; + while(p){ + rc = property__write(packet, p); + if(rc) return rc; + p = p->next; + } + + return MOSQ_ERR_SUCCESS; +} + + +int mosquitto_property_check_command(int command, int identifier) +{ + switch(identifier){ + case MQTT_PROP_PAYLOAD_FORMAT_INDICATOR: + case MQTT_PROP_MESSAGE_EXPIRY_INTERVAL: + case MQTT_PROP_CONTENT_TYPE: + case MQTT_PROP_RESPONSE_TOPIC: + case MQTT_PROP_CORRELATION_DATA: + if(command != CMD_PUBLISH && command != CMD_WILL){ + return MOSQ_ERR_PROTOCOL; + } + break; + + case MQTT_PROP_SUBSCRIPTION_IDENTIFIER: + if(command != CMD_PUBLISH && command != CMD_SUBSCRIBE){ + return MOSQ_ERR_PROTOCOL; + } + break; + + case MQTT_PROP_SESSION_EXPIRY_INTERVAL: + if(command != CMD_CONNECT && command != CMD_CONNACK && command != CMD_DISCONNECT){ + return MOSQ_ERR_PROTOCOL; + } + break; + + case MQTT_PROP_AUTHENTICATION_METHOD: + case MQTT_PROP_AUTHENTICATION_DATA: + if(command != CMD_CONNECT && command != CMD_CONNACK && command != CMD_AUTH){ + return MOSQ_ERR_PROTOCOL; + } + break; + + case MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER: + case MQTT_PROP_SERVER_KEEP_ALIVE: + case MQTT_PROP_RESPONSE_INFORMATION: + case MQTT_PROP_MAXIMUM_QOS: + case MQTT_PROP_RETAIN_AVAILABLE: + case MQTT_PROP_WILDCARD_SUB_AVAILABLE: + case MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE: + case MQTT_PROP_SHARED_SUB_AVAILABLE: + if(command != CMD_CONNACK){ + return MOSQ_ERR_PROTOCOL; + } + break; + + case MQTT_PROP_WILL_DELAY_INTERVAL: + if(command != CMD_WILL){ + return MOSQ_ERR_PROTOCOL; + } + break; + + case MQTT_PROP_REQUEST_PROBLEM_INFORMATION: + case MQTT_PROP_REQUEST_RESPONSE_INFORMATION: + if(command != CMD_CONNECT){ + return MOSQ_ERR_PROTOCOL; + } + break; + + case MQTT_PROP_SERVER_REFERENCE: + if(command != CMD_CONNACK && command != CMD_DISCONNECT){ + return MOSQ_ERR_PROTOCOL; + } + break; + + case MQTT_PROP_REASON_STRING: + if(command == CMD_CONNECT || command == CMD_PUBLISH || command == CMD_SUBSCRIBE || command == CMD_UNSUBSCRIBE){ + return MOSQ_ERR_PROTOCOL; + } + break; + + case MQTT_PROP_RECEIVE_MAXIMUM: + case MQTT_PROP_TOPIC_ALIAS_MAXIMUM: + case MQTT_PROP_MAXIMUM_PACKET_SIZE: + if(command != CMD_CONNECT && command != CMD_CONNACK){ + return MOSQ_ERR_PROTOCOL; + } + break; + + case MQTT_PROP_TOPIC_ALIAS: + if(command != CMD_PUBLISH){ + return MOSQ_ERR_PROTOCOL; + } + break; + + case MQTT_PROP_USER_PROPERTY: + break; + + default: + return MOSQ_ERR_PROTOCOL; + } + return MOSQ_ERR_SUCCESS; +} + + +int mosquitto_string_to_property_info(const char *propname, int *identifier, int *type) +{ + if(!propname) return MOSQ_ERR_INVAL; + + if(!strcasecmp(propname, "payload-format-indicator")){ + *identifier = MQTT_PROP_PAYLOAD_FORMAT_INDICATOR; + *type = MQTT_PROP_TYPE_BYTE; + }else if(!strcasecmp(propname, "message-expiry-interval")){ + *identifier = MQTT_PROP_MESSAGE_EXPIRY_INTERVAL; + *type = MQTT_PROP_TYPE_INT32; + }else if(!strcasecmp(propname, "content-type")){ + *identifier = MQTT_PROP_CONTENT_TYPE; + *type = MQTT_PROP_TYPE_STRING; + }else if(!strcasecmp(propname, "response-topic")){ + *identifier = MQTT_PROP_RESPONSE_TOPIC; + *type = MQTT_PROP_TYPE_STRING; + }else if(!strcasecmp(propname, "correlation-data")){ + *identifier = MQTT_PROP_CORRELATION_DATA; + *type = MQTT_PROP_TYPE_BINARY; + }else if(!strcasecmp(propname, "subscription-identifier")){ + *identifier = MQTT_PROP_SUBSCRIPTION_IDENTIFIER; + *type = MQTT_PROP_TYPE_VARINT; + }else if(!strcasecmp(propname, "session-expiry-interval")){ + *identifier = MQTT_PROP_SESSION_EXPIRY_INTERVAL; + *type = MQTT_PROP_TYPE_INT32; + }else if(!strcasecmp(propname, "assigned-client-identifier")){ + *identifier = MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER; + *type = MQTT_PROP_TYPE_STRING; + }else if(!strcasecmp(propname, "server-keep-alive")){ + *identifier = MQTT_PROP_SERVER_KEEP_ALIVE; + *type = MQTT_PROP_TYPE_INT16; + }else if(!strcasecmp(propname, "authentication-method")){ + *identifier = MQTT_PROP_AUTHENTICATION_METHOD; + *type = MQTT_PROP_TYPE_STRING; + }else if(!strcasecmp(propname, "authentication-data")){ + *identifier = MQTT_PROP_AUTHENTICATION_DATA; + *type = MQTT_PROP_TYPE_BINARY; + }else if(!strcasecmp(propname, "request-problem-information")){ + *identifier = MQTT_PROP_REQUEST_PROBLEM_INFORMATION; + *type = MQTT_PROP_TYPE_BYTE; + }else if(!strcasecmp(propname, "will-delay-interval")){ + *identifier = MQTT_PROP_WILL_DELAY_INTERVAL; + *type = MQTT_PROP_TYPE_INT32; + }else if(!strcasecmp(propname, "request-response-information")){ + *identifier = MQTT_PROP_REQUEST_RESPONSE_INFORMATION; + *type = MQTT_PROP_TYPE_BYTE; + }else if(!strcasecmp(propname, "response-information")){ + *identifier = MQTT_PROP_RESPONSE_INFORMATION; + *type = MQTT_PROP_TYPE_STRING; + }else if(!strcasecmp(propname, "server-reference")){ + *identifier = MQTT_PROP_SERVER_REFERENCE; + *type = MQTT_PROP_TYPE_STRING; + }else if(!strcasecmp(propname, "reason-string")){ + *identifier = MQTT_PROP_REASON_STRING; + *type = MQTT_PROP_TYPE_STRING; + }else if(!strcasecmp(propname, "receive-maximum")){ + *identifier = MQTT_PROP_RECEIVE_MAXIMUM; + *type = MQTT_PROP_TYPE_INT16; + }else if(!strcasecmp(propname, "topic-alias-maximum")){ + *identifier = MQTT_PROP_TOPIC_ALIAS_MAXIMUM; + *type = MQTT_PROP_TYPE_INT16; + }else if(!strcasecmp(propname, "topic-alias")){ + *identifier = MQTT_PROP_TOPIC_ALIAS; + *type = MQTT_PROP_TYPE_INT16; + }else if(!strcasecmp(propname, "maximum-qos")){ + *identifier = MQTT_PROP_MAXIMUM_QOS; + *type = MQTT_PROP_TYPE_BYTE; + }else if(!strcasecmp(propname, "retain-available")){ + *identifier = MQTT_PROP_RETAIN_AVAILABLE; + *type = MQTT_PROP_TYPE_BYTE; + }else if(!strcasecmp(propname, "user-property")){ + *identifier = MQTT_PROP_USER_PROPERTY; + *type = MQTT_PROP_TYPE_STRING_PAIR; + }else if(!strcasecmp(propname, "maximum-packet-size")){ + *identifier = MQTT_PROP_MAXIMUM_PACKET_SIZE; + *type = MQTT_PROP_TYPE_INT32; + }else if(!strcasecmp(propname, "wildcard-subscription-available")){ + *identifier = MQTT_PROP_WILDCARD_SUB_AVAILABLE; + *type = MQTT_PROP_TYPE_BYTE; + }else if(!strcasecmp(propname, "subscription-identifier-available")){ + *identifier = MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE; + *type = MQTT_PROP_TYPE_BYTE; + }else if(!strcasecmp(propname, "shared-subscription-available")){ + *identifier = MQTT_PROP_SHARED_SUB_AVAILABLE; + *type = MQTT_PROP_TYPE_BYTE; + }else{ + return MOSQ_ERR_INVAL; + } + return MOSQ_ERR_SUCCESS; +} + + +static void property__add(mosquitto_property **proplist, struct mqtt5__property *prop) +{ + mosquitto_property *p; + + if(!(*proplist)){ + *proplist = prop; + } + + p = *proplist; + while(p->next){ + p = p->next; + } + p->next = prop; + prop->next = NULL; +} + + +int mosquitto_property_add_byte(mosquitto_property **proplist, int identifier, uint8_t value) +{ + mosquitto_property *prop; + + if(!proplist) return MOSQ_ERR_INVAL; + if(identifier != MQTT_PROP_PAYLOAD_FORMAT_INDICATOR + && identifier != MQTT_PROP_REQUEST_PROBLEM_INFORMATION + && identifier != MQTT_PROP_REQUEST_RESPONSE_INFORMATION + && identifier != MQTT_PROP_MAXIMUM_QOS + && identifier != MQTT_PROP_RETAIN_AVAILABLE + && identifier != MQTT_PROP_WILDCARD_SUB_AVAILABLE + && identifier != MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE + && identifier != MQTT_PROP_SHARED_SUB_AVAILABLE){ + return MOSQ_ERR_INVAL; + } + + prop = mosquitto__calloc(1, sizeof(mosquitto_property)); + if(!prop) return MOSQ_ERR_NOMEM; + + prop->client_generated = true; + prop->identifier = identifier; + prop->value.i8 = value; + + property__add(proplist, prop); + return MOSQ_ERR_SUCCESS; +} + + +int mosquitto_property_add_int16(mosquitto_property **proplist, int identifier, uint16_t value) +{ + mosquitto_property *prop; + + if(!proplist) return MOSQ_ERR_INVAL; + if(identifier != MQTT_PROP_SERVER_KEEP_ALIVE + && identifier != MQTT_PROP_RECEIVE_MAXIMUM + && identifier != MQTT_PROP_TOPIC_ALIAS_MAXIMUM + && identifier != MQTT_PROP_TOPIC_ALIAS){ + return MOSQ_ERR_INVAL; + } + + prop = mosquitto__calloc(1, sizeof(mosquitto_property)); + if(!prop) return MOSQ_ERR_NOMEM; + + prop->client_generated = true; + prop->identifier = identifier; + prop->value.i16 = value; + + property__add(proplist, prop); + return MOSQ_ERR_SUCCESS; +} + + +int mosquitto_property_add_int32(mosquitto_property **proplist, int identifier, uint32_t value) +{ + mosquitto_property *prop; + + if(!proplist) return MOSQ_ERR_INVAL; + if(identifier != MQTT_PROP_MESSAGE_EXPIRY_INTERVAL + && identifier != MQTT_PROP_SESSION_EXPIRY_INTERVAL + && identifier != MQTT_PROP_WILL_DELAY_INTERVAL + && identifier != MQTT_PROP_MAXIMUM_PACKET_SIZE){ + + return MOSQ_ERR_INVAL; + } + + prop = mosquitto__calloc(1, sizeof(mosquitto_property)); + if(!prop) return MOSQ_ERR_NOMEM; + + prop->client_generated = true; + prop->identifier = identifier; + prop->value.i32 = value; + + property__add(proplist, prop); + return MOSQ_ERR_SUCCESS; +} + + +int mosquitto_property_add_varint(mosquitto_property **proplist, int identifier, uint32_t value) +{ + mosquitto_property *prop; + + if(!proplist || value > 268435455) return MOSQ_ERR_INVAL; + if(identifier != MQTT_PROP_SUBSCRIPTION_IDENTIFIER) return MOSQ_ERR_INVAL; + + prop = mosquitto__calloc(1, sizeof(mosquitto_property)); + if(!prop) return MOSQ_ERR_NOMEM; + + prop->client_generated = true; + prop->identifier = identifier; + prop->value.varint = value; + + property__add(proplist, prop); + return MOSQ_ERR_SUCCESS; +} + + +int mosquitto_property_add_binary(mosquitto_property **proplist, int identifier, const void *value, uint16_t len) +{ + mosquitto_property *prop; + + if(!proplist) return MOSQ_ERR_INVAL; + if(identifier != MQTT_PROP_CORRELATION_DATA + && identifier != MQTT_PROP_AUTHENTICATION_DATA){ + + return MOSQ_ERR_INVAL; + } + + prop = mosquitto__calloc(1, sizeof(mosquitto_property)); + if(!prop) return MOSQ_ERR_NOMEM; + + prop->client_generated = true; + prop->identifier = identifier; + + if(len){ + prop->value.bin.v = mosquitto__malloc(len); + if(!prop->value.bin.v){ + mosquitto__free(prop); + return MOSQ_ERR_NOMEM; + } + + memcpy(prop->value.bin.v, value, len); + prop->value.bin.len = len; + } + + property__add(proplist, prop); + return MOSQ_ERR_SUCCESS; +} + + +int mosquitto_property_add_string(mosquitto_property **proplist, int identifier, const char *value) +{ + mosquitto_property *prop; + + if(!proplist) return MOSQ_ERR_INVAL; + if(value){ + if(mosquitto_validate_utf8(value, strlen(value))) return MOSQ_ERR_MALFORMED_UTF8; + } + + if(identifier != MQTT_PROP_CONTENT_TYPE + && identifier != MQTT_PROP_RESPONSE_TOPIC + && identifier != MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER + && identifier != MQTT_PROP_AUTHENTICATION_METHOD + && identifier != MQTT_PROP_RESPONSE_INFORMATION + && identifier != MQTT_PROP_SERVER_REFERENCE + && identifier != MQTT_PROP_REASON_STRING){ + + return MOSQ_ERR_INVAL; + } + + prop = mosquitto__calloc(1, sizeof(mosquitto_property)); + if(!prop) return MOSQ_ERR_NOMEM; + + prop->client_generated = true; + prop->identifier = identifier; + if(value && strlen(value)){ + prop->value.s.v = mosquitto__strdup(value); + if(!prop->value.s.v){ + mosquitto__free(prop); + return MOSQ_ERR_NOMEM; + } + prop->value.s.len = strlen(value); + } + + property__add(proplist, prop); + return MOSQ_ERR_SUCCESS; +} + + +int mosquitto_property_add_string_pair(mosquitto_property **proplist, int identifier, const char *name, const char *value) +{ + mosquitto_property *prop; + + if(!proplist) return MOSQ_ERR_INVAL; + if(identifier != MQTT_PROP_USER_PROPERTY) return MOSQ_ERR_INVAL; + if(name){ + if(mosquitto_validate_utf8(name, strlen(name))) return MOSQ_ERR_MALFORMED_UTF8; + } + if(value){ + if(mosquitto_validate_utf8(value, strlen(value))) return MOSQ_ERR_MALFORMED_UTF8; + } + + prop = mosquitto__calloc(1, sizeof(mosquitto_property)); + if(!prop) return MOSQ_ERR_NOMEM; + + prop->client_generated = true; + prop->identifier = identifier; + + if(name && strlen(name)){ + prop->name.v = mosquitto__strdup(name); + if(!prop->name.v){ + mosquitto__free(prop); + return MOSQ_ERR_NOMEM; + } + prop->name.len = strlen(name); + } + + if(value && strlen(value)){ + prop->value.s.v = mosquitto__strdup(value); + if(!prop->value.s.v){ + mosquitto__free(prop->name.v); + mosquitto__free(prop); + return MOSQ_ERR_NOMEM; + } + prop->value.s.len = strlen(value); + } + + property__add(proplist, prop); + return MOSQ_ERR_SUCCESS; +} + +int mosquitto_property_check_all(int command, const mosquitto_property *properties) +{ + const mosquitto_property *p, *tail; + int rc; + + p = properties; + + while(p){ + /* Validity checks */ + if(p->identifier == MQTT_PROP_REQUEST_PROBLEM_INFORMATION + || p->identifier == MQTT_PROP_REQUEST_RESPONSE_INFORMATION + || p->identifier == MQTT_PROP_MAXIMUM_QOS + || p->identifier == MQTT_PROP_RETAIN_AVAILABLE + || p->identifier == MQTT_PROP_WILDCARD_SUB_AVAILABLE + || p->identifier == MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE + || p->identifier == MQTT_PROP_SHARED_SUB_AVAILABLE){ + + if(p->value.i8 > 1){ + return MOSQ_ERR_PROTOCOL; + } + }else if(p->identifier == MQTT_PROP_MAXIMUM_PACKET_SIZE){ + if( p->value.i32 == 0){ + return MOSQ_ERR_PROTOCOL; + } + }else if(p->identifier == MQTT_PROP_RECEIVE_MAXIMUM + || p->identifier == MQTT_PROP_TOPIC_ALIAS){ + + if(p->value.i16 == 0){ + return MOSQ_ERR_PROTOCOL; + } + } + + /* Check for properties on incorrect commands */ + rc = mosquitto_property_check_command(command, p->identifier); + if(rc) return rc; + + /* Check for duplicates */ + tail = p->next; + while(tail){ + if(p->identifier == tail->identifier + && p->identifier != MQTT_PROP_USER_PROPERTY){ + + return MOSQ_ERR_DUPLICATE_PROPERTY; + } + tail = tail->next; + } + + p = p->next; + } + + return MOSQ_ERR_SUCCESS; +} + +const mosquitto_property *property__get_property(const mosquitto_property *proplist, int identifier, bool skip_first) +{ + const mosquitto_property *p; + bool is_first = true; + + p = proplist; + + while(p){ + if(p->identifier == identifier){ + if(!is_first || !skip_first){ + return p; + } + is_first = false; + } + p = p->next; + } + return NULL; +} + + +const mosquitto_property *mosquitto_property_read_byte(const mosquitto_property *proplist, int identifier, uint8_t *value, bool skip_first) +{ + const mosquitto_property *p; + if(!proplist) return NULL; + + p = property__get_property(proplist, identifier, skip_first); + if(!p) return NULL; + if(p->identifier != MQTT_PROP_PAYLOAD_FORMAT_INDICATOR + && p->identifier != MQTT_PROP_REQUEST_PROBLEM_INFORMATION + && p->identifier != MQTT_PROP_REQUEST_RESPONSE_INFORMATION + && p->identifier != MQTT_PROP_MAXIMUM_QOS + && p->identifier != MQTT_PROP_RETAIN_AVAILABLE + && p->identifier != MQTT_PROP_WILDCARD_SUB_AVAILABLE + && p->identifier != MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE + && p->identifier != MQTT_PROP_SHARED_SUB_AVAILABLE){ + return NULL; + } + + if(value) *value = p->value.i8; + + return p; +} + + +const mosquitto_property *mosquitto_property_read_int16(const mosquitto_property *proplist, int identifier, uint16_t *value, bool skip_first) +{ + const mosquitto_property *p; + if(!proplist) return NULL; + + p = property__get_property(proplist, identifier, skip_first); + if(!p) return NULL; + if(p->identifier != MQTT_PROP_SERVER_KEEP_ALIVE + && p->identifier != MQTT_PROP_RECEIVE_MAXIMUM + && p->identifier != MQTT_PROP_TOPIC_ALIAS_MAXIMUM + && p->identifier != MQTT_PROP_TOPIC_ALIAS){ + return NULL; + } + + if(value) *value = p->value.i16; + + return p; +} + + +const mosquitto_property *mosquitto_property_read_int32(const mosquitto_property *proplist, int identifier, uint32_t *value, bool skip_first) +{ + const mosquitto_property *p; + if(!proplist) return NULL; + + p = property__get_property(proplist, identifier, skip_first); + if(!p) return NULL; + if(p->identifier != MQTT_PROP_MESSAGE_EXPIRY_INTERVAL + && p->identifier != MQTT_PROP_SESSION_EXPIRY_INTERVAL + && p->identifier != MQTT_PROP_WILL_DELAY_INTERVAL + && p->identifier != MQTT_PROP_MAXIMUM_PACKET_SIZE){ + + return NULL; + } + + if(value) *value = p->value.i32; + + return p; +} + + +const mosquitto_property *mosquitto_property_read_varint(const mosquitto_property *proplist, int identifier, uint32_t *value, bool skip_first) +{ + const mosquitto_property *p; + if(!proplist) return NULL; + + p = property__get_property(proplist, identifier, skip_first); + if(!p) return NULL; + if(p->identifier != MQTT_PROP_SUBSCRIPTION_IDENTIFIER){ + return NULL; + } + + if(value) *value = p->value.varint; + + return p; +} + + +const mosquitto_property *mosquitto_property_read_binary(const mosquitto_property *proplist, int identifier, void **value, int *len, bool skip_first) +{ + const mosquitto_property *p; + if(!proplist || (value && !len) || (!value && len)) return NULL; + + p = property__get_property(proplist, identifier, skip_first); + if(!p) return NULL; + if(p->identifier != MQTT_PROP_CORRELATION_DATA + && p->identifier != MQTT_PROP_AUTHENTICATION_DATA){ + + return NULL; + } + + if(value){ + *len = p->value.bin.len; + *value = malloc(*len); + if(!value) return NULL; + + memcpy(*value, p->value.bin.v, *len); + } + + return p; +} + + +const mosquitto_property *mosquitto_property_read_string(const mosquitto_property *proplist, int identifier, char **value, bool skip_first) +{ + const mosquitto_property *p; + if(!proplist) return NULL; + + p = property__get_property(proplist, identifier, skip_first); + if(!p) return NULL; + if(p->identifier != MQTT_PROP_CONTENT_TYPE + && p->identifier != MQTT_PROP_RESPONSE_TOPIC + && p->identifier != MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER + && p->identifier != MQTT_PROP_AUTHENTICATION_METHOD + && p->identifier != MQTT_PROP_RESPONSE_INFORMATION + && p->identifier != MQTT_PROP_SERVER_REFERENCE + && p->identifier != MQTT_PROP_REASON_STRING){ + + return NULL; + } + + if(value){ + *value = calloc(1, p->value.s.len+1); + if(!value) return NULL; + + memcpy(*value, p->value.s.v, p->value.s.len); + } + + + return p; +} + + +const mosquitto_property *mosquitto_property_read_string_pair(const mosquitto_property *proplist, int identifier, char **name, char **value, bool skip_first) +{ + const mosquitto_property *p; + if(!proplist) return NULL; + + p = property__get_property(proplist, identifier, skip_first); + if(!p) return NULL; + if(p->identifier != MQTT_PROP_USER_PROPERTY) return NULL; + + if(name){ + *name = calloc(1, p->name.len+1); + if(!name) return NULL; + memcpy(*name, p->name.v, p->name.len); + } + + if(value){ + *value = calloc(1, p->value.s.len+1); + if(!value){ + if(name) free(*name); + return NULL; + } + memcpy(*value, p->value.s.v, p->value.s.len); + } + + return p; +} + + +int mosquitto_property_copy_all(mosquitto_property **dest, const mosquitto_property *src) +{ + mosquitto_property *pnew, *plast = NULL; + + if(!src) return MOSQ_ERR_SUCCESS; + if(!dest) return MOSQ_ERR_INVAL; + + *dest = NULL; + + while(src){ + pnew = calloc(1, sizeof(mosquitto_property)); + if(!pnew){ + mosquitto_property_free_all(dest); + return MOSQ_ERR_NOMEM; + } + if(plast){ + plast->next = pnew; + }else{ + *dest = pnew; + } + plast = pnew; + + pnew->identifier = src->identifier; + switch(pnew->identifier){ + case MQTT_PROP_PAYLOAD_FORMAT_INDICATOR: + case MQTT_PROP_REQUEST_PROBLEM_INFORMATION: + case MQTT_PROP_REQUEST_RESPONSE_INFORMATION: + case MQTT_PROP_MAXIMUM_QOS: + case MQTT_PROP_RETAIN_AVAILABLE: + case MQTT_PROP_WILDCARD_SUB_AVAILABLE: + case MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE: + case MQTT_PROP_SHARED_SUB_AVAILABLE: + pnew->value.i8 = src->value.i8; + break; + + case MQTT_PROP_SERVER_KEEP_ALIVE: + case MQTT_PROP_RECEIVE_MAXIMUM: + case MQTT_PROP_TOPIC_ALIAS_MAXIMUM: + case MQTT_PROP_TOPIC_ALIAS: + pnew->value.i16 = src->value.i16; + break; + + case MQTT_PROP_MESSAGE_EXPIRY_INTERVAL: + case MQTT_PROP_SESSION_EXPIRY_INTERVAL: + case MQTT_PROP_WILL_DELAY_INTERVAL: + case MQTT_PROP_MAXIMUM_PACKET_SIZE: + pnew->value.i32 = src->value.i32; + break; + + case MQTT_PROP_SUBSCRIPTION_IDENTIFIER: + pnew->value.varint = src->value.varint; + break; + + case MQTT_PROP_CONTENT_TYPE: + case MQTT_PROP_RESPONSE_TOPIC: + case MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER: + case MQTT_PROP_AUTHENTICATION_METHOD: + case MQTT_PROP_RESPONSE_INFORMATION: + case MQTT_PROP_SERVER_REFERENCE: + case MQTT_PROP_REASON_STRING: + pnew->value.s.len = src->value.s.len; + pnew->value.s.v = strdup(src->value.s.v); + if(!pnew->value.s.v){ + mosquitto_property_free_all(dest); + return MOSQ_ERR_NOMEM; + } + break; + + case MQTT_PROP_AUTHENTICATION_DATA: + case MQTT_PROP_CORRELATION_DATA: + pnew->value.bin.len = src->value.bin.len; + pnew->value.bin.v = malloc(pnew->value.bin.len); + if(!pnew->value.bin.v){ + mosquitto_property_free_all(dest); + return MOSQ_ERR_NOMEM; + } + memcpy(pnew->value.bin.v, src->value.bin.v, pnew->value.bin.len); + break; + + case MQTT_PROP_USER_PROPERTY: + pnew->value.s.len = src->value.s.len; + pnew->value.s.v = strdup(src->value.s.v); + if(!pnew->value.s.v){ + mosquitto_property_free_all(dest); + return MOSQ_ERR_NOMEM; + } + + pnew->name.len = src->name.len; + pnew->name.v = strdup(src->name.v); + if(!pnew->name.v){ + mosquitto_property_free_all(dest); + return MOSQ_ERR_NOMEM; + } + break; + + default: + mosquitto_property_free_all(dest); + return MOSQ_ERR_INVAL; + } + + src = src->next; + } + + return MOSQ_ERR_SUCCESS; +} diff --git a/lib/property_mosq.h b/lib/property_mosq.h new file mode 100644 index 00000000..d965d8a3 --- /dev/null +++ b/lib/property_mosq.h @@ -0,0 +1,50 @@ +/* +Copyright (c) 2018 Roger Light + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ +#ifndef PROPERTY_MOSQ_H +#define PROPERTY_MOSQ_H + +#include "mosquitto_internal.h" +#include "mosquitto.h" + +struct mqtt__string { + char *v; + int len; +}; + +struct mqtt5__property { + struct mqtt5__property *next; + union { + uint8_t i8; + uint16_t i16; + uint32_t i32; + uint32_t varint; + struct mqtt__string bin; + struct mqtt__string s; + } value; + struct mqtt__string name; + int32_t identifier; + bool client_generated; +}; + + +int property__read_all(int command, struct mosquitto__packet *packet, mosquitto_property **property); +int property__write_all(struct mosquitto__packet *packet, const mosquitto_property *property, bool write_len); +void property__free(mosquitto_property **property); + +int property__get_length(const mosquitto_property *property); +int property__get_length_all(const mosquitto_property *property); + +#endif diff --git a/lib/read_handle.c b/lib/read_handle.c index 274a4da5..28c0e66b 100644 --- a/lib/read_handle.c +++ b/lib/read_handle.c @@ -24,7 +24,7 @@ Contributors: #include "logging_mosq.h" #include "memory_mosq.h" #include "messages_mosq.h" -#include "mqtt3_protocol.h" +#include "mqtt_protocol.h" #include "net_mosq.h" #include "packet_mosq.h" #include "read_handle.h" @@ -37,26 +37,30 @@ int handle__packet(struct mosquitto *mosq) assert(mosq); switch((mosq->in_packet.command)&0xF0){ - case PINGREQ: + case CMD_PINGREQ: return handle__pingreq(mosq); - case PINGRESP: + case CMD_PINGRESP: return handle__pingresp(mosq); - case PUBACK: + case CMD_PUBACK: return handle__pubackcomp(mosq, "PUBACK"); - case PUBCOMP: + case CMD_PUBCOMP: return handle__pubackcomp(mosq, "PUBCOMP"); - case PUBLISH: + case CMD_PUBLISH: return handle__publish(mosq); - case PUBREC: - return handle__pubrec(mosq); - case PUBREL: + case CMD_PUBREC: + return handle__pubrec(NULL, mosq); + case CMD_PUBREL: return handle__pubrel(NULL, mosq); - case CONNACK: + case CMD_CONNACK: return handle__connack(mosq); - case SUBACK: + case CMD_SUBACK: return handle__suback(mosq); - case UNSUBACK: + case CMD_UNSUBACK: return handle__unsuback(mosq); + case CMD_DISCONNECT: + return handle__disconnect(mosq); + case CMD_AUTH: + return handle__auth(mosq); default: /* If we don't recognise the command, return an error straight away. */ log__printf(mosq, MOSQ_LOG_ERR, "Error: Unrecognised command %d\n", (mosq->in_packet.command)&0xF0); diff --git a/lib/read_handle.h b/lib/read_handle.h index 887746de..d1c46cfd 100644 --- a/lib/read_handle.h +++ b/lib/read_handle.h @@ -26,10 +26,12 @@ int handle__pubackcomp(struct mosquitto_db *db, struct mosquitto *mosq, const ch #else int handle__packet(struct mosquitto *mosq); int handle__connack(struct mosquitto *mosq); +int handle__disconnect(struct mosquitto *mosq); int handle__pubackcomp(struct mosquitto *mosq, const char *type); int handle__publish(struct mosquitto *mosq); +int handle__auth(struct mosquitto *mosq); #endif -int handle__pubrec(struct mosquitto *mosq); +int handle__pubrec(struct mosquitto_db *db, struct mosquitto *mosq); int handle__pubrel(struct mosquitto_db *db, struct mosquitto *mosq); int handle__suback(struct mosquitto *mosq); int handle__unsuback(struct mosquitto *mosq); diff --git a/lib/send_connect.c b/lib/send_connect.c index 458f522f..d0bd3cd3 100644 --- a/lib/send_connect.c +++ b/lib/send_connect.c @@ -27,10 +27,11 @@ Contributors: #include "memory_mosq.h" #include "mosquitto.h" #include "mosquitto_internal.h" -#include "mqtt3_protocol.h" +#include "mqtt_protocol.h" #include "packet_mosq.h" +#include "property_mosq.h" -int send__connect(struct mosquitto *mosq, uint16_t keepalive, bool clean_session) +int send__connect(struct mosquitto *mosq, uint16_t keepalive, bool clean_session, const mosquitto_property *properties) { struct mosquitto__packet *packet = NULL; int payloadlen; @@ -40,9 +41,13 @@ int send__connect(struct mosquitto *mosq, uint16_t keepalive, bool clean_session uint8_t version; char *clientid, *username, *password; int headerlen; + int proplen = 0, will_proplen, varbytes; + mosquitto_property *local_props = NULL; + uint16_t receive_maximum; assert(mosq); - assert(mosq->id); + + if(mosq->protocol == mosq_p_mqtt31 && !mosq->id) return MOSQ_ERR_PROTOCOL; #if defined(WITH_BROKER) && defined(WITH_BRIDGE) if(mosq->bridge){ @@ -60,12 +65,29 @@ int send__connect(struct mosquitto *mosq, uint16_t keepalive, bool clean_session password = mosq->password; #endif - if(mosq->protocol == mosq_p_mqtt31){ - version = MQTT_PROTOCOL_V31; - headerlen = 12; + if(mosq->protocol == mosq_p_mqtt5){ + /* Generate properties from options */ + if(!mosquitto_property_read_int16(properties, MQTT_PROP_RECEIVE_MAXIMUM, &receive_maximum, false)){ + rc = mosquitto_property_add_int16(&local_props, MQTT_PROP_RECEIVE_MAXIMUM, mosq->receive_maximum); + if(rc) return rc; + }else{ + mosq->receive_maximum = receive_maximum; + mosq->receive_quota = receive_maximum; + } + + version = MQTT_PROTOCOL_V5; + headerlen = 10; + proplen = 0; + proplen += property__get_length_all(properties); + proplen += property__get_length_all(local_props); + varbytes = packet__varint_bytes(proplen); + headerlen += proplen + varbytes; }else if(mosq->protocol == mosq_p_mqtt311){ version = MQTT_PROTOCOL_V311; headerlen = 10; + }else if(mosq->protocol == mosq_p_mqtt31){ + version = MQTT_PROTOCOL_V31; + headerlen = 12; }else{ return MOSQ_ERR_INVAL; } @@ -73,12 +95,21 @@ int send__connect(struct mosquitto *mosq, uint16_t keepalive, bool clean_session packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet)); if(!packet) return MOSQ_ERR_NOMEM; - payloadlen = 2+strlen(clientid); + if(clientid){ + payloadlen = 2+strlen(clientid); + }else{ + payloadlen = 2; + } if(mosq->will){ will = 1; - assert(mosq->will->topic); + assert(mosq->will->msg.topic); - payloadlen += 2+strlen(mosq->will->topic) + 2+mosq->will->payloadlen; + payloadlen += 2+strlen(mosq->will->msg.topic) + 2+mosq->will->msg.payloadlen; + if(mosq->protocol == mosq_p_mqtt5){ + will_proplen = property__get_length_all(mosq->will->properties); + varbytes = packet__varint_bytes(will_proplen); + payloadlen += will_proplen + varbytes; + } } if(username){ payloadlen += 2+strlen(username); @@ -87,8 +118,8 @@ int send__connect(struct mosquitto *mosq, uint16_t keepalive, bool clean_session } } - packet->command = CONNECT; - packet->remaining_length = headerlen+payloadlen; + packet->command = CMD_CONNECT; + packet->remaining_length = headerlen + payloadlen; rc = packet__alloc(packet); if(rc){ mosquitto__free(packet); @@ -98,8 +129,8 @@ int send__connect(struct mosquitto *mosq, uint16_t keepalive, bool clean_session /* Variable header */ if(version == MQTT_PROTOCOL_V31){ packet__write_string(packet, PROTOCOL_NAME_v31, strlen(PROTOCOL_NAME_v31)); - }else if(version == MQTT_PROTOCOL_V311){ - packet__write_string(packet, PROTOCOL_NAME_v311, strlen(PROTOCOL_NAME_v311)); + }else{ + packet__write_string(packet, PROTOCOL_NAME, strlen(PROTOCOL_NAME)); } #if defined(WITH_BROKER) && defined(WITH_BRIDGE) if(mosq->bridge && mosq->bridge->try_private && mosq->bridge->try_private_accepted){ @@ -110,7 +141,7 @@ int send__connect(struct mosquitto *mosq, uint16_t keepalive, bool clean_session packet__write_byte(packet, version); byte = (clean_session&0x1)<<1; if(will){ - byte = byte | ((mosq->will->retain&0x1)<<5) | ((mosq->will->qos&0x3)<<3) | ((will&0x1)<<2); + byte = byte | ((mosq->will->msg.retain&0x1)<<5) | ((mosq->will->msg.qos&0x3)<<3) | ((will&0x1)<<2); } if(username){ byte = byte | 0x1<<7; @@ -121,11 +152,26 @@ int send__connect(struct mosquitto *mosq, uint16_t keepalive, bool clean_session packet__write_byte(packet, byte); packet__write_uint16(packet, keepalive); + if(mosq->protocol == mosq_p_mqtt5){ + /* Write properties */ + packet__write_varint(packet, proplen); + property__write_all(packet, properties, false); + property__write_all(packet, local_props, false); + } + /* Payload */ - packet__write_string(packet, clientid, strlen(clientid)); + if(clientid){ + packet__write_string(packet, clientid, strlen(clientid)); + }else{ + packet__write_uint16(packet, 0); + } if(will){ - packet__write_string(packet, mosq->will->topic, strlen(mosq->will->topic)); - packet__write_string(packet, (const char *)mosq->will->payload, mosq->will->payloadlen); + if(mosq->protocol == mosq_p_mqtt5){ + /* Write will properties */ + property__write_all(packet, mosq->will->properties, true); + } + packet__write_string(packet, mosq->will->msg.topic, strlen(mosq->will->msg.topic)); + packet__write_string(packet, (const char *)mosq->will->msg.payload, mosq->will->msg.payloadlen); } if(username){ packet__write_string(packet, username, strlen(username)); diff --git a/lib/send_disconnect.c b/lib/send_disconnect.c index 0cfea276..3615a001 100644 --- a/lib/send_disconnect.c +++ b/lib/send_disconnect.c @@ -25,12 +25,19 @@ Contributors: #include "mosquitto.h" #include "mosquitto_internal.h" #include "logging_mosq.h" -#include "mqtt3_protocol.h" +#include "memory_mosq.h" +#include "mqtt_protocol.h" +#include "packet_mosq.h" +#include "property_mosq.h" #include "send_mosq.h" -int send__disconnect(struct mosquitto *mosq) +int send__disconnect(struct mosquitto *mosq, uint8_t reason_code, const mosquitto_property *properties) { + struct mosquitto__packet *packet = NULL; + int rc; + int proplen, varbytes; + assert(mosq); #ifdef WITH_BROKER # ifdef WITH_BRIDGE @@ -39,6 +46,34 @@ int send__disconnect(struct mosquitto *mosq) #else log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending DISCONNECT", mosq->id); #endif - return send__simple_command(mosq, DISCONNECT); + assert(mosq); + packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet)); + if(!packet) return MOSQ_ERR_NOMEM; + + packet->command = CMD_DISCONNECT; + if(mosq->protocol == mosq_p_mqtt5 && (reason_code != 0 || properties)){ + packet->remaining_length = 1; + if(properties){ + proplen = property__get_length_all(properties); + varbytes = packet__varint_bytes(proplen); + packet->remaining_length += proplen + varbytes; + } + }else{ + packet->remaining_length = 0; + } + + rc = packet__alloc(packet); + if(rc){ + mosquitto__free(packet); + return rc; + } + if(mosq->protocol == mosq_p_mqtt5 && (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/lib/send_mosq.c b/lib/send_mosq.c index ee544e3e..6f39b36d 100644 --- a/lib/send_mosq.c +++ b/lib/send_mosq.c @@ -30,10 +30,11 @@ Contributors: #include "mosquitto.h" #include "mosquitto_internal.h" #include "logging_mosq.h" -#include "mqtt3_protocol.h" +#include "mqtt_protocol.h" #include "memory_mosq.h" #include "net_mosq.h" #include "packet_mosq.h" +#include "property_mosq.h" #include "send_mosq.h" #include "time_mosq.h" #include "util_mosq.h" @@ -47,7 +48,7 @@ int send__pingreq(struct mosquitto *mosq) #else log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending PINGREQ", mosq->id); #endif - rc = send__simple_command(mosq, PINGREQ); + rc = send__simple_command(mosq, CMD_PINGREQ); if(rc == MOSQ_ERR_SUCCESS){ mosq->ping_t = mosquitto_time(); } @@ -61,7 +62,7 @@ int send__pingresp(struct mosquitto *mosq) #else if(mosq) log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending PINGRESP", mosq->id); #endif - return send__simple_command(mosq, PINGRESP); + return send__simple_command(mosq, CMD_PINGRESP); } int send__puback(struct mosquitto *mosq, uint16_t mid) @@ -71,7 +72,9 @@ int send__puback(struct mosquitto *mosq, uint16_t mid) #else if(mosq) log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending PUBACK (Mid: %d)", mosq->id, mid); #endif - return send__command_with_mid(mosq, PUBACK, mid, false); + util__increment_receive_quota(mosq); + /* We don't use Reason String or User Property yet. */ + return send__command_with_mid(mosq, CMD_PUBACK, mid, false, 0, NULL); } int send__pubcomp(struct mosquitto *mosq, uint16_t mid) @@ -81,7 +84,9 @@ int send__pubcomp(struct mosquitto *mosq, uint16_t mid) #else if(mosq) log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending PUBCOMP (Mid: %d)", mosq->id, mid); #endif - return send__command_with_mid(mosq, PUBCOMP, mid, false); + util__increment_receive_quota(mosq); + /* We don't use Reason String or User Property yet. */ + return send__command_with_mid(mosq, CMD_PUBCOMP, mid, false, 0, NULL); } @@ -92,7 +97,13 @@ int send__pubrec(struct mosquitto *mosq, uint16_t mid) #else if(mosq) log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending PUBREC (Mid: %d)", mosq->id, mid); #endif - return send__command_with_mid(mosq, PUBREC, mid, false); + /* FIXME - if rc >= 0x80 quota needs incrementing + if(rc >= 0x80){ + util__increment_receive_quota(mosq); + } + */ + /* We don't use Reason String or User Property yet. */ + return send__command_with_mid(mosq, CMD_PUBREC, mid, false, 0, NULL); } int send__pubrel(struct mosquitto *mosq, uint16_t mid) @@ -102,14 +113,16 @@ int send__pubrel(struct mosquitto *mosq, uint16_t mid) #else if(mosq) log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending PUBREL (Mid: %d)", mosq->id, mid); #endif - return send__command_with_mid(mosq, PUBREL|2, mid, false); + /* We don't use Reason String or User Property yet. */ + return send__command_with_mid(mosq, CMD_PUBREL|2, mid, false, 0, NULL); } /* For PUBACK, PUBCOMP, PUBREC, and PUBREL */ -int send__command_with_mid(struct mosquitto *mosq, uint8_t command, uint16_t mid, bool dup) +int send__command_with_mid(struct mosquitto *mosq, uint8_t command, uint16_t mid, bool dup, uint8_t reason_code, const mosquitto_property *properties) { struct mosquitto__packet *packet = NULL; int rc; + int proplen, varbytes; assert(mosq); packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet)); @@ -120,14 +133,35 @@ int send__command_with_mid(struct mosquitto *mosq, uint8_t command, uint16_t mid packet->command |= 8; } packet->remaining_length = 2; + + if(mosq->protocol == mosq_p_mqtt5){ + 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); if(rc){ mosquitto__free(packet); return rc; } - packet->payload[packet->pos+0] = MOSQ_MSB(mid); - packet->payload[packet->pos+1] = MOSQ_LSB(mid); + packet__write_uint16(packet, mid); + + if(mosq->protocol == mosq_p_mqtt5){ + 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/lib/send_mosq.h b/lib/send_mosq.h index 35645952..e4935735 100644 --- a/lib/send_mosq.h +++ b/lib/send_mosq.h @@ -17,21 +17,22 @@ Contributors: #define SEND_MOSQ_H #include "mosquitto.h" +#include "property_mosq.h" int send__simple_command(struct mosquitto *mosq, uint8_t command); -int send__command_with_mid(struct mosquitto *mosq, uint8_t command, uint16_t mid, bool dup); -int send__real_publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint32_t payloadlen, const void *payload, int qos, bool retain, bool dup); +int send__command_with_mid(struct mosquitto *mosq, uint8_t command, uint16_t mid, bool dup, uint8_t reason_code, const mosquitto_property *properties); +int send__real_publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint32_t payloadlen, const void *payload, int qos, bool retain, bool dup, const mosquitto_property *cmsg_props, const mosquitto_property *store_props, uint32_t expiry_interval); -int send__connect(struct mosquitto *mosq, uint16_t keepalive, bool clean_session); -int send__disconnect(struct mosquitto *mosq); +int send__connect(struct mosquitto *mosq, uint16_t keepalive, bool clean_session, const mosquitto_property *properties); +int send__disconnect(struct mosquitto *mosq, uint8_t reason_code, const mosquitto_property *properties); int send__pingreq(struct mosquitto *mosq); int send__pingresp(struct mosquitto *mosq); int send__puback(struct mosquitto *mosq, uint16_t mid); int send__pubcomp(struct mosquitto *mosq, uint16_t mid); -int send__publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint32_t payloadlen, const void *payload, int qos, bool retain, bool dup); +int send__publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint32_t payloadlen, const void *payload, int qos, bool retain, bool dup, const mosquitto_property *cmsg_props, const mosquitto_property *store_props, uint32_t expiry_interval); int send__pubrec(struct mosquitto *mosq, uint16_t mid); int send__pubrel(struct mosquitto *mosq, uint16_t mid); -int send__subscribe(struct mosquitto *mosq, int *mid, int topic_count, char *const *const topic, int topic_qos); -int send__unsubscribe(struct mosquitto *mosq, int *mid, const char *topic); +int send__subscribe(struct mosquitto *mosq, int *mid, int topic_count, char *const *const topic, int topic_qos, const mosquitto_property *properties); +int send__unsubscribe(struct mosquitto *mosq, int *mid, int topic_count, char *const *const topic, const mosquitto_property *properties); #endif diff --git a/lib/send_publish.c b/lib/send_publish.c index e9aeeef8..e72572c6 100644 --- a/lib/send_publish.c +++ b/lib/send_publish.c @@ -29,14 +29,15 @@ Contributors: #include "mosquitto.h" #include "mosquitto_internal.h" #include "logging_mosq.h" -#include "mqtt3_protocol.h" +#include "mqtt_protocol.h" #include "memory_mosq.h" #include "net_mosq.h" #include "packet_mosq.h" +#include "property_mosq.h" #include "send_mosq.h" -int send__publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint32_t payloadlen, const void *payload, int qos, bool retain, bool dup) +int send__publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint32_t payloadlen, const void *payload, int qos, bool retain, bool dup, const mosquitto_property *cmsg_props, const mosquitto_property *store_props, uint32_t expiry_interval) { #ifdef WITH_BROKER size_t len; @@ -50,7 +51,6 @@ int send__publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint3 #endif #endif assert(mosq); - assert(topic); #if defined(WITH_BROKER) && defined(WITH_WEBSOCKETS) if(mosq->sock == INVALID_SOCKET && !mosq->wsi) return MOSQ_ERR_NO_CONN; @@ -110,7 +110,7 @@ int send__publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint3 } log__printf(NULL, MOSQ_LOG_DEBUG, "Sending PUBLISH to %s (d%d, q%d, r%d, m%d, '%s', ... (%ld bytes))", mosq->id, dup, qos, retain, mid, mapped_topic, (long)payloadlen); G_PUB_BYTES_SENT_INC(payloadlen); - rc = send__real_publish(mosq, mid, mapped_topic, payloadlen, payload, qos, retain, dup); + rc = send__real_publish(mosq, mid, mapped_topic, payloadlen, payload, qos, retain, dup, cmsg_props, store_props, expiry_interval); mosquitto__free(mapped_topic); return rc; } @@ -124,26 +124,63 @@ int send__publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint3 log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending PUBLISH (d%d, q%d, r%d, m%d, '%s', ... (%ld bytes))", mosq->id, dup, qos, retain, mid, topic, (long)payloadlen); #endif - return send__real_publish(mosq, mid, topic, payloadlen, payload, qos, retain, dup); + return send__real_publish(mosq, mid, topic, payloadlen, payload, qos, retain, dup, cmsg_props, store_props, expiry_interval); } -int send__real_publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint32_t payloadlen, const void *payload, int qos, bool retain, bool dup) +int send__real_publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint32_t payloadlen, const void *payload, int qos, bool retain, bool dup, const mosquitto_property *cmsg_props, const mosquitto_property *store_props, uint32_t expiry_interval) { struct mosquitto__packet *packet = NULL; int packetlen; + int proplen = 0, varbytes; int rc; + mosquitto_property expiry_prop; assert(mosq); - assert(topic); - packetlen = 2+strlen(topic) + payloadlen; + if(topic){ + packetlen = 2+strlen(topic) + payloadlen; + }else{ + packetlen = 2 + payloadlen; + } if(qos > 0) packetlen += 2; /* For message id */ + if(mosq->protocol == mosq_p_mqtt5){ + proplen = 0; + proplen += property__get_length_all(cmsg_props); + proplen += property__get_length_all(store_props); + if(expiry_interval > 0){ + expiry_prop.next = NULL; + expiry_prop.value.i32 = expiry_interval; + expiry_prop.identifier = MQTT_PROP_MESSAGE_EXPIRY_INTERVAL; + expiry_prop.client_generated = false; + + proplen += property__get_length_all(&expiry_prop); + } + + varbytes = packet__varint_bytes(proplen); + if(varbytes > 4){ + /* FIXME - Properties too big, don't publish any - should remove some first really */ + cmsg_props = NULL; + store_props = NULL; + expiry_interval = 0; + }else{ + packetlen += proplen + varbytes; + } + } + if(packet__check_oversize(mosq, packetlen)){ +#ifdef WITH_BROKER + log__printf(NULL, MOSQ_LOG_NOTICE, "Dropping too large outgoing PUBLISH for %s (%d bytes)", mosq->id, packetlen); +#else + log__printf(NULL, MOSQ_LOG_NOTICE, "Dropping too large outgoing PUBLISH (%d bytes)", packetlen); +#endif + return MOSQ_ERR_OVERSIZE_PACKET; + } + packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet)); if(!packet) return MOSQ_ERR_NOMEM; packet->mid = mid; - packet->command = PUBLISH | ((dup&0x1)<<3) | (qos<<1) | retain; + packet->command = CMD_PUBLISH | ((dup&0x1)<<3) | (qos<<1) | retain; packet->remaining_length = packetlen; rc = packet__alloc(packet); if(rc){ @@ -151,11 +188,24 @@ int send__real_publish(struct mosquitto *mosq, uint16_t mid, const char *topic, return rc; } /* Variable header (topic string) */ - packet__write_string(packet, topic, strlen(topic)); + if(topic){ + packet__write_string(packet, topic, strlen(topic)); + }else{ + packet__write_uint16(packet, 0); + } if(qos > 0){ packet__write_uint16(packet, mid); } + if(mosq->protocol == mosq_p_mqtt5){ + packet__write_varint(packet, proplen); + property__write_all(packet, cmsg_props, false); + property__write_all(packet, store_props, false); + if(expiry_interval > 0){ + property__write_all(packet, &expiry_prop, false); + } + } + /* Payload */ if(payloadlen){ packet__write_bytes(packet, payload, payloadlen); diff --git a/lib/send_subscribe.c b/lib/send_subscribe.c index d9c572d0..4095e4af 100644 --- a/lib/send_subscribe.c +++ b/lib/send_subscribe.c @@ -27,18 +27,20 @@ Contributors: #include "mosquitto_internal.h" #include "logging_mosq.h" #include "memory_mosq.h" -#include "mqtt3_protocol.h" +#include "mqtt_protocol.h" #include "packet_mosq.h" +#include "property_mosq.h" #include "util_mosq.h" -int send__subscribe(struct mosquitto *mosq, int *mid, int topic_count, const char **topic, int topic_qos) +int send__subscribe(struct mosquitto *mosq, int *mid, int topic_count, const char **topic, int topic_qos, const mosquitto_property *properties) { struct mosquitto__packet *packet = NULL; uint32_t packetlen; uint16_t local_mid; int rc; int i; + int proplen, varbytes; assert(mosq); assert(topic); @@ -47,11 +49,16 @@ int send__subscribe(struct mosquitto *mosq, int *mid, int topic_count, const cha if(!packet) return MOSQ_ERR_NOMEM; packetlen = 2; + if(mosq->protocol == mosq_p_mqtt5){ + proplen = property__get_length_all(properties); + varbytes = packet__varint_bytes(proplen); + packetlen += proplen + varbytes; + } for(i=0; icommand = SUBSCRIBE | (1<<1); + packet->command = CMD_SUBSCRIBE | (1<<1); packet->remaining_length = packetlen; rc = packet__alloc(packet); if(rc){ @@ -64,6 +71,10 @@ int send__subscribe(struct mosquitto *mosq, int *mid, int topic_count, const cha if(mid) *mid = (int)local_mid; packet__write_uint16(packet, local_mid); + if(mosq->protocol == mosq_p_mqtt5){ + property__write_all(packet, properties, true); + } + /* Payload */ for(i=0; iid, local_mid, topic[0], topic_qos); + log__printf(mosq, MOSQ_LOG_DEBUG, "Bridge %s sending SUBSCRIBE (Mid: %d, Topic: %s, QoS: %d, Options: 0x%02x)", mosq->id, local_mid, topic[0], topic_qos&0x03, topic_qos&0xFC); # endif #else for(i=0; iid, local_mid, topic[i], topic_qos); + log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending SUBSCRIBE (Mid: %d, Topic: %s, QoS: %d, Options: 0x%02x)", mosq->id, local_mid, topic[i], topic_qos&0x03, topic_qos&0xFC); } #endif diff --git a/lib/send_unsubscribe.c b/lib/send_unsubscribe.c index fb66e88c..ac68b2c3 100644 --- a/lib/send_unsubscribe.c +++ b/lib/send_unsubscribe.c @@ -26,18 +26,22 @@ Contributors: #include "mosquitto.h" #include "logging_mosq.h" #include "memory_mosq.h" -#include "mqtt3_protocol.h" +#include "mqtt_protocol.h" #include "packet_mosq.h" +#include "property_mosq.h" +#include "send_mosq.h" #include "util_mosq.h" -int send__unsubscribe(struct mosquitto *mosq, int *mid, const char *topic) +int send__unsubscribe(struct mosquitto *mosq, int *mid, int topic_count, char *const *const topic, const mosquitto_property *properties) { /* FIXME - only deals with a single topic */ struct mosquitto__packet *packet = NULL; uint32_t packetlen; uint16_t local_mid; int rc; + int proplen, varbytes; + int i; assert(mosq); assert(topic); @@ -45,9 +49,18 @@ int send__unsubscribe(struct mosquitto *mosq, int *mid, const char *topic) packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet)); if(!packet) return MOSQ_ERR_NOMEM; - packetlen = 2 + 2+strlen(topic); + packetlen = 2; - packet->command = UNSUBSCRIBE | (1<<1); + for(i=0; iprotocol == mosq_p_mqtt5){ + proplen = property__get_length_all(properties); + varbytes = packet__varint_bytes(proplen); + packetlen += proplen + varbytes; + } + + packet->command = CMD_UNSUBSCRIBE | (1<<1); packet->remaining_length = packetlen; rc = packet__alloc(packet); if(rc){ @@ -60,15 +73,26 @@ int send__unsubscribe(struct mosquitto *mosq, int *mid, const char *topic) if(mid) *mid = (int)local_mid; packet__write_uint16(packet, local_mid); + if(mosq->protocol == mosq_p_mqtt5){ + /* We don't use User Property yet. */ + property__write_all(packet, properties, true); + } + /* Payload */ - packet__write_string(packet, topic, strlen(topic)); + for(i=0; iid, local_mid, topic); + for(i=0; iid, local_mid, topic[i]); + } # endif #else - log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending UNSUBSCRIBE (Mid: %d, Topic: %s)", mosq->id, local_mid, topic); + for(i=0; iid, local_mid, topic[i]); + } #endif return packet__queue(mosq, packet); } diff --git a/lib/socks_mosq.c b/lib/socks_mosq.c index e7d597f7..60fcf657 100644 --- a/lib/socks_mosq.c +++ b/lib/socks_mosq.c @@ -424,7 +424,11 @@ int socks5__read(struct mosquitto *mosq) /* Auth passed */ packet__cleanup(&mosq->in_packet); mosq->state = mosq_cs_new; - return send__connect(mosq, mosq->keepalive, mosq->clean_session); + if(mosq->socks5_host){ + int rc = net__socket_connect_step3(mosq, mosq->host); + if(rc) return rc; + } + return send__connect(mosq, mosq->keepalive, mosq->clean_start, NULL); }else{ i = mosq->in_packet.payload[1]; packet__cleanup(&mosq->in_packet); diff --git a/lib/srv_mosq.c b/lib/srv_mosq.c index 0219dd8a..53e7881d 100644 --- a/lib/srv_mosq.c +++ b/lib/srv_mosq.c @@ -49,6 +49,11 @@ static void srv_callback(void *arg, int status, int timeouts, unsigned char *abu mosq->on_disconnect(mosq, mosq->userdata, MOSQ_ERR_LOOKUP); mosq->in_callback = false; } + if(mosq->on_disconnect_v5){ + mosq->in_callback = true; + mosq->on_disconnect_v5(mosq, mosq->userdata, MOSQ_ERR_LOOKUP, NULL); + mosq->in_callback = false; + } pthread_mutex_unlock(&mosq->callback_mutex); } } diff --git a/lib/utf8_mosq.c b/lib/utf8_mosq.c index 7ee60559..1edaceb2 100644 --- a/lib/utf8_mosq.c +++ b/lib/utf8_mosq.c @@ -80,13 +80,29 @@ int mosquitto_validate_utf8(const char *str, int len) } /* Check for overlong or out of range encodings */ - if(codelen == 2 && codepoint < 0x0080){ - return MOSQ_ERR_MALFORMED_UTF8; - }else if(codelen == 3 && codepoint < 0x0800){ + /* Checking codelen == 2 isn't necessary here, because it is already + * covered above in the C0 and C1 checks. + * if(codelen == 2 && codepoint < 0x0080){ + * return MOSQ_ERR_MALFORMED_UTF8; + * }else + */ + if(codelen == 3 && codepoint < 0x0800){ return MOSQ_ERR_MALFORMED_UTF8; }else if(codelen == 4 && (codepoint < 0x10000 || codepoint > 0x10FFFF)){ return MOSQ_ERR_MALFORMED_UTF8; } + + /* Check for non-characters */ + if(codepoint >= 0xFDD0 && codepoint <= 0xFDEF){ + return MOSQ_ERR_MALFORMED_UTF8; + } + if((codepoint & 0xFFFF) == 0xFFFE || (codepoint & 0xFFFF) == 0xFFFF){ + return MOSQ_ERR_MALFORMED_UTF8; + } + /* Check for control characters */ + if(codepoint <= 0x001F || (codepoint >= 0x007F && codepoint <= 0x009F)){ + return MOSQ_ERR_MALFORMED_UTF8; + } } return MOSQ_ERR_SUCCESS; } diff --git a/lib/util_mosq.c b/lib/util_mosq.c index ba0b563f..3ec471da 100644 --- a/lib/util_mosq.c +++ b/lib/util_mosq.c @@ -28,6 +28,9 @@ Contributors: # include #endif +#ifdef WITH_TLS +# include +#endif #ifdef WITH_BROKER #include "mosquitto_broker_internal.h" @@ -46,9 +49,9 @@ Contributors: #endif #ifdef WITH_BROKER -void mosquitto__check_keepalive(struct mosquitto_db *db, struct mosquitto *mosq) +int mosquitto__check_keepalive(struct mosquitto_db *db, struct mosquitto *mosq) #else -void mosquitto__check_keepalive(struct mosquitto *mosq) +int mosquitto__check_keepalive(struct mosquitto *mosq) #endif { time_t next_msg_out; @@ -67,7 +70,7 @@ void mosquitto__check_keepalive(struct mosquitto *mosq) log__printf(NULL, MOSQ_LOG_NOTICE, "Bridge connection %s has exceeded idle timeout, disconnecting.", mosq->id); net__socket_close(db, mosq); - return; + return MOSQ_ERR_SUCCESS; } #endif pthread_mutex_lock(&mosq->msgtime_mutex); @@ -86,11 +89,6 @@ void mosquitto__check_keepalive(struct mosquitto *mosq) pthread_mutex_unlock(&mosq->msgtime_mutex); }else{ #ifdef WITH_BROKER - if(mosq->listener){ - mosq->listener->client_count--; - assert(mosq->listener->client_count >= 0); - } - mosq->listener = NULL; net__socket_close(db, mosq); #else net__socket_close(mosq); @@ -107,10 +105,18 @@ void mosquitto__check_keepalive(struct mosquitto *mosq) mosq->on_disconnect(mosq, mosq->userdata, rc); mosq->in_callback = false; } + if(mosq->on_disconnect_v5){ + mosq->in_callback = true; + mosq->on_disconnect_v5(mosq, mosq->userdata, rc, NULL); + mosq->in_callback = false; + } pthread_mutex_unlock(&mosq->callback_mutex); + + return rc; #endif } } + return MOSQ_ERR_SUCCESS; } uint16_t mosquitto__mid_generate(struct mosquitto *mosq) @@ -134,217 +140,6 @@ uint16_t mosquitto__mid_generate(struct mosquitto *mosq) return mid; } -/* Check that a topic used for publishing is valid. - * Search for + or # in a topic. Return MOSQ_ERR_INVAL if found. - * Also returns MOSQ_ERR_INVAL if the topic string is too long. - * Returns MOSQ_ERR_SUCCESS if everything is fine. - */ -int mosquitto_pub_topic_check(const char *str) -{ - int len = 0; - while(str && str[0]){ - if(str[0] == '+' || str[0] == '#'){ - return MOSQ_ERR_INVAL; - } - len++; - str = &str[1]; - } - if(len > 65535) return MOSQ_ERR_INVAL; - - return MOSQ_ERR_SUCCESS; -} - -int mosquitto_pub_topic_check2(const char *str, size_t len) -{ - int i; - - if(len > 65535) return MOSQ_ERR_INVAL; - - for(i=0; i 65535) return MOSQ_ERR_INVAL; - - return MOSQ_ERR_SUCCESS; -} - -int mosquitto_sub_topic_check2(const char *str, size_t len) -{ - char c = '\0'; - int i; - - if(len > 65535) return MOSQ_ERR_INVAL; - - for(i=0; i 0 && sub[spos-1] != '/'){ - return MOSQ_ERR_INVAL; - } - spos++; - *result = true; - return MOSQ_ERR_SUCCESS; - } - }else{ - if(sub[spos] == '+'){ - /* Check for bad "+foo" or "a/+foo" subscription */ - if(spos > 0 && sub[spos-1] != '/'){ - return MOSQ_ERR_INVAL; - } - /* Check for bad "foo+" or "foo+/a" subscription */ - if(spos < sublen-1 && sub[spos+1] != '/'){ - return MOSQ_ERR_INVAL; - } - spos++; - while(tpos < topiclen && topic[tpos] != '/'){ - tpos++; - } - if(tpos == topiclen && spos == sublen){ - *result = true; - return MOSQ_ERR_SUCCESS; - } - }else if(sub[spos] == '#'){ - if(spos > 0 && sub[spos-1] != '/'){ - return MOSQ_ERR_INVAL; - } - multilevel_wildcard = true; - if(spos+1 != sublen){ - return MOSQ_ERR_INVAL; - }else{ - *result = true; - return MOSQ_ERR_SUCCESS; - } - }else{ - /* Check for e.g. foo/bar matching foo/+/# */ - if(spos > 0 - && spos+2 == sublen - && tpos == topiclen - && sub[spos-1] == '+' - && sub[spos] == '/' - && sub[spos+1] == '#') - { - *result = true; - multilevel_wildcard = true; - return MOSQ_ERR_SUCCESS; - } - return MOSQ_ERR_SUCCESS; - } - } - } - if(multilevel_wildcard == false && (tpos < topiclen || spos < sublen)){ - *result = false; - } - - return MOSQ_ERR_SUCCESS; -} #ifdef WITH_TLS int mosquitto__hex2bin_sha1(const char *hex, unsigned char **bin) @@ -412,6 +207,21 @@ FILE *mosquitto__fopen(const char *path, const char *mode, bool restrict_read) char username[UNLEN + 1]; int ulen = UNLEN; SECURITY_DESCRIPTOR sd; + DWORD dwCreationDisposition; + + switch(mode[0]){ + case 'a': + dwCreationDisposition = OPEN_ALWAYS; + break; + case 'r': + dwCreationDisposition = OPEN_EXISTING; + break; + case 'w': + dwCreationDisposition = CREATE_ALWAYS; + break; + default: + return NULL; + } GetUserName(username, &ulen); if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) { @@ -432,7 +242,7 @@ FILE *mosquitto__fopen(const char *path, const char *mode, bool restrict_read) hfile = CreateFile(buf, GENERIC_READ | GENERIC_WRITE, 0, &sec, - CREATE_NEW, + dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); @@ -469,3 +279,19 @@ FILE *mosquitto__fopen(const char *path, const char *mode, bool restrict_read) } #endif } + +void util__increment_receive_quota(struct mosquitto *mosq) +{ + if(mosq->protocol == mosq_p_mqtt5){ + if(mosq->receive_quota < mosq->receive_maximum){ + mosq->receive_quota++; + } + } +} + +void util__increment_send_quota(struct mosquitto *mosq) +{ + if(mosq->send_quota < mosq->send_maximum){ + mosq->send_quota++; + } +} diff --git a/lib/util_mosq.h b/lib/util_mosq.h index 98640464..c1e8ff7c 100644 --- a/lib/util_mosq.h +++ b/lib/util_mosq.h @@ -26,16 +26,18 @@ Contributors: #endif #ifdef WITH_BROKER -void mosquitto__check_keepalive(struct mosquitto_db *db, struct mosquitto *mosq); +int mosquitto__check_keepalive(struct mosquitto_db *db, struct mosquitto *mosq); #else -void mosquitto__check_keepalive(struct mosquitto *mosq); +int mosquitto__check_keepalive(struct mosquitto *mosq); #endif uint16_t mosquitto__mid_generate(struct mosquitto *mosq); FILE *mosquitto__fopen(const char *path, const char *mode, bool restrict_read); -#ifdef WITH_TLS +#ifdef FINAL_WITH_TLS_PSK int mosquitto__hex2bin_sha1(const char *hex, unsigned char **bin); int mosquitto__hex2bin(const char *hex, unsigned char *bin, int bin_max_len); #endif +void util__increment_receive_quota(struct mosquitto *mosq); +void util__increment_send_quota(struct mosquitto *mosq); #endif diff --git a/lib/util_topic.c b/lib/util_topic.c new file mode 100644 index 00000000..18fbceb1 --- /dev/null +++ b/lib/util_topic.c @@ -0,0 +1,268 @@ +/* +Copyright (c) 2009-2018 Roger Light + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#include +#include + +#ifdef WIN32 +# include +# include +# include +# include +#else +# include +#endif + + +#ifdef WITH_BROKER +#include "mosquitto_broker_internal.h" +#endif + +#include "mosquitto.h" +#include "memory_mosq.h" +#include "net_mosq.h" +#include "send_mosq.h" +#include "time_mosq.h" +#include "tls_mosq.h" +#include "util_mosq.h" + +/* Check that a topic used for publishing is valid. + * Search for + or # in a topic. Return MOSQ_ERR_INVAL if found. + * Also returns MOSQ_ERR_INVAL if the topic string is too long. + * Returns MOSQ_ERR_SUCCESS if everything is fine. + */ +int mosquitto_pub_topic_check(const char *str) +{ + int len = 0; + while(str && str[0]){ + if(str[0] == '+' || str[0] == '#'){ + return MOSQ_ERR_INVAL; + } + len++; + str = &str[1]; + } + if(len > 65535) return MOSQ_ERR_INVAL; + + return MOSQ_ERR_SUCCESS; +} + +int mosquitto_pub_topic_check2(const char *str, size_t len) +{ + int i; + + if(len > 65535) return MOSQ_ERR_INVAL; + + for(i=0; i 65535) return MOSQ_ERR_INVAL; + + return MOSQ_ERR_SUCCESS; +} + +int mosquitto_sub_topic_check2(const char *str, size_t len) +{ + char c = '\0'; + int i; + + if(len > 65535) return MOSQ_ERR_INVAL; + + for(i=0; i 0 && sub[spos-1] != '/'){ + return MOSQ_ERR_INVAL; + } + /* Check for bad "foo+" or "foo+/a" subscription */ + if(spos < sublen-1 && sub[spos+1] != '/'){ + return MOSQ_ERR_INVAL; + } + spos++; + while(tpos < topiclen && topic[tpos] != '/'){ + tpos++; + } + if(tpos == topiclen && spos == sublen){ + *result = true; + return MOSQ_ERR_SUCCESS; + } + }else if(sub[spos] == '#'){ + /* Check for bad "foo#" subscription */ + if(spos > 0 && sub[spos-1] != '/'){ + return MOSQ_ERR_INVAL; + } + /* Check for # not the final character of the sub, e.g. "#foo" */ + if(spos+1 != sublen){ + return MOSQ_ERR_INVAL; + }else{ + *result = true; + return MOSQ_ERR_SUCCESS; + } + }else{ + /* Check for e.g. foo/bar matching foo/+/# */ + if(spos > 0 + && spos+2 == sublen + && tpos == topiclen + && sub[spos-1] == '+' + && sub[spos] == '/' + && sub[spos+1] == '#') + { + *result = true; + multilevel_wildcard = true; + return MOSQ_ERR_SUCCESS; + } + + /* There is no match at this point, but is the sub invalid? */ + for(int i=spos; i 0 && sub[spos-1] != '/'){ + return MOSQ_ERR_INVAL; + } + spos++; + *result = true; + return MOSQ_ERR_SUCCESS; + } + } + } + if(multilevel_wildcard == false && (tpos < topiclen || spos < sublen)){ + *result = false; + } + + return MOSQ_ERR_SUCCESS; +} diff --git a/lib/will_mosq.c b/lib/will_mosq.c index 3e9495ff..154badbc 100644 --- a/lib/will_mosq.c +++ b/lib/will_mosq.c @@ -28,15 +28,17 @@ Contributors: #include "logging_mosq.h" #include "messages_mosq.h" #include "memory_mosq.h" -#include "mqtt3_protocol.h" +#include "mqtt_protocol.h" #include "net_mosq.h" #include "read_handle.h" #include "send_mosq.h" #include "util_mosq.h" +#include "will_mosq.h" -int will__set(struct mosquitto *mosq, const char *topic, int payloadlen, const void *payload, int qos, bool retain) +int will__set(struct mosquitto *mosq, const char *topic, int payloadlen, const void *payload, int qos, bool retain, mosquitto_property *properties) { int rc = MOSQ_ERR_SUCCESS; + mosquitto_property *p; if(!mosq || !topic) return MOSQ_ERR_INVAL; if(payloadlen < 0 || payloadlen > MQTT_MAX_PAYLOAD) return MOSQ_ERR_PAYLOAD_SIZE; @@ -45,42 +47,57 @@ int will__set(struct mosquitto *mosq, const char *topic, int payloadlen, const v if(mosquitto_pub_topic_check(topic)) return MOSQ_ERR_INVAL; if(mosquitto_validate_utf8(topic, strlen(topic))) return MOSQ_ERR_MALFORMED_UTF8; + if(properties){ + if(mosq->protocol != mosq_p_mqtt5){ + return MOSQ_ERR_NOT_SUPPORTED; + } + p = properties; + while(p){ + rc = mosquitto_property_check_command(CMD_WILL, p->identifier); + if(rc) return rc; + p = p->next; + } + } + if(mosq->will){ - mosquitto__free(mosq->will->topic); - mosquitto__free(mosq->will->payload); + mosquitto__free(mosq->will->msg.topic); + mosquitto__free(mosq->will->msg.payload); + mosquitto_property_free_all(&mosq->will->properties); mosquitto__free(mosq->will); } - mosq->will = mosquitto__calloc(1, sizeof(struct mosquitto_message)); + mosq->will = mosquitto__calloc(1, sizeof(struct mosquitto_message_all)); if(!mosq->will) return MOSQ_ERR_NOMEM; - mosq->will->topic = mosquitto__strdup(topic); - if(!mosq->will->topic){ + mosq->will->msg.topic = mosquitto__strdup(topic); + if(!mosq->will->msg.topic){ rc = MOSQ_ERR_NOMEM; goto cleanup; } - mosq->will->payloadlen = payloadlen; - if(mosq->will->payloadlen > 0){ + mosq->will->msg.payloadlen = payloadlen; + if(mosq->will->msg.payloadlen > 0){ if(!payload){ rc = MOSQ_ERR_INVAL; goto cleanup; } - mosq->will->payload = mosquitto__malloc(sizeof(char)*mosq->will->payloadlen); - if(!mosq->will->payload){ + mosq->will->msg.payload = mosquitto__malloc(sizeof(char)*mosq->will->msg.payloadlen); + if(!mosq->will->msg.payload){ rc = MOSQ_ERR_NOMEM; goto cleanup; } - memcpy(mosq->will->payload, payload, payloadlen); + memcpy(mosq->will->msg.payload, payload, payloadlen); } - mosq->will->qos = qos; - mosq->will->retain = retain; + mosq->will->msg.qos = qos; + mosq->will->msg.retain = retain; + + mosq->will->properties = properties; return MOSQ_ERR_SUCCESS; cleanup: if(mosq->will){ - mosquitto__free(mosq->will->topic); - mosquitto__free(mosq->will->payload); + mosquitto__free(mosq->will->msg.topic); + mosquitto__free(mosq->will->msg.payload); mosquitto__free(mosq->will); mosq->will = NULL; @@ -93,11 +110,13 @@ int will__clear(struct mosquitto *mosq) { if(!mosq->will) return MOSQ_ERR_SUCCESS; - mosquitto__free(mosq->will->topic); - mosq->will->topic = NULL; + mosquitto__free(mosq->will->msg.topic); + mosq->will->msg.topic = NULL; - mosquitto__free(mosq->will->payload); - mosq->will->payload = NULL; + mosquitto__free(mosq->will->msg.payload); + mosq->will->msg.payload = NULL; + + mosquitto_property_free_all(&mosq->will->properties); mosquitto__free(mosq->will); mosq->will = NULL; diff --git a/lib/will_mosq.h b/lib/will_mosq.h index 7afb4089..e46d1bd9 100644 --- a/lib/will_mosq.h +++ b/lib/will_mosq.h @@ -20,7 +20,7 @@ Contributors: #include "mosquitto.h" #include "mosquitto_internal.h" -int will__set(struct mosquitto *mosq, const char *topic, int payloadlen, const void *payload, int qos, bool retain); +int will__set(struct mosquitto *mosq, const char *topic, int payloadlen, const void *payload, int qos, bool retain, mosquitto_property *properties); int will__clear(struct mosquitto *mosq); #endif diff --git a/man/html.xsl b/man/html.xsl index 44f00187..3fba92a2 100644 --- a/man/html.xsl +++ b/man/html.xsl @@ -1,6 +1,6 @@ - + man.css diff --git a/man/manpage.xsl b/man/manpage.xsl index 0ebd8d4a..ec4e6b70 100644 --- a/man/manpage.xsl +++ b/man/manpage.xsl @@ -1,6 +1,6 @@ - + 0 diff --git a/man/mosquitto.conf.5.xml b/man/mosquitto.conf.5.xml index cdcea3ef..c14aa907 100644 --- a/man/mosquitto.conf.5.xml +++ b/man/mosquitto.conf.5.xml @@ -64,15 +64,17 @@ certificate in order to connect successfully. In this case, the second and third options, use_identity_as_username and use_subject_as_username, become relevant. If set to true, - use_identity_as_user causes the Common Name (CN) from the client - certificate to be used instead of the MQTT username for access - control purposes. The password is not replaced because it is - assumed that only authenticated clients have valid certificates. If - use_identity_as_username is false, the client must authenticate as - normal (if required by password_file) through the MQTT options. The - same principle applies for the use_subject_as_username option, but - the entire certificate subject is used as the username instead of - just the CN. + use_identity_as_username causes the Common Name (CN) from the + client certificate to be used instead of the MQTT username for + access control purposes. The password is not used because it is + assumed that only authenticated clients have valid certificates. + This means that any CA certificates you include in cafile or capath + will be able to issue client certificates that are valid for + connecting to your broker. If use_identity_as_username is false, + the client must authenticate as normal (if required by + password_file) through the MQTT options. The same principle applies + for the use_subject_as_username option, but the entire certificate + subject is used as the username instead of just the CN. When using pre-shared-key based encryption through the psk_hint and psk_file options, the client must provide a valid identity and key in order to connect to the broker before any MQTT communication @@ -262,7 +264,7 @@ true, this option allows you to set a string that will be prefixed to the automatically generated client ids to aid visibility in - logs. Defaults to no prefix. + logs. Defaults to . Reloaded on reload signal. @@ -294,6 +296,24 @@ Reloaded on reload signal. + + [ true | false ] + + This option affects the scenario when a client + subscribes to a topic that has retained messages. It is + possible that the client that published the retained + message to the topic had access at the time they + published, but that access has been subsequently + removed. If is set + to true, the default, the source of a retained message + will be checked for access rights before it is + republished. When set to false, no check will be made + and the retained message will always be + published. + This option applies globally, regardless of the + option. + + prefix @@ -435,6 +455,47 @@ Reloaded on reload signal. + + value + + For MQTT v5 clients, it is possible to have the + server send a "server keepalive" value that will + override the keepalive value set by the client. This + is intended to be used as a mechanism to say that the + server will disconnect the client earlier than it + anticipated, and that the client should use the new + keepalive value. The max_keepalive option allows you to + specify that clients may only connect with keepalive + less than or equal to this value, otherwise they will + be sent a server keepalive telling them to use + max_keepalive. This only applies to MQTT v5 clients. + The maximum value allowable, and default value, is + 65535. Do not set below 10 seconds. + Reloaded on reload signal. + + + + value + + For MQTT v5 clients, it is possible to have the + server send a "maximum packet size" value that will + instruct the client it will not accept MQTT packets + with size greater than bytes. + This applies to the full MQTT packet, not just the + payload. Setting this option to a positive value will + set the maximum packet size to that number of bytes. If + a client sends a packet which is larger than this + value, it will be disconnected. This applies to all + clients regardless of the protocol version they are + using, but v3.1.1 and earlier clients will of course + not have received the maximum packet size information. + Defaults to no limit. + Setting below 20 bytes is forbidden because it is + likely to interfere with normal client operation even + with small payloads. + Reloaded on reload signal. + + count @@ -515,6 +576,10 @@ , , . + Note that if set to true, then a durable client (i.e. + with clean session set to false) that has disconnected + will use the ACL settings defined for the listener that + it was most recently connected to. The default behaviour is for this to be set to false, which maintains the settings behaviour from previous versions of @@ -628,6 +693,16 @@ Reloaded on reload signal. + + [ true | false ] + + If set to false, then retained messages are not + supported. Clients that send a message with the retain + bit will be disconnected if this option is set to + false. Defaults to true. + Reloaded on reload signal. + + [ true | false ] @@ -773,6 +848,20 @@ Not reloaded on reload signal. + + count + + Limit the QoS value allowed when using this + listener. Defaults to 2, which means any QoS can be + used. Set to 0 or 1 to limit to those QoS values. + This makes use of an MQTT v5 feature to notify + clients of the limitation. MQTT v3.1.1 clients will + not be aware of the limitation. Clients publshing + to this listener with a too-high QoS will be + disconnected. + Not reloaded on reload signal. + + topic prefix @@ -813,6 +902,27 @@ Not reloaded on reload signal. + + [ ipv4 | ipv6 ] + + By default, a listener will attempt to listen on + all supported IP protocol versions. If you do not + have an IPv4 or IPv6 interface you may wish to + disable support for either of those protocol + versions. In particular, note that due to the + limitations of the websockets library, it will only + ever attempt to open IPv6 sockets if IPv6 support + is compiled in, and so will fail if IPv6 is not + available. + Set to to force the + listener to only use IPv4, or set to + to force the listener to only + use IPv6. If you want support for both IPv4 and + IPv6, then do not use the + option. + Not reloaded on reload signal. + + [ true | false ] @@ -1368,27 +1478,35 @@ "". Using the empty marker for the topic itself is also valid. The table below defines what combination of empty or value is - valid. + valid. The and + show the resulting + topics that would be used on the local and remote ends + of the bridge. For example, for the first table row if + you publish to on the local + broker, then the remote broker will receive a message + on the topic . + - + - - Topic + Pattern Local Prefix Remote Prefix Validity + Full Local Topic + Full Remote Topic - 1valuevaluevaluevalid - 2valuevalue""valid - 3value""valuevalid - 4value""""valid (no remapping) - 5""valuevaluevalid (remap single local topic to remote) - 6""value""invalid - 7""""valueinvalid - 8""""""invalid + patternL/R/validL/patternR/pattern + patternL/""validL/patternpattern + pattern""R/validpatternR/pattern + pattern""""valid (no remapping)patternpattern + ""localremotevalid (remap single local topic to remote)localremote + ""local""invalid + """"remoteinvalid + """"""invalid diff --git a/man/mosquitto_pub.1.xml b/man/mosquitto_pub.1.xml index b7e45868..363a9aec 100644 --- a/man/mosquitto_pub.1.xml +++ b/man/mosquitto_pub.1.xml @@ -11,7 +11,7 @@ mosquitto_pub - an MQTT version 3.1.1/3.1 client for publishing simple messages + an MQTT version 5/3.1.1/3.1 client for publishing simple messages @@ -20,7 +20,7 @@ hostname - port number + port-number username password @@ -29,13 +29,14 @@ URL - bind_address + bind-address - client_id - client id prefix - keepalive time - message QoS + command identifier value + client-id + client-id-prefix + keepalive-time + message-QoS @@ -80,7 +81,6 @@ socks-url protocol-version - mosquitto_pub @@ -92,7 +92,7 @@ Description - mosquitto_pub is a simple MQTT version 3.1.1 + mosquitto_pub is a simple MQTT version 5/3.1.1 client that will publish a single message on a topic and exit. @@ -194,6 +194,41 @@ Enable debug messages. + + + + + Use an MQTT v5 property with this publish. If you use + this option, the client will be set to be an MQTT v5 + client. This option has two forms: + + + is the MQTT command/packet + identifier and can be one of CONNECT, PUBLISH, PUBREL, + DISCONNECT, AUTH, or WILL. The properties available for + each command are listed in the + Properties + section. + + is the name of the + property to add. This is as described in the + specification, but with '-' as a word separator. For + example: + . More details + are in the Properties + section. + + is the value of the property + to add, with a data type that is property + specific. + + is only used for the + property as the first of + the two strings in the string pair. In that case, + is the second of the strings in + the pair. + + @@ -468,8 +503,11 @@ Specify which version of the MQTT protocol should be used when connecting to the rmeote broker. Can be - or . - Defaults to . + , , + , or the more verbose + , , or + . + Defaults to . @@ -519,6 +557,63 @@ arguments to modify the other will parameters. + + Properties + The / option + allows adding properties to different stages of the mosquitto_pub + run. The properties supported for each command are as + follows: + + + Connect + + (binary data - note treated as a string in mosquitto_pub) + (UTF-8 string pair) + (32-bit unsigned integer) + (16-bit unsigned integer) + (8-bit unsigned integer) + (8-bit unsigned integer) + (32-bit unsigned integer) + (16-bit unsigned integer) + (UTF-8 string pair) + + + + + Publish + + (UTF-8 string) + (binary data - note treated as a string in mosquitto_pub) + (32-bit unsigned integer) + (8-bit unsigned integer) + (UTF-8 string) + (16-bit unsigned integer) + (UTF-8 string pair) + + + + + Disconnect + + (32-bit unsigned integer) + (UTF-8 string pair) + + + + + Will properties + + (UTF-8 string) + (binary data - note treated as a string in mosquitto_pub) + (32-bit unsigned integer) + (8-bit unsigned integer) + (UTF-8 string) + (UTF-8 string pair) + (32-bit unsigned integer) + + + + Examples Publish temperature information to localhost with QoS 1: diff --git a/man/mosquitto_sub.1.xml b/man/mosquitto_sub.1.xml index d2dd77b9..88a24c68 100644 --- a/man/mosquitto_sub.1.xml +++ b/man/mosquitto_sub.1.xml @@ -11,7 +11,7 @@ mosquitto_sub - an MQTT version 3.1.1/3.1 client for subscribing to topics + an MQTT version 5/3.1.1/3.1 client for subscribing to topics @@ -20,7 +20,7 @@ hostname - port number + port-number username password @@ -32,26 +32,28 @@ message-topic - bind_address + bind-address - msg count + msg-count + command identifier value - client_id - client id prefix - keepalive time + client-id + client-id-prefix + keepalive-time - message QoS + message-QoS + filter-out unsub-topic protocol-version - message processing timeout + message-processing-timeout socks-url @@ -95,7 +97,7 @@ Description - mosquitto_sub is a simple MQTT version 3.1.1 + mosquitto_sub is a simple MQTT version 5/3.1.1 client that will subscribe to topics and print the messages that it receives. In addition to subscribing to topics, @@ -217,6 +219,40 @@ Enable debug messages. + + + + + Use an MQTT v5 property with this publish. If you use + this option, the client will be set to be an MQTT v5 + client. This option has two forms: + + + is the MQTT command/packet + identifier and can be one of CONNECT, PUBACK, PUBREC, + PUBCOMP, SUBSCRIBE, UNSUBSCRIBE, DISCONNECT, AUTH, or + WILL. The properties available for each command are + listed in the Properties section. + + is the name of the + property to add. This is as described in the + specification, but with '-' as a word separator. For + example: + . More details + are in the Properties + section. + + is the value of the property + to add, with a data type that is property + specific. + + is only used for the + property as the first of + the two strings in the string pair. In that case, + is the second of the strings in + the pair. + + @@ -443,7 +479,20 @@ Messages with retain set are "stale", in that it is not known when they were originally published. With this argument in use, the receipt of the first non-stale - message will cause the client to exit. + message will cause the client to exit. See also the + option. + + + + + + If this argument is given, the subscriptions will + have the "retain as published" option set. This means + that the retain flag on an incoming message will be + exactly as set by the publishing client, rather than + indicating whether the message is fresh/stale. + This option is not valid for MQTT v3.1/v3.1.1 + clients. @@ -577,8 +626,11 @@ Specify which version of the MQTT protocol should be used when connecting to the rmeote broker. Can be - or . - Defaults to . + , , + , or the more verbose + , , or + . + Defaults to . @@ -741,6 +793,64 @@ modify the other will parameters. + + Properties + The / option + allows adding properties to different stages of the mosquitto_sub + run. The properties supported for each command are as + follows: + + + Connect + + (binary data - note treated as a string in mosquitto_pub) + (UTF-8 string pair) + (32-bit unsigned integer) + (16-bit unsigned integer) + (8-bit unsigned integer) + (8-bit unsigned integer) + (32-bit unsigned integer) + (16-bit unsigned integer) + (UTF-8 string pair) + + + + + Subscribe + + (UTF-8 string pair) + + + + + Unsubscribe + + (UTF-8 string pair) + + + + + Disconnect + + (32-bit unsigned integer) + (UTF-8 string pair) + + + + + Will properties + + (UTF-8 string) + (binary data - note treated as a string in mosquitto_pub) + (32-bit unsigned integer) + (8-bit unsigned integer) + (UTF-8 string) + (UTF-8 string pair) + (32-bit unsigned integer) + + + + Examples Note that these really are examples - the subscriptions will work diff --git a/mosquitto.conf b/mosquitto.conf index 96450050..248417b7 100644 --- a/mosquitto.conf +++ b/mosquitto.conf @@ -89,7 +89,8 @@ # If allow_zero_length_clientid is true, this option allows you to set a prefix # to automatically generated client ids to aid visibility in logs. -#auto_id_prefix +# Defaults to 'auto-' +#auto_id_prefix auto- # This option allows persistent clients (those with clean session set to false) # to be removed if they do not reconnect within a certain time frame. @@ -144,10 +145,54 @@ # password_file acl_file psk_file auth_plugin auth_opt_* allow_anonymous # auto_id_prefix allow_zero_length_clientid # +# Note that if set to true, then a durable client (i.e. with clean session set +# to false) that has disconnected will use the ACL settings defined for the +# listener that it was most recently connected to. +# # The default behaviour is for this to be set to false, which maintains the # setting behaviour from previous versions of mosquitto. #per_listener_settings false +# This option affects the scenario when a client subscribes to a topic that has +# retained messages. It is possible that the client that published the retained +# message to the topic had access at the time they published, but that access +# has been subsequently removed. If check_retain_source is set to true, the +# default, the source of a retained message will be checked for access rights +# before it is republished. When set to false, no check will be made and the +# retained message will always be published. This affects all listeners. +#check_retain_source true + + +# Set to false to disable retained message support. If a client publishes a +# message with the retain bit set, it will be disconnected if this is set to +# false. +#retain_available true + +# For MQTT v5 clients, it is possible to have the server send a "server +# keepalive" value that will override the keepalive value set by the client. +# This is intended to be used as a mechanism to say that the server will +# disconnect the client earlier than it anticipated, and that the client should +# use the new keepalive value. The max_keepalive option allows you to specify +# that clients may only connect with keepalive less than or equal to this +# value, otherwise they will be sent a server keepalive telling them to use +# max_keepalive. This only applies to MQTT v5 clients. The maximum value +# allowable is 65535. Do not set below 10. +#max_keepalive 65535 + + +# For MQTT v5 clients, it is possible to have the server send a "maximum packet +# size" value that will instruct the client it will not accept MQTT packets +# with size greater than max_packet_size bytes. This applies to the full MQTT +# packet, not just the payload. Setting this option to a positive value will +# set the maximum packet size to that number of bytes. If a client sends a +# packet which is larger than this value, it will be disconnected. This applies +# to all clients regardless of the protocol version they are using, but v3.1.1 +# and earlier clients will of course not have received the maximum packet size +# information. Defaults to no limit. Setting below 20 bytes is forbidden +# because it is likely to interfere with ordinary client operation, even with +# very small payloads. +#max_packet_size 0 + # ================================================================= # Default listener diff --git a/readme.md b/readme.md index 32726894..ee038fbc 100644 --- a/readme.md +++ b/readme.md @@ -66,7 +66,6 @@ already be built. Use `make binary` to skip building the man pages, or install ### Build Dependencies * c-ares (libc-ares-dev on Debian based systems) - disable with `make WITH_SRV=no` -* libuuid (uuid-dev) - disable with `make WITH_UUID=no` * libwebsockets (libwebsockets-dev) - enable with `make WITH_WEBSOCKETS=yes` * openssl (libssl-dev on Debian based systems) - disable with `make WITH_TLS=no` * xsltproc (xsltproc and docbook-xsl on Debian based systems) - only needed when building from git sources - disable with `make WITH_DOCS=no` diff --git a/set-version.sh b/set-version.sh index 54958189..5cb46a3c 100755 --- a/set-version.sh +++ b/set-version.sh @@ -2,7 +2,7 @@ MAJOR=1 MINOR=5 -REVISION=4 +REVISION=90 sed -i "s/^VERSION=.*/VERSION=${MAJOR}.${MINOR}.${REVISION}/" config.mk diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index fa1127ed..dced140b 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -1,5 +1,5 @@ name: mosquitto -version: 1.5.4 +version: 1.5.90 summary: Eclipse Mosquitto MQTT broker description: This is a message broker that supports version 3.1 and 3.1.1 of the MQTT protocol. @@ -54,14 +54,12 @@ parts: build-packages: - libssl-dev - - uuid-dev - xsltproc - docbook-xsl - gcc - g++ stage-packages: - libssl1.0.0 - - libuuid1 prime: - usr/sbin/mosquitto - usr/bin/mosquitto_pub @@ -70,7 +68,6 @@ parts: - usr/lib/libmosquitto.so* - lib/*-linux-gnu/libcrypto.so* - lib/*-linux-gnu/libssl.so* - - lib/*-linux-gnu/libuuid.so* - usr/include/mosquitto.h - usr/include/mosquitto_broker.h - usr/include/mosquitto_plugin.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 187d0aac..2b109076 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,6 +7,7 @@ set (MOSQ_SRCS conf_includedir.c context.c database.c + handle_auth.c handle_connack.c handle_connect.c ../lib/handle_ping.c @@ -26,9 +27,12 @@ set (MOSQ_SRCS mosquitto_broker.h mosquitto_broker_internal.h net.c ../lib/net_mosq.c ../lib/net_mosq.h + ../lib/packet_datatypes.c ../lib/packet_mosq.c ../lib/packet_mosq.h persist.c persist.h plugin.c + property_broker.c + ../lib/property_mosq.c ../lib/property_mosq.h read_handle.c ../lib/read_handle.h subs.c @@ -41,73 +45,74 @@ set (MOSQ_SRCS send_suback.c signals.c ../lib/send_subscribe.c + send_unsuback.c ../lib/send_unsubscribe.c sys_tree.c sys_tree.h ../lib/time_mosq.c ../lib/tls_mosq.c - ../lib/util_mosq.c ../lib/util_mosq.h + ../lib/util_mosq.c ../lib/util_topic.c ../lib/util_mosq.h ../lib/utf8_mosq.c websockets.c ../lib/will_mosq.c ../lib/will_mosq.h) option(WITH_BUNDLED_DEPS "Build with bundled dependencies?" ON) -if (${WITH_BUNDLED_DEPS} STREQUAL ON) +if (WITH_BUNDLED_DEPS) include_directories(${mosquitto_SOURCE_DIR} ${mosquitto_SOURCE_DIR}/src/deps) -endif (${WITH_BUNDLED_DEPS} STREQUAL ON) +endif (WITH_BUNDLED_DEPS) option(INC_BRIDGE_SUPPORT "Include bridge support for connecting to other brokers?" ON) -if (${INC_BRIDGE_SUPPORT} STREQUAL ON) +if (INC_BRIDGE_SUPPORT) set (MOSQ_SRCS ${MOSQ_SRCS} bridge.c) add_definitions("-DWITH_BRIDGE") -endif (${INC_BRIDGE_SUPPORT} STREQUAL ON) +endif (INC_BRIDGE_SUPPORT) option(USE_LIBWRAP "Include tcp-wrappers support?" OFF) -if (${USE_LIBWRAP} STREQUAL ON) +if (USE_LIBWRAP) set (MOSQ_LIBS ${MOSQ_LIBS} wrap) add_definitions("-DWITH_WRAP") -endif (${USE_LIBWRAP} STREQUAL ON) +endif (USE_LIBWRAP) option(INC_DB_UPGRADE "Include database upgrade support? (recommended)" ON) option(INC_MEMTRACK "Include memory tracking support?" ON) -if (${INC_MEMTRACK} STREQUAL ON) +if (INC_MEMTRACK) add_definitions("-DWITH_MEMORY_TRACKING") -endif (${INC_MEMTRACK} STREQUAL ON) +endif (INC_MEMTRACK) option(WITH_PERSISTENCE "Include persistence support?" ON) -if (${WITH_PERSISTENCE} STREQUAL ON) +if (WITH_PERSISTENCE) add_definitions("-DWITH_PERSISTENCE") -endif (${WITH_PERSISTENCE} STREQUAL ON) +endif (WITH_PERSISTENCE) option(WITH_SYS_TREE "Include $SYS tree support?" ON) -if (${WITH_SYS_TREE} STREQUAL ON) +if (WITH_SYS_TREE) add_definitions("-DWITH_SYS_TREE") -endif (${WITH_SYS_TREE} STREQUAL ON) +endif (WITH_SYS_TREE) if (CMAKE_SYSTEM_NAME STREQUAL Linux) option(WITH_SYSTEMD "Include systemd support?" OFF) - if (${WITH_SYSTEMD} STREQUAL ON) + if (WITH_SYSTEMD) add_definitions("-DWITH_SYSTEMD") find_library(SYSTEMD_LIBRARY systemd) set (MOSQ_LIBS ${MOSQ_LIBS} ${SYSTEMD_LIBRARY}) - endif (${WITH_SYSTEMD} STREQUAL ON) + endif (WITH_SYSTEMD) endif (CMAKE_SYSTEM_NAME STREQUAL Linux) option(WITH_WEBSOCKETS "Include websockets support?" OFF) option(STATIC_WEBSOCKETS "Use the static libwebsockets library?" OFF) -if (${WITH_WEBSOCKETS} STREQUAL ON ) +if (WITH_WEBSOCKETS) add_definitions("-DWITH_WEBSOCKETS") -endif (${WITH_WEBSOCKETS} STREQUAL ON) +endif (WITH_WEBSOCKETS) if (WIN32 OR CYGWIN) set (MOSQ_SRCS ${MOSQ_SRCS} service.c) @@ -144,26 +149,17 @@ if (WIN32) set (MOSQ_LIBS ${MOSQ_LIBS} ws2_32) endif (WIN32) -if (${WITH_WEBSOCKETS} STREQUAL ON) - if (${STATIC_WEBSOCKETS} STREQUAL ON) +if (WITH_WEBSOCKETS) + if (STATIC_WEBSOCKETS) set (MOSQ_LIBS ${MOSQ_LIBS} websockets_static) if (WIN32) set (MOSQ_LIBS ${MOSQ_LIBS} iphlpapi) link_directories(${mosquitto_SOURCE_DIR}) endif (WIN32) - else (${STATIC_WEBSOCKETS} STREQUAL ON) + else (STATIC_WEBSOCKETS) set (MOSQ_LIBS ${MOSQ_LIBS} websockets) - endif (${STATIC_WEBSOCKETS} STREQUAL ON) -endif (${WITH_WEBSOCKETS} STREQUAL ON) - -# Simple detect libuuid -if(NOT APPLE) - FIND_PATH(UUID_HEADER uuid/uuid.h) - if (UUID_HEADER) - add_definitions(-DWITH_UUID) - set (MOSQ_LIBS ${MOSQ_LIBS} uuid) - endif (UUID_HEADER) -endif(NOT APPLE) + endif (STATIC_WEBSOCKETS) +endif (WITH_WEBSOCKETS) add_executable(mosquitto ${MOSQ_SRCS}) target_link_libraries(mosquitto ${MOSQ_LIBS}) @@ -179,14 +175,12 @@ endif (UNIX) install(TARGETS mosquitto RUNTIME DESTINATION "${SBINDIR}" LIBRARY DESTINATION "${LIBDIR}") install(FILES mosquitto_broker.h mosquitto_plugin.h DESTINATION "${INCLUDEDIR}") -if (${WITH_TLS} STREQUAL ON) +if (WITH_TLS) add_executable(mosquitto_passwd mosquitto_passwd.c) target_link_libraries(mosquitto_passwd ${OPENSSL_LIBRARIES}) install(TARGETS mosquitto_passwd RUNTIME DESTINATION "${BINDIR}" LIBRARY DESTINATION "${LIBDIR}") -endif (${WITH_TLS} STREQUAL ON) +endif (WITH_TLS) if (UNIX AND NOT APPLE) install(CODE "EXEC_PROGRAM(/sbin/ldconfig)") endif (UNIX AND NOT APPLE) - - diff --git a/src/Makefile b/src/Makefile index 227f6623..bf7775ef 100644 --- a/src/Makefile +++ b/src/Makefile @@ -9,11 +9,13 @@ all : mosquitto endif OBJS= mosquitto.o \ + alias_mosq.o \ bridge.o \ conf.o \ conf_includedir.o \ context.o \ database.o \ + handle_auth.o \ handle_connack.o \ handle_connect.o \ handle_ping.o \ @@ -30,7 +32,10 @@ OBJS= mosquitto.o \ memory_mosq.o \ net.o \ net_mosq.o \ + packet_datatypes.o \ packet_mosq.o \ + property_broker.o \ + property_mosq.o \ persist.o \ plugin.o \ read_handle.o \ @@ -43,6 +48,7 @@ OBJS= mosquitto.o \ send_publish.o \ send_suback.o \ send_subscribe.o \ + send_unsuback.o \ send_unsubscribe.o \ service.o \ signals.o \ @@ -52,15 +58,19 @@ OBJS= mosquitto.o \ tls_mosq.o \ utf8_mosq.o \ util_mosq.o \ + util_topic.o \ websockets.o \ will_mosq.o mosquitto : ${OBJS} - ${CROSS_COMPILE}${CC} $^ -o $@ ${LDFLAGS} $(BROKER_LIBS) + ${CROSS_COMPILE}${CC} $^ -o $@ ${BROKER_LDFLAGS} $(BROKER_LIBS) mosquitto.o : mosquitto.c mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CFLAGS) -c $< -o $@ +alias_mosq.o : ../lib/alias_mosq.c ../lib/alias_mosq.h + ${CROSS_COMPILE}${CC} $(BROKER_CFLAGS) -c $< -o $@ + bridge.o : bridge.c mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CFLAGS) -c $< -o $@ @@ -76,6 +86,9 @@ context.o : context.c mosquitto_broker_internal.h database.o : database.c mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CFLAGS) -c $< -o $@ +handle_auth.o : handle_auth.c mosquitto_broker_internal.h + ${CROSS_COMPILE}${CC} $(BROKER_CFLAGS) -c $< -o $@ + handle_connack.o : handle_connack.c mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CFLAGS) -c $< -o $@ @@ -127,9 +140,18 @@ net_mosq.o : ../lib/net_mosq.c ../lib/net_mosq.h persist.o : persist.c persist.h mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CFLAGS) -c $< -o $@ +packet_datatypes.o : ../lib/packet_datatypes.c ../lib/packet_mosq.h + ${CROSS_COMPILE}${CC} $(BROKER_CFLAGS) -c $< -o $@ + packet_mosq.o : ../lib/packet_mosq.c ../lib/packet_mosq.h ${CROSS_COMPILE}${CC} $(BROKER_CFLAGS) -c $< -o $@ +property_broker.o : property_broker.c mosquitto_broker_internal.h + ${CROSS_COMPILE}${CC} $(BROKER_CFLAGS) -c $< -o $@ + +property_mosq.o : ../lib/property_mosq.c ../lib/property_mosq.h + ${CROSS_COMPILE}${CC} $(BROKER_CFLAGS) -c $< -o $@ + plugin.o : plugin.c mosquitto_plugin.h mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CFLAGS) -c $< -o $@ @@ -163,6 +185,9 @@ send_suback.o : send_suback.c mosquitto_broker_internal.h send_subscribe.o : ../lib/send_subscribe.c ../lib/send_mosq.h ${CROSS_COMPILE}${CC} $(BROKER_CFLAGS) -c $< -o $@ +send_unsuback.o : send_unsuback.c mosquitto_broker_internal.h + ${CROSS_COMPILE}${CC} $(BROKER_CFLAGS) -c $< -o $@ + send_unsubscribe.o : ../lib/send_unsubscribe.c ../lib/send_mosq.h ${CROSS_COMPILE}${CC} $(BROKER_CFLAGS) -c $< -o $@ @@ -187,6 +212,9 @@ tls_mosq.o : ../lib/tls_mosq.c util_mosq.o : ../lib/util_mosq.c ../lib/util_mosq.h ${CROSS_COMPILE}${CC} $(BROKER_CFLAGS) -c $< -o $@ +util_topic.o : ../lib/util_topic.c ../lib/util_mosq.h + ${CROSS_COMPILE}${CC} $(BROKER_CFLAGS) -c $< -o $@ + utf8_mosq.o : ../lib/utf8_mosq.c ${CROSS_COMPILE}${CC} $(BROKER_CFLAGS) -c $< -o $@ @@ -223,7 +251,7 @@ uninstall : -rm -f "${DESTDIR}${prefix}/bin/mosquitto_passwd" clean : - -rm -f *.o mosquitto mosquitto_passwd + -rm -f *.o mosquitto mosquitto_passwd *.gcda *.gcno reallyclean : clean -rm -rf *.orig *.db diff --git a/src/bridge.c b/src/bridge.c index a3f2ed7d..072de063 100644 --- a/src/bridge.c +++ b/src/bridge.c @@ -29,6 +29,7 @@ Contributors: #include #endif +#include "mqtt_protocol.h" #include "mosquitto.h" #include "mosquitto_broker_internal.h" #include "mosquitto_internal.h" @@ -82,7 +83,7 @@ int bridge__new(struct mosquitto_db *db, struct mosquitto__bridge *bridge) new_context->tls_cert_reqs = SSL_VERIFY_PEER; new_context->tls_version = new_context->bridge->tls_version; new_context->tls_insecure = new_context->bridge->tls_insecure; -#ifdef WITH_TLS_PSK +#ifdef FINAL_WITH_TLS_PSK new_context->tls_psk_identity = new_context->bridge->tls_psk_identity; new_context->tls_psk = new_context->bridge->tls_psk; #endif @@ -124,18 +125,18 @@ int bridge__connect_step1(struct mosquitto_db *db, struct mosquitto *context) context->last_msg_in = mosquitto_time(); context->next_msg_out = mosquitto_time() + context->bridge->keepalive; context->keepalive = context->bridge->keepalive; - context->clean_session = context->bridge->clean_session; + context->clean_start = context->bridge->clean_start; context->in_packet.payload = NULL; context->ping_t = 0; context->bridge->lazy_reconnect = false; bridge__packet_cleanup(context); db__message_reconnect_reset(db, context); - if(context->clean_session){ + if(context->clean_start){ db__messages_delete(db, context); } - /* Delete all local subscriptions even for clean_session==false. We don't + /* Delete all local subscriptions even for clean_start==false. We don't * remove any messages and the next loop carries out the resubscription * anyway. This means any unwanted subs will be removed. */ @@ -144,10 +145,18 @@ int bridge__connect_step1(struct mosquitto_db *db, struct mosquitto *context) for(i=0; ibridge->topic_count; i++){ if(context->bridge->topics[i].direction == bd_out || context->bridge->topics[i].direction == bd_both){ log__printf(NULL, MOSQ_LOG_DEBUG, "Bridge %s doing local SUBSCRIBE on topic %s", context->id, context->bridge->topics[i].local_topic); - if(sub__add(db, context, context->bridge->topics[i].local_topic, context->bridge->topics[i].qos, &db->subs)) return 1; + if(sub__add(db, + context, + context->bridge->topics[i].local_topic, + context->bridge->topics[i].qos, + 0, + MQTT_SUB_OPT_NO_LOCAL | MQTT_SUB_OPT_RETAIN_AS_PUBLISHED, + &db->subs) > 0){ + return 1; + } sub__retain_queue(db, context, context->bridge->topics[i].local_topic, - context->bridge->topics[i].qos); + context->bridge->topics[i].qos, 0); } } @@ -155,11 +164,11 @@ int bridge__connect_step1(struct mosquitto_db *db, struct mosquitto *context) if(context->bridge->notification_topic){ if(!context->bridge->initial_notification_done){ notification_payload = '0'; - db__messages_easy_queue(db, context, context->bridge->notification_topic, 1, 1, ¬ification_payload, 1); + db__messages_easy_queue(db, context, context->bridge->notification_topic, 1, 1, ¬ification_payload, 1, 0, NULL); context->bridge->initial_notification_done = true; } notification_payload = '0'; - rc = will__set(context, context->bridge->notification_topic, 1, ¬ification_payload, 1, true); + rc = will__set(context, context->bridge->notification_topic, 1, ¬ification_payload, 1, true, NULL); if(rc != MOSQ_ERR_SUCCESS){ return rc; } @@ -172,12 +181,12 @@ int bridge__connect_step1(struct mosquitto_db *db, struct mosquitto *context) if(!context->bridge->initial_notification_done){ notification_payload = '0'; - db__messages_easy_queue(db, context, notification_topic, 1, 1, ¬ification_payload, 1); + db__messages_easy_queue(db, context, notification_topic, 1, 1, ¬ification_payload, 1, 0, NULL); context->bridge->initial_notification_done = true; } notification_payload = '0'; - rc = will__set(context, notification_topic, 1, ¬ification_payload, 1, true); + rc = will__set(context, notification_topic, 1, ¬ification_payload, 1, true, NULL); mosquitto__free(notification_topic); if(rc != MOSQ_ERR_SUCCESS){ return rc; @@ -238,7 +247,7 @@ int bridge__connect_step3(struct mosquitto_db *db, struct mosquitto *context) { int rc; - rc = net__socket_connect_step3(context, context->bridge->addresses[context->bridge->cur_address].address, context->bridge->addresses[context->bridge->cur_address].port, NULL, false); + rc = net__socket_connect_step3(context, context->bridge->addresses[context->bridge->cur_address].address); if(rc > 0){ if(rc == MOSQ_ERR_TLS){ net__socket_close(db, context); @@ -256,7 +265,7 @@ int bridge__connect_step3(struct mosquitto_db *db, struct mosquitto *context) context->bridge->primary_retry = mosquitto_time() + 5; } - rc = send__connect(context, context->keepalive, context->clean_session); + rc = send__connect(context, context->keepalive, context->clean_start, NULL); if(rc == MOSQ_ERR_SUCCESS){ return MOSQ_ERR_SUCCESS; }else if(rc == MOSQ_ERR_ERRNO && errno == ENOTCONN){ @@ -290,18 +299,18 @@ int bridge__connect(struct mosquitto_db *db, struct mosquitto *context) context->last_msg_in = mosquitto_time(); context->next_msg_out = mosquitto_time() + context->bridge->keepalive; context->keepalive = context->bridge->keepalive; - context->clean_session = context->bridge->clean_session; + context->clean_start = context->bridge->clean_start; context->in_packet.payload = NULL; context->ping_t = 0; context->bridge->lazy_reconnect = false; bridge__packet_cleanup(context); db__message_reconnect_reset(db, context); - if(context->clean_session){ + if(context->clean_start){ db__messages_delete(db, context); } - /* Delete all local subscriptions even for clean_session==false. We don't + /* Delete all local subscriptions even for clean_start==false. We don't * remove any messages and the next loop carries out the resubscription * anyway. This means any unwanted subs will be removed. */ @@ -310,10 +319,19 @@ int bridge__connect(struct mosquitto_db *db, struct mosquitto *context) for(i=0; ibridge->topic_count; i++){ if(context->bridge->topics[i].direction == bd_out || context->bridge->topics[i].direction == bd_both){ log__printf(NULL, MOSQ_LOG_DEBUG, "Bridge %s doing local SUBSCRIBE on topic %s", context->id, context->bridge->topics[i].local_topic); - if(sub__add(db, context, context->bridge->topics[i].local_topic, context->bridge->topics[i].qos, &db->subs)) return 1; + if(sub__add(db, + context, + context->bridge->topics[i].local_topic, + context->bridge->topics[i].qos, + 0, + MQTT_SUB_OPT_NO_LOCAL | MQTT_SUB_OPT_RETAIN_AS_PUBLISHED, + &db->subs) > 0){ + + return 1; + } sub__retain_queue(db, context, context->bridge->topics[i].local_topic, - context->bridge->topics[i].qos); + context->bridge->topics[i].qos, 0); } } @@ -321,13 +339,13 @@ int bridge__connect(struct mosquitto_db *db, struct mosquitto *context) if(context->bridge->notification_topic){ if(!context->bridge->initial_notification_done){ notification_payload = '0'; - db__messages_easy_queue(db, context, context->bridge->notification_topic, 1, 1, ¬ification_payload, 1); + db__messages_easy_queue(db, context, context->bridge->notification_topic, 1, 1, ¬ification_payload, 1, 0, NULL); context->bridge->initial_notification_done = true; } if (!context->bridge->notifications_local_only) { notification_payload = '0'; - rc = will__set(context, context->bridge->notification_topic, 1, ¬ification_payload, 1, true); + rc = will__set(context, context->bridge->notification_topic, 1, ¬ification_payload, 1, true, NULL); if(rc != MOSQ_ERR_SUCCESS){ return rc; } @@ -341,13 +359,13 @@ int bridge__connect(struct mosquitto_db *db, struct mosquitto *context) if(!context->bridge->initial_notification_done){ notification_payload = '0'; - db__messages_easy_queue(db, context, notification_topic, 1, 1, ¬ification_payload, 1); + db__messages_easy_queue(db, context, notification_topic, 1, 1, ¬ification_payload, 1, 0, NULL); context->bridge->initial_notification_done = true; } if (!context->bridge->notifications_local_only) { notification_payload = '0'; - rc = will__set(context, notification_topic, 1, ¬ification_payload, 1, true); + rc = will__set(context, notification_topic, 1, ¬ification_payload, 1, true, NULL); mosquitto__free(notification_topic); if(rc != MOSQ_ERR_SUCCESS){ return rc; @@ -375,7 +393,7 @@ int bridge__connect(struct mosquitto_db *db, struct mosquitto *context) HASH_ADD(hh_sock, db->contexts_by_sock, sock, sizeof(context->sock), context); - rc2 = send__connect(context, context->keepalive, context->clean_session); + rc2 = send__connect(context, context->keepalive, context->clean_start, NULL); if(rc2 == MOSQ_ERR_SUCCESS){ return rc; }else if(rc2 == MOSQ_ERR_ERRNO && errno == ENOTCONN){ diff --git a/src/conf.c b/src/conf.c index 2107b64c..d92dd2bb 100644 --- a/src/conf.c +++ b/src/conf.c @@ -44,7 +44,7 @@ Contributors: #include "memory_mosq.h" #include "tls_mosq.h" #include "util_mosq.h" -#include "mqtt3_protocol.h" +#include "mqtt_protocol.h" struct config_recurse { int log_dest; @@ -53,7 +53,6 @@ struct config_recurse { int log_type_set; unsigned long max_inflight_bytes; unsigned long max_queued_bytes; - int max_inflight_messages; int max_queued_messages; }; @@ -62,6 +61,8 @@ struct config_recurse { extern SERVICE_STATUS_HANDLE service_handle; #endif +static struct mosquitto__security_options *cur_security_options = NULL; + static int conf__parse_bool(char **token, const char *name, bool *value, char *saveptr); static int conf__parse_int(char **token, const char *name, int *value, char *saveptr); static int conf__parse_ssize_t(char **token, const char *name, ssize_t *value, char *saveptr); @@ -212,6 +213,9 @@ static void config__init_reload(struct mosquitto_db *db, struct mosquitto__confi } #endif config->log_timestamp = true; + config->max_keepalive = 65535; + config->max_packet_size = 0; + config->max_inflight_messages = 20; config->persistence = false; mosquitto__free(config->persistence_location); config->persistence_location = NULL; @@ -219,6 +223,7 @@ static void config__init_reload(struct mosquitto_db *db, struct mosquitto__confi config->persistence_file = NULL; config->persistent_client_expiration = 0; config->queue_qos0_messages = false; + config->retain_available = true; config->set_tcp_nodelay = false; config->sys_interval = 10; config->upgrade_outgoing_qos = false; @@ -264,12 +269,15 @@ void config__init(struct mosquitto_db *db, struct mosquitto__config *config) config->default_listener.max_connections = -1; config->default_listener.protocol = mp_mqtt; config->default_listener.security_options.allow_anonymous = -1; + config->default_listener.maximum_qos = 2; } void config__cleanup(struct mosquitto__config *config) { int i; +#ifdef WITH_BRIDGE int j; +#endif mosquitto__free(config->clientid_prefixes); mosquitto__free(config->persistence_location); @@ -343,7 +351,7 @@ void config__cleanup(struct mosquitto__config *config) #ifdef WITH_TLS mosquitto__free(config->bridges[i].tls_version); mosquitto__free(config->bridges[i].tls_cafile); -#ifdef WITH_TLS_PSK +#ifdef FINAL_WITH_TLS_PSK mosquitto__free(config->bridges[i].tls_psk_identity); mosquitto__free(config->bridges[i].tls_psk); #endif @@ -449,8 +457,10 @@ int config__parse_args(struct mosquitto_db *db, struct mosquitto__config *config || config->default_listener.host || config->default_listener.port || config->default_listener.max_connections != -1 + || config->default_listener.maximum_qos != 2 || config->default_listener.mount_point || config->default_listener.protocol != mp_mqtt + || config->default_listener.socket_domain || config->default_listener.security_options.password_file || config->default_listener.security_options.psk_file || config->default_listener.security_options.auth_plugin_config_count @@ -481,11 +491,13 @@ int config__parse_args(struct mosquitto_db *db, struct mosquitto__config *config } config->listeners[config->listener_count-1].max_connections = config->default_listener.max_connections; config->listeners[config->listener_count-1].protocol = config->default_listener.protocol; + config->listeners[config->listener_count-1].socket_domain = config->default_listener.socket_domain; config->listeners[config->listener_count-1].client_count = 0; config->listeners[config->listener_count-1].socks = NULL; config->listeners[config->listener_count-1].sock_count = 0; config->listeners[config->listener_count-1].client_count = 0; config->listeners[config->listener_count-1].use_username_as_clientid = config->default_listener.use_username_as_clientid; + config->listeners[config->listener_count-1].maximum_qos = config->default_listener.maximum_qos; #ifdef WITH_TLS config->listeners[config->listener_count-1].tls_version = config->default_listener.tls_version; config->listeners[config->listener_count-1].tls_engine = config->default_listener.tls_engine; @@ -503,6 +515,7 @@ int config__parse_args(struct mosquitto_db *db, struct mosquitto__config *config config->listeners[config->listener_count-1].use_identity_as_username = config->default_listener.use_identity_as_username; config->listeners[config->listener_count-1].use_subject_as_username = config->default_listener.use_subject_as_username; #endif + config->listeners[config->listener_count-1].security_options.acl_file = config->default_listener.security_options.acl_file; config->listeners[config->listener_count-1].security_options.password_file = config->default_listener.security_options.password_file; config->listeners[config->listener_count-1].security_options.psk_file = config->default_listener.security_options.psk_file; config->listeners[config->listener_count-1].security_options.auth_plugin_configs = config->default_listener.security_options.auth_plugin_configs; @@ -588,7 +601,9 @@ int config__read(struct mosquitto_db *db, struct mosquitto__config *config, bool int rc = MOSQ_ERR_SUCCESS; struct config_recurse cr; int lineno = 0; +#ifdef WITH_PERSISTENCE int len; +#endif struct mosquitto__config config_reload; int i; @@ -601,7 +616,6 @@ int config__read(struct mosquitto_db *db, struct mosquitto__config *config, bool cr.log_type = MOSQ_LOG_NONE; cr.log_type_set = 0; cr.max_inflight_bytes = 0; - cr.max_inflight_messages = 20; cr.max_queued_bytes = 0; cr.max_queued_messages = 100; @@ -684,7 +698,7 @@ int config__read(struct mosquitto_db *db, struct mosquitto__config *config, bool config->user = "mosquitto"; } - db__limits_set(cr.max_inflight_messages, cr.max_inflight_bytes, cr.max_queued_messages, cr.max_queued_bytes); + db__limits_set(cr.max_inflight_bytes, cr.max_queued_messages, cr.max_queued_bytes); #ifdef WITH_BRIDGE for(i=0; ibridge_count; i++){ @@ -692,7 +706,7 @@ int config__read(struct mosquitto_db *db, struct mosquitto__config *config, bool log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); return MOSQ_ERR_INVAL; } -#ifdef WITH_TLS_PSK +#ifdef FINAL_WITH_TLS_PSK if(config->bridges[i].tls_psk && !config->bridges[i].tls_psk_identity){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration: missing bridge_identity.\n"); return MOSQ_ERR_INVAL; @@ -731,20 +745,10 @@ int config__read_file_core(struct mosquitto__config *config, bool reload, struct time_t expiration_mult; char *key; - char *conf_file; -#ifdef WIN32 - HANDLE fh; - char dirpath[MAX_PATH]; - WIN32_FIND_DATA find_data; -#else - DIR *dh; - struct dirent *de; -#endif int len; struct mosquitto__listener *cur_listener = &config->default_listener; int i; int lineno_ext; - struct mosquitto__security_options *cur_security_options = NULL; *lineno = 0; @@ -771,6 +775,9 @@ int config__read_file_core(struct mosquitto__config *config, bool reload, struct return MOSQ_ERR_INVAL; } while((token = strtok_r(NULL, " ", &saveptr))){ + if (token[0] == '#'){ + break; + } cur_bridge->address_count++; cur_bridge->addresses = mosquitto__realloc(cur_bridge->addresses, sizeof(struct bridge_address)*cur_bridge->address_count); if(!cur_bridge->addresses){ @@ -824,6 +831,7 @@ int config__read_file_core(struct mosquitto__config *config, bool reload, struct conf__set_cur_security_options(config, cur_listener, &cur_security_options); if(conf__parse_bool(&token, "allow_zero_length_clientid", &cur_security_options->allow_zero_length_clientid, saveptr)) return MOSQ_ERR_INVAL; }else if(!strncmp(token, "auth_opt_", 9)){ + if(reload) continue; // Auth plugin not currently valid for reloading. if(!cur_auth_plugin_config){ log__printf(NULL, MOSQ_LOG_ERR, "Error: An auth_opt_ option exists in the config file without an auth_plugin."); return MOSQ_ERR_INVAL; @@ -925,7 +933,7 @@ int config__read_file_core(struct mosquitto__config *config, bool reload, struct log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); return MOSQ_ERR_INVAL; } -#ifdef WITH_TLS_PSK +#ifdef FINAL_WITH_TLS_PSK if(cur_bridge->tls_psk_identity || cur_bridge->tls_psk){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Cannot use both certificate and psk encryption in a single bridge."); return MOSQ_ERR_INVAL; @@ -942,7 +950,7 @@ int config__read_file_core(struct mosquitto__config *config, bool reload, struct log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); return MOSQ_ERR_INVAL; } -#ifdef WITH_TLS_PSK +#ifdef FINAL_WITH_TLS_PSK if(cur_bridge->tls_psk_identity || cur_bridge->tls_psk){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Cannot use both certificate and psk encryption in a single bridge."); return MOSQ_ERR_INVAL; @@ -959,7 +967,7 @@ int config__read_file_core(struct mosquitto__config *config, bool reload, struct log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); return MOSQ_ERR_INVAL; } -#ifdef WITH_TLS_PSK +#ifdef FINAL_WITH_TLS_PSK if(cur_bridge->tls_psk_identity || cur_bridge->tls_psk){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Cannot use both certificate and psk encryption in a single bridge."); return MOSQ_ERR_INVAL; @@ -970,7 +978,7 @@ int config__read_file_core(struct mosquitto__config *config, bool reload, struct log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge and/or TLS support not available."); #endif }else if(!strcmp(token, "bridge_identity")){ -#if defined(WITH_BRIDGE) && defined(WITH_TLS_PSK) +#if defined(WITH_BRIDGE) && defined(FINAL_WITH_TLS_PSK) if(reload) continue; // FIXME if(!cur_bridge){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); @@ -1005,7 +1013,7 @@ int config__read_file_core(struct mosquitto__config *config, bool reload, struct log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); return MOSQ_ERR_INVAL; } -#ifdef WITH_TLS_PSK +#ifdef FINAL_WITH_TLS_PSK if(cur_bridge->tls_psk_identity || cur_bridge->tls_psk){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Cannot use both certificate and psk encryption in a single bridge."); return MOSQ_ERR_INVAL; @@ -1040,7 +1048,7 @@ int config__read_file_core(struct mosquitto__config *config, bool reload, struct log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge support not available."); #endif }else if(!strcmp(token, "bridge_psk")){ -#if defined(WITH_BRIDGE) && defined(WITH_TLS_PSK) +#if defined(WITH_BRIDGE) && defined(FINAL_WITH_TLS_PSK) if(reload) continue; // FIXME if(!cur_bridge){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); @@ -1094,38 +1102,9 @@ int config__read_file_core(struct mosquitto__config *config, bool reload, struct #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: TLS support not available."); #endif - }else if(!strcmp(token, "tls_engine")){ -#ifdef WITH_TLS - if(reload) continue; // Listeners not valid for reloading. - if(conf__parse_string(&token, "tls_engine", &cur_listener->tls_engine, saveptr)) return MOSQ_ERR_INVAL; -#else - log__printf(NULL, MOSQ_LOG_WARNING, "Warning: TLS support not available."); -#endif - }else if(!strcmp(token, "tls_keyform")){ -#ifdef WITH_TLS - if(reload) continue; // Listeners not valid for reloading. - char *keyform = NULL; - if(conf__parse_string(&token, "tls_keyform", &keyform, saveptr)) return MOSQ_ERR_INVAL; - cur_listener->tls_keyform = mosq_k_pem; - if(!strcmp(keyform, "engine")) cur_listener->tls_keyform = mosq_k_engine; - mosquitto__free(keyform); -#else - log__printf(NULL, MOSQ_LOG_WARNING, "Warning: TLS support not available."); -#endif - }else if(!strcmp(token, "tls_engine_kpass_sha1")){ -#ifdef WITH_TLS - if(reload) continue; // Listeners not valid for reloading. - char *kpass_sha = NULL, *kpass_sha_bin = NULL; - if(conf__parse_string(&token, "tls_engine_kpass_sha1", &kpass_sha, saveptr)) return MOSQ_ERR_INVAL; - if(mosquitto__hex2bin_sha1(kpass_sha, (unsigned char**)&kpass_sha_bin) != MOSQ_ERR_SUCCESS){ - mosquitto__free(kpass_sha); - return MOSQ_ERR_INVAL; - } - cur_listener->tls_engine_kpass_sha1 = kpass_sha_bin; - mosquitto__free(kpass_sha); -#else - log__printf(NULL, MOSQ_LOG_WARNING, "Warning: TLS support not available."); -#endif + }else if(!strcmp(token, "check_retain_source")){ + conf__set_cur_security_options(config, cur_listener, &cur_security_options); + if(conf__parse_bool(&token, "check_retain_source", &config->check_retain_source, saveptr)) return MOSQ_ERR_INVAL; }else if(!strcmp(token, "ciphers")){ #ifdef WITH_TLS if(reload) continue; // Listeners not valid for reloading. @@ -1151,7 +1130,7 @@ int config__read_file_core(struct mosquitto__config *config, bool reload, struct log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); return MOSQ_ERR_INVAL; } - if(conf__parse_bool(&token, "cleansession", &cur_bridge->clean_session, saveptr)) return MOSQ_ERR_INVAL; + if(conf__parse_bool(&token, "cleansession", &cur_bridge->clean_start, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge support not available."); #endif @@ -1244,66 +1223,27 @@ int config__read_file_core(struct mosquitto__config *config, bool reload, struct log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty include_dir value in configuration."); return 1; } -#ifdef WIN32 - snprintf(dirpath, MAX_PATH, "%s\\*.conf", token); - fh = FindFirstFile(dirpath, &find_data); - if(fh == INVALID_HANDLE_VALUE){ - /* No files found */ - continue; - } - do{ - len = strlen(token)+1+strlen(find_data.cFileName)+1; - conf_file = mosquitto__malloc(len+1); - if(!conf_file){ - FindClose(fh); - return MOSQ_ERR_NOMEM; - } - snprintf(conf_file, len, "%s\\%s", token, find_data.cFileName); - conf_file[len] = '\0'; + char **files; + int file_count; + rc = config__get_dir_files(token, &files, &file_count); + if(rc) return rc; - rc = config__read_file(config, reload, conf_file, cr, level+1, &lineno_ext); + for(i=0; id_name) > 5){ - if(!strcmp(&de->d_name[strlen(de->d_name)-5], ".conf")){ - len = strlen(token)+1+strlen(de->d_name)+1; - conf_file = mosquitto__malloc(len+1); - if(!conf_file){ - closedir(dh); - return MOSQ_ERR_NOMEM; - } - snprintf(conf_file, len, "%s/%s", token, de->d_name); - conf_file[len] = '\0'; - - rc = config__read_file(config, reload, conf_file, cr, level+1, &lineno_ext); - if(rc){ - closedir(dh); - log__printf(NULL, MOSQ_LOG_ERR, "Error found at %s:%d.", conf_file, lineno_ext); - mosquitto__free(conf_file); - return rc; - } - mosquitto__free(conf_file); - } + log__printf(NULL, MOSQ_LOG_ERR, "Error found at %s:%d.", files[i], lineno_ext); + /* Free happens below */ + break; } } - closedir(dh); -#endif + for(i=0; isecurity_options.allow_anonymous = -1; cur_listener->protocol = mp_mqtt; cur_listener->port = tmp_int; - token = strtok_r(NULL, "", &saveptr); + cur_listener->maximum_qos = 2; + token = strtok_r(NULL, " ", &saveptr); + if (token != NULL && token[0] == '#'){ + token = NULL; + } mosquitto__free(cur_listener->host); if(token){ cur_listener->host = mosquitto__strdup(token); @@ -1537,6 +1481,14 @@ int config__read_file_core(struct mosquitto__config *config, bool reload, struct }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty max_connections value in configuration."); } + }else if(!strcmp(token, "maximum_qos")){ + if(reload) continue; // Listeners not valid for reloading. + if(conf__parse_int(&token, "maximum_qos", &tmp_int, saveptr)) return MOSQ_ERR_INVAL; + if(tmp_int < 0 || tmp_int > 2){ + log__printf(NULL, MOSQ_LOG_ERR, "Error: maximum_qos must be between 0 and 2 inclusive."); + return MOSQ_ERR_INVAL; + } + cur_listener->maximum_qos = tmp_int; }else if(!strcmp(token, "max_inflight_bytes")){ token = strtok_r(NULL, " ", &saveptr); if(token){ @@ -1545,13 +1497,28 @@ int config__read_file_core(struct mosquitto__config *config, bool reload, struct log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty max_inflight_bytes value in configuration."); } }else if(!strcmp(token, "max_inflight_messages")){ - token = strtok_r(NULL, " ", &saveptr); - if(token){ - cr->max_inflight_messages = atoi(token); - if(cr->max_inflight_messages < 0) cr->max_inflight_messages = 0; - }else{ - log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty max_inflight_messages value in configuration."); + if(conf__parse_int(&token, "max_inflight_messages", &tmp_int, saveptr)) return MOSQ_ERR_INVAL; + if(tmp_int < 0 || tmp_int == 65535){ + tmp_int = 0; + }else if(tmp_int > 65535){ + log__printf(NULL, MOSQ_LOG_ERR, "Error: max_inflight_messages must be <= 65535."); + return MOSQ_ERR_INVAL; } + config->max_inflight_messages = tmp_int; + }else if(!strcmp(token, "max_keepalive")){ + if(conf__parse_int(&token, "max_keepalive", &tmp_int, saveptr)) return MOSQ_ERR_INVAL; + if(tmp_int < 10 || tmp_int > 65535){ + log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid max_keepalive value (%d).", tmp_int); + return MOSQ_ERR_INVAL; + } + config->max_keepalive = tmp_int; + }else if(!strcmp(token, "max_packet_size")){ + if(conf__parse_int(&token, "max_packet_size", &tmp_int, saveptr)) return MOSQ_ERR_INVAL; + if(tmp_int < 20){ + log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid max_packet_size value (%d).", tmp_int); + return MOSQ_ERR_INVAL; + } + config->max_packet_size = tmp_int; }else if(!strcmp(token, "max_queued_bytes")){ token = strtok_r(NULL, " ", &saveptr); if(token){ @@ -1728,7 +1695,7 @@ int config__read_file_core(struct mosquitto__config *config, bool reload, struct log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty protocol value in configuration."); } }else if(!strcmp(token, "psk_file")){ -#ifdef WITH_TLS_PSK +#ifdef FINAL_WITH_TLS_PSK conf__set_cur_security_options(config, cur_listener, &cur_security_options); if(reload){ mosquitto__free(cur_security_options->psk_file); @@ -1739,7 +1706,7 @@ int config__read_file_core(struct mosquitto__config *config, bool reload, struct log__printf(NULL, MOSQ_LOG_WARNING, "Warning: TLS/TLS-PSK support not available."); #endif }else if(!strcmp(token, "psk_hint")){ -#ifdef WITH_TLS_PSK +#ifdef FINAL_WITH_TLS_PSK if(reload) continue; // Listeners not valid for reloading. if(conf__parse_string(&token, "psk_hint", &cur_listener->psk_hint, saveptr)) return MOSQ_ERR_INVAL; #else @@ -1769,6 +1736,8 @@ 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, "retain_available")){ + if(conf__parse_bool(&token, token, &config->retain_available, saveptr)) return MOSQ_ERR_INVAL; }else if(!strcmp(token, "retry_interval")){ log__printf(NULL, MOSQ_LOG_WARNING, "Warning: The retry_interval option is no longer available."); }else if(!strcmp(token, "round_robin")){ @@ -1813,6 +1782,22 @@ 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, "socket_domain")){ + if(reload) continue; // Listeners not valid for reloading. + token = strtok_r(NULL, " ", &saveptr); + if(token){ + if(!strcmp(token, "ipv4")){ + cur_listener->socket_domain = AF_INET; + }else if(!strcmp(token, "ipv6")){ + cur_listener->socket_domain = AF_INET6; + }else{ + log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid socket_domain value \"%s\" in configuration.", token); + return MOSQ_ERR_INVAL; + } + }else{ + log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty socket_domain value in configuration."); + return MOSQ_ERR_INVAL; + } }else if(!strcmp(token, "store_clean_interval")){ log__printf(NULL, MOSQ_LOG_WARNING, "Warning: store_clean_interval is no longer needed."); }else if(!strcmp(token, "sys_interval")){ @@ -1835,6 +1820,38 @@ 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, "tls_engine")){ +#ifdef WITH_TLS + if(reload) continue; // Listeners not valid for reloading. + if(conf__parse_string(&token, "tls_engine", &cur_listener->tls_engine, saveptr)) return MOSQ_ERR_INVAL; +#else + log__printf(NULL, MOSQ_LOG_WARNING, "Warning: TLS support not available."); +#endif + }else if(!strcmp(token, "tls_engine_kpass_sha1")){ +#ifdef WITH_TLS + if(reload) continue; // Listeners not valid for reloading. + char *kpass_sha = NULL, *kpass_sha_bin = NULL; + if(conf__parse_string(&token, "tls_engine_kpass_sha1", &kpass_sha, saveptr)) return MOSQ_ERR_INVAL; + if(mosquitto__hex2bin_sha1(kpass_sha, (unsigned char**)&kpass_sha_bin) != MOSQ_ERR_SUCCESS){ + mosquitto__free(kpass_sha); + return MOSQ_ERR_INVAL; + } + cur_listener->tls_engine_kpass_sha1 = kpass_sha_bin; + mosquitto__free(kpass_sha); +#else + log__printf(NULL, MOSQ_LOG_WARNING, "Warning: TLS support not available."); +#endif + }else if(!strcmp(token, "tls_keyform")){ +#ifdef WITH_TLS + if(reload) continue; // Listeners not valid for reloading. + char *keyform = NULL; + if(conf__parse_string(&token, "tls_keyform", &keyform, saveptr)) return MOSQ_ERR_INVAL; + cur_listener->tls_keyform = mosq_k_pem; + if(!strcmp(keyform, "engine")) cur_listener->tls_keyform = mosq_k_engine; + mosquitto__free(keyform); +#else + log__printf(NULL, MOSQ_LOG_WARNING, "Warning: TLS support not available."); #endif }else if(!strcmp(token, "tls_version")){ #if defined(WITH_TLS) @@ -1891,6 +1908,9 @@ int config__read_file_core(struct mosquitto__config *config, bool reload, struct } token = strtok_r(NULL, " ", &saveptr); if(token){ + if (token[0] == '#'){ + strtok_r(NULL, "", &saveptr); + } cur_topic->qos = atoi(token); if(cur_topic->qos < 0 || cur_topic->qos > 2){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge QoS level '%s'.", token); @@ -1900,8 +1920,11 @@ int config__read_file_core(struct mosquitto__config *config, bool reload, struct token = strtok_r(NULL, " ", &saveptr); if(token){ cur_bridge->topic_remapping = true; - if(!strcmp(token, "\"\"")){ + if(!strcmp(token, "\"\"") || token[0] == '#'){ cur_topic->local_prefix = NULL; + if (token[0] == '#'){ + strtok_r(NULL, "", &saveptr); + } }else{ if(mosquitto_pub_topic_check(token) != MOSQ_ERR_SUCCESS){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge topic local prefix '%s'.", token); @@ -1916,7 +1939,7 @@ int config__read_file_core(struct mosquitto__config *config, bool reload, struct token = strtok_r(NULL, " ", &saveptr); if(token){ - if(!strcmp(token, "\"\"")){ + if(!strcmp(token, "\"\"") || token[0] == '#'){ cur_topic->remote_prefix = NULL; }else{ if(mosquitto_pub_topic_check(token) != MOSQ_ERR_SUCCESS){ @@ -2031,21 +2054,7 @@ int config__read_file_core(struct mosquitto__config *config, bool reload, struct log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); return MOSQ_ERR_INVAL; } - token = strtok_r(NULL, " ", &saveptr); - if(token){ - if(cur_bridge->remote_username){ - log__printf(NULL, MOSQ_LOG_ERR, "Error: Duplicate username value in bridge configuration."); - return MOSQ_ERR_INVAL; - } - cur_bridge->remote_username = mosquitto__strdup(token); - if(!cur_bridge->remote_username){ - log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); - return MOSQ_ERR_NOMEM; - } - }else{ - log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty username value in configuration."); - return MOSQ_ERR_INVAL; - } + if(conf__parse_string(&token, "bridge remote_username", &cur_bridge->remote_username, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge support not available."); #endif @@ -2103,8 +2112,10 @@ static int config__check(struct mosquitto__config *config) { /* Checks that are easy to make after the config has been loaded. */ + int i; + #ifdef WITH_BRIDGE - int i, j; + int j; struct mosquitto__bridge *bridge1, *bridge2; char hostname[256]; int len; @@ -2152,6 +2163,28 @@ static int config__check(struct mosquitto__config *config) } } #endif + + /* Default to auto_id_prefix = 'auto-' if none set. */ + if(config->per_listener_settings){ + for(i=0; ilistener_count; i++){ + if(!config->listeners[i].security_options.auto_id_prefix){ + config->listeners[i].security_options.auto_id_prefix = mosquitto__strdup("auto-"); + if(!config->listeners[i].security_options.auto_id_prefix){ + return MOSQ_ERR_NOMEM; + } + config->listeners[i].security_options.auto_id_prefix_len = strlen("auto-"); + } + } + }else{ + if(!config->security_options.auto_id_prefix){ + config->security_options.auto_id_prefix = mosquitto__strdup("auto-"); + if(!config->security_options.auto_id_prefix){ + return MOSQ_ERR_NOMEM; + } + config->security_options.auto_id_prefix_len = strlen("auto-"); + } + } + return MOSQ_ERR_SUCCESS; } diff --git a/src/conf_includedir.c b/src/conf_includedir.c index 783a4048..a23c350b 100644 --- a/src/conf_includedir.c +++ b/src/conf_includedir.c @@ -44,15 +44,15 @@ Contributors: #include "memory_mosq.h" #include "tls_mosq.h" #include "util_mosq.h" -#include "mqtt3_protocol.h" +#include "mqtt_protocol.h" -int strcasecmp_p(const void *p1, const void *p2) + +#ifdef WIN32 +int scmp_p(const void *p1, const void *p2) { return strcasecmp(*(const char **)p1, *(const char **)p2); } - -#ifdef WIN32 int config__get_dir_files(const char *include_dir, char ***files, int *file_count) { int len; @@ -102,6 +102,7 @@ int config__get_dir_files(const char *include_dir, char ***files, int *file_coun FindClose(fh); + qsort(l_files, l_file_count, sizeof(char *), scmp_p); *files = l_files; *file_count = l_file_count; @@ -111,6 +112,11 @@ int config__get_dir_files(const char *include_dir, char ***files, int *file_coun #ifndef WIN32 +int scmp_p(const void *p1, const void *p2) +{ + return strcmp(*(const char **)p1, *(const char **)p2); +} + int config__get_dir_files(const char *include_dir, char ***files, int *file_count) { char **l_files = NULL; @@ -160,6 +166,7 @@ int config__get_dir_files(const char *include_dir, char ***files, int *file_coun } closedir(dh); + qsort(l_files, l_file_count, sizeof(char *), scmp_p); *files = l_files; *file_count = l_file_count; diff --git a/src/context.c b/src/context.c index 1d3ae163..bb8ee605 100644 --- a/src/context.c +++ b/src/context.c @@ -20,8 +20,10 @@ Contributors: #include #include "mosquitto_broker_internal.h" +#include "alias_mosq.h" #include "memory_mosq.h" #include "packet_mosq.h" +#include "property_mosq.h" #include "time_mosq.h" #include "uthash.h" @@ -40,7 +42,7 @@ struct mosquitto *context__init(struct mosquitto_db *db, mosq_sock_t sock) context->last_msg_in = mosquitto_time(); context->next_msg_out = mosquitto_time() + 60; context->keepalive = 60; /* Default to 60s */ - context->clean_session = true; + context->clean_start = true; context->disconnect_t = 0; context->id = NULL; context->last_mid = 0; @@ -75,6 +77,9 @@ struct mosquitto *context__init(struct mosquitto_db *db, mosq_sock_t sock) context->last_inflight_msg = NULL; context->queued_msgs = NULL; context->last_queued_msg = NULL; + context->receive_maximum = db->config->max_inflight_messages; + context->send_maximum = db->config->max_inflight_messages; + context->maximum_qos = 2; context->msg_bytes = 0; context->msg_bytes12 = 0; context->msg_count = 0; @@ -99,7 +104,9 @@ void context__cleanup(struct mosquitto_db *db, struct mosquitto *context, bool d { struct mosquitto__packet *packet; struct mosquitto_client_msg *msg, *next; +#ifdef WITH_BRIDGE int i; +#endif if(!context) return; @@ -136,6 +143,8 @@ void context__cleanup(struct mosquitto_db *db, struct mosquitto *context, bool d } #endif + alias__free_all(context); + mosquitto__free(context->username); context->username = NULL; @@ -143,7 +152,7 @@ void context__cleanup(struct mosquitto_db *db, struct mosquitto *context, bool d context->password = NULL; net__socket_close(db, context); - if((do_free || context->clean_session) && db){ + if((do_free || context->clean_start) && db){ sub__clean_session(db, context); db__messages_delete(db, context); } @@ -157,14 +166,10 @@ void context__cleanup(struct mosquitto_db *db, struct mosquitto *context, bool d assert(db); /* db can only be NULL here if the client hasn't sent a CONNECT and hence wouldn't have an id. */ - HASH_DELETE(hh_id, db->contexts_by_id, context); + context__remove_from_by_id(db, context); mosquitto__free(context->id); context->id = NULL; } - if(context->old_id){ - mosquitto__free(context->old_id); - context->old_id = NULL; - } packet__cleanup(&(context->in_packet)); if(context->current_out_packet){ packet__cleanup(context->current_out_packet); @@ -177,7 +182,7 @@ void context__cleanup(struct mosquitto_db *db, struct mosquitto *context, bool d context->out_packet = context->out_packet->next; mosquitto__free(packet); } - if(do_free || context->clean_session){ + if(do_free || context->clean_start){ msg = context->inflight_msgs; while(msg){ next = msg->next; @@ -197,6 +202,13 @@ void context__cleanup(struct mosquitto_db *db, struct mosquitto *context, bool d context->queued_msgs = NULL; context->last_queued_msg = NULL; } +#if defined(WITH_BROKER) && defined(__GLIBC__) && defined(WITH_ADNS) + if(context->adns){ + gai_cancel(context->adns); + mosquitto__free((struct addrinfo *)context->adns->ar_request); + mosquitto__free(context->adns); + } +#endif if(do_free){ mosquitto__free(context); } @@ -206,15 +218,29 @@ void context__cleanup(struct mosquitto_db *db, struct mosquitto *context, bool d void context__send_will(struct mosquitto_db *db, struct mosquitto *ctxt) { if(ctxt->state != mosq_cs_disconnecting && ctxt->will){ - if(mosquitto_acl_check(db, ctxt, ctxt->will->topic, ctxt->will->payloadlen, ctxt->will->payload, - ctxt->will->qos, ctxt->will->retain, MOSQ_ACL_WRITE) == MOSQ_ERR_SUCCESS){ + if(mosquitto_acl_check(db, ctxt, + ctxt->will->msg.topic, + ctxt->will->msg.payloadlen, + ctxt->will->msg.payload, + ctxt->will->msg.qos, + ctxt->will->msg.retain, + MOSQ_ACL_WRITE) == MOSQ_ERR_SUCCESS){ + /* Unexpected disconnect, queue the client will. */ - db__messages_easy_queue(db, ctxt, ctxt->will->topic, ctxt->will->qos, ctxt->will->payloadlen, ctxt->will->payload, ctxt->will->retain); + db__messages_easy_queue(db, ctxt, + ctxt->will->msg.topic, + ctxt->will->msg.qos, + ctxt->will->msg.payloadlen, + ctxt->will->msg.payload, + ctxt->will->msg.retain, + ctxt->will->expiry_interval, + &ctxt->will->properties); } } if(ctxt->will){ - mosquitto__free(ctxt->will->topic); - mosquitto__free(ctxt->will->payload); + mosquitto_property_free_all(&ctxt->will->properties); + mosquitto__free(ctxt->will->msg.topic); + mosquitto__free(ctxt->will->msg.payload); mosquitto__free(ctxt->will); ctxt->will = NULL; } @@ -253,3 +279,11 @@ void context__free_disused(struct mosquitto_db *db) db->ll_for_free = NULL; } + +void context__remove_from_by_id(struct mosquitto_db *db, struct mosquitto *context) +{ + if(context->removed_from_by_id == false && context->id){ + HASH_DELETE(hh_id, db->contexts_by_id, context); + context->removed_from_by_id = true; + } +} diff --git a/src/database.c b/src/database.c index d2f482d1..0755b174 100644 --- a/src/database.c +++ b/src/database.c @@ -25,7 +25,6 @@ Contributors: #include "sys_tree.h" #include "time_mosq.h" -static int max_inflight = 20; static unsigned long max_inflight_bytes = 0; static int max_queued = 100; static unsigned long max_queued_bytes = 0; @@ -38,14 +37,14 @@ static unsigned long max_queued_bytes = 0; */ static bool db__ready_for_flight(struct mosquitto *context, int qos) { - if(qos == 0 || (max_inflight == 0 && max_inflight_bytes == 0)){ + if(qos == 0 || (context->send_maximum == 0 && max_inflight_bytes == 0)){ return true; } bool valid_bytes = context->msg_bytes12 < max_inflight_bytes; - bool valid_count = context->msg_count12 < max_inflight; + bool valid_count = context->msg_count12 < context->send_maximum; - if(max_inflight == 0){ + if(context->send_maximum == 0){ return valid_bytes; } if(max_inflight_bytes == 0){ @@ -73,7 +72,7 @@ static bool db__ready_for_queue(struct mosquitto *context, int qos) unsigned long source_bytes = context->msg_bytes12; int source_count = context->msg_count12; unsigned long adjust_bytes = max_inflight_bytes; - int adjust_count = max_inflight; + int adjust_count = context->send_maximum; /* nothing in flight for offline clients */ if(context->sock == INVALID_SOCKET){ @@ -200,6 +199,7 @@ void db__msg_store_remove(struct mosquitto_db *db, struct mosquitto_msg_store *s db->msg_store_bytes -= store->payloadlen; mosquitto__free(store->source_id); + mosquitto__free(store->source_username); if(store->dest_ids){ for(i=0; idest_id_count; i++){ mosquitto__free(store->dest_ids[i]); @@ -207,6 +207,7 @@ void db__msg_store_remove(struct mosquitto_db *db, struct mosquitto_msg_store *s mosquitto__free(store->dest_ids); } mosquitto__free(store->topic); + mosquitto_property_free_all(&store->properties); UHPA_FREE_PAYLOAD(store); mosquitto__free(store); } @@ -288,7 +289,7 @@ void db__message_dequeue_first(struct mosquitto *context) msg->next = NULL; } -int db__message_delete(struct mosquitto_db *db, struct mosquitto *context, uint16_t mid, enum mosquitto_msg_direction dir) +int db__message_delete(struct mosquitto_db *db, struct mosquitto *context, uint16_t mid, enum mosquitto_msg_direction dir, enum mosquitto_msg_state expect_state, int qos) { struct mosquitto_client_msg *tail, *last = NULL; int msg_index = 0; @@ -299,6 +300,11 @@ int db__message_delete(struct mosquitto_db *db, struct mosquitto *context, uint1 while(tail){ msg_index++; if(tail->mid == mid && tail->direction == dir){ + if(tail->qos != qos){ + return MOSQ_ERR_PROTOCOL; + }else if(qos == 2 && tail->state != expect_state){ + return MOSQ_ERR_PROTOCOL; + } msg_index--; db__message_remove(db, context, &tail, last); }else{ @@ -306,7 +312,7 @@ int db__message_delete(struct mosquitto_db *db, struct mosquitto *context, uint1 tail = tail->next; } } - while (context->queued_msgs && (max_inflight == 0 || msg_index < max_inflight)){ + while (context->queued_msgs && (context->send_maximum == 0 || msg_index < context->send_maximum)){ msg_index++; tail = context->queued_msgs; tail->timestamp = mosquitto_time(); @@ -335,7 +341,7 @@ int db__message_delete(struct mosquitto_db *db, struct mosquitto *context, uint1 return MOSQ_ERR_SUCCESS; } -int db__message_insert(struct mosquitto_db *db, struct mosquitto *context, uint16_t mid, enum mosquitto_msg_direction dir, int qos, bool retain, struct mosquitto_msg_store *stored) +int db__message_insert(struct mosquitto_db *db, struct mosquitto *context, uint16_t mid, enum mosquitto_msg_direction dir, int qos, bool retain, struct mosquitto_msg_store *stored, mosquitto_property *properties) { struct mosquitto_client_msg *msg; struct mosquitto_client_msg **msgs, **last_msg; @@ -361,6 +367,7 @@ int db__message_insert(struct mosquitto_db *db, struct mosquitto *context, uint1 for(i=0; idest_id_count; i++){ if(!strcmp(stored->dest_ids[i], context->id)){ /* We have already sent this message to this client. */ + mosquitto_property_free_all(&properties); return MOSQ_ERR_SUCCESS; } } @@ -369,9 +376,11 @@ int db__message_insert(struct mosquitto_db *db, struct mosquitto *context, uint1 /* Client is not connected only queue messages with QoS>0. */ if(qos == 0 && !db->config->queue_qos0_messages){ if(!context->bridge){ + mosquitto_property_free_all(&properties); return 2; }else{ if(context->bridge->start_type != bst_lazy){ + mosquitto_property_free_all(&properties); return 2; } } @@ -396,6 +405,7 @@ int db__message_insert(struct mosquitto_db *db, struct mosquitto *context, uint1 if(qos == 2){ state = mosq_ms_wait_for_pubrel; }else{ + mosquitto_property_free_all(&properties); return 1; } } @@ -411,6 +421,7 @@ int db__message_insert(struct mosquitto_db *db, struct mosquitto *context, uint1 context->id); } G_MSGS_DROPPED_INC(); + mosquitto_property_free_all(&properties); return 2; } }else{ @@ -424,6 +435,7 @@ int db__message_insert(struct mosquitto_db *db, struct mosquitto *context, uint1 "Outgoing messages are being dropped for client %s.", context->id); } + mosquitto_property_free_all(&properties); return 2; } } @@ -445,8 +457,13 @@ int db__message_insert(struct mosquitto_db *db, struct mosquitto *context, uint1 msg->direction = dir; msg->state = state; msg->dup = false; - msg->qos = qos; + if(qos > context->maximum_qos){ + msg->qos = context->maximum_qos; + }else{ + msg->qos = qos; + } msg->retain = retain; + msg->properties = properties; if (state == mosq_ms_queued){ msgs = &(context->queued_msgs); @@ -509,13 +526,16 @@ int db__message_insert(struct mosquitto_db *db, struct mosquitto *context, uint1 #endif } -int db__message_update(struct mosquitto *context, uint16_t mid, enum mosquitto_msg_direction dir, enum mosquitto_msg_state state) +int db__message_update(struct mosquitto *context, uint16_t mid, enum mosquitto_msg_direction dir, enum mosquitto_msg_state state, int qos) { struct mosquitto_client_msg *tail; tail = context->inflight_msgs; while(tail){ if(tail->mid == mid && tail->direction == dir){ + if(tail->qos != qos){ + return MOSQ_ERR_PROTOCOL; + } tail->state = state; tail->timestamp = mosquitto_time(); return MOSQ_ERR_SUCCESS; @@ -558,12 +578,13 @@ int db__messages_delete(struct mosquitto_db *db, struct mosquitto *context) return MOSQ_ERR_SUCCESS; } -int db__messages_easy_queue(struct mosquitto_db *db, struct mosquitto *context, const char *topic, int qos, uint32_t payloadlen, const void *payload, int retain) +int db__messages_easy_queue(struct mosquitto_db *db, struct mosquitto *context, const char *topic, int qos, uint32_t payloadlen, const void *payload, int retain, uint32_t message_expiry_interval, mosquitto_property **properties) { struct mosquitto_msg_store *stored; char *source_id; char *topic_heap; mosquitto__payload_uhpa payload_uhpa; + mosquitto_property *local_properties = NULL; assert(db); @@ -573,6 +594,10 @@ int db__messages_easy_queue(struct mosquitto_db *db, struct mosquitto *context, topic_heap = mosquitto__strdup(topic); if(!topic_heap) return MOSQ_ERR_INVAL; + if(db->config->retain_available == false){ + retain = 0; + } + if(UHPA_ALLOC(payload_uhpa, payloadlen) == 0){ mosquitto__free(topic_heap); return MOSQ_ERR_NOMEM; @@ -584,13 +609,17 @@ int db__messages_easy_queue(struct mosquitto_db *db, struct mosquitto *context, }else{ source_id = ""; } - if(db__message_store(db, source_id, 0, topic_heap, qos, payloadlen, &payload_uhpa, retain, &stored, 0)) return 1; + if(properties){ + local_properties = *properties; + *properties = NULL; + } + if(db__message_store(db, context, 0, topic_heap, qos, payloadlen, &payload_uhpa, retain, &stored, message_expiry_interval, local_properties, 0)) return 1; return sub__messages_queue(db, source_id, topic_heap, qos, retain, &stored); } -/* This function requires topic to be allocated on the heap. Once called, it owns topic and will free it on error. Likewise payload. */ -int db__message_store(struct mosquitto_db *db, const char *source, uint16_t source_mid, char *topic, int qos, uint32_t payloadlen, mosquitto__payload_uhpa *payload, int retain, struct mosquitto_msg_store **stored, dbid_t store_id) +/* This function requires topic to be allocated on the heap. Once called, it owns topic and will free it on error. Likewise payload and properties. */ +int db__message_store(struct mosquitto_db *db, const struct mosquitto *source, uint16_t source_mid, char *topic, int qos, uint32_t payloadlen, mosquitto__payload_uhpa *payload, int retain, struct mosquitto_msg_store **stored, uint32_t message_expiry_interval, mosquitto_property *properties, dbid_t store_id) { struct mosquitto_msg_store *temp = NULL; int rc = MOSQ_ERR_SUCCESS; @@ -598,7 +627,7 @@ int db__message_store(struct mosquitto_db *db, const char *source, uint16_t sour assert(db); assert(stored); - temp = mosquitto__malloc(sizeof(struct mosquitto_msg_store)); + temp = mosquitto__calloc(1, sizeof(struct mosquitto_msg_store)); if(!temp){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); rc = MOSQ_ERR_NOMEM; @@ -609,8 +638,8 @@ int db__message_store(struct mosquitto_db *db, const char *source, uint16_t sour temp->payload.ptr = NULL; temp->ref_count = 0; - if(source){ - temp->source_id = mosquitto__strdup(source); + if(source && source->id){ + temp->source_id = mosquitto__strdup(source->id); }else{ temp->source_id = mosquitto__strdup(""); } @@ -619,6 +648,17 @@ int db__message_store(struct mosquitto_db *db, const char *source, uint16_t sour rc = MOSQ_ERR_NOMEM; goto error; } + + if(source && source->username){ + temp->source_username = mosquitto__strdup(source->username); + if(!temp->source_username){ + rc = MOSQ_ERR_NOMEM; + goto error; + } + } + if(source){ + temp->source_listener = source->listener; + } temp->source_mid = source_mid; temp->mid = 0; temp->qos = qos; @@ -626,11 +666,17 @@ int db__message_store(struct mosquitto_db *db, const char *source, uint16_t sour temp->topic = topic; topic = NULL; temp->payloadlen = payloadlen; + temp->properties = properties; if(payloadlen){ UHPA_MOVE(temp->payload, *payload, payloadlen); }else{ temp->payload.ptr = NULL; } + if(message_expiry_interval > 0){ + temp->message_expiry_time = time(NULL) + message_expiry_interval; + }else{ + temp->message_expiry_time = 0; + } temp->dest_ids = NULL; temp->dest_id_count = 0; @@ -651,9 +697,11 @@ error: mosquitto__free(topic); if(temp){ mosquitto__free(temp->source_id); + mosquitto__free(temp->source_username); mosquitto__free(temp->topic); mosquitto__free(temp); } + mosquitto_property_free_all(&properties); return rc; } @@ -780,7 +828,6 @@ int db__message_reconnect_reset(struct mosquitto_db *db, struct mosquitto *conte int db__message_release(struct mosquitto_db *db, struct mosquitto *context, uint16_t mid, enum mosquitto_msg_direction dir) { struct mosquitto_client_msg *tail, *last = NULL; - int qos; int retain; char *topic; char *source_id; @@ -793,7 +840,9 @@ int db__message_release(struct mosquitto_db *db, struct mosquitto *context, uint while(tail){ msg_index++; if(tail->mid == mid && tail->direction == dir){ - qos = tail->store->qos; + if(tail->store->qos != 2){ + return MOSQ_ERR_PROTOCOL; + } topic = tail->store->topic; retain = tail->retain; source_id = tail->store->source_id; @@ -802,7 +851,7 @@ int db__message_release(struct mosquitto_db *db, struct mosquitto *context, uint * denied/dropped and is being processed so the client doesn't * keep resending it. That means we don't send it to other * clients. */ - if(!topic || !sub__messages_queue(db, source_id, topic, qos, retain, &tail->store)){ + if(!topic || !sub__messages_queue(db, source_id, topic, 2, retain, &tail->store)){ db__message_remove(db, context, &tail, last); deleted = true; }else{ @@ -814,7 +863,7 @@ int db__message_release(struct mosquitto_db *db, struct mosquitto *context, uint } } - while(context->queued_msgs && (max_inflight == 0 || msg_index < max_inflight)){ + while(context->queued_msgs && (context->send_maximum == 0 || msg_index < context->send_maximum)){ msg_index++; tail = context->queued_msgs; tail->timestamp = mosquitto_time(); @@ -849,7 +898,7 @@ int db__message_release(struct mosquitto_db *db, struct mosquitto *context, uint int db__message_write(struct mosquitto_db *db, struct mosquitto *context) { int rc; - struct mosquitto_client_msg *tail, *last = NULL; + struct mosquitto_client_msg *tail, *last = NULL, *tmp; uint16_t mid; int retries; int retain; @@ -858,6 +907,9 @@ int db__message_write(struct mosquitto_db *db, struct mosquitto *context) uint32_t payloadlen; const void *payload; int msg_count = 0; + mosquitto_property *cmsg_props = NULL, *store_props = NULL; + time_t now; + uint32_t expiry_interval = 0; if(!context || context->sock == INVALID_SOCKET || (context->state == mosq_cs_connected && !context->id)){ @@ -868,9 +920,19 @@ int db__message_write(struct mosquitto_db *db, struct mosquitto *context) return MOSQ_ERR_SUCCESS; } + now = time(NULL); tail = context->inflight_msgs; while(tail){ msg_count++; + if(tail->store->message_expiry_time){ + if(now > tail->store->message_expiry_time){ + /* Message is expired, must not send. */ + db__message_remove(db, context, &tail, last); + continue; + }else{ + expiry_interval = tail->store->message_expiry_time - now; + } + } mid = tail->mid; retries = tail->dup; retain = tail->retain; @@ -878,11 +940,13 @@ int db__message_write(struct mosquitto_db *db, struct mosquitto *context) qos = tail->qos; payloadlen = tail->store->payloadlen; payload = UHPA_ACCESS_PAYLOAD(tail->store); + cmsg_props = tail->properties; + store_props = tail->store->properties; switch(tail->state){ case mosq_ms_publish_qos0: - rc = send__publish(context, mid, topic, payloadlen, payload, qos, retain, retries); - if(!rc){ + rc = send__publish(context, mid, topic, payloadlen, payload, qos, retain, retries, cmsg_props, store_props, expiry_interval); + if(rc == MOSQ_ERR_SUCCESS || rc == MOSQ_ERR_OVERSIZE_PACKET){ db__message_remove(db, context, &tail, last); }else{ return rc; @@ -890,29 +954,39 @@ int db__message_write(struct mosquitto_db *db, struct mosquitto *context) break; case mosq_ms_publish_qos1: - rc = send__publish(context, mid, topic, payloadlen, payload, qos, retain, retries); - if(!rc){ + rc = send__publish(context, mid, topic, payloadlen, payload, qos, retain, retries, cmsg_props, store_props, expiry_interval); + if(rc == MOSQ_ERR_SUCCESS){ tail->timestamp = mosquitto_time(); tail->dup = 1; /* Any retry attempts are a duplicate. */ tail->state = mosq_ms_wait_for_puback; + + last = tail; + tail = tail->next; + }else if(rc == MOSQ_ERR_OVERSIZE_PACKET){ + tmp = tail->next; + db__message_remove(db, context, &tail, last); + tail = tmp; }else{ return rc; } - last = tail; - tail = tail->next; break; case mosq_ms_publish_qos2: - rc = send__publish(context, mid, topic, payloadlen, payload, qos, retain, retries); - if(!rc){ + rc = send__publish(context, mid, topic, payloadlen, payload, qos, retain, retries, cmsg_props, store_props, expiry_interval); + if(rc == MOSQ_ERR_SUCCESS){ tail->timestamp = mosquitto_time(); tail->dup = 1; /* Any retry attempts are a duplicate. */ tail->state = mosq_ms_wait_for_pubrec; + + last = tail; + tail = tail->next; + }else if(rc == MOSQ_ERR_OVERSIZE_PACKET){ + tmp = tail->next; + db__message_remove(db, context, &tail, last); + tail = tmp; }else{ return rc; } - last = tail; - tail = tail->next; break; case mosq_ms_send_pubrec: @@ -955,7 +1029,7 @@ int db__message_write(struct mosquitto_db *db, struct mosquitto *context) } } - while(context->queued_msgs && (max_inflight == 0 || msg_count < max_inflight)){ + while(context->queued_msgs && (context->send_maximum == 0 || msg_count < context->send_maximum)){ msg_count++; tail = context->queued_msgs; if(tail->direction == mosq_md_out){ @@ -988,9 +1062,8 @@ int db__message_write(struct mosquitto_db *db, struct mosquitto *context) return MOSQ_ERR_SUCCESS; } -void db__limits_set(int inflight, unsigned long inflight_bytes, int queued, unsigned long queued_bytes) +void db__limits_set(unsigned long inflight_bytes, int queued, unsigned long queued_bytes) { - max_inflight = inflight; max_inflight_bytes = inflight_bytes; max_queued = queued; max_queued_bytes = queued_bytes; diff --git a/src/db_dump/Makefile b/src/db_dump/Makefile index 13ae261b..202af870 100644 --- a/src/db_dump/Makefile +++ b/src/db_dump/Makefile @@ -1,6 +1,6 @@ include ../../config.mk -CFLAGS_FINAL=${CFLAGS} -I.. -I../../lib -I../.. +CFLAGS_FINAL=${CFLAGS} -I.. -I../../lib -I../.. -I../deps .PHONY: all clean reallyclean diff --git a/src/db_dump/db_dump.c b/src/db_dump/db_dump.c index e009cce5..62bf24be 100644 --- a/src/db_dump/db_dump.c +++ b/src/db_dump/db_dump.c @@ -83,7 +83,9 @@ struct db_msg uint8_t qos, retain; uint8_t *payload; char *source_id; + char *source_username; char *topic; + uint16_t source_port; }; static uint32_t db_version; @@ -177,6 +179,8 @@ print_db_msg(struct db_msg *msg, int length) printf("\tLength: %d\n", length); printf("\tStore ID: %" PRIu64 "\n", msg->store_id); printf("\tSource ID: %s\n", msg->source_id); + printf("\tSource Username: %s\n", msg->source_username); + printf("\tSource Port: %d\n", msg->source_port); printf("\tSource MID: %d\n", msg->source_mid); printf("\tMID: %d\n", msg->mid); printf("\tTopic: %s\n", msg->topic); @@ -194,26 +198,49 @@ print_db_msg(struct db_msg *msg, int length) } +static int persist__read_string(FILE *db_fptr, char **str) +{ + uint16_t i16temp; + uint16_t slen; + char *s = NULL; + + if(fread(&i16temp, 1, sizeof(uint16_t), db_fptr) != sizeof(uint16_t)){ + return MOSQ_ERR_INVAL; + } + + slen = ntohs(i16temp); + if(slen){ + s = mosquitto__malloc(slen+1); + if(!s){ + fclose(db_fptr); + fprintf(stderr, "Error: Out of memory.\n"); + return MOSQ_ERR_NOMEM; + } + if(fread(s, 1, slen, db_fptr) != slen){ + mosquitto__free(s); + fprintf(stderr, "Error: %s.\n", strerror(errno)); + return MOSQ_ERR_INVAL; + } + s[slen] = '\0'; + } + + *str = s; + return MOSQ_ERR_SUCCESS; +} + + static int db__client_chunk_restore(struct mosquitto_db *db, FILE *db_fd, struct db_client *client) { - uint16_t i16temp, slen; + uint16_t i16temp; int rc = 0; struct client_chunk *cc; - read_e(db_fd, &i16temp, sizeof(uint16_t)); - slen = ntohs(i16temp); - if(!slen){ + rc = persist__read_string(db_fd, &client->client_id); + if(rc){ fprintf(stderr, "Error: Corrupt persistent database."); fclose(db_fd); - return 1; + return rc; } - client->client_id = calloc(slen+1, sizeof(char)); - if(!client->client_id){ - fclose(db_fd); - fprintf(stderr, "Error: Out of memory."); - return 1; - } - read_e(db_fd, client->client_id, slen); read_e(db_fd, &i16temp, sizeof(uint16_t)); client->last_mid = ntohs(i16temp); @@ -245,24 +272,17 @@ error: static int db__client_msg_chunk_restore(struct mosquitto_db *db, FILE *db_fd, uint32_t length, struct db_client_msg *msg) { dbid_t i64temp; - uint16_t i16temp, slen; + uint16_t i16temp; struct client_chunk *cc; struct msg_store_chunk *msc; + int rc; - read_e(db_fd, &i16temp, sizeof(uint16_t)); - slen = ntohs(i16temp); - if(!slen){ + rc = persist__read_string(db_fd, &msg->client_id); + if(rc){ fprintf(stderr, "Error: Corrupt persistent database."); fclose(db_fd); - return 1; + return rc; } - msg->client_id = calloc(slen+1, sizeof(char)); - if(!msg->client_id){ - fclose(db_fd); - fprintf(stderr, "Error: Out of memory."); - return 1; - } - read_e(db_fd, msg->client_id, slen); read_e(db_fd, &i64temp, sizeof(dbid_t)); msg->store_id = i64temp; @@ -301,58 +321,48 @@ static int db__msg_store_chunk_restore(struct mosquitto_db *db, FILE *db_fd, uin { dbid_t i64temp; uint32_t i32temp; - uint16_t i16temp, slen; + uint16_t i16temp; int rc = 0; struct msg_store_chunk *mcs; read_e(db_fd, &i64temp, sizeof(dbid_t)); msg->store_id = i64temp; - read_e(db_fd, &i16temp, sizeof(uint16_t)); - slen = ntohs(i16temp); - if(slen){ - msg->source_id = calloc(slen+1, sizeof(char)); - if(!msg->source_id){ - fclose(db_fd); - fprintf(stderr, "Error: Out of memory."); - return 1; - } - if(fread(msg->source_id, 1, slen, db_fd) != slen){ + rc = persist__read_string(db_fd, &msg->source_id); + if(rc){ + fprintf(stderr, "Error: Corrupt persistent database."); + fclose(db_fd); + return rc; + } + if(db_version == 4){ + rc = persist__read_string(db_fd, &msg->source_username); + if(rc){ fprintf(stderr, "Error: %s.", strerror(errno)); fclose(db_fd); free(msg->source_id); + free(msg->topic); + free(msg->payload); + free(msg->source_id); return 1; } + read_e(db_fd, &i16temp, sizeof(uint16_t)); + msg->source_port = ntohs(i16temp); } + + read_e(db_fd, &i16temp, sizeof(uint16_t)); msg->source_mid = ntohs(i16temp); read_e(db_fd, &i16temp, sizeof(uint16_t)); msg->mid = ntohs(i16temp); - read_e(db_fd, &i16temp, sizeof(uint16_t)); - slen = ntohs(i16temp); - if(slen){ - msg->topic = calloc(slen+1, sizeof(char)); - if(!msg->topic){ - fclose(db_fd); - free(msg->source_id); - fprintf(stderr, "Error: Out of memory."); - return 1; - } - if(fread(msg->topic, 1, slen, db_fd) != slen){ - fprintf(stderr, "Error: %s.", strerror(errno)); - fclose(db_fd); - free(msg->source_id); - free(msg->topic); - return 1; - } - }else{ - fprintf(stderr, "Error: Invalid msg_store chunk when restoring persistent database."); + rc = persist__read_string(db_fd, &msg->topic); + if(rc){ + fprintf(stderr, "Error: Corrupt persistent database."); fclose(db_fd); - free(msg->source_id); - return 1; + return rc; } + read_e(db_fd, &msg->qos, sizeof(uint8_t)); read_e(db_fd, &msg->retain, sizeof(uint8_t)); @@ -415,29 +425,23 @@ static int db__retain_chunk_restore(struct mosquitto_db *db, FILE *db_fd) static int db__sub_chunk_restore(struct mosquitto_db *db, FILE *db_fd, uint32_t length, struct db_sub *sub) { - uint16_t i16temp, slen; int rc = 0; struct client_chunk *cc; - read_e(db_fd, &i16temp, sizeof(uint16_t)); - slen = ntohs(i16temp); - sub->client_id = calloc(slen+1, sizeof(char)); - if(!sub->client_id){ + rc = persist__read_string(db_fd, &sub->client_id); + if(rc){ + fprintf(stderr, "Error: Corrupt persistent database."); fclose(db_fd); - fprintf(stderr, "Error: Out of memory."); - return 1; + return rc; } - read_e(db_fd, sub->client_id, slen); - read_e(db_fd, &i16temp, sizeof(uint16_t)); - slen = ntohs(i16temp); - sub->topic = calloc(slen+1, sizeof(char)); - if(!sub->topic){ + + rc = persist__read_string(db_fd, &sub->topic); + if(rc){ + fprintf(stderr, "Error: Corrupt persistent database."); fclose(db_fd); - fprintf(stderr, "Error: Out of memory."); - free(sub->client_id); - return 1; + return rc; } - read_e(db_fd, sub->topic, slen); + read_e(db_fd, &sub->qos, sizeof(uint8_t)); if(client_stats){ diff --git a/src/handle_auth.c b/src/handle_auth.c new file mode 100644 index 00000000..fd659fb7 --- /dev/null +++ b/src/handle_auth.c @@ -0,0 +1,53 @@ +/* +Copyright (c) 2018 Roger Light + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#include +#include + +#include "mosquitto_broker_internal.h" +#include "mqtt_protocol.h" +#include "packet_mosq.h" +#include "property_mosq.h" +#include "send_mosq.h" + + +int handle__auth(struct mosquitto_db *db, struct mosquitto *context) +{ + int rc = 0; + uint8_t reason_code = 0; + mosquitto_property *properties = NULL; + + if(!context) return MOSQ_ERR_INVAL; + log__printf(NULL, MOSQ_LOG_DEBUG, "Received AUTH from %s", context->id); + + if(context->protocol != mosq_p_mqtt5){ + return MOSQ_ERR_PROTOCOL; + } + + if(context->in_packet.remaining_length > 0){ + if(packet__read_byte(&context->in_packet, &reason_code)) return 1; + + rc = property__read_all(CMD_AUTH, &context->in_packet, &properties); + if(rc) return rc; + mosquitto_property_free_all(&properties); /* FIXME - TEMPORARY UNTIL PROPERTIES PROCESSED */ + } + + /* FIXME - Extended auth not currently supported */ + send__disconnect(context, MQTT_RC_NOT_AUTHORIZED, NULL); + return 1; +} diff --git a/src/handle_connack.c b/src/handle_connack.c index 3257ea44..b853a9b8 100644 --- a/src/handle_connack.c +++ b/src/handle_connack.c @@ -21,27 +21,37 @@ Contributors: #include "mosquitto_broker_internal.h" #include "memory_mosq.h" -#include "mqtt3_protocol.h" +#include "mqtt_protocol.h" #include "packet_mosq.h" #include "send_mosq.h" #include "util_mosq.h" int handle__connack(struct mosquitto_db *db, struct mosquitto *context) { - uint8_t byte; - uint8_t rc; + int rc; + uint8_t connect_acknowledge; + uint8_t reason_code; int i; char *notification_topic; int notification_topic_len; char notification_payload; + mosquitto_property *properties = NULL; if(!context){ return MOSQ_ERR_INVAL; } log__printf(NULL, MOSQ_LOG_DEBUG, "Received CONNACK on connection %s.", context->id); - if(packet__read_byte(&context->in_packet, &byte)) return 1; // Reserved byte, not used - if(packet__read_byte(&context->in_packet, &rc)) return 1; - switch(rc){ + if(packet__read_byte(&context->in_packet, &connect_acknowledge)) return 1; + if(packet__read_byte(&context->in_packet, &reason_code)) return 1; + + if(context->protocol == mosq_p_mqtt5){ + rc = property__read_all(CMD_CONNACK, &context->in_packet, &properties); + if(rc) return rc; + mosquitto_property_free_all(&properties); + } + mosquitto_property_free_all(&properties); /* FIXME - TEMPORARY UNTIL PROPERTIES PROCESSED */ + + switch(reason_code){ case CONNACK_ACCEPTED: if(context->bridge){ if(context->bridge->notifications){ @@ -49,12 +59,12 @@ int handle__connack(struct mosquitto_db *db, struct mosquitto *context) if(context->bridge->notification_topic){ if(!context->bridge->notifications_local_only){ if(send__real_publish(context, mosquitto__mid_generate(context), - context->bridge->notification_topic, 1, ¬ification_payload, 1, true, 0)){ + context->bridge->notification_topic, 1, ¬ification_payload, 1, true, 0, NULL, NULL, 0)){ return 1; } } - db__messages_easy_queue(db, context, context->bridge->notification_topic, 1, 1, ¬ification_payload, 1); + db__messages_easy_queue(db, context, context->bridge->notification_topic, 1, 1, ¬ification_payload, 1, 0, NULL); }else{ notification_topic_len = strlen(context->bridge->remote_clientid)+strlen("$SYS/broker/connection//state"); notification_topic = mosquitto__malloc(sizeof(char)*(notification_topic_len+1)); @@ -64,24 +74,24 @@ int handle__connack(struct mosquitto_db *db, struct mosquitto *context) notification_payload = '1'; if(!context->bridge->notifications_local_only){ if(send__real_publish(context, mosquitto__mid_generate(context), - notification_topic, 1, ¬ification_payload, 1, true, 0)){ + notification_topic, 1, ¬ification_payload, 1, true, 0, NULL, NULL, 0)){ mosquitto__free(notification_topic); return 1; } } - db__messages_easy_queue(db, context, notification_topic, 1, 1, ¬ification_payload, 1); + db__messages_easy_queue(db, context, notification_topic, 1, 1, ¬ification_payload, 1, 0, NULL); mosquitto__free(notification_topic); } } for(i=0; ibridge->topic_count; i++){ if(context->bridge->topics[i].direction == bd_in || context->bridge->topics[i].direction == bd_both){ - if(send__subscribe(context, NULL, 1, &context->bridge->topics[i].remote_topic, context->bridge->topics[i].qos)){ + if(send__subscribe(context, NULL, 1, &context->bridge->topics[i].remote_topic, context->bridge->topics[i].qos, NULL)){ return 1; } }else{ if(context->bridge->attempt_unsubscribe){ - if(send__unsubscribe(context, NULL, context->bridge->topics[i].remote_topic)){ + if(send__unsubscribe(context, NULL, 1, &context->bridge->topics[i].remote_topic, NULL)){ /* direction = inwards only. This means we should not be subscribed * to the topic. It is possible that we used to be subscribed to * this topic so unsubscribe. */ @@ -90,6 +100,21 @@ int handle__connack(struct mosquitto_db *db, struct mosquitto *context) } } } + for(i=0; ibridge->topic_count; i++){ + if(context->bridge->topics[i].direction == bd_out || context->bridge->topics[i].direction == bd_both){ + log__printf(NULL, MOSQ_LOG_DEBUG, "Bridge %s doing local SUBSCRIBE on topic %s", context->id, context->bridge->topics[i].local_topic); + if(sub__add(db, + context, + context->bridge->topics[i].local_topic, + context->bridge->topics[i].qos, + 0, + MQTT_SUB_OPT_NO_LOCAL | MQTT_SUB_OPT_RETAIN_AS_PUBLISHED, + &db->subs)) return 1; + sub__retain_queue(db, context, + context->bridge->topics[i].local_topic, + context->bridge->topics[i].qos, 0); + } + } } context->state = mosq_cs_connected; return MOSQ_ERR_SUCCESS; diff --git a/src/handle_connect.c b/src/handle_connect.c index fd60addf..09e29e37 100644 --- a/src/handle_connect.c +++ b/src/handle_connect.c @@ -20,37 +20,85 @@ Contributors: #include #include "mosquitto_broker_internal.h" -#include "mqtt3_protocol.h" +#include "mqtt_protocol.h" #include "memory_mosq.h" #include "packet_mosq.h" +#include "property_mosq.h" #include "send_mosq.h" #include "sys_tree.h" #include "time_mosq.h" #include "tls_mosq.h" #include "util_mosq.h" -#ifdef WITH_UUID -# include +#ifdef WITH_TLS +# include +#endif + +#ifdef __linux__ +# include #endif #ifdef WITH_WEBSOCKETS # include #endif + +static int random_16_bytes(uint8_t *bytes) +{ + int rc = MOSQ_ERR_UNKNOWN; + +#ifdef WITH_TLS + if(RAND_bytes(bytes, 16) == 1){ + rc = MOSQ_ERR_SUCCESS; + } +#else +# ifdef __GLIBC__ + if(getrandom(bytes, 16, 0) == 0){ + rc = MOSQ_ERR_SUCCESS; + } +# elif defined(WIN32) + HRYPTPROV provider; + + if(!CryptAcquireContext(&provider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)){ + return MOSQ_ERR_UNKNOWN; + } + + if(CryptGenRandom(provider, 16, bytes)){ + rc = MOSQ_ERR_SUCCESS; + } + + CryptReleaseContext(provider, 0); +# else + int i; + + for(i=0; i<16; i++){ + bytes[i] = (uint8_t )(random()&0xFF); + } + rc = MOSQ_ERR_SUCCESS; +# endif +#endif + return rc; +} + +static char nibble_to_hex(uint8_t value) +{ + if(value < 0x0A){ + return '0'+value; + }else{ + return 'A'+value-0x0A; + } +} + static char *client_id_gen(struct mosquitto_db *db, int *idlen, const char *auto_id_prefix, int auto_id_prefix_len) { char *client_id; -#ifdef WITH_UUID - uuid_t uuid; -#else + uint8_t rnd[16]; int i; -#endif + int pos; + + if(random_16_bytes(rnd)) return NULL; -#ifdef WITH_UUID *idlen = 36 + auto_id_prefix_len; -#else - *idlen = 64 + auto_id_prefix_len; -#endif client_id = (char *)mosquitto__calloc((*idlen) + 1, sizeof(char)); if(!client_id){ @@ -60,16 +108,17 @@ static char *client_id_gen(struct mosquitto_db *db, int *idlen, const char *auto memcpy(client_id, auto_id_prefix, auto_id_prefix_len); } - -#ifdef WITH_UUID - uuid_generate_random(uuid); - uuid_unparse_lower(uuid, &client_id[auto_id_prefix_len]); -#else - for(i=0; i<64; i++){ - client_id[i+auto_id_prefix_len] = (rand()%73)+48; + pos = 0; + for(i=0; i<16; i++){ + client_id[auto_id_prefix_len + pos + 0] = nibble_to_hex(rnd[i] & 0x0F); + client_id[auto_id_prefix_len + pos + 1] = nibble_to_hex((rnd[i] >> 4) & 0x0F); + pos += 2; + if(pos == 8 || pos == 13 || pos == 18 || pos == 23){ + client_id[auto_id_prefix_len + pos] = '-'; + pos++; + } } - client_id[i] = '\0'; -#endif + return client_id; } @@ -118,18 +167,19 @@ int handle__connect(struct mosquitto_db *db, struct mosquitto *context) char *will_payload = NULL, *will_topic = NULL; char *will_topic_mount; uint16_t will_payloadlen; - struct mosquitto_message *will_struct = NULL; - uint8_t will, will_retain, will_qos, clean_session; + uint32_t will_expiry_interval = 0; + struct mosquitto_message_all *will_struct = NULL; + uint8_t will, will_retain, will_qos, clean_start; uint8_t username_flag, password_flag; char *username = NULL, *password = NULL; int rc; - struct mosquitto__acl_user *acl_tail; struct mosquitto *found_context; int slen; uint16_t slen16; struct mosquitto__subleaf *leaf; int i; - struct mosquitto__security_options *security_opts; + mosquitto_property *properties = NULL; + mosquitto_property *connack_props = NULL; #ifdef WITH_TLS X509 *client_cert = NULL; X509_NAME *name; @@ -145,6 +195,7 @@ int handle__connect(struct mosquitto_db *db, struct mosquitto *context) /* Don't accept multiple CONNECT commands. */ if(context->state != mosq_cs_new){ + log__printf(NULL, MOSQ_LOG_NOTICE, "Bad client %s sending multiple CONNECT messages.", context->id); rc = MOSQ_ERR_PROTOCOL; goto handle_connect_error; } @@ -177,18 +228,22 @@ int handle__connect(struct mosquitto_db *db, struct mosquitto *context) log__printf(NULL, MOSQ_LOG_INFO, "Invalid protocol version %d in CONNECT from %s.", protocol_version, context->address); } - send__connack(context, 0, CONNACK_REFUSED_PROTOCOL_VERSION); + send__connack(db, context, 0, CONNACK_REFUSED_PROTOCOL_VERSION, NULL); rc = MOSQ_ERR_PROTOCOL; goto handle_connect_error; } context->protocol = mosq_p_mqtt31; - }else if(!strcmp(protocol_name, PROTOCOL_NAME_v311)){ - if((protocol_version&0x7F) != PROTOCOL_VERSION_v311){ + }else if(!strcmp(protocol_name, PROTOCOL_NAME)){ + if((protocol_version&0x7F) == PROTOCOL_VERSION_v311){ + context->protocol = mosq_p_mqtt311; + }else if((protocol_version&0x7F) == PROTOCOL_VERSION_v5){ + context->protocol = mosq_p_mqtt5; + }else{ if(db->config->connection_messages == true){ log__printf(NULL, MOSQ_LOG_INFO, "Invalid protocol version %d in CONNECT from %s.", protocol_version, context->address); } - send__connack(context, 0, CONNACK_REFUSED_PROTOCOL_VERSION); + send__connack(db, context, 0, CONNACK_REFUSED_PROTOCOL_VERSION, NULL); rc = MOSQ_ERR_PROTOCOL; goto handle_connect_error; } @@ -197,7 +252,6 @@ int handle__connect(struct mosquitto_db *db, struct mosquitto *context) rc = MOSQ_ERR_PROTOCOL; goto handle_connect_error; } - context->protocol = mosq_p_mqtt311; }else{ if(db->config->connection_messages == true){ log__printf(NULL, MOSQ_LOG_INFO, "Invalid protocol \"%s\" in CONNECT from %s.", @@ -207,18 +261,26 @@ int handle__connect(struct mosquitto_db *db, struct mosquitto *context) goto handle_connect_error; } + context->maximum_qos = context->listener->maximum_qos; + if(protocol_version == PROTOCOL_VERSION_v5 && context->maximum_qos != 2){ + if(mosquitto_property_add_byte(&connack_props, MQTT_PROP_MAXIMUM_QOS, context->maximum_qos)){ + rc = MOSQ_ERR_NOMEM; + goto handle_connect_error; + } + } + if(packet__read_byte(&context->in_packet, &connect_flags)){ rc = 1; goto handle_connect_error; } - if(context->protocol == mosq_p_mqtt311){ + if(context->protocol == mosq_p_mqtt311 || context->protocol == mosq_p_mqtt5){ if((connect_flags & 0x01) != 0x00){ rc = MOSQ_ERR_PROTOCOL; goto handle_connect_error; } } - clean_session = (connect_flags & 0x02) >> 1; + clean_start = (connect_flags & 0x02) >> 1; will = connect_flags & 0x04; will_qos = (connect_flags & 0x18) >> 3; if(will_qos == 3){ @@ -231,10 +293,33 @@ int handle__connect(struct mosquitto_db *db, struct mosquitto *context) password_flag = connect_flags & 0x40; username_flag = connect_flags & 0x80; + if(will && will_retain && db->config->retain_available == false){ + if(protocol_version == mosq_p_mqtt5){ + send__connack(db, context, 0, MQTT_RC_RETAIN_NOT_SUPPORTED, NULL); + } + rc = 1; + goto handle_connect_error; + } + if(packet__read_uint16(&context->in_packet, &(context->keepalive))){ rc = 1; goto handle_connect_error; } + if(protocol_version == PROTOCOL_VERSION_v5 && context->keepalive > db->config->max_keepalive){ + context->keepalive = db->config->max_keepalive; + if(mosquitto_property_add_int16(&connack_props, MQTT_PROP_SERVER_KEEP_ALIVE, context->keepalive)){ + rc = MOSQ_ERR_NOMEM; + goto handle_connect_error; + } + } + + if(protocol_version == PROTOCOL_VERSION_v5){ + rc = property__read_all(CMD_CONNECT, &context->in_packet, &properties); + if(rc) goto handle_connect_error; + } + property__process_connect(context, properties); + + mosquitto_property_free_all(&properties); /* FIXME - TEMPORARY UNTIL PROPERTIES PROCESSED */ if(packet__read_string(&context->in_packet, &client_id, &slen)){ rc = 1; @@ -243,10 +328,10 @@ int handle__connect(struct mosquitto_db *db, struct mosquitto *context) if(slen == 0){ if(context->protocol == mosq_p_mqtt31){ - send__connack(context, 0, CONNACK_REFUSED_IDENTIFIER_REJECTED); + send__connack(db, context, 0, CONNACK_REFUSED_IDENTIFIER_REJECTED, NULL); rc = MOSQ_ERR_PROTOCOL; goto handle_connect_error; - }else{ /* mqtt311 */ + }else{ /* mqtt311/mqtt5 */ mosquitto__free(client_id); client_id = NULL; @@ -256,8 +341,8 @@ int handle__connect(struct mosquitto_db *db, struct mosquitto *context) }else{ allow_zero_length_clientid = db->config->security_options.allow_zero_length_clientid; } - if(clean_session == 0 || allow_zero_length_clientid == false){ - send__connack(context, 0, CONNACK_REFUSED_IDENTIFIER_REJECTED); + if(clean_start == 0 || allow_zero_length_clientid == false){ + send__connack(db, context, 0, CONNACK_REFUSED_IDENTIFIER_REJECTED, NULL); rc = MOSQ_ERR_PROTOCOL; goto handle_connect_error; }else{ @@ -270,6 +355,12 @@ int handle__connect(struct mosquitto_db *db, struct mosquitto *context) rc = MOSQ_ERR_NOMEM; goto handle_connect_error; } + if(context->protocol == mosq_p_mqtt5){ + if(mosquitto_property_add_string(&connack_props, MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER, client_id)){ + rc = MOSQ_ERR_NOMEM; + goto handle_connect_error; + } + } } } } @@ -277,23 +368,24 @@ int handle__connect(struct mosquitto_db *db, struct mosquitto *context) /* clientid_prefixes check */ if(db->config->clientid_prefixes){ if(strncmp(db->config->clientid_prefixes, client_id, strlen(db->config->clientid_prefixes))){ - send__connack(context, 0, CONNACK_REFUSED_NOT_AUTHORIZED); + send__connack(db, context, 0, CONNACK_REFUSED_NOT_AUTHORIZED, NULL); rc = 1; goto handle_connect_error; } } - if(mosquitto_validate_utf8(client_id, slen) != MOSQ_ERR_SUCCESS){ - rc = 1; - goto handle_connect_error; - } - if(will){ - will_struct = mosquitto__calloc(1, sizeof(struct mosquitto_message)); + will_struct = mosquitto__calloc(1, sizeof(struct mosquitto_message_all)); if(!will_struct){ rc = MOSQ_ERR_NOMEM; goto handle_connect_error; } + if(protocol_version == PROTOCOL_VERSION_v5){ + rc = property__read_all(CMD_WILL, &context->in_packet, &will_struct->properties); + if(rc) return rc; + mosquitto_property_read_int32(properties, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, &will_expiry_interval, false); + mosquitto_property_free_all(&properties); /* FIXME - TEMPORARY UNTIL PROPERTIES PROCESSED */ + } if(packet__read_string(&context->in_packet, &will_topic, &slen)){ rc = 1; goto handle_connect_error; @@ -302,14 +394,6 @@ int handle__connect(struct mosquitto_db *db, struct mosquitto *context) rc = 1; goto handle_connect_error; } - if(mosquitto_validate_utf8(will_topic, slen)){ - log__printf(NULL, MOSQ_LOG_INFO, - "Malformed UTF-8 in will topic string from %s, disconnecting.", - client_id); - - rc = 1; - goto handle_connect_error; - } if(context->listener->mount_point){ slen = strlen(context->listener->mount_point) + strlen(will_topic) + 1; @@ -348,7 +432,7 @@ int handle__connect(struct mosquitto_db *db, struct mosquitto *context) } } }else{ - if(context->protocol == mosq_p_mqtt311){ + if(context->protocol == mosq_p_mqtt311 || context->protocol == mosq_p_mqtt5){ if(will_qos != 0 || will_retain != 0){ rc = MOSQ_ERR_PROTOCOL; goto handle_connect_error; @@ -359,13 +443,8 @@ int handle__connect(struct mosquitto_db *db, struct mosquitto *context) if(username_flag){ rc = packet__read_string(&context->in_packet, &username, &slen); if(rc == MOSQ_ERR_SUCCESS){ - if(mosquitto_validate_utf8(username, slen) != MOSQ_ERR_SUCCESS){ - rc = MOSQ_ERR_PROTOCOL; - goto handle_connect_error; - } - if(password_flag){ - rc = packet__read_string(&context->in_packet, &password, &slen); + rc = packet__read_binary(&context->in_packet, (uint8_t **)&password, &slen); if(rc == MOSQ_ERR_NOMEM){ rc = MOSQ_ERR_NOMEM; goto handle_connect_error; @@ -373,7 +452,7 @@ int handle__connect(struct mosquitto_db *db, struct mosquitto *context) if(context->protocol == mosq_p_mqtt31){ /* Password flag given, but no password. Ignore. */ password_flag = 0; - }else if(context->protocol == mosq_p_mqtt311){ + }else{ rc = MOSQ_ERR_PROTOCOL; goto handle_connect_error; } @@ -386,13 +465,13 @@ int handle__connect(struct mosquitto_db *db, struct mosquitto *context) if(context->protocol == mosq_p_mqtt31){ /* Username flag given, but no username. Ignore. */ username_flag = 0; - }else if(context->protocol == mosq_p_mqtt311){ + }else{ rc = MOSQ_ERR_PROTOCOL; goto handle_connect_error; } } }else{ - if(context->protocol == mosq_p_mqtt311){ + if(context->protocol == mosq_p_mqtt311 || context->protocol == mosq_p_mqtt5){ if(password_flag){ /* username_flag == 0 && password_flag == 1 is forbidden */ log__printf(NULL, MOSQ_LOG_ERR, "Protocol error from %s: password without username, closing connection.", client_id); @@ -416,36 +495,36 @@ int handle__connect(struct mosquitto_db *db, struct mosquitto *context) password = NULL; if(!context->ssl){ - send__connack(context, 0, CONNACK_REFUSED_BAD_USERNAME_PASSWORD); + send__connack(db, context, 0, CONNACK_REFUSED_BAD_USERNAME_PASSWORD, NULL); rc = 1; goto handle_connect_error; } -#ifdef WITH_TLS_PSK +#ifdef FINAL_WITH_TLS_PSK if(context->listener->psk_hint){ /* Client should have provided an identity to get this far. */ if(!context->username){ - send__connack(context, 0, CONNACK_REFUSED_BAD_USERNAME_PASSWORD); + send__connack(db, context, 0, CONNACK_REFUSED_BAD_USERNAME_PASSWORD, NULL); rc = 1; goto handle_connect_error; } }else{ -#endif /* WITH_TLS_PSK */ +#endif /* FINAL_WITH_TLS_PSK */ client_cert = SSL_get_peer_certificate(context->ssl); if(!client_cert){ - send__connack(context, 0, CONNACK_REFUSED_BAD_USERNAME_PASSWORD); + send__connack(db, context, 0, CONNACK_REFUSED_BAD_USERNAME_PASSWORD, NULL); rc = 1; goto handle_connect_error; } name = X509_get_subject_name(client_cert); if(!name){ - send__connack(context, 0, CONNACK_REFUSED_BAD_USERNAME_PASSWORD); + send__connack(db, context, 0, CONNACK_REFUSED_BAD_USERNAME_PASSWORD, NULL); rc = 1; goto handle_connect_error; } if (context->listener->use_identity_as_username) { //use_identity_as_username i = X509_NAME_get_index_by_NID(name, NID_commonName, -1); if(i == -1){ - send__connack(context, 0, CONNACK_REFUSED_BAD_USERNAME_PASSWORD); + send__connack(db, context, 0, CONNACK_REFUSED_BAD_USERNAME_PASSWORD, NULL); rc = 1; goto handle_connect_error; } @@ -453,19 +532,23 @@ int handle__connect(struct mosquitto_db *db, struct mosquitto *context) if(name_entry){ name_asn1 = X509_NAME_ENTRY_get_data(name_entry); if (name_asn1 == NULL) { - send__connack(context, 0, CONNACK_REFUSED_BAD_USERNAME_PASSWORD); + send__connack(db, context, 0, CONNACK_REFUSED_BAD_USERNAME_PASSWORD, NULL); rc = 1; goto handle_connect_error; } +#if OPENSSL_VERSION_NUMBER < 0x10100000L context->username = mosquitto__strdup((char *) ASN1_STRING_data(name_asn1)); +#else + context->username = mosquitto__strdup((char *) ASN1_STRING_get0_data(name_asn1)); +#endif if(!context->username){ - send__connack(context, 0, CONNACK_REFUSED_SERVER_UNAVAILABLE); + send__connack(db, context, 0, CONNACK_REFUSED_SERVER_UNAVAILABLE, NULL); rc = MOSQ_ERR_NOMEM; goto handle_connect_error; } /* Make sure there isn't an embedded NUL character in the CN */ if ((size_t)ASN1_STRING_length(name_asn1) != strlen(context->username)) { - send__connack(context, 0, CONNACK_REFUSED_BAD_USERNAME_PASSWORD); + send__connack(db, context, 0, CONNACK_REFUSED_BAD_USERNAME_PASSWORD, NULL); rc = 1; goto handle_connect_error; } @@ -492,9 +575,9 @@ int handle__connect(struct mosquitto_db *db, struct mosquitto *context) } X509_free(client_cert); client_cert = NULL; -#ifdef WITH_TLS_PSK +#ifdef FINAL_WITH_TLS_PSK } -#endif /* WITH_TLS_PSK */ +#endif /* FINAL_WITH_TLS_PSK */ }else{ #endif /* WITH_TLS */ if(username_flag){ @@ -509,7 +592,7 @@ int handle__connect(struct mosquitto_db *db, struct mosquitto *context) case MOSQ_ERR_SUCCESS: break; case MOSQ_ERR_AUTH: - send__connack(context, 0, CONNACK_REFUSED_NOT_AUTHORIZED); + send__connack(db, context, 0, CONNACK_REFUSED_NOT_AUTHORIZED, NULL); context__disconnect(db, context); rc = 1; goto handle_connect_error; @@ -530,7 +613,7 @@ int handle__connect(struct mosquitto_db *db, struct mosquitto *context) if((db->config->per_listener_settings && context->listener->security_options.allow_anonymous == false) || (!db->config->per_listener_settings && db->config->security_options.allow_anonymous == false)){ - send__connack(context, 0, CONNACK_REFUSED_NOT_AUTHORIZED); + send__connack(db, context, 0, CONNACK_REFUSED_NOT_AUTHORIZED, NULL); rc = 1; goto handle_connect_error; } @@ -548,7 +631,7 @@ int handle__connect(struct mosquitto_db *db, struct mosquitto *context) goto handle_connect_error; } }else{ - send__connack(context, 0, CONNACK_REFUSED_NOT_AUTHORIZED); + send__connack(db, context, 0, CONNACK_REFUSED_NOT_AUTHORIZED, NULL); rc = 1; goto handle_connect_error; } @@ -569,15 +652,15 @@ int handle__connect(struct mosquitto_db *db, struct mosquitto *context) } } - if(context->protocol == mosq_p_mqtt311){ - if(clean_session == 0){ + if(context->protocol == mosq_p_mqtt311 || context->protocol == mosq_p_mqtt5){ + if(clean_start == 0){ connect_ack |= 0x01; } } - context->clean_session = clean_session; + context->clean_start = clean_start; - if(context->clean_session == false && found_context->clean_session == false){ + if(context->clean_start == false && found_context->clean_start == false){ if(found_context->inflight_msgs || found_context->queued_msgs){ context->inflight_msgs = found_context->inflight_msgs; context->queued_msgs = found_context->queued_msgs; @@ -604,74 +687,51 @@ int handle__connect(struct mosquitto_db *db, struct mosquitto *context) } } - found_context->clean_session = true; + found_context->clean_start = true; found_context->state = mosq_cs_duplicate; do_disconnect(db, found_context); } - /* Associate user with its ACL, assuming we have ACLs loaded. */ - if(db->config->per_listener_settings){ - if(!context->listener){ - return 1; - } - security_opts = &context->listener->security_options; - }else{ - security_opts = &db->config->security_options; - } - - if(security_opts->acl_list){ - acl_tail = security_opts->acl_list; - while(acl_tail){ - if(context->username){ - if(acl_tail->username && !strcmp(context->username, acl_tail->username)){ - context->acl_list = acl_tail; - break; - } - }else{ - if(acl_tail->username == NULL){ - context->acl_list = acl_tail; - break; - } - } - acl_tail = acl_tail->next; - } - }else{ - context->acl_list = NULL; - } - + rc = acl__find_acls(db, context); + if(rc) return rc; if(will_struct){ context->will = will_struct; - context->will->topic = will_topic; + context->will->msg.topic = will_topic; if(will_payload){ - context->will->payload = will_payload; - context->will->payloadlen = will_payloadlen; + context->will->msg.payload = will_payload; + context->will->msg.payloadlen = will_payloadlen; }else{ - context->will->payload = NULL; - context->will->payloadlen = 0; + context->will->msg.payload = NULL; + context->will->msg.payloadlen = 0; } - context->will->qos = will_qos; - context->will->retain = will_retain; + context->will->msg.qos = will_qos; + context->will->msg.retain = will_retain; + context->will->expiry_interval = will_expiry_interval; } if(db->config->connection_messages == true){ if(context->is_bridge){ if(context->username){ - log__printf(NULL, MOSQ_LOG_NOTICE, "New bridge connected from %s as %s (c%d, k%d, u'%s').", context->address, client_id, clean_session, context->keepalive, context->username); + log__printf(NULL, MOSQ_LOG_NOTICE, "New bridge connected from %s as %s (c%d, k%d, u'%s').", context->address, client_id, clean_start, context->keepalive, context->username); }else{ - log__printf(NULL, MOSQ_LOG_NOTICE, "New bridge connected from %s as %s (c%d, k%d).", context->address, client_id, clean_session, context->keepalive); + log__printf(NULL, MOSQ_LOG_NOTICE, "New bridge connected from %s as %s (c%d, k%d).", context->address, client_id, clean_start, context->keepalive); } }else{ if(context->username){ - log__printf(NULL, MOSQ_LOG_NOTICE, "New client connected from %s as %s (c%d, k%d, u'%s').", context->address, client_id, clean_session, context->keepalive, context->username); + log__printf(NULL, MOSQ_LOG_NOTICE, "New client connected from %s as %s (c%d, k%d, u'%s').", context->address, client_id, clean_start, context->keepalive, context->username); }else{ - log__printf(NULL, MOSQ_LOG_NOTICE, "New client connected from %s as %s (c%d, k%d).", context->address, client_id, clean_session, context->keepalive); + log__printf(NULL, MOSQ_LOG_NOTICE, "New client connected from %s as %s (p%d, c%d, k%d).", context->address, client_id, context->protocol, clean_start, context->keepalive); } } if(context->will) { - log__printf(NULL, MOSQ_LOG_DEBUG, "Will message specified (%ld bytes) (r%d, q%d).", (long)context->will->payloadlen, context->will->retain, context->will->qos); - log__printf(NULL, MOSQ_LOG_DEBUG, "\t%s", context->will->topic); + log__printf(NULL, MOSQ_LOG_DEBUG, "Will message specified (%ld bytes) (r%d, q%d).", + (long)context->will->msg.payloadlen, + context->will->msg.retain, + context->will->msg.qos); + + log__printf(NULL, MOSQ_LOG_DEBUG, "\t%s", context->will->msg.topic); } else { log__printf(NULL, MOSQ_LOG_DEBUG, "No will message specified."); } @@ -679,7 +739,7 @@ int handle__connect(struct mosquitto_db *db, struct mosquitto *context) context->id = client_id; client_id = NULL; - context->clean_session = clean_session; + context->clean_start = clean_start; context->ping_t = 0; context->is_dropping = false; if((protocol_version&0x80) == 0x80){ @@ -692,12 +752,14 @@ int handle__connect(struct mosquitto_db *db, struct mosquitto *context) HASH_ADD_KEYPTR(hh_id, db->contexts_by_id, context->id, strlen(context->id), context); #ifdef WITH_PERSISTENCE - if(!clean_session){ + if(!clean_start){ db->persistence_changes++; } #endif context->state = mosq_cs_connected; - return send__connack(context, connect_ack, CONNACK_ACCEPTED); + rc = send__connack(db, context, connect_ack, CONNACK_ACCEPTED, connack_props); + mosquitto_property_free_all(&connack_props); + return rc; handle_connect_error: mosquitto__free(client_id); @@ -705,6 +767,10 @@ handle_connect_error: mosquitto__free(password); mosquitto__free(will_payload); mosquitto__free(will_topic); + if(will_struct){ + mosquitto_property_free_all(&will_struct->properties); + } + mosquitto_property_free_all(&connack_props); mosquitto__free(will_struct); #ifdef WITH_TLS if(client_cert) X509_free(client_cert); @@ -713,16 +779,42 @@ handle_connect_error: return rc; } + int handle__disconnect(struct mosquitto_db *db, struct mosquitto *context) { + int rc; + uint8_t reason_code; + mosquitto_property *properties = NULL; + if(!context){ return MOSQ_ERR_INVAL; } + + if(context->protocol == mosq_p_mqtt5 && context->in_packet.remaining_length > 1){ + /* FIXME - must handle reason code */ + rc = packet__read_byte(&context->in_packet, &reason_code); + if(rc) return rc; + + if(context->in_packet.remaining_length > 2){ + rc = property__read_all(CMD_DISCONNECT, &context->in_packet, &properties); + if(rc) return rc; + } + } + rc = property__process_disconnect(context, properties); + if(rc){ + if(rc == MOSQ_ERR_PROTOCOL){ + send__disconnect(context, MQTT_RC_PROTOCOL_ERROR, NULL); + } + mosquitto_property_free_all(&properties); + return rc; + } + mosquitto_property_free_all(&properties); /* FIXME - TEMPORARY UNTIL PROPERTIES PROCESSED */ + if(context->in_packet.remaining_length != 0){ return MOSQ_ERR_PROTOCOL; } log__printf(NULL, MOSQ_LOG_DEBUG, "Received DISCONNECT from %s", context->id); - if(context->protocol == mosq_p_mqtt311){ + if(context->protocol == mosq_p_mqtt311 || context->protocol == mosq_p_mqtt5){ if((context->in_packet.command&0x0F) != 0x00){ do_disconnect(db, context); return MOSQ_ERR_PROTOCOL; diff --git a/src/handle_publish.c b/src/handle_publish.c index 54976afc..fe536589 100644 --- a/src/handle_publish.c +++ b/src/handle_publish.c @@ -21,9 +21,11 @@ Contributors: #include #include "mosquitto_broker_internal.h" -#include "mqtt3_protocol.h" +#include "alias_mosq.h" +#include "mqtt_protocol.h" #include "memory_mosq.h" #include "packet_mosq.h" +#include "property_mosq.h" #include "read_handle.h" #include "send_mosq.h" #include "sys_tree.h" @@ -44,6 +46,12 @@ int handle__publish(struct mosquitto_db *db, struct mosquitto *context) int len; int slen; char *topic_mount; + mosquitto_property *properties = NULL; + mosquitto_property *p, *p_prev; + mosquitto_property *msg_properties = NULL, *msg_properties_last; + uint32_t message_expiry_interval = 0; + uint16_t topic_alias = 0; + #ifdef WITH_BRIDGE char *topic_temp; int i; @@ -60,16 +68,112 @@ int handle__publish(struct mosquitto_db *db, struct mosquitto *context) "Invalid QoS in PUBLISH from %s, disconnecting.", context->id); return 1; } + if(qos > context->maximum_qos){ + log__printf(NULL, MOSQ_LOG_INFO, + "Too high QoS in PUBLISH from %s, disconnecting.", context->id); + return 1; + } retain = (header & 0x01); + if(retain && db->config->retain_available == false){ + if(context->protocol == mosq_p_mqtt5){ + send__disconnect(context, MQTT_RC_RETAIN_NOT_SUPPORTED, NULL); + } + return 1; + } + if(packet__read_string(&context->in_packet, &topic, &slen)) return 1; - if(!slen){ + if(!slen && context->protocol != mosq_p_mqtt5){ /* Invalid publish topic, disconnect client. */ mosquitto__free(topic); return 1; } + if(qos > 0){ + if(packet__read_uint16(&context->in_packet, &mid)){ + mosquitto__free(topic); + return 1; + } + if(mid == 0){ + mosquitto__free(topic); + return MOSQ_ERR_PROTOCOL; + } + } + + /* Handle properties */ + if(context->protocol == mosq_p_mqtt5){ + rc = property__read_all(CMD_PUBLISH, &context->in_packet, &properties); + if(rc) return rc; + + p = properties; + p_prev = NULL; + msg_properties = NULL; + msg_properties_last = NULL; + while(p){ + switch(p->identifier){ + case MQTT_PROP_CONTENT_TYPE: + case MQTT_PROP_CORRELATION_DATA: + case MQTT_PROP_PAYLOAD_FORMAT_INDICATOR: + case MQTT_PROP_RESPONSE_TOPIC: + case MQTT_PROP_USER_PROPERTY: + if(msg_properties){ + msg_properties_last->next = p; + msg_properties_last = p; + }else{ + msg_properties = p; + msg_properties_last = p; + } + if(p_prev){ + p_prev->next = p->next; + p = p_prev->next; + }else{ + properties = p->next; + p = properties; + } + msg_properties_last->next = NULL; + break; + + case MQTT_PROP_TOPIC_ALIAS: + topic_alias = p->value.i16; + p_prev = p; + p = p->next; + break; + + case MQTT_PROP_MESSAGE_EXPIRY_INTERVAL: + message_expiry_interval = p->value.i32; + p_prev = p; + p = p->next; + break; + + case MQTT_PROP_SUBSCRIPTION_IDENTIFIER: + p_prev = p; + p = p->next; + break; + + default: + p = p->next; + break; + } + } + } + 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){ + return MOSQ_ERR_PROTOCOL; + } 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); mosquitto__free(topic); return 1; } @@ -128,13 +232,6 @@ int handle__publish(struct mosquitto_db *db, struct mosquitto *context) return 1; } - if(qos > 0){ - if(packet__read_uint16(&context->in_packet, &mid)){ - mosquitto__free(topic); - return 1; - } - } - payloadlen = context->in_packet.remaining_length - context->in_packet.pos; G_PUB_BYTES_RECEIVED_INC(payloadlen); if(context->listener && context->listener->mount_point){ @@ -142,6 +239,7 @@ int handle__publish(struct mosquitto_db *db, struct mosquitto *context) topic_mount = mosquitto__malloc(len+1); if(!topic_mount){ mosquitto__free(topic); + mosquitto_property_free_all(&msg_properties); return MOSQ_ERR_NOMEM; } snprintf(topic_mount, len, "%s%s", context->listener->mount_point, topic); @@ -158,11 +256,13 @@ int handle__publish(struct mosquitto_db *db, struct mosquitto *context) } if(UHPA_ALLOC(payload, payloadlen+1) == 0){ mosquitto__free(topic); + mosquitto_property_free_all(&msg_properties); return MOSQ_ERR_NOMEM; } if(packet__read_bytes(&context->in_packet, UHPA_ACCESS(payload, payloadlen), payloadlen)){ mosquitto__free(topic); UHPA_FREE(payload, payloadlen); + mosquitto_property_free_all(&msg_properties); return 1; } } @@ -175,6 +275,7 @@ int handle__publish(struct mosquitto_db *db, struct mosquitto *context) }else if(rc != MOSQ_ERR_SUCCESS){ mosquitto__free(topic); UHPA_FREE(payload, payloadlen); + mosquitto_property_free_all(&msg_properties); return rc; } @@ -184,13 +285,16 @@ int handle__publish(struct mosquitto_db *db, struct mosquitto *context) } if(!stored){ dup = 0; - if(db__message_store(db, context->id, mid, topic, qos, payloadlen, &payload, retain, &stored, 0)){ + if(db__message_store(db, context, mid, topic, qos, payloadlen, &payload, retain, &stored, message_expiry_interval, msg_properties, 0)){ + mosquitto_property_free_all(&msg_properties); return 1; } + msg_properties = NULL; /* Now belongs to db__message_store() */ }else{ mosquitto__free(topic); topic = stored->topic; dup = 1; + mosquitto_property_free_all(&msg_properties); } switch(qos){ @@ -203,7 +307,7 @@ int handle__publish(struct mosquitto_db *db, struct mosquitto *context) break; case 2: if(!dup){ - res = db__message_insert(db, context, mid, mosq_md_in, qos, retain, stored); + res = db__message_insert(db, context, mid, mosq_md_in, qos, retain, stored, NULL); }else{ res = 0; } @@ -229,10 +333,10 @@ process_bad_message: case 2: db__message_store_find(context, mid, &stored); if(!stored){ - if(db__message_store(db, context->id, mid, NULL, qos, 0, NULL, false, &stored, 0)){ + if(db__message_store(db, context, mid, NULL, qos, 0, NULL, false, &stored, 0, NULL, 0)){ return 1; } - res = db__message_insert(db, context, mid, mosq_md_in, qos, false, stored); + res = db__message_insert(db, context, mid, mosq_md_in, qos, false, stored, NULL); }else{ res = 0; } diff --git a/src/handle_subscribe.c b/src/handle_subscribe.c index 8f594351..e50075a3 100644 --- a/src/handle_subscribe.c +++ b/src/handle_subscribe.c @@ -21,7 +21,9 @@ Contributors: #include "mosquitto_broker_internal.h" #include "memory_mosq.h" +#include "mqtt_protocol.h" #include "packet_mosq.h" +#include "property_mosq.h" @@ -31,23 +33,45 @@ int handle__subscribe(struct mosquitto_db *db, struct mosquitto *context) int rc2; uint16_t mid; char *sub; + uint8_t subscription_options; + uint32_t subscription_identifier = 0; uint8_t qos; + uint8_t retain_handling = 0; uint8_t *payload = NULL, *tmp_payload; uint32_t payloadlen = 0; int len; int slen; char *sub_mount; + mosquitto_property *properties = NULL; if(!context) return MOSQ_ERR_INVAL; log__printf(NULL, MOSQ_LOG_DEBUG, "Received SUBSCRIBE from %s", context->id); - /* FIXME - plenty of potential for memory leaks here */ - if(context->protocol == mosq_p_mqtt311){ + if(context->protocol != mosq_p_mqtt31){ if((context->in_packet.command&0x0F) != 0x02){ return MOSQ_ERR_PROTOCOL; } } if(packet__read_uint16(&context->in_packet, &mid)) return 1; + if(mid == 0) return MOSQ_ERR_PROTOCOL; + + if(context->protocol == mosq_p_mqtt5){ + rc = property__read_all(CMD_SUBSCRIBE, &context->in_packet, &properties); + if(rc) return rc; + + if(mosquitto_property_read_varint(properties, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, + &subscription_identifier, false)){ + + /* If the identifier was force set to 0, this is an error */ + if(subscription_identifier == 0){ + mosquitto_property_free_all(&properties); + return MOSQ_ERR_PROTOCOL; + } + } + + mosquitto_property_free_all(&properties); + /* Note - User Property not handled */ + } while(context->in_packet.pos < context->in_packet.remaining_length){ sub = NULL; @@ -73,19 +97,26 @@ int handle__subscribe(struct mosquitto_db *db, struct mosquitto *context) mosquitto__free(payload); return 1; } - if(mosquitto_validate_utf8(sub, slen)){ - log__printf(NULL, MOSQ_LOG_INFO, - "Malformed UTF-8 in subscription string from %s, disconnecting.", - context->id); - mosquitto__free(sub); - return 1; - } - if(packet__read_byte(&context->in_packet, &qos)){ + if(packet__read_byte(&context->in_packet, &subscription_options)){ mosquitto__free(sub); mosquitto__free(payload); return 1; } + if(context->protocol == mosq_p_mqtt31 || context->protocol == mosq_p_mqtt311){ + qos = subscription_options; + if(context->is_bridge){ + subscription_options = MQTT_SUB_OPT_RETAIN_AS_PUBLISHED | MQTT_SUB_OPT_NO_LOCAL; + } + }else{ + qos = subscription_options & 0x03; + subscription_options &= 0xFC; + + retain_handling = (subscription_options & 0x30); + if(retain_handling == 0x30 || (subscription_options & 0xC0) != 0){ + return MOSQ_ERR_PROTOCOL; + } + } if(qos > 2){ log__printf(NULL, MOSQ_LOG_INFO, "Invalid QoS in subscription command from %s, disconnecting.", @@ -94,6 +125,8 @@ int handle__subscribe(struct mosquitto_db *db, struct mosquitto *context) mosquitto__free(payload); return 1; } + + if(context->listener && context->listener->mount_point){ len = strlen(context->listener->mount_point) + slen + 1; sub_mount = mosquitto__malloc(len+1); @@ -111,7 +144,7 @@ int handle__subscribe(struct mosquitto_db *db, struct mosquitto *context) } log__printf(NULL, MOSQ_LOG_DEBUG, "\t%s (QoS %d)", sub, qos); - if(context->protocol == mosq_p_mqtt311){ + if(context->protocol != mosq_p_mqtt31){ rc2 = mosquitto_acl_check(db, context, sub, 0, NULL, qos, false, MOSQ_ACL_SUBSCRIBE); switch(rc2){ case MOSQ_ERR_SUCCESS: @@ -126,12 +159,23 @@ int handle__subscribe(struct mosquitto_db *db, struct mosquitto *context) } if(qos != 0x80){ - rc2 = sub__add(db, context, sub, qos, &db->subs); - if(rc2 == MOSQ_ERR_SUCCESS){ - if(sub__retain_queue(db, context, sub, qos)) rc = 1; - }else if(rc2 != -1){ - rc = rc2; + rc2 = sub__add(db, context, sub, qos, subscription_identifier, subscription_options, &db->subs); + if(rc2 > 0){ + mosquitto__free(sub); + return rc2; } + if(context->protocol == mosq_p_mqtt311 || context->protocol == mosq_p_mqtt31){ + if(rc2 == MOSQ_ERR_SUCCESS || rc2 == MOSQ_ERR_SUB_EXISTS){ + if(sub__retain_queue(db, context, sub, qos, 0)) rc = 1; + } + }else{ + if((retain_handling == MQTT_SUB_OPT_SEND_RETAIN_ALWAYS) + || (rc2 == MOSQ_ERR_SUCCESS && retain_handling == MQTT_SUB_OPT_SEND_RETAIN_NEW)){ + + if(sub__retain_queue(db, context, sub, qos, subscription_identifier)) rc = 1; + } + } + log__printf(NULL, MOSQ_LOG_SUBSCRIBE, "%s %d %s", context->id, qos, sub); } mosquitto__free(sub); @@ -149,7 +193,7 @@ int handle__subscribe(struct mosquitto_db *db, struct mosquitto *context) } } - if(context->protocol == mosq_p_mqtt311){ + if(context->protocol != mosq_p_mqtt31){ if(payloadlen == 0){ /* No subscriptions specified, protocol error. */ return MOSQ_ERR_PROTOCOL; diff --git a/src/handle_unsubscribe.c b/src/handle_unsubscribe.c index a0f00046..075bde44 100644 --- a/src/handle_unsubscribe.c +++ b/src/handle_unsubscribe.c @@ -21,71 +21,90 @@ Contributors: #include "mosquitto_broker_internal.h" #include "memory_mosq.h" -#include "mqtt3_protocol.h" +#include "mqtt_protocol.h" #include "packet_mosq.h" #include "send_mosq.h" -/* -#include "sys_tree.h" -#include "time_mosq.h" -#include "tls_mosq.h" -#include "util_mosq.h" -*/ int handle__unsubscribe(struct mosquitto_db *db, struct mosquitto *context) { uint16_t mid; char *sub; int slen; + int rc; + uint8_t reason; + 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; log__printf(NULL, MOSQ_LOG_DEBUG, "Received UNSUBSCRIBE from %s", context->id); - if(context->protocol == mosq_p_mqtt311){ + if(context->protocol != mosq_p_mqtt31){ if((context->in_packet.command&0x0F) != 0x02){ return MOSQ_ERR_PROTOCOL; } } if(packet__read_uint16(&context->in_packet, &mid)) return 1; + if(mid == 0) return MOSQ_ERR_PROTOCOL; - if(context->protocol == mosq_p_mqtt311){ + if(context->protocol == mosq_p_mqtt5){ + rc = property__read_all(CMD_UNSUBSCRIBE, &context->in_packet, &properties); + if(rc) return rc; + /* Immediately free, we don't do anything with User Property at the moment */ + mosquitto_property_free_all(&properties); + } + + if(context->protocol == mosq_p_mqtt311 || context->protocol == mosq_p_mqtt5){ if(context->in_packet.pos == context->in_packet.remaining_length){ /* No topic specified, protocol error. */ 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; - } - if(mosquitto_validate_utf8(sub, slen)){ - log__printf(NULL, MOSQ_LOG_INFO, - "Malformed UTF-8 in 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); + rc = sub__remove(db, context, sub, db->subs, &reason); + log__printf(NULL, MOSQ_LOG_UNSUBSCRIBE, "%s %s", context->id, sub); + mosquitto__free(sub); + if(rc) return rc; + + reason_codes[reason_code_count] = reason; + 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 @@ -94,6 +113,9 @@ int handle__unsubscribe(struct mosquitto_db *db, struct mosquitto *context) log__printf(NULL, MOSQ_LOG_DEBUG, "Sending UNSUBACK to %s", context->id); - return send__command_with_mid(context, UNSUBACK, mid, false); + /* We don't use Reason String or User Property yet. */ + rc = send__unsuback(context, mid, reason_code_count, reason_codes, NULL); + mosquitto__free(reason_codes); + return rc; } diff --git a/src/logging.c b/src/logging.c index 0b7b792a..78ba9ebe 100644 --- a/src/logging.c +++ b/src/logging.c @@ -239,10 +239,10 @@ int log__vprintf(int priority, const char *fmt, va_list va) return MOSQ_ERR_NOMEM; } snprintf(st, len, "%d: %s", (int)now, s); - db__messages_easy_queue(&int_db, NULL, topic, 2, strlen(st), st, 0); + db__messages_easy_queue(&int_db, NULL, topic, 2, strlen(st), st, 0, 20, NULL); mosquitto__free(st); }else{ - db__messages_easy_queue(&int_db, NULL, topic, 2, strlen(s), s, 0); + db__messages_easy_queue(&int_db, NULL, topic, 2, strlen(s), s, 0, 20, NULL); } } mosquitto__free(s); diff --git a/src/loop.c b/src/loop.c index c049c8ef..4678dada 100644 --- a/src/loop.c +++ b/src/loop.c @@ -87,7 +87,9 @@ static void temp__expire_websockets_clients(struct mosquitto_db *db) }else{ id = ""; } - log__printf(NULL, MOSQ_LOG_NOTICE, "Client %s has exceeded timeout, disconnecting.", id); + if(db->config->connection_messages == true){ + log__printf(NULL, MOSQ_LOG_NOTICE, "Client %s has exceeded timeout, disconnecting.", id); + } } /* Client has exceeded keepalive*1.5 */ do_disconnect(db, context); @@ -126,11 +128,11 @@ int mosquitto_main_loop(struct mosquitto_db *db, mosq_sock_t *listensock, int li #endif #ifdef WITH_BRIDGE int rc; + int err; + socklen_t len; #endif time_t expiration_check_time = 0; char *id; - int err; - socklen_t len; #ifndef WIN32 sigemptyset(&sigblock); @@ -408,6 +410,10 @@ int mosquitto_main_loop(struct mosquitto_db *db, mosq_sock_t *listensock, int li context->bridge->restart_t = 0; } }else{ +#ifdef WITH_EPOLL + /* clean any events triggered in previous connection */ + context->events = 0; +#endif rc = bridge__connect_step1(db, context); if(rc){ context->bridge->cur_address++; @@ -466,7 +472,7 @@ int mosquitto_main_loop(struct mosquitto_db *db, mosq_sock_t *listensock, int li now_time = time(NULL); if(db->config->persistent_client_expiration > 0 && now_time > expiration_check_time){ HASH_ITER(hh_id, db->contexts_by_id, context, ctxt_tmp){ - if(context->sock == INVALID_SOCKET && context->clean_session == 0){ + if(context->sock == INVALID_SOCKET && context->clean_start == 0){ /* This is a persistent client, check to see if the * last time it connected was longer than * persistent_client_expiration seconds ago. If so, @@ -480,7 +486,7 @@ int mosquitto_main_loop(struct mosquitto_db *db, mosq_sock_t *listensock, int li } log__printf(NULL, MOSQ_LOG_NOTICE, "Expiring persistent client %s due to timeout.", id); G_CLIENTS_EXPIRED_INC(); - context->clean_session = true; + context->clean_start = true; context->state = mosq_cs_expiring; do_disconnect(db, context); } @@ -638,19 +644,13 @@ void do_disconnect(struct mosquitto_db *db, struct mosquitto *context) context->sock = INVALID_SOCKET; context->pollfd_index = -1; } - if(context->id){ - HASH_DELETE(hh_id, db->contexts_by_id, context); - context->old_id = context->id; - context->id = NULL; - } + context__remove_from_by_id(db, context); }else #endif { if(db->config->connection_messages == true){ if(context->id){ id = context->id; - }else if(context->old_id){ - id = context->old_id; }else{ id = ""; } @@ -662,18 +662,20 @@ void do_disconnect(struct mosquitto_db *db, struct mosquitto *context) } #ifdef WITH_EPOLL if (context->sock != INVALID_SOCKET && epoll_ctl(db->epollfd, EPOLL_CTL_DEL, context->sock, &ev) == -1) { - log__printf(NULL, MOSQ_LOG_DEBUG, "Error in epoll disconnecting: %s", strerror(errno)); + if(db->config->connection_messages == true){ + log__printf(NULL, MOSQ_LOG_DEBUG, "Error in epoll disconnecting: %s", strerror(errno)); + } } #endif context__disconnect(db, context); #ifdef WITH_BRIDGE - if(context->clean_session && !context->bridge){ + if(context->clean_start && !context->bridge){ #else - if(context->clean_session){ + if(context->clean_start){ #endif context__add_to_disused(db, context); if(context->id){ - HASH_DELETE(hh_id, db->contexts_by_id, context); + context__remove_from_by_id(db, context); mosquitto__free(context->id); context->id = NULL; } diff --git a/src/mosquitto_broker_internal.h b/src/mosquitto_broker_internal.h index 1b438b85..7e09933e 100644 --- a/src/mosquitto_broker_internal.h +++ b/src/mosquitto_broker_internal.h @@ -218,7 +218,9 @@ struct mosquitto__listener { int sock_count; int client_count; enum mosquitto_protocol protocol; + int socket_domain; bool use_username_as_clientid; + uint8_t maximum_qos; #ifdef WITH_TLS char *cafile; char *capath; @@ -250,6 +252,7 @@ struct mosquitto__config { bool allow_duplicate_messages; int autosave_interval; bool autosave_on_changes; + bool check_retain_source; char *clientid_prefixes; bool connection_messages; bool daemon; @@ -262,6 +265,9 @@ struct mosquitto__config { bool log_timestamp; char *log_file; FILE *log_fptr; + uint16_t max_inflight_messages; + uint16_t max_keepalive; + uint32_t max_packet_size; uint32_t message_size_limit; bool persistence; char *persistence_location; @@ -271,6 +277,7 @@ struct mosquitto__config { char *pid_file; bool queue_qos0_messages; bool per_listener_settings; + bool retain_available; bool set_tcp_nodelay; int sys_interval; bool upgrade_outgoing_qos; @@ -290,7 +297,10 @@ struct mosquitto__subleaf { struct mosquitto__subleaf *prev; struct mosquitto__subleaf *next; struct mosquitto *context; - int qos; + uint32_t identifier; + uint8_t qos; + bool no_local; + bool retain_as_published; }; struct mosquitto__subhier { @@ -314,11 +324,15 @@ struct mosquitto_msg_store{ struct mosquitto_msg_store *prev; dbid_t db_id; char *source_id; + char *source_username; + struct mosquitto__listener *source_listener; char **dest_ids; int dest_id_count; int ref_count; char* topic; + mosquitto_property *properties; mosquitto__payload_uhpa payload; + time_t message_expiry_time; uint32_t payloadlen; uint16_t source_mid; uint16_t mid; @@ -329,6 +343,7 @@ struct mosquitto_msg_store{ struct mosquitto_client_msg{ struct mosquitto_client_msg *next; struct mosquitto_msg_store *store; + mosquitto_property *properties; time_t timestamp; uint16_t mid; uint8_t qos; @@ -435,7 +450,7 @@ struct mosquitto__bridge{ bool round_robin; bool try_private; bool try_private_accepted; - bool clean_session; + bool clean_start; int keepalive; struct mosquitto__bridge_topic *topics; int topic_count; @@ -465,7 +480,7 @@ struct mosquitto__bridge{ char *tls_certfile; char *tls_keyfile; char *tls_version; -# ifdef WITH_TLS_PSK +# ifdef FINAL_WITH_TLS_PSK char *tls_psk_identity; char *tls_psk; # endif @@ -512,8 +527,9 @@ int restore_privileges(void); /* ============================================================ * Server send functions * ============================================================ */ -int send__connack(struct mosquitto *context, int ack, int result); +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, int reason_code_count, uint8_t *reason_codes, const mosquitto_property *properties); /* ============================================================ * Network functions @@ -534,6 +550,7 @@ int handle__disconnect(struct mosquitto_db *db, struct mosquitto *context); int handle__publish(struct mosquitto_db *db, struct mosquitto *context); int handle__subscribe(struct mosquitto_db *db, struct mosquitto *context); int handle__unsubscribe(struct mosquitto_db *db, struct mosquitto *context); +int handle__auth(struct mosquitto_db *db, struct mosquitto *context); /* ============================================================ * Database handling @@ -544,18 +561,18 @@ int db__close(struct mosquitto_db *db); int persist__backup(struct mosquitto_db *db, bool shutdown); int persist__restore(struct mosquitto_db *db); #endif -void db__limits_set(int inflight, unsigned long inflight_bytes, int queued, unsigned long queued_bytes); +void db__limits_set(unsigned long inflight_bytes, int queued, unsigned long queued_bytes); /* Return the number of in-flight messages in count. */ int db__message_count(int *count); -int db__message_delete(struct mosquitto_db *db, struct mosquitto *context, uint16_t mid, enum mosquitto_msg_direction dir); -int db__message_insert(struct mosquitto_db *db, struct mosquitto *context, uint16_t mid, enum mosquitto_msg_direction dir, int qos, bool retain, struct mosquitto_msg_store *stored); +int db__message_delete(struct mosquitto_db *db, struct mosquitto *context, uint16_t mid, enum mosquitto_msg_direction dir, enum mosquitto_msg_state expect_state, int qos); +int db__message_insert(struct mosquitto_db *db, struct mosquitto *context, uint16_t mid, enum mosquitto_msg_direction dir, int qos, bool retain, struct mosquitto_msg_store *stored, mosquitto_property *properties); int db__message_release(struct mosquitto_db *db, struct mosquitto *context, uint16_t mid, enum mosquitto_msg_direction dir); -int db__message_update(struct mosquitto *context, uint16_t mid, enum mosquitto_msg_direction dir, enum mosquitto_msg_state state); +int db__message_update(struct mosquitto *context, uint16_t mid, enum mosquitto_msg_direction dir, enum mosquitto_msg_state state, int qos); int db__message_write(struct mosquitto_db *db, struct mosquitto *context); void db__message_dequeue_first(struct mosquitto *context); int db__messages_delete(struct mosquitto_db *db, struct mosquitto *context); -int db__messages_easy_queue(struct mosquitto_db *db, struct mosquitto *context, const char *topic, int qos, uint32_t payloadlen, const void *payload, int retain); -int db__message_store(struct mosquitto_db *db, const char *source, uint16_t source_mid, char *topic, int qos, uint32_t payloadlen, mosquitto__payload_uhpa *payload, int retain, struct mosquitto_msg_store **stored, dbid_t store_id); +int db__messages_easy_queue(struct mosquitto_db *db, struct mosquitto *context, const char *topic, int qos, uint32_t payloadlen, const void *payload, int retain, uint32_t message_expiry_interval, mosquitto_property **properties); +int db__message_store(struct mosquitto_db *db, const struct mosquitto *source, uint16_t source_mid, char *topic, int qos, uint32_t payloadlen, mosquitto__payload_uhpa *payload, int retain, struct mosquitto_msg_store **stored, uint32_t message_expiry_interval, mosquitto_property *properties, dbid_t store_id); int db__message_store_find(struct mosquitto *context, uint16_t mid, struct mosquitto_msg_store **stored); void db__msg_store_add(struct mosquitto_db *db, struct mosquitto_msg_store *store); void db__msg_store_remove(struct mosquitto_db *db, struct mosquitto_msg_store *store); @@ -569,12 +586,12 @@ void sys_tree__update(struct mosquitto_db *db, int interval, time_t start_time); /* ============================================================ * Subscription functions * ============================================================ */ -int sub__add(struct mosquitto_db *db, struct mosquitto *context, const char *sub, int qos, struct mosquitto__subhier **root); +int sub__add(struct mosquitto_db *db, struct mosquitto *context, const char *sub, int qos, uint32_t identifier, int options, struct mosquitto__subhier **root); struct mosquitto__subhier *sub__add_hier_entry(struct mosquitto__subhier *parent, struct mosquitto__subhier **sibling, const char *topic, size_t len); -int sub__remove(struct mosquitto_db *db, struct mosquitto *context, const char *sub, struct mosquitto__subhier *root); +int sub__remove(struct mosquitto_db *db, struct mosquitto *context, const char *sub, struct mosquitto__subhier *root, uint8_t *reason); void sub__tree_print(struct mosquitto__subhier *root, int level); int sub__clean_session(struct mosquitto_db *db, struct mosquitto *context); -int sub__retain_queue(struct mosquitto_db *db, struct mosquitto *context, const char *sub, int sub_qos); +int sub__retain_queue(struct mosquitto_db *db, struct mosquitto *context, const char *sub, int sub_qos, uint32_t subscription_identifier); int sub__messages_queue(struct mosquitto_db *db, const char *source_id, const char *topic, int qos, int retain, struct mosquitto_msg_store **stored); /* ============================================================ @@ -586,6 +603,7 @@ void context__disconnect(struct mosquitto_db *db, struct mosquitto *context); void context__add_to_disused(struct mosquitto_db *db, struct mosquitto *context); void context__free_disused(struct mosquitto_db *db); void context__send_will(struct mosquitto_db *db, struct mosquitto *context); +void context__remove_from_by_id(struct mosquitto_db *db, struct mosquitto *context); /* ============================================================ * Logging functions @@ -606,9 +624,16 @@ int bridge__connect_step3(struct mosquitto_db *db, struct mosquitto *context); void bridge__packet_cleanup(struct mosquitto *context); #endif +/* ============================================================ + * Property related functions + * ============================================================ */ +int property__process_connect(struct mosquitto *context, mosquitto_property *props); +int property__process_disconnect(struct mosquitto *context, mosquitto_property *props); + /* ============================================================ * Security related functions * ============================================================ */ +int acl__find_acls(struct mosquitto_db *db, struct mosquitto *context); int mosquitto_security_module_init(struct mosquitto_db *db); int mosquitto_security_module_cleanup(struct mosquitto_db *db); diff --git a/src/mosquitto_passwd.c b/src/mosquitto_passwd.c index 5b303c1a..c9d5cbe8 100644 --- a/src/mosquitto_passwd.c +++ b/src/mosquitto_passwd.c @@ -387,7 +387,9 @@ int main(int argc, char *argv[]) signal(SIGINT, handle_sigint); signal(SIGTERM, handle_sigint); +#if OPENSSL_VERSION_NUMBER < 0x10100000L || OPENSSL_API_COMPAT < 0x10100000L OpenSSL_add_all_digests(); +#endif if(argc == 1){ print_usage(); diff --git a/src/net.c b/src/net.c index a93094ef..ec71fd77 100644 --- a/src/net.c +++ b/src/net.c @@ -45,7 +45,7 @@ Contributors: #endif #include "mosquitto_broker_internal.h" -#include "mqtt3_protocol.h" +#include "mqtt_protocol.h" #include "memory_mosq.h" #include "net_mosq.h" #include "util_mosq.h" @@ -152,8 +152,10 @@ int net__socket_accept(struct mosquitto_db *db, mosq_sock_t listensock) fromhost(&wrap_req); if(!hosts_access(&wrap_req)){ /* Access is denied */ - if(!net__socket_get_address(new_sock, address, 1024)){ - log__printf(NULL, MOSQ_LOG_NOTICE, "Client connection from %s denied access by tcpd.", address); + if(db->config->connection_messages == true){ + if(!net__socket_get_address(new_sock, address, 1024)){ + log__printf(NULL, MOSQ_LOG_NOTICE, "Client connection from %s denied access by tcpd.", address); + } } COMPAT_CLOSE(new_sock); return -1; @@ -187,7 +189,9 @@ int net__socket_accept(struct mosquitto_db *db, mosq_sock_t listensock) } if(new_context->listener->max_connections > 0 && new_context->listener->client_count > new_context->listener->max_connections){ - log__printf(NULL, MOSQ_LOG_NOTICE, "Client connection from %s denied: max_connections exceeded.", new_context->address); + if(db->config->connection_messages == true){ + log__printf(NULL, MOSQ_LOG_NOTICE, "Client connection from %s denied: max_connections exceeded.", new_context->address); + } context__cleanup(db, new_context, true); return -1; } @@ -217,12 +221,14 @@ int net__socket_accept(struct mosquitto_db *db, mosq_sock_t listensock) }else if(rc == SSL_ERROR_WANT_WRITE){ new_context->want_write = true; }else{ - e = ERR_get_error(); - while(e){ - log__printf(NULL, MOSQ_LOG_NOTICE, - "Client connection from %s failed: %s.", - new_context->address, ERR_error_string(e, ebuf)); + if(db->config->connection_messages == true){ e = ERR_get_error(); + while(e){ + log__printf(NULL, MOSQ_LOG_NOTICE, + "Client connection from %s failed: %s.", + new_context->address, ERR_error_string(e, ebuf)); + e = ERR_get_error(); + } } context__cleanup(db, new_context, true); return -1; @@ -234,7 +240,9 @@ int net__socket_accept(struct mosquitto_db *db, mosq_sock_t listensock) } #endif - log__printf(NULL, MOSQ_LOG_NOTICE, "New connection from %s on port %d.", new_context->address, new_context->listener->port); + if(db->config->connection_messages == true){ + log__printf(NULL, MOSQ_LOG_NOTICE, "New connection from %s on port %d.", new_context->address, new_context->listener->port); + } return new_sock; } @@ -247,7 +255,7 @@ static int client_certificate_verify(int preverify_ok, X509_STORE_CTX *ctx) } #endif -#ifdef WITH_TLS_PSK +#ifdef FINAL_WITH_TLS_PSK static unsigned int psk_server_callback(SSL *ssl, const char *identity, unsigned char *psk, unsigned int max_psk_len) { struct mosquitto_db *db; @@ -392,7 +400,11 @@ int net__socket_listen(struct mosquitto__listener *listener) snprintf(service, 10, "%d", listener->port); memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_family = AF_UNSPEC; + if(listener->socket_domain){ + hints.ai_family = listener->socket_domain; + }else{ + hints.ai_family = AF_UNSPEC; + } hints.ai_flags = AI_PASSIVE; hints.ai_socktype = SOCK_STREAM; @@ -570,7 +582,7 @@ int net__socket_listen(struct mosquitto__listener *listener) X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK); } -# ifdef WITH_TLS_PSK +# ifdef FINAL_WITH_TLS_PSK }else if(listener->psk_hint){ if(tls_ex_index_context == -1){ tls_ex_index_context = SSL_get_ex_new_index(0, "client context", NULL, NULL, NULL); @@ -595,7 +607,7 @@ int net__socket_listen(struct mosquitto__listener *listener) return 1; } } -# endif /* WITH_TLS_PSK */ +# endif /* FINAL_WITH_TLS_PSK */ } #endif /* WITH_TLS */ return 0; diff --git a/src/persist.c b/src/persist.c index b83327c3..f70f411f 100644 --- a/src/persist.c +++ b/src/persist.c @@ -39,6 +39,8 @@ static uint32_t db_version; static int persist__restore_sub(struct mosquitto_db *db, const char *client_id, const char *sub, int qos); +static int persist__read_string(FILE *db_fptr, char **str); +static int persist__write_string(FILE *db_fptr, const char *str, bool nullok); static struct mosquitto *persist__find_or_add_context(struct mosquitto_db *db, const char *client_id, uint16_t last_mid) { @@ -55,7 +57,7 @@ static struct mosquitto *persist__find_or_add_context(struct mosquitto_db *db, c return NULL; } - context->clean_session = false; + context->clean_start = false; HASH_ADD_KEYPTR(hh_id, db->contexts_by_id, context->id, strlen(context->id), context); } @@ -139,7 +141,7 @@ static int persist__message_store_write(struct mosquitto_db *db, FILE *db_fptr) uint32_t length; dbid_t i64temp; uint32_t i32temp; - uint16_t i16temp, slen, tlen; + uint16_t i16temp, tlen; uint8_t i8temp; struct mosquitto_msg_store *stored; bool force_no_retain; @@ -168,10 +170,19 @@ static int persist__message_store_write(struct mosquitto_db *db, FILE *db_fptr) }else{ tlen = 0; } - length = htonl(sizeof(dbid_t) + 2+strlen(stored->source_id) + + length = sizeof(dbid_t) + 2+strlen(stored->source_id) + sizeof(uint16_t) + sizeof(uint16_t) + 2+tlen + sizeof(uint32_t) + - stored->payloadlen + sizeof(uint8_t) + sizeof(uint8_t)); + stored->payloadlen + sizeof(uint8_t) + sizeof(uint8_t) + + 2*sizeof(uint16_t); + + if(stored->source_id){ + length += strlen(stored->source_id); + } + if(stored->source_username){ + length += strlen(stored->source_username); + } + length = htonl(length); i16temp = htons(DB_CHUNK_MSG_STORE); write_e(db_fptr, &i16temp, sizeof(uint16_t)); @@ -180,12 +191,15 @@ static int persist__message_store_write(struct mosquitto_db *db, FILE *db_fptr) i64temp = stored->db_id; write_e(db_fptr, &i64temp, sizeof(dbid_t)); - slen = strlen(stored->source_id); - i16temp = htons(slen); - write_e(db_fptr, &i16temp, sizeof(uint16_t)); - if(slen){ - write_e(db_fptr, stored->source_id, slen); + if(persist__write_string(db_fptr, stored->source_id, false)) return 1; + if(persist__write_string(db_fptr, stored->source_username, true)) return 1; + if(stored->source_listener){ + i16temp = htons(stored->source_listener->port); + }else{ + i16temp = 0; } + write_e(db_fptr, &i16temp, sizeof(uint16_t)); + i16temp = htons(stored->source_mid); write_e(db_fptr, &i16temp, sizeof(uint16_t)); @@ -214,6 +228,7 @@ static int persist__message_store_write(struct mosquitto_db *db, FILE *db_fptr) if(stored->payloadlen){ write_e(db_fptr, UHPA_ACCESS_PAYLOAD(stored), (unsigned int)stored->payloadlen); } + stored = stored->next; } @@ -234,7 +249,7 @@ static int persist__client_write(struct mosquitto_db *db, FILE *db_fptr) assert(db_fptr); HASH_ITER(hh_id, db->contexts_by_id, context, ctxt_tmp){ - if(context && context->clean_session == false){ + if(context && context->clean_start == false){ length = htonl(2+strlen(context->id) + sizeof(uint16_t) + sizeof(time_t)); i16temp = htons(DB_CHUNK_CLIENT); @@ -265,6 +280,60 @@ error: return 1; } + +static int persist__read_string(FILE *db_fptr, char **str) +{ + uint16_t i16temp; + uint16_t slen; + char *s = NULL; + + if(fread(&i16temp, 1, sizeof(uint16_t), db_fptr) != sizeof(uint16_t)){ + return MOSQ_ERR_INVAL; + } + + slen = ntohs(i16temp); + if(slen){ + s = mosquitto__malloc(slen+1); + if(!s){ + fclose(db_fptr); + log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); + return MOSQ_ERR_NOMEM; + } + if(fread(s, 1, slen, db_fptr) != slen){ + mosquitto__free(s); + return MOSQ_ERR_NOMEM; + } + s[slen] = '\0'; + } + + *str = s; + return MOSQ_ERR_SUCCESS; +} + + +static int persist__write_string(FILE *db_fptr, const char *str, bool nullok) +{ + uint16_t i16temp, slen; + + if(str){ + slen = strlen(str); + i16temp = htons(slen); + write_e(db_fptr, &i16temp, sizeof(uint16_t)); + write_e(db_fptr, str, slen); + }else if(nullok){ + i16temp = htons(0); + write_e(db_fptr, &i16temp, sizeof(uint16_t)); + }else{ + return 1; + } + + return MOSQ_ERR_SUCCESS; +error: + log__printf(NULL, MOSQ_LOG_ERR, "Error: %s.", strerror(errno)); + return 1; +} + + static int persist__subs_retain_write(struct mosquitto_db *db, FILE *db_fptr, struct mosquitto__subhier *node, const char *topic, int level) { struct mosquitto__subhier *subhier, *subhier_tmp; @@ -287,7 +356,7 @@ static int persist__subs_retain_write(struct mosquitto_db *db, FILE *db_fptr, st sub = node->subs; while(sub){ - if(sub->context->clean_session == false){ + if(sub->context->clean_start == false && sub->context->id){ length = htonl(2+strlen(sub->context->id) + 2+strlen(thistopic) + sizeof(uint8_t)); i16temp = htons(DB_CHUNK_SUB); @@ -642,15 +711,16 @@ static int persist__msg_store_chunk_restore(struct mosquitto_db *db, FILE *db_fp { dbid_t i64temp, store_id; uint32_t i32temp, payloadlen = 0; - uint16_t i16temp, slen, source_mid; + uint16_t i16temp, source_mid, source_port = 0; uint8_t qos, retain; mosquitto__payload_uhpa payload; - char *source_id = NULL; + struct mosquitto source; char *topic = NULL; int rc = 0; struct mosquitto_msg_store *stored = NULL; struct mosquitto_msg_store_load *load; char *err; + int i; payload.ptr = NULL; @@ -664,41 +734,45 @@ static int persist__msg_store_chunk_restore(struct mosquitto_db *db, FILE *db_fp read_e(db_fptr, &i64temp, sizeof(dbid_t)); store_id = i64temp; - read_e(db_fptr, &i16temp, sizeof(uint16_t)); - slen = ntohs(i16temp); - if(slen){ - source_id = mosquitto__malloc(slen+1); - if(!source_id){ - mosquitto__free(load); - fclose(db_fptr); - log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); - return MOSQ_ERR_NOMEM; - } - read_e(db_fptr, source_id, slen); - source_id[slen] = '\0'; + memset(&source, 0, sizeof(struct mosquitto)); + + rc = persist__read_string(db_fptr, &source.id); + if(rc){ + mosquitto__free(load); + return rc; } + if(db_version == 4){ + rc = persist__read_string(db_fptr, &source.username); + if(rc){ + mosquitto__free(load); + return rc; + } + read_e(db_fptr, &i16temp, sizeof(uint16_t)); + source_port = ntohs(i16temp); + if(source_port){ + for(i=0; iconfig->listener_count; i++){ + if(db->config->listeners[i].port == source_port){ + source.listener = &db->config->listeners[i]; + break; + } + } + } + } + read_e(db_fptr, &i16temp, sizeof(uint16_t)); source_mid = ntohs(i16temp); /* This is the mid - don't need it */ read_e(db_fptr, &i16temp, sizeof(uint16_t)); - read_e(db_fptr, &i16temp, sizeof(uint16_t)); - slen = ntohs(i16temp); - if(slen){ - topic = mosquitto__malloc(slen+1); - if(!topic){ - mosquitto__free(load); - fclose(db_fptr); - mosquitto__free(source_id); - log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); - return MOSQ_ERR_NOMEM; - } - read_e(db_fptr, topic, slen); - topic[slen] = '\0'; - }else{ - topic = NULL; + rc = persist__read_string(db_fptr, &topic); + if(rc){ + mosquitto__free(load); + fclose(db_fptr); + mosquitto__free(source.id); + return rc; } + read_e(db_fptr, &qos, sizeof(uint8_t)); read_e(db_fptr, &retain, sizeof(uint8_t)); @@ -709,7 +783,7 @@ static int persist__msg_store_chunk_restore(struct mosquitto_db *db, FILE *db_fp if(UHPA_ALLOC(payload, payloadlen) == 0){ mosquitto__free(load); fclose(db_fptr); - mosquitto__free(source_id); + mosquitto__free(source.id); mosquitto__free(topic); log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); return MOSQ_ERR_NOMEM; @@ -717,8 +791,8 @@ static int persist__msg_store_chunk_restore(struct mosquitto_db *db, FILE *db_fp read_e(db_fptr, UHPA_ACCESS(payload, payloadlen), payloadlen); } - rc = db__message_store(db, source_id, source_mid, topic, qos, payloadlen, &payload, retain, &stored, store_id); - mosquitto__free(source_id); + rc = db__message_store(db, &source, source_mid, topic, qos, payloadlen, &payload, retain, &stored, 0, NULL, store_id); + mosquitto__free(source.id); if(rc == MOSQ_ERR_SUCCESS){ load->db_id = stored->db_id; @@ -737,7 +811,7 @@ error: err = strerror(errno); log__printf(NULL, MOSQ_LOG_ERR, "Error: %s.", err); fclose(db_fptr); - mosquitto__free(source_id); + mosquitto__free(source.id); mosquitto__free(topic); UHPA_FREE(payload, payloadlen); return 1; @@ -768,38 +842,27 @@ static int persist__retain_chunk_restore(struct mosquitto_db *db, FILE *db_fptr) static int persist__sub_chunk_restore(struct mosquitto_db *db, FILE *db_fptr) { - uint16_t i16temp, slen; uint8_t qos; char *client_id; char *topic; int rc = 0; char *err; - read_e(db_fptr, &i16temp, sizeof(uint16_t)); - slen = ntohs(i16temp); - client_id = mosquitto__malloc(slen+1); - if(!client_id){ + rc = persist__read_string(db_fptr, &client_id); + if(rc){ fclose(db_fptr); - log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); - return MOSQ_ERR_NOMEM; + return rc; } - read_e(db_fptr, client_id, slen); - client_id[slen] = '\0'; - read_e(db_fptr, &i16temp, sizeof(uint16_t)); - slen = ntohs(i16temp); - topic = mosquitto__malloc(slen+1); - if(!topic){ - fclose(db_fptr); - log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); + rc = persist__read_string(db_fptr, &topic); + if(rc){ mosquitto__free(client_id); - return MOSQ_ERR_NOMEM; + fclose(db_fptr); + return rc; } - read_e(db_fptr, topic, slen); - topic[slen] = '\0'; read_e(db_fptr, &qos, sizeof(uint8_t)); - if(persist__restore_sub(db, client_id, topic, qos)){ + if(persist__restore_sub(db, client_id, topic, qos) > 0){ rc = 1; } mosquitto__free(client_id); @@ -852,7 +915,9 @@ int persist__restore(struct mosquitto_db *db) * Is your DB change still compatible with previous versions? */ if(db_version > MOSQ_DB_VERSION && db_version != 0){ - if(db_version == 2){ + if(db_version == 3){ + /* Addition of source_username and source_port to msg_store chunk in v4, v1.5.6 */ + }else if(db_version == 2){ /* Addition of disconnect_t to client chunk in v3. */ }else{ fclose(fptr); @@ -935,7 +1000,8 @@ static int persist__restore_sub(struct mosquitto_db *db, const char *client_id, context = persist__find_or_add_context(db, client_id, 0); if(!context) return 1; - return sub__add(db, context, sub, qos, &db->subs); + /* FIXME - identifer, options need saving */ + return sub__add(db, context, sub, qos, 0, 0, &db->subs); } #endif diff --git a/src/persist.h b/src/persist.h index 63a1a0cd..04f26342 100644 --- a/src/persist.h +++ b/src/persist.h @@ -17,7 +17,7 @@ Contributors: #ifndef PERSIST_H #define PERSIST_H -#define MOSQ_DB_VERSION 3 +#define MOSQ_DB_VERSION 4 /* DB read/write */ const unsigned char magic[15] = {0x00, 0xB5, 0x00, 'm','o','s','q','u','i','t','t','o',' ','d','b'}; diff --git a/src/plugin.c b/src/plugin.c index 3365bfbf..fd387869 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -32,7 +32,7 @@ const char *mosquitto_client_address(const struct mosquitto *client) bool mosquitto_client_clean_session(const struct mosquitto *client) { - return client->clean_session; + return client->clean_start; } diff --git a/src/property_broker.c b/src/property_broker.c new file mode 100644 index 00000000..654c802b --- /dev/null +++ b/src/property_broker.c @@ -0,0 +1,76 @@ +/* +Copyright (c) 2018 Roger Light + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#include +#include +#include + +#include "mosquitto_broker_internal.h" +#include "mqtt_protocol.h" +#include "property_mosq.h" + +/* Process the incoming properties, we should be able to assume that only valid + * properties for CONNECT are present here. */ +int property__process_connect(struct mosquitto *context, mosquitto_property *props) +{ + mosquitto_property *p; + + p = props; + + while(p){ + if(p->identifier == MQTT_PROP_SESSION_EXPIRY_INTERVAL){ + context->session_expiry_interval = p->value.i32; + }else if(p->identifier == MQTT_PROP_RECEIVE_MAXIMUM){ + if(p->value.i16 == 0){ + return MOSQ_ERR_PROTOCOL; + } + + context->send_maximum = p->value.i16; + context->send_quota = context->send_maximum; + }else if(p->identifier == MQTT_PROP_MAXIMUM_PACKET_SIZE){ + if(p->value.i32 == 0){ + return MOSQ_ERR_PROTOCOL; + } + context->maximum_packet_size = p->value.i32; + } + p = p->next; + } + + return MOSQ_ERR_SUCCESS; +} + +/* Process the incoming properties, we should be able to assume that only valid + * properties for DISCONNECT are present here. */ +int property__process_disconnect(struct mosquitto *context, mosquitto_property *props) +{ + mosquitto_property *p; + + p = props; + + while(p){ + if(p->identifier == MQTT_PROP_SESSION_EXPIRY_INTERVAL){ + if(context->session_expiry_interval == 0 && p->value.i32 != 0){ + return MOSQ_ERR_PROTOCOL; + } + context->session_expiry_interval = p->value.i32; + } + p = p->next; + } + return MOSQ_ERR_SUCCESS; +} + diff --git a/src/read_handle.c b/src/read_handle.c index ac448aab..f71a76f9 100644 --- a/src/read_handle.c +++ b/src/read_handle.c @@ -21,7 +21,7 @@ Contributors: #include #include "mosquitto_broker_internal.h" -#include "mqtt3_protocol.h" +#include "mqtt_protocol.h" #include "memory_mosq.h" #include "packet_mosq.h" #include "read_handle.h" @@ -35,36 +35,38 @@ int handle__packet(struct mosquitto_db *db, struct mosquitto *context) if(!context) return MOSQ_ERR_INVAL; switch((context->in_packet.command)&0xF0){ - case PINGREQ: + case CMD_PINGREQ: return handle__pingreq(context); - case PINGRESP: + case CMD_PINGRESP: return handle__pingresp(context); - case PUBACK: + case CMD_PUBACK: return handle__pubackcomp(db, context, "PUBACK"); - case PUBCOMP: + case CMD_PUBCOMP: return handle__pubackcomp(db, context, "PUBCOMP"); - case PUBLISH: + case CMD_PUBLISH: return handle__publish(db, context); - case PUBREC: - return handle__pubrec(context); - case PUBREL: + case CMD_PUBREC: + return handle__pubrec(db, context); + case CMD_PUBREL: return handle__pubrel(db, context); - case CONNECT: + case CMD_CONNECT: return handle__connect(db, context); - case DISCONNECT: + case CMD_DISCONNECT: return handle__disconnect(db, context); - case SUBSCRIBE: + case CMD_SUBSCRIBE: return handle__subscribe(db, context); - case UNSUBSCRIBE: + case CMD_UNSUBSCRIBE: return handle__unsubscribe(db, context); #ifdef WITH_BRIDGE - case CONNACK: + case CMD_CONNACK: return handle__connack(db, context); - case SUBACK: + case CMD_SUBACK: return handle__suback(context); - case UNSUBACK: + case CMD_UNSUBACK: return handle__unsuback(context); #endif + case CMD_AUTH: + return handle__auth(db, context); default: /* If we don't recognise the command, return an error straight away. */ return MOSQ_ERR_PROTOCOL; diff --git a/src/security_default.c b/src/security_default.c index 26d8b59e..58ee8b48 100644 --- a/src/security_default.c +++ b/src/security_default.c @@ -317,7 +317,7 @@ int mosquitto_acl_check_default(struct mosquitto_db *db, struct mosquitto *conte }else{ security_opts = &db->config->security_options; } - if(!security_opts->acl_list && !security_opts->acl_patterns){ + if(!security_opts->acl_file && !security_opts->acl_list && !security_opts->acl_patterns){ return MOSQ_ERR_PLUGIN_DEFER; } @@ -535,6 +535,10 @@ static int aclfile__parse(struct mosquitto_db *db, struct mosquitto__security_op fclose(aclfptr); return 1; } + }else{ + log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid line in acl_file \"%s\": %s.", security_opts->acl_file, buf); + fclose(aclfptr); + return 1; } } } @@ -606,6 +610,49 @@ static int acl__cleanup(struct mosquitto_db *db, bool reload) return MOSQ_ERR_SUCCESS; } + +int acl__find_acls(struct mosquitto_db *db, struct mosquitto *context) +{ + struct mosquitto__acl_user *acl_tail; + struct mosquitto__security_options *security_opts; + + /* Associate user with its ACL, assuming we have ACLs loaded. */ + if(db->config->per_listener_settings){ + if(!context->listener){ + return MOSQ_ERR_INVAL; + } + security_opts = &context->listener->security_options; + }else{ + security_opts = &db->config->security_options; + } + + if(security_opts->acl_list){ + acl_tail = security_opts->acl_list; + while(acl_tail){ + if(context->username){ + if(acl_tail->username && !strcmp(context->username, acl_tail->username)){ + context->acl_list = acl_tail; + break; + } + }else{ + if(acl_tail->username == NULL){ + context->acl_list = acl_tail; + break; + } + } + acl_tail = acl_tail->next; + } + if(context->username && context->acl_list == NULL){ + return MOSQ_ERR_INVAL; + } + }else{ + context->acl_list = NULL; + } + + return MOSQ_ERR_SUCCESS; +} + + static int pwfile__parse(const char *file, struct mosquitto__unpwd **root) { FILE *pwfile; @@ -623,6 +670,9 @@ static int pwfile__parse(const char *file, struct mosquitto__unpwd **root) while(!feof(pwfile)){ if(fgets(buf, 256, pwfile)){ + if(buf[0] == '#') continue; + if(!strchr(buf, ':')) continue; + username = strtok_r(buf, ":", &saveptr); if(username){ unpwd = mosquitto__calloc(1, sizeof(struct mosquitto__unpwd)); @@ -655,8 +705,13 @@ static int pwfile__parse(const char *file, struct mosquitto__unpwd **root) unpwd->password[len-1] = '\0'; len = strlen(unpwd->password); } + + HASH_ADD_KEYPTR(hh, *root, unpwd->username, strlen(unpwd->username), unpwd); + }else{ + log__printf(NULL, MOSQ_LOG_NOTICE, "Warning: Invalid line in password file '%s': %s", file, buf); + mosquitto__free(unpwd->username); + mosquitto__free(unpwd); } - HASH_ADD_KEYPTR(hh, *root, unpwd->username, strlen(unpwd->username), unpwd); } } } @@ -693,34 +748,39 @@ static int unpwd__file_parse(struct mosquitto__unpwd **unpwd, const char *passwo token = strtok(NULL, "$"); if(token){ rc = base64__decode(token, &salt, &salt_len); - if(rc){ - log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to decode password salt for user %s.", u->username); - return MOSQ_ERR_INVAL; - } - u->salt = salt; - u->salt_len = salt_len; - token = strtok(NULL, "$"); - if(token){ - rc = base64__decode(token, &password, &password_len); - if(rc){ - log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to decode password for user %s.", u->username); - return MOSQ_ERR_INVAL; + if(rc == MOSQ_ERR_SUCCESS && salt_len == 12){ + u->salt = salt; + u->salt_len = salt_len; + token = strtok(NULL, "$"); + if(token){ + rc = base64__decode(token, &password, &password_len); + if(rc == MOSQ_ERR_SUCCESS && password_len == 64){ + mosquitto__free(u->password); + u->password = (char *)password; + u->password_len = password_len; + }else{ + log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to decode password for user %s, removing entry.", u->username); + HASH_DEL(*unpwd, u); + } + }else{ + log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid password hash for user %s, removing entry.", u->username); + HASH_DEL(*unpwd, u); } - mosquitto__free(u->password); - u->password = (char *)password; - u->password_len = password_len; }else{ - log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid password hash for user %s.", u->username); - return MOSQ_ERR_INVAL; + log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to decode password salt for user %s, removing entry.", u->username); + HASH_DEL(*unpwd, u); } }else{ - log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid password hash for user %s.", u->username); - return MOSQ_ERR_INVAL; + log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid password hash for user %s, removing entry.", u->username); + HASH_DEL(*unpwd, u); } }else{ - log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid password hash for user %s.", u->username); - return MOSQ_ERR_INVAL; + log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid password hash for user %s, removing entry.", u->username); + HASH_DEL(*unpwd, u); } + }else{ + log__printf(NULL, MOSQ_LOG_ERR, "Error: Missing password hash for user %s, removing entry.", u->username); + HASH_DEL(*unpwd, u); } } #endif @@ -794,7 +854,12 @@ int mosquitto_unpwd_check_default(struct mosquitto_db *db, struct mosquitto *con if(!db->unpwd) return MOSQ_ERR_PLUGIN_DEFER; unpwd_ref = db->unpwd; } - if(!username) return MOSQ_ERR_INVAL; /* Check must be made only after checking unpwd_ref. */ + if(!username){ + /* Check must be made only after checking unpwd_ref. + * This is DENY here, because in MQTT v5 username can be missing when + * password is present, but we don't support that. */ + return MOSQ_ERR_AUTH; + } HASH_ITER(hh, unpwd_ref, u, tmp){ if(!strcmp(u->username, username)){ diff --git a/src/send_connack.c b/src/send_connack.c index 12b20724..5b12bbc8 100644 --- a/src/send_connack.c +++ b/src/send_connack.c @@ -17,36 +17,86 @@ Contributors: #include "config.h" #include "mosquitto_broker_internal.h" -#include "mqtt3_protocol.h" +#include "mqtt_protocol.h" #include "memory_mosq.h" #include "packet_mosq.h" +#include "property_mosq.h" #include "util_mosq.h" -int send__connack(struct mosquitto *context, int ack, int result) +int send__connack(struct mosquitto_db *db, struct mosquitto *context, int ack, int reason_code, const mosquitto_property *properties) { struct mosquitto__packet *packet = NULL; int rc; + mosquitto_property *connack_props = NULL; + int proplen, varbytes; + uint32_t remaining_length; + + rc = mosquitto_property_copy_all(&connack_props, properties); + if(rc){ + return rc; + } if(context){ if(context->id){ - log__printf(NULL, MOSQ_LOG_DEBUG, "Sending CONNACK to %s (%d, %d)", context->id, ack, result); + log__printf(NULL, MOSQ_LOG_DEBUG, "Sending CONNACK to %s (%d, %d)", context->id, ack, reason_code); }else{ - log__printf(NULL, MOSQ_LOG_DEBUG, "Sending CONNACK to %s (%d, %d)", context->address, ack, result); + log__printf(NULL, MOSQ_LOG_DEBUG, "Sending CONNACK to %s (%d, %d)", context->address, ack, reason_code); } } + remaining_length = 2; + + if(context->protocol == mosq_p_mqtt5){ + if(reason_code < 128 && db->config->retain_available == false){ + rc = mosquitto_property_add_byte(&connack_props, MQTT_PROP_RETAIN_AVAILABLE, 0); + if(rc){ + mosquitto_property_free_all(&connack_props); + return rc; + } + } + if(db->config->max_packet_size > 0){ + rc = mosquitto_property_add_int32(&connack_props, MQTT_PROP_MAXIMUM_PACKET_SIZE, db->config->max_packet_size); + if(rc){ + mosquitto_property_free_all(&connack_props); + return rc; + } + } + + /* FIXME - disable support until available */ + rc = mosquitto_property_add_byte(&connack_props, MQTT_PROP_SHARED_SUB_AVAILABLE, 0); + if(rc){ + mosquitto_property_free_all(&connack_props); + return rc; + } + + proplen = property__get_length_all(connack_props); + varbytes = packet__varint_bytes(proplen); + remaining_length += proplen + varbytes; + } + + if(packet__check_oversize(context, remaining_length)){ + mosquitto_property_free_all(&connack_props); + mosquitto__free(packet); + return MOSQ_ERR_OVERSIZE_PACKET; + } + packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet)); if(!packet) return MOSQ_ERR_NOMEM; - packet->command = CONNACK; - packet->remaining_length = 2; + packet->command = CMD_CONNACK; + packet->remaining_length = remaining_length; + rc = packet__alloc(packet); if(rc){ mosquitto__free(packet); return rc; } - packet->payload[packet->pos+0] = ack; - packet->payload[packet->pos+1] = result; + packet__write_byte(packet, ack); + packet__write_byte(packet, reason_code); + if(context->protocol == mosq_p_mqtt5){ + property__write_all(packet, connack_props, true); + } + mosquitto_property_free_all(&connack_props); return packet__queue(context, packet); } diff --git a/src/send_suback.c b/src/send_suback.c index adaeb16b..98c13bc7 100644 --- a/src/send_suback.c +++ b/src/send_suback.c @@ -17,9 +17,10 @@ Contributors: #include "config.h" #include "mosquitto_broker_internal.h" -#include "mqtt3_protocol.h" +#include "mqtt_protocol.h" #include "memory_mosq.h" #include "packet_mosq.h" +#include "property_mosq.h" #include "util_mosq.h" @@ -27,20 +28,33 @@ int send__suback(struct mosquitto *context, uint16_t mid, uint32_t payloadlen, c { struct mosquitto__packet *packet = NULL; int rc; + mosquitto_property *properties = NULL; + int proplen, varbytes; log__printf(NULL, MOSQ_LOG_DEBUG, "Sending SUBACK to %s", context->id); packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet)); if(!packet) return MOSQ_ERR_NOMEM; - packet->command = SUBACK; + packet->command = CMD_SUBACK; packet->remaining_length = 2+payloadlen; + if(context->protocol == mosq_p_mqtt5){ + proplen = property__get_length_all(properties); + varbytes = packet__varint_bytes(proplen); + packet->remaining_length += proplen + varbytes; + } rc = packet__alloc(packet); if(rc){ mosquitto__free(packet); return rc; } packet__write_uint16(packet, mid); + + if(context->protocol == mosq_p_mqtt5){ + /* We don't use Reason String or User Property yet. */ + property__write_all(packet, properties, true); + } + if(payloadlen){ packet__write_bytes(packet, payload, payloadlen); } diff --git a/src/send_unsuback.c b/src/send_unsuback.c new file mode 100644 index 00000000..9a310fac --- /dev/null +++ b/src/send_unsuback.c @@ -0,0 +1,61 @@ +/* +Copyright (c) 2009-2018 Roger Light + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Roger Light - initial implementation and documentation. +*/ + +#include "config.h" + +#include + +#include "mosquitto_broker_internal.h" +#include "mqtt_protocol.h" +#include "memory_mosq.h" +#include "packet_mosq.h" +#include "property_mosq.h" + + +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; + int proplen, varbytes; + + assert(mosq); + packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet)); + if(!packet) return MOSQ_ERR_NOMEM; + + packet->command = CMD_UNSUBACK; + packet->remaining_length = 2; + + if(mosq->protocol == mosq_p_mqtt5){ + proplen = property__get_length_all(properties); + varbytes = packet__varint_bytes(proplen); + packet->remaining_length += varbytes + proplen + reason_code_count; + } + + rc = packet__alloc(packet); + if(rc){ + mosquitto__free(packet); + return rc; + } + + packet__write_uint16(packet, mid); + + 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); +} diff --git a/src/service.c b/src/service.c index 11b4b018..c0fdc68a 100644 --- a/src/service.c +++ b/src/service.c @@ -27,6 +27,18 @@ SERVICE_STATUS_HANDLE service_handle = 0; static SERVICE_STATUS service_status; int main(int argc, char *argv[]); +static void print_error(void) +{ + char *buf; + + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, GetLastError(), LANG_NEUTRAL, &buf, 0, NULL); + + fprintf(stderr, "Error: %s\n", buf); + LocalFree(buf); +} + + /* Service control callback */ void __stdcall service_handler(DWORD fdwControl) { @@ -112,8 +124,12 @@ void service_install(void) svc_desc.lpDescription = "MQTT v3.1.1 broker"; ChangeServiceConfig2(svc_handle, SERVICE_CONFIG_DESCRIPTION, &svc_desc); CloseServiceHandle(svc_handle); + }else{ + print_error(); } CloseServiceHandle(sc_manager); + } else { + print_error(); } } @@ -132,8 +148,12 @@ void service_uninstall(void) } } CloseServiceHandle(svc_handle); + }else{ + print_error(); } CloseServiceHandle(sc_manager); + }else{ + print_error(); } } diff --git a/src/subs.c b/src/subs.c index 5953055c..ad71b73f 100644 --- a/src/subs.c +++ b/src/subs.c @@ -53,6 +53,7 @@ Contributors: #include "mosquitto_broker_internal.h" #include "memory_mosq.h" +#include "mqtt_protocol.h" #include "util_mosq.h" struct sub__token { @@ -69,6 +70,7 @@ static int subs__process(struct mosquitto_db *db, struct mosquitto__subhier *hie uint16_t mid; struct mosquitto__subleaf *leaf; bool client_retain; + mosquitto_property *properties = NULL; leaf = hier->subs; @@ -97,7 +99,7 @@ static int subs__process(struct mosquitto_db *db, struct mosquitto__subhier *hie } } while(source_id && leaf){ - if(!leaf->context->id || (leaf->context->is_bridge && !strcmp(leaf->context->id, source_id))){ + if(!leaf->context->id || (leaf->no_local && !strcmp(leaf->context->id, source_id))){ leaf = leaf->next; continue; } @@ -123,17 +125,15 @@ static int subs__process(struct mosquitto_db *db, struct mosquitto__subhier *hie }else{ mid = 0; } - if(leaf->context->is_bridge){ - /* If we know the client is a bridge then we should set retain - * even if the message is fresh. If we don't do this, retained - * messages won't be propagated. */ + if(leaf->retain_as_published){ client_retain = retain; }else{ - /* Client is not a bridge and this isn't a stale message so - * retain should be false. */ client_retain = false; } - if(db__message_insert(db, leaf->context, mid, mosq_md_out, msg_qos, client_retain, stored) == 1) rc = 1; + if(leaf->identifier){ + mosquitto_property_add_varint(&properties, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, leaf->identifier); + } + if(db__message_insert(db, leaf->context, mid, mosq_md_out, msg_qos, client_retain, stored, properties) == 1) rc = 1; }else{ return 1; /* Application error */ } @@ -245,7 +245,7 @@ static void sub__topic_tokens_free(struct sub__token *tokens) } } -static int sub__add_recurse(struct mosquitto_db *db, struct mosquitto *context, int qos, struct mosquitto__subhier *subhier, struct sub__token *tokens) +static int sub__add_recurse(struct mosquitto_db *db, struct mosquitto *context, int qos, uint32_t identifier, int options, struct mosquitto__subhier *subhier, struct sub__token *tokens) /* FIXME - this function has the potential to leak subhier, audit calling functions. */ { struct mosquitto__subhier *branch; @@ -260,15 +260,16 @@ static int sub__add_recurse(struct mosquitto_db *db, struct mosquitto *context, while(leaf){ if(leaf->context && leaf->context->id && !strcmp(leaf->context->id, context->id)){ /* Client making a second subscription to same topic. Only - * need to update QoS. Return -1 to indicate this to the - * calling function. */ + * need to update QoS. Return MOSQ_ERR_SUB_EXISTS to + * indicate this to the calling function. */ leaf->qos = qos; - if(context->protocol == mosq_p_mqtt31){ - return -1; + leaf->identifier = identifier; + if(context->protocol == mosq_p_mqtt31 || context->protocol == mosq_p_mqtt5){ + return MOSQ_ERR_SUB_EXISTS; }else{ - /* mqttv311 requires retained messages are resent on + /* mqttv311/mqttv5 requires retained messages are resent on * resubscribe. */ - return 0; + return MOSQ_ERR_SUCCESS; } } last_leaf = leaf; @@ -279,6 +280,9 @@ static int sub__add_recurse(struct mosquitto_db *db, struct mosquitto *context, leaf->next = NULL; leaf->context = context; leaf->qos = qos; + leaf->identifier = identifier; + leaf->no_local = ((options & MQTT_SUB_OPT_NO_LOCAL) != 0); + leaf->retain_as_published = ((options & MQTT_SUB_OPT_RETAIN_AS_PUBLISHED) != 0); for(i=0; isub_count; i++){ if(!context->subs[i]){ context->subs[i] = subhier; @@ -311,17 +315,17 @@ static int sub__add_recurse(struct mosquitto_db *db, struct mosquitto *context, HASH_FIND(hh, subhier->children, UHPA_ACCESS_TOPIC(tokens), tokens->topic_len, branch); if(branch){ - return sub__add_recurse(db, context, qos, branch, tokens->next); + return sub__add_recurse(db, context, qos, identifier, options, branch, tokens->next); }else{ /* Not found */ branch = sub__add_hier_entry(subhier, &subhier->children, UHPA_ACCESS_TOPIC(tokens), tokens->topic_len); if(!branch) return MOSQ_ERR_NOMEM; - return sub__add_recurse(db, context, qos, branch, tokens->next); + return sub__add_recurse(db, context, qos, identifier, options, branch, tokens->next); } } -static int sub__remove_recurse(struct mosquitto_db *db, struct mosquitto *context, struct mosquitto__subhier *subhier, struct sub__token *tokens) +static int sub__remove_recurse(struct mosquitto_db *db, struct mosquitto *context, struct mosquitto__subhier *subhier, struct sub__token *tokens, uint8_t *reason) { struct mosquitto__subhier *branch; struct mosquitto__subleaf *leaf; @@ -354,6 +358,7 @@ static int sub__remove_recurse(struct mosquitto_db *db, struct mosquitto *contex break; } } + *reason = 0; return MOSQ_ERR_SUCCESS; } leaf = leaf->next; @@ -363,7 +368,7 @@ static int sub__remove_recurse(struct mosquitto_db *db, struct mosquitto *contex HASH_FIND(hh, subhier->children, UHPA_ACCESS_TOPIC(tokens), tokens->topic_len, branch); if(branch){ - sub__remove_recurse(db, context, branch, tokens->next); + sub__remove_recurse(db, context, branch, tokens->next, reason); if(!branch->children && !branch->subs && !branch->retained){ HASH_DELETE(hh, subhier->children, branch); UHPA_FREE_TOPIC(branch); @@ -446,7 +451,7 @@ struct mosquitto__subhier *sub__add_hier_entry(struct mosquitto__subhier *parent } -int sub__add(struct mosquitto_db *db, struct mosquitto *context, const char *sub, int qos, struct mosquitto__subhier **root) +int sub__add(struct mosquitto_db *db, struct mosquitto *context, const char *sub, int qos, uint32_t identifier, int options, struct mosquitto__subhier **root) { int rc = 0; struct mosquitto__subhier *subhier; @@ -468,16 +473,14 @@ int sub__add(struct mosquitto_db *db, struct mosquitto *context, const char *sub } } - rc = sub__add_recurse(db, context, qos, subhier, tokens); + rc = sub__add_recurse(db, context, qos, identifier, options, subhier, tokens); sub__topic_tokens_free(tokens); - /* We aren't worried about -1 (already subscribed) return codes. */ - if(rc == -1) rc = MOSQ_ERR_SUCCESS; return rc; } -int sub__remove(struct mosquitto_db *db, struct mosquitto *context, const char *sub, struct mosquitto__subhier *root) +int sub__remove(struct mosquitto_db *db, struct mosquitto *context, const char *sub, struct mosquitto__subhier *root, uint8_t *reason) { int rc = 0; struct mosquitto__subhier *subhier; @@ -490,7 +493,8 @@ int sub__remove(struct mosquitto_db *db, struct mosquitto *context, const char * HASH_FIND(hh, root, UHPA_ACCESS_TOPIC(tokens), tokens->topic_len, subhier); if(subhier){ - rc = sub__remove_recurse(db, context, subhier, tokens); + *reason = MQTT_RC_NO_SUBSCRIPTION_EXISTED; + rc = sub__remove_recurse(db, context, subhier, tokens, reason); } sub__topic_tokens_free(tokens); @@ -521,7 +525,7 @@ int sub__messages_queue(struct mosquitto_db *db, const char *source_id, const ch /* We have a message that needs to be retained, so ensure that the subscription * tree for its topic exists. */ - sub__add_recurse(db, NULL, 0, subhier, tokens); + sub__add_recurse(db, NULL, 0, 0, 0, subhier, tokens); } sub__search(db, subhier, tokens, source_id, topic, qos, retain, *stored, true); } @@ -645,11 +649,24 @@ void sub__tree_print(struct mosquitto__subhier *root, int level) } } -static int retain__process(struct mosquitto_db *db, struct mosquitto_msg_store *retained, struct mosquitto *context, const char *sub, int sub_qos) +static int retain__process(struct mosquitto_db *db, struct mosquitto__subhier *branch, struct mosquitto *context, const char *sub, int sub_qos, uint32_t subscription_identifier, time_t now) { int rc = 0; int qos; uint16_t mid; + mosquitto_property *properties = NULL; + struct mosquitto_msg_store *retained; + + if(branch->retained->message_expiry_time > 0 && now > branch->retained->message_expiry_time){ + db__msg_store_deref(db, &branch->retained); + branch->retained = NULL; +#ifdef WITH_SYS_TREE + db->retained_count--; +#endif + return MOSQ_ERR_SUCCESS; + } + + retained = branch->retained; rc = mosquitto_acl_check(db, context, retained->topic, retained->payloadlen, UHPA_ACCESS(retained->payload, retained->payloadlen), retained->qos, retained->retain, MOSQ_ACL_READ); @@ -659,6 +676,27 @@ static int retain__process(struct mosquitto_db *db, struct mosquitto_msg_store * return rc; } + /* Check for original source access */ + if(db->config->check_retain_source && retained->source_id){ + struct mosquitto retain_ctxt; + memset(&retain_ctxt, 0, sizeof(struct mosquitto)); + + retain_ctxt.id = retained->source_id; + retain_ctxt.username = retained->source_username; + retain_ctxt.listener = retained->source_listener; + + rc = acl__find_acls(db, &retain_ctxt); + if(rc) return rc; + + rc = mosquitto_acl_check(db, &retain_ctxt, retained->topic, retained->payloadlen, UHPA_ACCESS(retained->payload, retained->payloadlen), + retained->qos, retained->retain, MOSQ_ACL_WRITE); + if(rc == MOSQ_ERR_ACL_DENIED){ + return MOSQ_ERR_SUCCESS; + }else if(rc != MOSQ_ERR_SUCCESS){ + return rc; + } + } + if (db->config->upgrade_outgoing_qos){ qos = sub_qos; } else { @@ -670,10 +708,13 @@ static int retain__process(struct mosquitto_db *db, struct mosquitto_msg_store * }else{ mid = 0; } - return db__message_insert(db, context, mid, mosq_md_out, qos, true, retained); + if(subscription_identifier > 0){ + mosquitto_property_add_varint(&properties, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, subscription_identifier); + } + return db__message_insert(db, context, mid, mosq_md_out, qos, true, retained, properties); } -static int retain__search(struct mosquitto_db *db, struct mosquitto__subhier *subhier, struct sub__token *tokens, struct mosquitto *context, const char *sub, int sub_qos, int level) +static int retain__search(struct mosquitto_db *db, struct mosquitto__subhier *subhier, struct sub__token *tokens, struct mosquitto *context, const char *sub, int sub_qos, uint32_t subscription_identifier, time_t now, int level) { struct mosquitto__subhier *branch, *branch_tmp; int flag = 0; @@ -689,25 +730,25 @@ static int retain__search(struct mosquitto_db *db, struct mosquitto__subhier *su */ flag = -1; if(branch->retained){ - retain__process(db, branch->retained, context, sub, sub_qos); + retain__process(db, branch, context, sub, sub_qos, subscription_identifier, now); } if(branch->children){ - retain__search(db, branch, tokens, context, sub, sub_qos, level+1); + retain__search(db, branch, tokens, context, sub, sub_qos, subscription_identifier, now, level+1); } }else if(strcmp(UHPA_ACCESS_TOPIC(branch), "+") && (!strcmp(UHPA_ACCESS_TOPIC(branch), UHPA_ACCESS_TOPIC(tokens)) || !strcmp(UHPA_ACCESS_TOPIC(tokens), "+"))){ if(tokens->next){ - if(retain__search(db, branch, tokens->next, context, sub, sub_qos, level+1) == -1 + if(retain__search(db, branch, tokens->next, context, sub, sub_qos, subscription_identifier, now, level+1) == -1 || (!branch_tmp && tokens->next && !strcmp(UHPA_ACCESS_TOPIC(tokens->next), "#") && level>0)){ if(branch->retained){ - retain__process(db, branch->retained, context, sub, sub_qos); + retain__process(db, branch, context, sub, sub_qos, subscription_identifier, now); } } }else{ if(branch->retained){ - retain__process(db, branch->retained, context, sub, sub_qos); + retain__process(db, branch, context, sub, sub_qos, subscription_identifier, now); } } } @@ -715,10 +756,11 @@ static int retain__search(struct mosquitto_db *db, struct mosquitto__subhier *su return flag; } -int sub__retain_queue(struct mosquitto_db *db, struct mosquitto *context, const char *sub, int sub_qos) +int sub__retain_queue(struct mosquitto_db *db, struct mosquitto *context, const char *sub, int sub_qos, uint32_t subscription_identifier) { struct mosquitto__subhier *subhier; struct sub__token *tokens = NULL, *tail; + time_t now; assert(db); assert(context); @@ -729,7 +771,8 @@ int sub__retain_queue(struct mosquitto_db *db, struct mosquitto *context, const HASH_FIND(hh, db->subs, UHPA_ACCESS_TOPIC(tokens), tokens->topic_len, subhier); if(subhier){ - retain__search(db, subhier, tokens, context, sub, sub_qos, 0); + now = time(NULL); + retain__search(db, subhier, tokens, context, sub, sub_qos, subscription_identifier, now, 0); } while(tokens){ tail = tokens->next; diff --git a/src/sys_tree.c b/src/sys_tree.c index 65800fcf..207f44c6 100644 --- a/src/sys_tree.c +++ b/src/sys_tree.c @@ -52,7 +52,7 @@ void sys_tree__init(struct mosquitto_db *db) /* Set static $SYS messages */ snprintf(buf, 64, "mosquitto version %s", VERSION); - db__messages_easy_queue(db, NULL, "$SYS/broker/version", SYS_TREE_QOS, strlen(buf), buf, 1); + db__messages_easy_queue(db, NULL, "$SYS/broker/version", SYS_TREE_QOS, strlen(buf), buf, 1, 60, NULL); } static void sys_tree__update_clients(struct mosquitto_db *db, char *buf) @@ -71,12 +71,12 @@ static void sys_tree__update_clients(struct mosquitto_db *db, char *buf) if(client_count != count_total){ client_count = count_total; snprintf(buf, BUFLEN, "%d", client_count); - db__messages_easy_queue(db, NULL, "$SYS/broker/clients/total", SYS_TREE_QOS, strlen(buf), buf, 1); + db__messages_easy_queue(db, NULL, "$SYS/broker/clients/total", SYS_TREE_QOS, strlen(buf), buf, 1, 60, NULL); if(client_count > client_max){ client_max = client_count; snprintf(buf, BUFLEN, "%d", client_max); - db__messages_easy_queue(db, NULL, "$SYS/broker/clients/maximum", SYS_TREE_QOS, strlen(buf), buf, 1); + db__messages_easy_queue(db, NULL, "$SYS/broker/clients/maximum", SYS_TREE_QOS, strlen(buf), buf, 1, 60, NULL); } } @@ -90,19 +90,19 @@ static void sys_tree__update_clients(struct mosquitto_db *db, char *buf) disconnected_count = 0; } snprintf(buf, BUFLEN, "%d", disconnected_count); - db__messages_easy_queue(db, NULL, "$SYS/broker/clients/inactive", SYS_TREE_QOS, strlen(buf), buf, 1); - db__messages_easy_queue(db, NULL, "$SYS/broker/clients/disconnected", SYS_TREE_QOS, strlen(buf), buf, 1); + db__messages_easy_queue(db, NULL, "$SYS/broker/clients/inactive", SYS_TREE_QOS, strlen(buf), buf, 1, 60, NULL); + db__messages_easy_queue(db, NULL, "$SYS/broker/clients/disconnected", SYS_TREE_QOS, strlen(buf), buf, 1, 60, NULL); } if(connected_count != count_by_sock){ connected_count = count_by_sock; snprintf(buf, BUFLEN, "%d", connected_count); - db__messages_easy_queue(db, NULL, "$SYS/broker/clients/active", SYS_TREE_QOS, strlen(buf), buf, 1); - db__messages_easy_queue(db, NULL, "$SYS/broker/clients/connected", SYS_TREE_QOS, strlen(buf), buf, 1); + db__messages_easy_queue(db, NULL, "$SYS/broker/clients/active", SYS_TREE_QOS, strlen(buf), buf, 1, 60, NULL); + db__messages_easy_queue(db, NULL, "$SYS/broker/clients/connected", SYS_TREE_QOS, strlen(buf), buf, 1, 60, NULL); } if(g_clients_expired != clients_expired){ clients_expired = g_clients_expired; snprintf(buf, BUFLEN, "%d", clients_expired); - db__messages_easy_queue(db, NULL, "$SYS/broker/clients/expired", SYS_TREE_QOS, strlen(buf), buf, 1); + db__messages_easy_queue(db, NULL, "$SYS/broker/clients/expired", SYS_TREE_QOS, strlen(buf), buf, 1, 60, NULL); } } @@ -117,13 +117,13 @@ static void sys_tree__update_memory(struct mosquitto_db *db, char *buf) if(current_heap != value_ul){ current_heap = value_ul; snprintf(buf, BUFLEN, "%lu", current_heap); - db__messages_easy_queue(db, NULL, "$SYS/broker/heap/current", SYS_TREE_QOS, strlen(buf), buf, 1); + db__messages_easy_queue(db, NULL, "$SYS/broker/heap/current", SYS_TREE_QOS, strlen(buf), buf, 1, 60, NULL); } value_ul =mosquitto__max_memory_used(); if(max_heap != value_ul){ max_heap = value_ul; snprintf(buf, BUFLEN, "%lu", max_heap); - db__messages_easy_queue(db, NULL, "$SYS/broker/heap/maximum", SYS_TREE_QOS, strlen(buf), buf, 1); + db__messages_easy_queue(db, NULL, "$SYS/broker/heap/maximum", SYS_TREE_QOS, strlen(buf), buf, 1, 60, NULL); } } #endif @@ -135,12 +135,12 @@ static void calc_load(struct mosquitto_db *db, char *buf, const char *topic, boo if (initial) { new_value = *current; snprintf(buf, BUFLEN, "%.2f", new_value); - db__messages_easy_queue(db, NULL, topic, SYS_TREE_QOS, strlen(buf), buf, 1); + db__messages_easy_queue(db, NULL, topic, SYS_TREE_QOS, strlen(buf), buf, 1, 60, NULL); } else { new_value = interval + exponent*((*current) - interval); if(fabs(new_value - (*current)) >= 0.01){ snprintf(buf, BUFLEN, "%.2f", new_value); - db__messages_easy_queue(db, NULL, topic, SYS_TREE_QOS, strlen(buf), buf, 1); + db__messages_easy_queue(db, NULL, topic, SYS_TREE_QOS, strlen(buf), buf, 1, 60, NULL); } } (*current) = new_value; @@ -218,7 +218,7 @@ void sys_tree__update(struct mosquitto_db *db, int interval, time_t start_time) if(interval && now - interval > last_update){ uptime = now - start_time; snprintf(buf, BUFLEN, "%d seconds", (int)uptime); - db__messages_easy_queue(db, NULL, "$SYS/broker/uptime", SYS_TREE_QOS, strlen(buf), buf, 1); + db__messages_easy_queue(db, NULL, "$SYS/broker/uptime", SYS_TREE_QOS, strlen(buf), buf, 1, 60, NULL); sys_tree__update_clients(db, buf); bool initial_publish = false; @@ -287,26 +287,26 @@ void sys_tree__update(struct mosquitto_db *db, int interval, time_t start_time) if(db->msg_store_count != msg_store_count){ msg_store_count = db->msg_store_count; snprintf(buf, BUFLEN, "%d", msg_store_count); - db__messages_easy_queue(db, NULL, "$SYS/broker/messages/stored", SYS_TREE_QOS, strlen(buf), buf, 1); - db__messages_easy_queue(db, NULL, "$SYS/broker/store/messages/count", SYS_TREE_QOS, strlen(buf), buf, 1); + db__messages_easy_queue(db, NULL, "$SYS/broker/messages/stored", SYS_TREE_QOS, strlen(buf), buf, 1, 60, NULL); + db__messages_easy_queue(db, NULL, "$SYS/broker/store/messages/count", SYS_TREE_QOS, strlen(buf), buf, 1, 60, NULL); } if (db->msg_store_bytes != msg_store_bytes){ msg_store_bytes = db->msg_store_bytes; snprintf(buf, BUFLEN, "%lu", msg_store_bytes); - db__messages_easy_queue(db, NULL, "$SYS/broker/store/messages/bytes", SYS_TREE_QOS, strlen(buf), buf, 1); + db__messages_easy_queue(db, NULL, "$SYS/broker/store/messages/bytes", SYS_TREE_QOS, strlen(buf), buf, 1, 60, NULL); } if(db->subscription_count != subscription_count){ subscription_count = db->subscription_count; snprintf(buf, BUFLEN, "%d", subscription_count); - db__messages_easy_queue(db, NULL, "$SYS/broker/subscriptions/count", SYS_TREE_QOS, strlen(buf), buf, 1); + db__messages_easy_queue(db, NULL, "$SYS/broker/subscriptions/count", SYS_TREE_QOS, strlen(buf), buf, 1, 60, NULL); } if(db->retained_count != retained_count){ retained_count = db->retained_count; snprintf(buf, BUFLEN, "%d", retained_count); - db__messages_easy_queue(db, NULL, "$SYS/broker/retained messages/count", SYS_TREE_QOS, strlen(buf), buf, 1); + db__messages_easy_queue(db, NULL, "$SYS/broker/retained messages/count", SYS_TREE_QOS, strlen(buf), buf, 1, 60, NULL); } #ifdef REAL_WITH_MEMORY_TRACKING @@ -316,55 +316,55 @@ void sys_tree__update(struct mosquitto_db *db, int interval, time_t start_time) if(msgs_received != g_msgs_received){ msgs_received = g_msgs_received; snprintf(buf, BUFLEN, "%lu", msgs_received); - db__messages_easy_queue(db, NULL, "$SYS/broker/messages/received", SYS_TREE_QOS, strlen(buf), buf, 1); + db__messages_easy_queue(db, NULL, "$SYS/broker/messages/received", SYS_TREE_QOS, strlen(buf), buf, 1, 60, NULL); } if(msgs_sent != g_msgs_sent){ msgs_sent = g_msgs_sent; snprintf(buf, BUFLEN, "%lu", msgs_sent); - db__messages_easy_queue(db, NULL, "$SYS/broker/messages/sent", SYS_TREE_QOS, strlen(buf), buf, 1); + db__messages_easy_queue(db, NULL, "$SYS/broker/messages/sent", SYS_TREE_QOS, strlen(buf), buf, 1, 60, NULL); } if(publish_dropped != g_msgs_dropped){ publish_dropped = g_msgs_dropped; snprintf(buf, BUFLEN, "%lu", publish_dropped); - db__messages_easy_queue(db, NULL, "$SYS/broker/publish/messages/dropped", SYS_TREE_QOS, strlen(buf), buf, 1); + db__messages_easy_queue(db, NULL, "$SYS/broker/publish/messages/dropped", SYS_TREE_QOS, strlen(buf), buf, 1, 60, NULL); } if(pub_msgs_received != g_pub_msgs_received){ pub_msgs_received = g_pub_msgs_received; snprintf(buf, BUFLEN, "%lu", pub_msgs_received); - db__messages_easy_queue(db, NULL, "$SYS/broker/publish/messages/received", SYS_TREE_QOS, strlen(buf), buf, 1); + db__messages_easy_queue(db, NULL, "$SYS/broker/publish/messages/received", SYS_TREE_QOS, strlen(buf), buf, 1, 60, NULL); } if(pub_msgs_sent != g_pub_msgs_sent){ pub_msgs_sent = g_pub_msgs_sent; snprintf(buf, BUFLEN, "%lu", pub_msgs_sent); - db__messages_easy_queue(db, NULL, "$SYS/broker/publish/messages/sent", SYS_TREE_QOS, strlen(buf), buf, 1); + db__messages_easy_queue(db, NULL, "$SYS/broker/publish/messages/sent", SYS_TREE_QOS, strlen(buf), buf, 1, 60, NULL); } if(bytes_received != g_bytes_received){ bytes_received = g_bytes_received; snprintf(buf, BUFLEN, "%llu", bytes_received); - db__messages_easy_queue(db, NULL, "$SYS/broker/bytes/received", SYS_TREE_QOS, strlen(buf), buf, 1); + db__messages_easy_queue(db, NULL, "$SYS/broker/bytes/received", SYS_TREE_QOS, strlen(buf), buf, 1, 60, NULL); } if(bytes_sent != g_bytes_sent){ bytes_sent = g_bytes_sent; snprintf(buf, BUFLEN, "%llu", bytes_sent); - db__messages_easy_queue(db, NULL, "$SYS/broker/bytes/sent", SYS_TREE_QOS, strlen(buf), buf, 1); + db__messages_easy_queue(db, NULL, "$SYS/broker/bytes/sent", SYS_TREE_QOS, strlen(buf), buf, 1, 60, NULL); } if(pub_bytes_received != g_pub_bytes_received){ pub_bytes_received = g_pub_bytes_received; snprintf(buf, BUFLEN, "%llu", pub_bytes_received); - db__messages_easy_queue(db, NULL, "$SYS/broker/publish/bytes/received", SYS_TREE_QOS, strlen(buf), buf, 1); + db__messages_easy_queue(db, NULL, "$SYS/broker/publish/bytes/received", SYS_TREE_QOS, strlen(buf), buf, 1, 60, NULL); } if(pub_bytes_sent != g_pub_bytes_sent){ pub_bytes_sent = g_pub_bytes_sent; snprintf(buf, BUFLEN, "%llu", pub_bytes_sent); - db__messages_easy_queue(db, NULL, "$SYS/broker/publish/bytes/sent", SYS_TREE_QOS, strlen(buf), buf, 1); + db__messages_easy_queue(db, NULL, "$SYS/broker/publish/bytes/sent", SYS_TREE_QOS, strlen(buf), buf, 1, 60, NULL); } last_update = mosquitto_time(); diff --git a/src/websockets.c b/src/websockets.c index bf2804b8..8362a237 100644 --- a/src/websockets.c +++ b/src/websockets.c @@ -34,13 +34,14 @@ POSSIBILITY OF SUCH DAMAGE. #include #include "mosquitto_internal.h" #include "mosquitto_broker_internal.h" -#include "mqtt3_protocol.h" +#include "mqtt_protocol.h" #include "memory_mosq.h" #include "packet_mosq.h" #include "sys_tree.h" #include #include +#include #include extern struct mosquitto_db int_db; @@ -229,7 +230,9 @@ static int callback_mqtt(struct libwebsocket_context *context, return -1; } if(mosq->listener->max_connections > 0 && mosq->listener->client_count > mosq->listener->max_connections){ - log__printf(NULL, MOSQ_LOG_NOTICE, "Client connection from %s denied: max_connections exceeded.", mosq->address); + if(db->config->connection_messages == true){ + log__printf(NULL, MOSQ_LOG_NOTICE, "Client connection from %s denied: max_connections exceeded.", mosq->address); + } mosquitto__free(mosq); u->mosq = NULL; return -1; @@ -244,7 +247,7 @@ static int callback_mqtt(struct libwebsocket_context *context, } mosq = u->mosq; if(mosq){ - if(mosq->sock > 0){ + if(mosq->sock != INVALID_SOCKET){ HASH_DELETE(hh_sock, db->contexts_by_sock, mosq); mosq->sock = INVALID_SOCKET; mosq->pollfd_index = -1; @@ -310,7 +313,7 @@ static int callback_mqtt(struct libwebsocket_context *context, #ifdef WITH_SYS_TREE g_msgs_sent++; - if(((packet->command)&0xF6) == PUBLISH){ + if(((packet->command)&0xF6) == CMD_PUBLISH){ g_pub_msgs_sent++; } #endif @@ -350,7 +353,7 @@ static int callback_mqtt(struct libwebsocket_context *context, mosq->in_packet.command = buf[pos]; pos++; /* Clients must send CONNECT as their first command. */ - if(mosq->state == mosq_cs_new && (mosq->in_packet.command&0xF0) != CONNECT){ + if(mosq->state == mosq_cs_new && (mosq->in_packet.command&0xF0) != CMD_CONNECT){ return -1; } } @@ -401,7 +404,7 @@ static int callback_mqtt(struct libwebsocket_context *context, #ifdef WITH_SYS_TREE G_MSGS_RECEIVED_INC(1); - if(((mosq->in_packet.command)&0xF5) == PUBLISH){ + if(((mosq->in_packet.command)&0xF5) == CMD_PUBLISH){ G_PUB_MSGS_RECEIVED_INC(1); } #endif @@ -729,6 +732,9 @@ struct libwebsocket_context *mosq_websockets_init(struct mosquitto__listener *li #if LWS_LIBRARY_VERSION_MAJOR>1 info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; #endif + if(listener->socket_domain == AF_INET){ + info.options |= LWS_SERVER_OPTION_DISABLE_IPV6; + } user = mosquitto__calloc(1, sizeof(struct libws_mqtt_hack)); if(!user){ diff --git a/test/Makefile b/test/Makefile index ba68201a..9b3849bd 100644 --- a/test/Makefile +++ b/test/Makefile @@ -7,6 +7,7 @@ all : test : $(MAKE) -C broker test $(MAKE) -C lib test + $(MAKE) -C unit test ptest : $(MAKE) -C broker ptest diff --git a/test/broker/01-connect-anon-denied.py b/test/broker/01-connect-anon-denied.py index b2c761f5..8df8518d 100755 --- a/test/broker/01-connect-anon-denied.py +++ b/test/broker/01-connect-anon-denied.py @@ -2,13 +2,7 @@ # Test whether an anonymous connection is correctly denied. -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: diff --git a/test/broker/01-connect-bad-packet.py b/test/broker/01-connect-bad-packet.py new file mode 100755 index 00000000..723e07e5 --- /dev/null +++ b/test/broker/01-connect-bad-packet.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python + +# Test whether a first packet of non-CONNECT is rejected. + +from mosq_test_helper import * + +rc = 1 +mid = 2 +publish_packet = mosq_test.gen_publish("pub/qos1/test", qos=1, mid=mid, payload="message") +puback_packet = mosq_test.gen_puback(mid) + + +port = mosq_test.get_port() +broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) + +try: + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.settimeout(5) + sock.connect(("localhost", port)) + sock.send(publish_packet) + data = sock.recv(1) + sock.close() + if len(data) == 0: + rc = 0 +except socket.error: + rc = 0 +finally: + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde) + +exit(rc) + diff --git a/test/broker/01-connect-disconnect-v5.py b/test/broker/01-connect-disconnect-v5.py new file mode 100755 index 00000000..96dd4d89 --- /dev/null +++ b/test/broker/01-connect-disconnect-v5.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python + +# loop through the different v5 DISCONNECT reason_code/properties options. + +from mosq_test_helper import * + +def disco_test(test, disconnect_packet): + global rc + + sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) + sock.send(disconnect_packet) + sock.close() + rc -= 1 + + +rc = 4 +keepalive = 10 +connect_packet = mosq_test.gen_connect("connect-disconnect-test", proto_ver=5, keepalive=keepalive) +connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) + +port = mosq_test.get_port() +broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) + + +try: + # No reason code, no properties, len=0 + disconnect_packet = mosq_test.gen_disconnect(proto_ver=5) + disco_test("disco len=0", disconnect_packet) + + # Reason code, no properties, len=1 + disconnect_packet = mosq_test.gen_disconnect(proto_ver=5, reason_code=0) + disco_test("disco len=1", disconnect_packet) + + # Reason code, empty properties, len=2 + disconnect_packet = mosq_test.gen_disconnect(proto_ver=5, reason_code=0, properties="") + disco_test("disco len=2", disconnect_packet) + + # Reason code, one property, len>2 + props = mqtt5_props.gen_string_pair_prop(mqtt5_props.PROP_USER_PROPERTY, "key", "value") + disconnect_packet = mosq_test.gen_disconnect(proto_ver=5, reason_code=0, properties=props) + disco_test("disco len>2", disconnect_packet) +finally: + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde) + +if rc != 0: + print(test) + exit(rc) diff --git a/test/broker/01-connect-duplicate-v5.py b/test/broker/01-connect-duplicate-v5.py new file mode 100755 index 00000000..a7640e7c --- /dev/null +++ b/test/broker/01-connect-duplicate-v5.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python + +# Test whether a duplicate CONNECT is rejected. MQTT v5 + +from mosq_test_helper import * + +rc = 1 +keepalive = 10 +connect_packet = mosq_test.gen_connect("connect-test", keepalive=keepalive, proto_ver=5) +connack_packet = mosq_test.gen_connack(rc=0, 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.settimeout(3) + sock.send(connect_packet) + data = sock.recv(1) + if len(data) == 0: + rc = 0 +except socket.error: + rc = 0 +finally: + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde) + +exit(rc) + diff --git a/test/broker/01-connect-duplicate.py b/test/broker/01-connect-duplicate.py new file mode 100755 index 00000000..d99297b6 --- /dev/null +++ b/test/broker/01-connect-duplicate.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python + +# Test whether a duplicate CONNECT is rejected. + +from mosq_test_helper import * + +rc = 1 +keepalive = 10 +connect_packet = mosq_test.gen_connect("connect-test", keepalive=keepalive) +connack_packet = mosq_test.gen_connack(rc=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) + sock.settimeout(3) + sock.send(connect_packet) + data = sock.recv(1) + if len(data) == 0: + rc = 0 +except socket.error: + rc = 0 +finally: + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde) + +exit(rc) + diff --git a/test/broker/01-connect-invalid-id-0-311.py b/test/broker/01-connect-invalid-id-0-311.py index 4730c3b1..41a87701 100755 --- a/test/broker/01-connect-invalid-id-0-311.py +++ b/test/broker/01-connect-invalid-id-0-311.py @@ -2,13 +2,7 @@ # Test whether a CONNECT with a zero length client id results in the correct CONNACK packet. -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * rc = 1 keepalive = 10 diff --git a/test/broker/01-connect-invalid-id-0.py b/test/broker/01-connect-invalid-id-0.py index bed2d241..fff5d7af 100755 --- a/test/broker/01-connect-invalid-id-0.py +++ b/test/broker/01-connect-invalid-id-0.py @@ -2,13 +2,7 @@ # Test whether a CONNECT with a zero length client id results in the correct CONNACK packet. -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * rc = 1 keepalive = 10 diff --git a/test/broker/01-connect-invalid-id-missing.py b/test/broker/01-connect-invalid-id-missing.py index 279b9f50..63273e93 100755 --- a/test/broker/01-connect-invalid-id-missing.py +++ b/test/broker/01-connect-invalid-id-missing.py @@ -2,23 +2,18 @@ # Test whether a CONNECT with a zero length client id results in the correct CONNACK packet. -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * rc = 1 keepalive = 10 -connect_packet = mosq_test.gen_connect(None, keepalive=keepalive) +connect_packet = mosq_test.gen_connect(None, keepalive=keepalive, proto_ver=3) +connack_packet = mosq_test.gen_connack(rc=2) 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, "", port=port) + sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) sock.close() rc = 0 finally: diff --git a/test/broker/01-connect-invalid-id-utf8.py b/test/broker/01-connect-invalid-id-utf8.py index adb5a304..99ba1494 100755 --- a/test/broker/01-connect-invalid-id-utf8.py +++ b/test/broker/01-connect-invalid-id-utf8.py @@ -2,15 +2,7 @@ # Test whether a client id with invalid UTF-8 fails. -import time -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import struct -import mosq_test +from mosq_test_helper import * rc = 1 keepalive = 60 diff --git a/test/broker/01-connect-invalid-protonum.py b/test/broker/01-connect-invalid-protonum.py index 58ca6311..72b2ae90 100755 --- a/test/broker/01-connect-invalid-protonum.py +++ b/test/broker/01-connect-invalid-protonum.py @@ -2,13 +2,7 @@ # Test whether a CONNECT with an invalid protocol number results in the correct CONNACK packet. -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * rc = 1 keepalive = 10 diff --git a/test/broker/01-connect-invalid-reserved.py b/test/broker/01-connect-invalid-reserved.py index 1245805e..c98869bf 100755 --- a/test/broker/01-connect-invalid-reserved.py +++ b/test/broker/01-connect-invalid-reserved.py @@ -2,13 +2,7 @@ # Test whether a CONNECT with reserved set to 1 results in a disconnect. MQTT-3.1.2-3 -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * rc = 1 keepalive = 10 diff --git a/test/broker/01-connect-success-v5.py b/test/broker/01-connect-success-v5.py new file mode 100755 index 00000000..6533ce65 --- /dev/null +++ b/test/broker/01-connect-success-v5.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python + +# Test whether a valid CONNECT results in the correct CONNACK packet for MQTT v5. + +from mosq_test_helper import * + +rc = 1 +keepalive = 10 +connect_packet = mosq_test.gen_connect("connect-success-test", proto_ver=5, keepalive=keepalive) +connack_packet = mosq_test.gen_connack(rc=0, 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.close() + rc = 0 +finally: + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde) + +exit(rc) + diff --git a/test/broker/01-connect-success.py b/test/broker/01-connect-success.py index 45b39d9d..4d3959e1 100755 --- a/test/broker/01-connect-success.py +++ b/test/broker/01-connect-success.py @@ -2,14 +2,7 @@ # Test whether a valid CONNECT results in the correct CONNACK packet. -import inspect, os, sys - -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * rc = 1 keepalive = 10 diff --git a/test/broker/01-connect-uname-invalid-utf8.py b/test/broker/01-connect-uname-invalid-utf8.py index 0ddb44ea..21313789 100755 --- a/test/broker/01-connect-uname-invalid-utf8.py +++ b/test/broker/01-connect-uname-invalid-utf8.py @@ -2,15 +2,7 @@ # Test whether a username with invalid UTF-8 fails. -import time -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import struct -import mosq_test +from mosq_test_helper import * rc = 1 keepalive = 60 diff --git a/test/broker/01-connect-uname-no-flag.py b/test/broker/01-connect-uname-no-flag.py index 8eb626a4..a5467927 100755 --- a/test/broker/01-connect-uname-no-flag.py +++ b/test/broker/01-connect-uname-no-flag.py @@ -3,14 +3,7 @@ # Test whether a connection is disconnected if it provides a username but the # username flag is 0. -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import struct -import mosq_test +from mosq_test_helper import * rc = 1 keepalive = 10 diff --git a/test/broker/01-connect-uname-no-password-denied.py b/test/broker/01-connect-uname-no-password-denied.py index 9951f488..46885910 100755 --- a/test/broker/01-connect-uname-no-password-denied.py +++ b/test/broker/01-connect-uname-no-password-denied.py @@ -3,13 +3,7 @@ # Test whether a connection is denied if it provides just a username when it # needs a username and password. -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: diff --git a/test/broker/01-connect-uname-password-denied.py b/test/broker/01-connect-uname-password-denied.py index 1c9236f6..4ee76652 100755 --- a/test/broker/01-connect-uname-password-denied.py +++ b/test/broker/01-connect-uname-password-denied.py @@ -3,13 +3,7 @@ # Test whether a connection is denied if it provides a correct username but # incorrect password. -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: diff --git a/test/broker/01-connect-uname-password-success-no-tls.conf b/test/broker/01-connect-uname-password-success-no-tls.conf deleted file mode 100644 index 776e3045..00000000 --- a/test/broker/01-connect-uname-password-success-no-tls.conf +++ /dev/null @@ -1,3 +0,0 @@ -port 9999 -password_file 01-connect-uname-password-success-no-tls.pwfile -allow_anonymous false diff --git a/test/broker/01-connect-uname-password-success-no-tls.py b/test/broker/01-connect-uname-password-success-no-tls.py index 2f168e70..93de845e 100755 --- a/test/broker/01-connect-uname-password-success-no-tls.py +++ b/test/broker/01-connect-uname-password-success-no-tls.py @@ -3,13 +3,7 @@ # Test whether a connection is denied if it provides a correct username but # incorrect password. -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: diff --git a/test/broker/01-connect-uname-password-success.py b/test/broker/01-connect-uname-password-success.py index 2f168e70..93de845e 100755 --- a/test/broker/01-connect-uname-password-success.py +++ b/test/broker/01-connect-uname-password-success.py @@ -3,13 +3,7 @@ # Test whether a connection is denied if it provides a correct username but # incorrect password. -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: diff --git a/test/broker/01-connect-uname-pwd-no-flag.py b/test/broker/01-connect-uname-pwd-no-flag.py index 5ece7beb..f09f690e 100755 --- a/test/broker/01-connect-uname-pwd-no-flag.py +++ b/test/broker/01-connect-uname-pwd-no-flag.py @@ -3,14 +3,7 @@ # Test whether a connection is disconnected if it provides a password but the # password flag is 0. -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import struct -import mosq_test +from mosq_test_helper import * rc = 1 keepalive = 10 diff --git a/test/broker/02-subhier-crash.py b/test/broker/02-subhier-crash.py index b72605c0..b6cd0f9a 100755 --- a/test/broker/02-subhier-crash.py +++ b/test/broker/02-subhier-crash.py @@ -2,14 +2,7 @@ # Test related to https://github.com/eclipse/mosquitto/issues/505 -import time -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * rc = 1 keepalive = 60 diff --git a/test/broker/02-subpub-qos0-retain-as-publish.py b/test/broker/02-subpub-qos0-retain-as-publish.py new file mode 100755 index 00000000..d6436eef --- /dev/null +++ b/test/broker/02-subpub-qos0-retain-as-publish.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python + +# Test whether a client subscribed to a topic with retain-as-published set works as expected. +# MQTT v5 + +from mosq_test_helper import * + +rc = 1 +keepalive = 60 +connect_packet = mosq_test.gen_connect("subpub-qos1-test", keepalive=keepalive, proto_ver=5) +connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) + +mid = 530 +subscribe1_packet = mosq_test.gen_subscribe(mid, "subpub/normal", 0, proto_ver=5) +suback1_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) + +mid = 531 +subscribe2_packet = mosq_test.gen_subscribe(mid, "subpub/rap", 0 | mqtt5_opts.MQTT_SUB_OPT_RETAIN_AS_PUBLISHED, proto_ver=5) +suback2_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) + +publish1_packet = mosq_test.gen_publish("subpub/normal", qos=0, retain=True, payload="message", proto_ver=5) +publish2_packet = mosq_test.gen_publish("subpub/rap", qos=0, retain=True, payload="message", proto_ver=5) + +publish1r_packet = mosq_test.gen_publish("subpub/normal", qos=0, retain=False, payload="message", proto_ver=5) +publish2r_packet = mosq_test.gen_publish("subpub/rap", qos=0, retain=True, payload="message", proto_ver=5) + +mid = 1 +publish3_packet = mosq_test.gen_publish("subpub/receive", qos=1, mid=mid, payload="success", 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, timeout=20, port=port) + + mosq_test.do_send_receive(sock, subscribe1_packet, suback1_packet, "suback1") + mosq_test.do_send_receive(sock, subscribe2_packet, suback2_packet, "suback2") + + mosq_test.do_send_receive(sock, publish1_packet, publish1r_packet, "publish1") + mosq_test.do_send_receive(sock, publish2_packet, publish2r_packet, "publish2") + rc = 0 + + sock.close() +finally: + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde) + +exit(rc) + diff --git a/test/broker/02-subpub-qos0-send-retain.py b/test/broker/02-subpub-qos0-send-retain.py new file mode 100755 index 00000000..ed9ba735 --- /dev/null +++ b/test/broker/02-subpub-qos0-send-retain.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python + +# Test whether "send retain" subscribe options work +# MQTT v5 + +from mosq_test_helper import * + +rc = 1 +keepalive = 60 +connect_packet = mosq_test.gen_connect("subpub-test", keepalive=keepalive, proto_ver=5) +connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) + +mid = 530 +subscribe1_packet = mosq_test.gen_subscribe(mid, "subpub/always", 0 | mqtt5_opts.MQTT_SUB_OPT_SEND_RETAIN_ALWAYS, proto_ver=5) +suback1_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) + +mid = 531 +subscribe2_packet = mosq_test.gen_subscribe(mid, "subpub/new", 0 | mqtt5_opts.MQTT_SUB_OPT_SEND_RETAIN_NEW, proto_ver=5) +suback2_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) + +mid = 532 +subscribe3_packet = mosq_test.gen_subscribe(mid, "subpub/never", 0 | mqtt5_opts.MQTT_SUB_OPT_SEND_RETAIN_NEVER, proto_ver=5) +suback3_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) + + +publish1_packet = mosq_test.gen_publish("subpub/always", qos=0, retain=True, payload="message", proto_ver=5) +publish2_packet = mosq_test.gen_publish("subpub/new", qos=0, retain=True, payload="message", proto_ver=5) +publish3_packet = mosq_test.gen_publish("subpub/never", qos=0, retain=True, payload="message", proto_ver=5) + +publish1r1_packet = mosq_test.gen_publish("subpub/always", qos=0, retain=True, payload="message", proto_ver=5) +publish1r2_packet = mosq_test.gen_publish("subpub/always", qos=0, retain=True, payload="message", proto_ver=5) +publish2r1_packet = mosq_test.gen_publish("subpub/new", qos=0, retain=True, payload="message", proto_ver=5) +publish2r2_packet = mosq_test.gen_publish("subpub/new", qos=0, retain=False, payload="message", proto_ver=5) +publish3r1_packet = mosq_test.gen_publish("subpub/never", qos=0, retain=False, payload="message", proto_ver=5) +publish3r2_packet = mosq_test.gen_publish("subpub/never", qos=0, retain=False, payload="message", 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, timeout=5, port=port) + + sock.send(publish1_packet) + sock.send(publish2_packet) + sock.send(publish3_packet) + + # Don't expect a message after this + mosq_test.do_send_receive(sock, subscribe3_packet, suback3_packet, "suback3") + # Don't expect a message after this + mosq_test.do_send_receive(sock, subscribe3_packet, suback3_packet, "suback3") + + # Expect a message after this, because it is the first subscribe + mosq_test.do_send_receive(sock, subscribe2_packet, suback2_packet, "suback2") + if mosq_test.expect_packet(sock, "publish2r1", publish2r1_packet): + # Don't expect a message after this, it is the second subscribe + mosq_test.do_send_receive(sock, subscribe2_packet, suback2_packet, "suback2") + + # Always expect a message after this + mosq_test.do_send_receive(sock, subscribe1_packet, suback1_packet, "suback1") + if mosq_test.expect_packet(sock, "publish1r1", publish1r1_packet): + # Always expect a message after this + mosq_test.do_send_receive(sock, subscribe1_packet, suback1_packet, "suback1") + if mosq_test.expect_packet(sock, "publish1r1", publish1r2_packet): + rc = 0 + + sock.close() +finally: + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde) + +exit(rc) + diff --git a/test/broker/02-subpub-qos0-subscription-id.py b/test/broker/02-subpub-qos0-subscription-id.py new file mode 100755 index 00000000..c25dda54 --- /dev/null +++ b/test/broker/02-subpub-qos0-subscription-id.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python + +# Does setting and updating subscription identifiers work as expected? +# MQTT v5 + +from mosq_test_helper import * + +rc = 1 +keepalive = 60 +connect_packet = mosq_test.gen_connect("subpub-test", keepalive=keepalive, proto_ver=5) +connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) + +mid = 1 +props = mqtt5_props.gen_varint_prop(mqtt5_props.PROP_SUBSCRIPTION_IDENTIFIER, 1) +subscribe1_packet = mosq_test.gen_subscribe(mid, "subpub/id1", 0, proto_ver=5, properties=props) +suback1_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) + +mid = 2 +props = mqtt5_props.gen_varint_prop(mqtt5_props.PROP_SUBSCRIPTION_IDENTIFIER, 14) +subscribe2_packet = mosq_test.gen_subscribe(mid, "subpub/+/id2", 0, proto_ver=5, properties=props) +suback2_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) + +mid = 3 +subscribe3_packet = mosq_test.gen_subscribe(mid, "subpub/noid", 0, proto_ver=5) +suback3_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) + +# Updated version of subscribe1, now without a subscription identifier +mid = 4 +subscribe1u_packet = mosq_test.gen_subscribe(mid, "subpub/id1", 0, proto_ver=5) +suback1u_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) + +# Updated version of subscribe2, with a new subscription identifier +mid = 5 +props = mqtt5_props.gen_varint_prop(mqtt5_props.PROP_SUBSCRIPTION_IDENTIFIER, 19) +subscribe2u_packet = mosq_test.gen_subscribe(mid, "subpub/+/id2", 0, proto_ver=5, properties=props) +suback2u_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) + +# Updated version of subscribe3, now with a subscription identifier +mid = 6 +props = mqtt5_props.gen_varint_prop(mqtt5_props.PROP_SUBSCRIPTION_IDENTIFIER, 21) +subscribe3u_packet = mosq_test.gen_subscribe(mid, "subpub/noid", 0, proto_ver=5, properties=props) +suback3u_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) + + +publish1_packet = mosq_test.gen_publish("subpub/id1", qos=0, payload="message1", proto_ver=5) + +props = mqtt5_props.gen_varint_prop(mqtt5_props.PROP_SUBSCRIPTION_IDENTIFIER, 1) +publish1r_packet = mosq_test.gen_publish("subpub/id1", qos=0, payload="message1", proto_ver=5, properties=props) + +publish2_packet = mosq_test.gen_publish("subpub/test/id2", qos=0, payload="message2", proto_ver=5) +props = mqtt5_props.gen_varint_prop(mqtt5_props.PROP_SUBSCRIPTION_IDENTIFIER, 14) +publish2r_packet = mosq_test.gen_publish("subpub/test/id2", qos=0, payload="message2", proto_ver=5, properties=props) + +publish3_packet = mosq_test.gen_publish("subpub/noid", qos=0, payload="message3", proto_ver=5) + + +# Updated version of publish1r, now with no id +publish1ru_packet = mosq_test.gen_publish("subpub/id1", qos=0, payload="message1", proto_ver=5) + +# Updated verison of publish2r, with updated id +props = mqtt5_props.gen_varint_prop(mqtt5_props.PROP_SUBSCRIPTION_IDENTIFIER, 19) +publish2ru_packet = mosq_test.gen_publish("subpub/test/id2", qos=0, payload="message2", proto_ver=5, properties=props) + +# Updated version of publish3r, now with an id +props = mqtt5_props.gen_varint_prop(mqtt5_props.PROP_SUBSCRIPTION_IDENTIFIER, 21) +publish3ru_packet = mosq_test.gen_publish("subpub/noid", qos=0, payload="message3", proto_ver=5, properties=props) + + +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, timeout=20, port=port) + + mosq_test.do_send_receive(sock, subscribe1_packet, suback1_packet, "suback1") + mosq_test.do_send_receive(sock, subscribe2_packet, suback2_packet, "suback2") + mosq_test.do_send_receive(sock, subscribe3_packet, suback3_packet, "suback3") + + mosq_test.do_send_receive(sock, publish3_packet, publish3_packet, "publish3") + mosq_test.do_send_receive(sock, publish2_packet, publish2r_packet, "publish2") + mosq_test.do_send_receive(sock, publish1_packet, publish1r_packet, "publish1") + + # Now update the subscription identifiers + mosq_test.do_send_receive(sock, subscribe1u_packet, suback1u_packet, "suback1u") + mosq_test.do_send_receive(sock, subscribe2u_packet, suback2u_packet, "suback2u") + mosq_test.do_send_receive(sock, subscribe3u_packet, suback3u_packet, "suback3u") + + mosq_test.do_send_receive(sock, publish2_packet, publish2ru_packet, "publish2u") + mosq_test.do_send_receive(sock, publish3_packet, publish3ru_packet, "publish3u") + mosq_test.do_send_receive(sock, publish1_packet, publish1ru_packet, "publish1u") + + rc = 0 + + sock.close() +finally: + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde) + +exit(rc) + diff --git a/test/broker/02-subpub-qos0-topic-alias-unknown.py b/test/broker/02-subpub-qos0-topic-alias-unknown.py new file mode 100755 index 00000000..a79c19e2 --- /dev/null +++ b/test/broker/02-subpub-qos0-topic-alias-unknown.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python + +# Test whether "topic alias" works to the broker +# MQTT v5 + +from mosq_test_helper import * + +rc = 1 +keepalive = 60 +connect_packet = mosq_test.gen_connect("sub-test", keepalive=keepalive, proto_ver=5) +connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) + +props = mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_TOPIC_ALIAS, 3) +publish1_packet = mosq_test.gen_publish("", qos=0, payload="message", proto_ver=5, properties=props) + +disconnect_packet = mosq_test.gen_disconnect(reason_code=148, 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, timeout=5, port=port) + sock.send(publish1_packet) + + if mosq_test.expect_packet(sock, "disconnect", disconnect_packet): + rc = 0 + + sock.close() +finally: + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde) + +exit(rc) + diff --git a/test/broker/02-subpub-qos0-topic-alias.py b/test/broker/02-subpub-qos0-topic-alias.py new file mode 100755 index 00000000..89b86fbf --- /dev/null +++ b/test/broker/02-subpub-qos0-topic-alias.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python + +# Test whether "topic alias" works to the broker +# MQTT v5 + +from mosq_test_helper import * + +rc = 1 +keepalive = 60 +connect1_packet = mosq_test.gen_connect("sub-test", keepalive=keepalive, proto_ver=5) +connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=5) + +connect2_packet = mosq_test.gen_connect("pub-test", keepalive=keepalive, proto_ver=5) +connack2_packet = mosq_test.gen_connack(rc=0, proto_ver=5) + +mid = 1 +subscribe_packet = mosq_test.gen_subscribe(mid, "subpub/alias", 0, proto_ver=5) +suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) + +props = mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_TOPIC_ALIAS, 3) +publish1_packet = mosq_test.gen_publish("subpub/alias", qos=0, payload="message", proto_ver=5, properties=props) + +props = mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_TOPIC_ALIAS, 3) +publish2s_packet = mosq_test.gen_publish("", qos=0, payload="message", proto_ver=5, properties=props) +publish2r_packet = mosq_test.gen_publish("subpub/alias", qos=0, payload="message", proto_ver=5) + + +port = mosq_test.get_port() +broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) + +try: + sock1 = mosq_test.do_client_connect(connect1_packet, connack1_packet, timeout=5, port=port) + sock2 = mosq_test.do_client_connect(connect2_packet, connack2_packet, timeout=5, port=port) + + sock1.send(publish1_packet) + + mosq_test.do_send_receive(sock2, subscribe_packet, suback_packet, "suback") + + sock1.send(publish2s_packet) + + if mosq_test.expect_packet(sock2, "publish2r", publish2r_packet): + rc = 0 + + sock1.close() + sock2.close() +finally: + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde) + +exit(rc) + diff --git a/test/broker/02-subpub-qos0-v5.py b/test/broker/02-subpub-qos0-v5.py new file mode 100755 index 00000000..debb06f6 --- /dev/null +++ b/test/broker/02-subpub-qos0-v5.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python + +# Test whether a client subscribed to a topic receives its own message sent to that topic. +# MQTT v5 + +from mosq_test_helper import * + +rc = 1 +mid = 53 +keepalive = 60 +connect_packet = mosq_test.gen_connect("subpub-qos0-test", keepalive=keepalive, proto_ver=5) +connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) + +subscribe_packet = mosq_test.gen_subscribe(mid, "subpub/qos0", 0, proto_ver=5) +suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) + +publish_packet = mosq_test.gen_publish("subpub/qos0", qos=0, payload="message", 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, timeout=20, port=port) + + mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") + mosq_test.do_send_receive(sock, publish_packet, publish_packet, "publish") + + rc = 0 + + sock.close() +finally: + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde) + +exit(rc) + diff --git a/test/broker/02-subpub-qos0.py b/test/broker/02-subpub-qos0.py index 00cdb176..803dcb90 100755 --- a/test/broker/02-subpub-qos0.py +++ b/test/broker/02-subpub-qos0.py @@ -2,13 +2,7 @@ # Test whether a client subscribed to a topic receives its own message sent to that topic. -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * rc = 1 mid = 53 diff --git a/test/broker/02-subpub-qos1-bad-pubcomp.py b/test/broker/02-subpub-qos1-bad-pubcomp.py new file mode 100755 index 00000000..6c38d9b6 --- /dev/null +++ b/test/broker/02-subpub-qos1-bad-pubcomp.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python + +# Test what the broker does if receiving a PUBCOMP in response to a QoS 1 PUBLISH. + +import inspect, os, sys +# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder +cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) +if cmd_subfolder not in sys.path: + sys.path.insert(0, cmd_subfolder) + +import mosq_test +import time + +rc = 1 +keepalive = 60 + +connect_packet = mosq_test.gen_connect("subpub-qos1-test", keepalive=keepalive) +connack_packet = mosq_test.gen_connack(rc=0) + +mid = 1 +subscribe_packet = mosq_test.gen_subscribe(mid, "subpub/qos1", 1) +suback_packet = mosq_test.gen_suback(mid, 1) + +mid = 1 +publish_packet2 = mosq_test.gen_publish("subpub/qos1", qos=1, mid=mid, payload="message") + + +helper_connect = mosq_test.gen_connect("helper", keepalive=keepalive) +helper_connack = mosq_test.gen_connack(rc=0) + +mid = 1 +publish1s_packet = mosq_test.gen_publish("subpub/qos1", qos=1, mid=mid, payload="message") +puback1s_packet = mosq_test.gen_puback(mid) + +mid = 1 +publish1r_packet = mosq_test.gen_publish("subpub/qos1", qos=1, mid=mid, payload="message") +pubcomp1r_packet = mosq_test.gen_pubcomp(mid) + +pingreq_packet = mosq_test.gen_pingreq() +pingresp_packet = mosq_test.gen_pingresp() + +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, timeout=20, port=port) + mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") + + helper = mosq_test.do_client_connect(helper_connect, helper_connack, timeout=20, port=port) + mosq_test.do_send_receive(helper, publish1s_packet, puback1s_packet, "puback 1s") + helper.close() + + if mosq_test.expect_packet(sock, "publish 1r", publish1r_packet): + sock.send(pubcomp1r_packet) + sock.send(pingreq_packet) + p = sock.recv(len(pingresp_packet)) + if len(p) == 0: + rc = 0 + + sock.close() +finally: + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde) + +exit(rc) + diff --git a/test/broker/02-subpub-qos1-bad-pubrec.py b/test/broker/02-subpub-qos1-bad-pubrec.py new file mode 100755 index 00000000..9f926fd0 --- /dev/null +++ b/test/broker/02-subpub-qos1-bad-pubrec.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python + +# Test what the broker does if receiving a PUBREC in response to a QoS 1 PUBLISH. + +import inspect, os, sys +# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder +cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) +if cmd_subfolder not in sys.path: + sys.path.insert(0, cmd_subfolder) + +import mosq_test +import time + +rc = 1 +keepalive = 60 + +connect_packet = mosq_test.gen_connect("subpub-qos1-test", keepalive=keepalive) +connack_packet = mosq_test.gen_connack(rc=0) + +mid = 1 +subscribe_packet = mosq_test.gen_subscribe(mid, "subpub/qos1", 1) +suback_packet = mosq_test.gen_suback(mid, 1) + +helper_connect = mosq_test.gen_connect("helper", keepalive=keepalive) +helper_connack = mosq_test.gen_connack(rc=0) + +mid = 1 +publish1s_packet = mosq_test.gen_publish("subpub/qos1", qos=1, mid=mid, payload="message") +puback1s_packet = mosq_test.gen_puback(mid) + +mid = 1 +publish1r_packet = mosq_test.gen_publish("subpub/qos1", qos=1, mid=mid, payload="message") +pubrec1r_packet = mosq_test.gen_pubrec(mid) + +pingreq_packet = mosq_test.gen_pingreq() +pingresp_packet = mosq_test.gen_pingresp() + +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, timeout=20, port=port) + mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") + + helper = mosq_test.do_client_connect(helper_connect, helper_connack, timeout=20, port=port) + mosq_test.do_send_receive(helper, publish1s_packet, puback1s_packet, "puback 1s") + helper.close() + + if mosq_test.expect_packet(sock, "publish 1r", publish1r_packet): + sock.send(pubrec1r_packet) + sock.send(pingreq_packet) + p = sock.recv(len(pingresp_packet)) + if len(p) == 0: + rc = 0 + + sock.close() +finally: + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde) + +exit(rc) + diff --git a/test/broker/02-subpub-qos1-message-expiry-retain.py b/test/broker/02-subpub-qos1-message-expiry-retain.py new file mode 100755 index 00000000..8ff512ea --- /dev/null +++ b/test/broker/02-subpub-qos1-message-expiry-retain.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python + +# Test whether the broker reduces the message expiry interval when republishing +# a retained message, and eventually removes it. +# MQTT v5 + +# Helper publishes a message, with a medium length expiry with retained set. It +# publishes a second message with retained set but no expiry. +# Client connects, subscribes, gets messages, disconnects. +# We wait until the expiry will have expired. +# Client connects, subscribes, doesn't get expired message, does get +# non-expired message. + +from mosq_test_helper import * + +rc = 1 +keepalive = 60 +connect_packet = mosq_test.gen_connect("subpub", keepalive=keepalive, proto_ver=5) +connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) + +mid = 1 +subscribe1_packet = mosq_test.gen_subscribe(mid, "subpub/expired", 1, proto_ver=5) +suback1_packet = mosq_test.gen_suback(mid, 1, proto_ver=5) + +mid = 2 +subscribe2_packet = mosq_test.gen_subscribe(mid, "subpub/kept", 1, proto_ver=5) +suback2_packet = mosq_test.gen_suback(mid, 1, proto_ver=5) + +helper_connect = mosq_test.gen_connect("helper", proto_ver=5) +helper_connack = mosq_test.gen_connack(rc=0, proto_ver=5) + +mid=1 +props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_MESSAGE_EXPIRY_INTERVAL, 4) +publish1_packet = mosq_test.gen_publish("subpub/expired", mid=mid, qos=1, retain=True, payload="message1", proto_ver=5, properties=props) +puback1_packet = mosq_test.gen_puback(mid) + +mid=2 +publish2s_packet = mosq_test.gen_publish("subpub/kept", mid=mid, qos=1, retain=True, payload="message2", proto_ver=5) +puback2s_packet = mosq_test.gen_puback(mid) + +mid=1 +publish2r_packet = mosq_test.gen_publish("subpub/kept", mid=mid, qos=1, retain=True, payload="message2", proto_ver=5) +puback2r_packet = mosq_test.gen_puback(mid) + +pingreq_packet = mosq_test.gen_pingreq() +pingresp_packet = mosq_test.gen_pingresp() + + +port = mosq_test.get_port() +broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) + +try: + helper = mosq_test.do_client_connect(helper_connect, helper_connack, timeout=20, port=port) + mosq_test.do_send_receive(helper, publish1_packet, puback1_packet, "puback 1") + mosq_test.do_send_receive(helper, publish2s_packet, puback2s_packet, "puback 2") + helper.close() + + sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port) + mosq_test.do_send_receive(sock, subscribe1_packet, suback1_packet, "suback 1-1") + + if mosq_test.expect_packet(sock, "publish 1", publish1_packet): + sock.send(puback1_packet) + + mosq_test.do_send_receive(sock, subscribe2_packet, suback2_packet, "suback 2-1") + if mosq_test.expect_packet(sock, "publish 2", publish2s_packet): + sock.send(puback2s_packet) + + sock.close() + + time.sleep(5) + sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port) + mosq_test.do_send_receive(sock, subscribe1_packet, suback1_packet, "suback 1-2") + # We shouldn't receive a publish here + # This will fail if we do receive a publish + mosq_test.do_send_receive(sock, subscribe2_packet, suback2_packet, "suback 2-2") + if mosq_test.expect_packet(sock, "publish 2", publish2r_packet): + sock.send(puback2r_packet) + sock.close() + rc = 0 + +finally: + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde) + +exit(rc) + diff --git a/test/broker/02-subpub-qos1-message-expiry-will.py b/test/broker/02-subpub-qos1-message-expiry-will.py new file mode 100755 index 00000000..b8b139b2 --- /dev/null +++ b/test/broker/02-subpub-qos1-message-expiry-will.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python + +# Test whether the broker reduces the message expiry interval when republishing a will. +# MQTT v5 + +# Client connects with clean session set false, subscribes with qos=1, then disconnects +# Helper publishes two messages, one with a short expiry and one with a long expiry +# We wait until the short expiry will have expired but the long one not. +# Client reconnects, expects delivery of the long expiry message with a reduced +# expiry interval property. + +from mosq_test_helper import * + +rc = 1 +mid = 53 +keepalive = 60 +connect_packet = mosq_test.gen_connect("subpub-qos0-test", keepalive=keepalive, proto_ver=5, clean_session=False) +connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=5) +connack2_packet = mosq_test.gen_connack(rc=0, proto_ver=5, flags=1) + +subscribe_packet = mosq_test.gen_subscribe(mid, "subpub/qos1", 1, proto_ver=5) +suback_packet = mosq_test.gen_suback(mid, 1, proto_ver=5) + + +props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_MESSAGE_EXPIRY_INTERVAL, 10) +helper_connect = mosq_test.gen_connect("helper", proto_ver=5, will_topic="subpub/qos1", will_qos=1, will_payload="message", will_properties=props, keepalive=2) +helper_connack = mosq_test.gen_connack(rc=0, proto_ver=5) + +#mid=2 +props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_MESSAGE_EXPIRY_INTERVAL, 10) +publish2s_packet = mosq_test.gen_publish("subpub/qos1", mid=mid, qos=1, payload="message2", proto_ver=5, properties=props) +puback2s_packet = mosq_test.gen_puback(mid) + + +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, connack1_packet, timeout=20, port=port) + mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") + sock.close() + + helper = mosq_test.do_client_connect(helper_connect, helper_connack, timeout=20, port=port) + + time.sleep(2) + + sock = mosq_test.do_client_connect(connect_packet, connack2_packet, timeout=20, port=port) + packet = sock.recv(len(publish2s_packet)) + for i in range(10, 5, -1): + props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_MESSAGE_EXPIRY_INTERVAL, i) + publish2r_packet = mosq_test.gen_publish("subpub/qos1", mid=1, qos=1, payload="message", proto_ver=5, properties=props) + if packet == publish2r_packet: + rc = 0 + break + + sock.close() +finally: + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde) + +exit(rc) + diff --git a/test/broker/02-subpub-qos1-message-expiry.py b/test/broker/02-subpub-qos1-message-expiry.py new file mode 100755 index 00000000..3ba16773 --- /dev/null +++ b/test/broker/02-subpub-qos1-message-expiry.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python + +# Test whether the broker reduces the message expiry interval when republishing. +# MQTT v5 + +# Client connects with clean session set false, subscribes with qos=1, then disconnects +# Helper publishes two messages, one with a short expiry and one with a long expiry +# We wait until the short expiry will have expired but the long one not. +# Client reconnects, expects delivery of the long expiry message with a reduced +# expiry interval property. + +from mosq_test_helper import * + +rc = 1 +mid = 53 +keepalive = 60 +connect_packet = mosq_test.gen_connect("subpub-qos0-test", keepalive=keepalive, proto_ver=5, clean_session=False) +connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=5) +connack2_packet = mosq_test.gen_connack(rc=0, proto_ver=5, flags=1) + +subscribe_packet = mosq_test.gen_subscribe(mid, "subpub/qos1", 1, proto_ver=5) +suback_packet = mosq_test.gen_suback(mid, 1, proto_ver=5) + + + +helper_connect = mosq_test.gen_connect("helper", proto_ver=5) +helper_connack = mosq_test.gen_connack(rc=0, proto_ver=5) + +mid=1 +props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_MESSAGE_EXPIRY_INTERVAL, 1) +publish1s_packet = mosq_test.gen_publish("subpub/qos1", mid=mid, qos=1, payload="message1", proto_ver=5, properties=props) +puback1s_packet = mosq_test.gen_puback(mid) + +mid=2 +props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_MESSAGE_EXPIRY_INTERVAL, 10) +publish2s_packet = mosq_test.gen_publish("subpub/qos1", mid=mid, qos=1, payload="message2", proto_ver=5, properties=props) +puback2s_packet = mosq_test.gen_puback(mid) + + +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, connack1_packet, timeout=20, port=port) + mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") + sock.close() + + helper = mosq_test.do_client_connect(helper_connect, helper_connack, timeout=20, port=port) + mosq_test.do_send_receive(helper, publish1s_packet, puback1s_packet, "puback 1") + mosq_test.do_send_receive(helper, publish2s_packet, puback2s_packet, "puback 2") + + time.sleep(2) + + sock = mosq_test.do_client_connect(connect_packet, connack2_packet, timeout=20, port=port) + packet = sock.recv(len(publish2s_packet)) + for i in range(9, 5, -1): + props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_MESSAGE_EXPIRY_INTERVAL, i) + publish2r_packet = mosq_test.gen_publish("subpub/qos1", mid=2, qos=1, payload="message2", proto_ver=5, properties=props) + if packet == publish2r_packet: + rc = 0 + break + + sock.close() +finally: + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde) + +exit(rc) + diff --git a/test/broker/02-subpub-qos1-nolocal.py b/test/broker/02-subpub-qos1-nolocal.py new file mode 100755 index 00000000..d66be515 --- /dev/null +++ b/test/broker/02-subpub-qos1-nolocal.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python + +# Test whether a client subscribed to a topic does not receive its own message +# sent to that topic if no local is set. +# MQTT v5 + +from mosq_test_helper import * + +rc = 1 +keepalive = 60 +connect_packet = mosq_test.gen_connect("subpub-qos1-test", keepalive=keepalive, proto_ver=5) +connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) + +mid = 530 +subscribe_packet = mosq_test.gen_subscribe(mid, "subpub/qos1", 1 | mqtt5_opts.MQTT_SUB_OPT_NO_LOCAL, proto_ver=5) +suback_packet = mosq_test.gen_suback(mid, 1, proto_ver=5) + +mid = 531 +subscribe2_packet = mosq_test.gen_subscribe(mid, "subpub/receive", 1, proto_ver=5) +suback2_packet = mosq_test.gen_suback(mid, 1, proto_ver=5) + +mid = 300 +publish_packet = mosq_test.gen_publish("subpub/qos1", qos=1, mid=mid, payload="message", proto_ver=5) +puback_packet = mosq_test.gen_puback(mid, proto_ver=5) + +mid = 301 +publish2_packet = mosq_test.gen_publish("subpub/receive", qos=1, mid=mid, payload="success", proto_ver=5) +puback2_packet = mosq_test.gen_puback(mid, proto_ver=5) + +mid = 1 +publish3_packet = mosq_test.gen_publish("subpub/receive", qos=1, mid=mid, payload="success", 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, timeout=20, port=port) + + mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") + mosq_test.do_send_receive(sock, subscribe2_packet, suback2_packet, "suback2") + + mosq_test.do_send_receive(sock, publish_packet, puback_packet, "puback") + mosq_test.do_send_receive(sock, publish2_packet, puback2_packet, "puback2") + + if mosq_test.expect_packet(sock, "publish3", publish3_packet): + rc = 0 + + sock.close() +finally: + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde) + +exit(rc) + diff --git a/test/broker/02-subpub-qos1-v5.py b/test/broker/02-subpub-qos1-v5.py new file mode 100755 index 00000000..cf87a989 --- /dev/null +++ b/test/broker/02-subpub-qos1-v5.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python + +# Test whether a client subscribed to a topic receives its own message sent to that topic. +# MQTT v5 + +from mosq_test_helper import * + +rc = 1 +mid = 530 +keepalive = 60 +connect_packet = mosq_test.gen_connect("subpub-qos1-test", keepalive=keepalive, proto_ver=5) +connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) + +subscribe_packet = mosq_test.gen_subscribe(mid, "subpub/qos1", 1, proto_ver=5) +suback_packet = mosq_test.gen_suback(mid, 1, proto_ver=5) + +mid = 300 +publish_packet = mosq_test.gen_publish("subpub/qos1", qos=1, mid=mid, payload="message", proto_ver=5) +puback_packet = mosq_test.gen_puback(mid, proto_ver=5) + +mid = 1 +publish_packet2 = mosq_test.gen_publish("subpub/qos1", qos=1, mid=mid, payload="message", 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, timeout=20, port=port) + + mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") + + mosq_test.do_send_receive(sock, publish_packet, puback_packet, "puback") + + if mosq_test.expect_packet(sock, "publish2", publish_packet2): + rc = 0 + + sock.close() +finally: + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde) + +exit(rc) + diff --git a/test/broker/02-subpub-qos1.py b/test/broker/02-subpub-qos1.py index c4a86599..ca78af11 100755 --- a/test/broker/02-subpub-qos1.py +++ b/test/broker/02-subpub-qos1.py @@ -2,13 +2,7 @@ # Test whether a client subscribed to a topic receives its own message sent to that topic. -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * rc = 1 mid = 530 diff --git a/test/broker/02-subpub-qos2-bad-puback-1.py b/test/broker/02-subpub-qos2-bad-puback-1.py new file mode 100755 index 00000000..e228c3d5 --- /dev/null +++ b/test/broker/02-subpub-qos2-bad-puback-1.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python + +# Test what the broker does if receiving a PUBACK in response to a QoS 2 PUBLISH. + +import inspect, os, sys +# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder +cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) +if cmd_subfolder not in sys.path: + sys.path.insert(0, cmd_subfolder) + +import mosq_test +import time + +rc = 1 +keepalive = 60 + +connect_packet = mosq_test.gen_connect("subpub-qos2-test", keepalive=keepalive) +connack_packet = mosq_test.gen_connack(rc=0) + +mid = 1 +subscribe_packet = mosq_test.gen_subscribe(mid, "subpub/qos2", 2) +suback_packet = mosq_test.gen_suback(mid, 2) + +helper_connect = mosq_test.gen_connect("helper", keepalive=keepalive) +helper_connack = mosq_test.gen_connack(rc=0) + +mid = 1 +publish1s_packet = mosq_test.gen_publish("subpub/qos2", qos=2, mid=mid, payload="message") +pubrec1s_packet = mosq_test.gen_pubrec(mid) +pubrel1s_packet = mosq_test.gen_pubrel(mid) +pubcomp1s_packet = mosq_test.gen_pubcomp(mid) + +mid = 1 +publish1r_packet = mosq_test.gen_publish("subpub/qos2", qos=2, mid=mid, payload="message") +puback1r_packet = mosq_test.gen_puback(mid) + +pingreq_packet = mosq_test.gen_pingreq() +pingresp_packet = mosq_test.gen_pingresp() + +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, timeout=20, port=port) + mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") + + helper = mosq_test.do_client_connect(helper_connect, helper_connack, timeout=20, port=port) + mosq_test.do_send_receive(helper, publish1s_packet, pubrec1s_packet, "pubrec 1s") + mosq_test.do_send_receive(helper, pubrel1s_packet, pubcomp1s_packet, "pubcomp 1s") + helper.close() + + if mosq_test.expect_packet(sock, "publish 1r", publish1r_packet): + sock.send(puback1r_packet) + sock.send(pingreq_packet) + p = sock.recv(len(pingresp_packet)) + if len(p) == 0: + rc = 0 + + sock.close() +finally: + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde) + +exit(rc) + diff --git a/test/broker/02-subpub-qos2-bad-puback-2.py b/test/broker/02-subpub-qos2-bad-puback-2.py new file mode 100755 index 00000000..67189990 --- /dev/null +++ b/test/broker/02-subpub-qos2-bad-puback-2.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python + +# Test what the broker does if receiving a PUBACK in response to a QoS 2 PUBREL. + +import inspect, os, sys +# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder +cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) +if cmd_subfolder not in sys.path: + sys.path.insert(0, cmd_subfolder) + +import mosq_test +import time + +rc = 1 +keepalive = 60 + +connect_packet = mosq_test.gen_connect("subpub-qos2-test", keepalive=keepalive) +connack_packet = mosq_test.gen_connack(rc=0) + +mid = 1 +subscribe_packet = mosq_test.gen_subscribe(mid, "subpub/qos2", 2) +suback_packet = mosq_test.gen_suback(mid, 2) + +helper_connect = mosq_test.gen_connect("helper", keepalive=keepalive) +helper_connack = mosq_test.gen_connack(rc=0) + +mid = 1 +publish1s_packet = mosq_test.gen_publish("subpub/qos2", qos=2, mid=mid, payload="message") +pubrec1s_packet = mosq_test.gen_pubrec(mid) +pubrel1s_packet = mosq_test.gen_pubrel(mid) +pubcomp1s_packet = mosq_test.gen_pubcomp(mid) + +mid = 1 +publish1r_packet = mosq_test.gen_publish("subpub/qos2", qos=2, mid=mid, payload="message") +pubrec1r_packet = mosq_test.gen_pubrec(mid) +pubrel1r_packet = mosq_test.gen_pubrel(mid) +puback1r_packet = mosq_test.gen_puback(mid) + +pingreq_packet = mosq_test.gen_pingreq() +pingresp_packet = mosq_test.gen_pingresp() + +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, timeout=20, port=port) + mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") + + helper = mosq_test.do_client_connect(helper_connect, helper_connack, timeout=20, port=port) + mosq_test.do_send_receive(helper, publish1s_packet, pubrec1s_packet, "pubrec 1s") + mosq_test.do_send_receive(helper, pubrel1s_packet, pubcomp1s_packet, "pubcomp 1s") + helper.close() + + if mosq_test.expect_packet(sock, "publish 1r", publish1r_packet): + mosq_test.do_send_receive(sock, pubrec1s_packet, pubrel1s_packet, "pubrel 1r") + sock.send(puback1r_packet) + sock.send(pingreq_packet) + p = sock.recv(len(pingresp_packet)) + if len(p) == 0: + rc = 0 + + sock.close() +finally: + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde) + +exit(rc) + diff --git a/test/broker/02-subpub-qos2-bad-pubcomp.py b/test/broker/02-subpub-qos2-bad-pubcomp.py new file mode 100755 index 00000000..acaac552 --- /dev/null +++ b/test/broker/02-subpub-qos2-bad-pubcomp.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python + +# Test what the broker does if receiving a PUBCOMP in response to a QoS 2 PUBLISH. + +import inspect, os, sys +# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder +cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) +if cmd_subfolder not in sys.path: + sys.path.insert(0, cmd_subfolder) + +import mosq_test +import time + +rc = 1 +keepalive = 60 + +connect_packet = mosq_test.gen_connect("subpub-qos2-test", keepalive=keepalive) +connack_packet = mosq_test.gen_connack(rc=0) + +mid = 1 +subscribe_packet = mosq_test.gen_subscribe(mid, "subpub/qos2", 2) +suback_packet = mosq_test.gen_suback(mid, 2) + +helper_connect = mosq_test.gen_connect("helper", keepalive=keepalive) +helper_connack = mosq_test.gen_connack(rc=0) + +mid = 1 +publish1s_packet = mosq_test.gen_publish("subpub/qos2", qos=2, mid=mid, payload="message") +pubrec1s_packet = mosq_test.gen_pubrec(mid) +pubrel1s_packet = mosq_test.gen_pubrel(mid) +pubcomp1s_packet = mosq_test.gen_pubcomp(mid) + +mid = 1 +publish1r_packet = mosq_test.gen_publish("subpub/qos2", qos=2, mid=mid, payload="message") +pubcomp1r_packet = mosq_test.gen_pubcomp(mid) + +pingreq_packet = mosq_test.gen_pingreq() +pingresp_packet = mosq_test.gen_pingresp() + +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, timeout=20, port=port) + mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") + + helper = mosq_test.do_client_connect(helper_connect, helper_connack, timeout=20, port=port) + mosq_test.do_send_receive(helper, publish1s_packet, pubrec1s_packet, "pubrec 1s") + mosq_test.do_send_receive(helper, pubrel1s_packet, pubcomp1s_packet, "pubcomp 1s") + helper.close() + + if mosq_test.expect_packet(sock, "publish 1r", publish1r_packet): + sock.send(pubcomp1r_packet) + sock.send(pingreq_packet) + p = sock.recv(len(pingresp_packet)) + if len(p) == 0: + rc = 0 + + sock.close() +finally: + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde) + +exit(rc) + diff --git a/test/broker/02-subpub-qos2-pubrec-error-helper.py b/test/broker/02-subpub-qos2-pubrec-error-helper.py new file mode 100755 index 00000000..4d1bbc93 --- /dev/null +++ b/test/broker/02-subpub-qos2-pubrec-error-helper.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python + +from mosq_test_helper import * + +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_1_packet = mosq_test.gen_publish("qos2/pubrec/rejected", qos=2, mid=mid, payload="rejected-message") +pubrec_1_packet = mosq_test.gen_pubrec(mid) +pubrel_1_packet = mosq_test.gen_pubrel(mid) +pubcomp_1_packet = mosq_test.gen_pubcomp(mid) + +mid = 2 +publish_2_packet = mosq_test.gen_publish("qos2/pubrec/accepted", qos=2, mid=mid, payload="accepted-message") +pubrec_2_packet = mosq_test.gen_pubrec(mid) +pubrel_2_packet = mosq_test.gen_pubrel(mid) +pubcomp_2_packet = mosq_test.gen_pubcomp(mid) + +port = mosq_test.get_port() +sock = mosq_test.do_client_connect(connect_packet, connack_packet, connack_error="helper connack", port=port) + +mosq_test.do_send_receive(sock, publish_1_packet, pubrec_1_packet, "helper pubrec") +mosq_test.do_send_receive(sock, pubrel_1_packet, pubcomp_1_packet, "helper pubcomp") + +mosq_test.do_send_receive(sock, publish_2_packet, pubrec_2_packet, "helper pubrec") +mosq_test.do_send_receive(sock, pubrel_2_packet, pubcomp_2_packet, "helper pubcomp") + +rc = 0 + +sock.close() + +exit(rc) + diff --git a/test/broker/02-subpub-qos2-pubrec-error.py b/test/broker/02-subpub-qos2-pubrec-error.py new file mode 100755 index 00000000..2e3b3d34 --- /dev/null +++ b/test/broker/02-subpub-qos2-pubrec-error.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python + +# Test whether a PUBREC with reason code >= 0x80 is handled correctly + +from mosq_test_helper import * + +rc = 1 +keepalive = 60 +connect_packet = mosq_test.gen_connect("pub-qo2-timeout-test", keepalive=keepalive, proto_ver=5) +connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) + +mid = 1 +subscribe_packet = mosq_test.gen_subscribe(mid, "qos2/pubrec/+", 2, proto_ver=5) +suback_packet = mosq_test.gen_suback(mid, 2, proto_ver=5) + +mid = 1 +publish_1_packet = mosq_test.gen_publish("qos2/pubrec/rejected", qos=2, mid=mid, payload="rejected-message", proto_ver=5) +pubrec_1_packet = mosq_test.gen_pubrec(mid, proto_ver=5, reason_code=0x80) + +mid = 2 +publish_2_packet = mosq_test.gen_publish("qos2/pubrec/accepted", qos=2, mid=mid, payload="accepted-message", proto_ver=5) +pubrec_2_packet = mosq_test.gen_pubrec(mid, proto_ver=5) +pubrel_2_packet = mosq_test.gen_pubrel(mid, proto_ver=5) +pubcomp_2_packet = mosq_test.gen_pubcomp(mid, 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) + mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") + pub = subprocess.Popen(['./02-subpub-qos2-pubrec-error-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 1", publish_1_packet): + sock.send(pubrec_1_packet) + + if mosq_test.expect_packet(sock, "publish 2", publish_2_packet): + mosq_test.do_send_receive(sock, pubrec_2_packet, pubrel_2_packet, "pubrel 2") + sock.send(pubcomp_2_packet) + rc = 0 + + sock.close() +finally: + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde) + +exit(rc) + diff --git a/test/broker/02-subpub-qos2-receive-maximum-1.py b/test/broker/02-subpub-qos2-receive-maximum-1.py new file mode 100755 index 00000000..db291c52 --- /dev/null +++ b/test/broker/02-subpub-qos2-receive-maximum-1.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python + +# Does the broker respect receive maximum==1? +# MQTT v5 + +from mosq_test_helper import * + +rc = 1 +keepalive = 60 +props = mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_RECEIVE_MAXIMUM, 1) +connect_packet = mosq_test.gen_connect("subpub-qos2-test", keepalive=keepalive, proto_ver=5, properties=props) +connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) + +mid = 1 +subscribe_packet = mosq_test.gen_subscribe(mid, "subpub/qos2", 2, proto_ver=5) +suback_packet = mosq_test.gen_suback(mid, 2, proto_ver=5) + +mid = 1 +publish_packet1 = mosq_test.gen_publish("subpub/qos2", qos=2, mid=mid, payload="message1", proto_ver=5) +pubrec_packet1 = mosq_test.gen_pubrec(mid, proto_ver=5) +pubrel_packet1 = mosq_test.gen_pubrel(mid, proto_ver=5) +pubcomp_packet1 = mosq_test.gen_pubcomp(mid, proto_ver=5) + +mid = 2 +publish_packet2 = mosq_test.gen_publish("subpub/qos2", qos=2, mid=mid, payload="message2", proto_ver=5) +pubrec_packet2 = mosq_test.gen_pubrec(mid, proto_ver=5) +pubrel_packet2 = mosq_test.gen_pubrel(mid, proto_ver=5) +pubcomp_packet2 = mosq_test.gen_pubcomp(mid, proto_ver=5) + +mid = 3 +publish_packet3 = mosq_test.gen_publish("subpub/qos2", qos=2, mid=mid, payload="message3", proto_ver=5) +pubrec_packet3 = mosq_test.gen_pubrec(mid, proto_ver=5) +pubrel_packet3 = mosq_test.gen_pubrel(mid, proto_ver=5) +pubcomp_packet3 = mosq_test.gen_pubcomp(mid, 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, timeout=20, port=port) + + mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") + + pub = subprocess.Popen(['./02-subpub-qos2-receive-maximum-helper.py', str(port)], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + pub.wait() + (stdo, stde) = pub.communicate() + + if mosq_test.expect_packet(sock, "publish1", publish_packet1): + mosq_test.do_send_receive(sock, pubrec_packet1, pubrel_packet1, "pubrel1") + sock.send(pubcomp_packet1) + + if mosq_test.expect_packet(sock, "publish2", publish_packet2): + mosq_test.do_send_receive(sock, pubrec_packet2, pubrel_packet2, "pubrel2") + sock.send(pubcomp_packet2) + + if mosq_test.expect_packet(sock, "publish3", publish_packet3): + mosq_test.do_send_receive(sock, pubrec_packet3, pubrel_packet3, "pubrel3") + sock.send(pubcomp_packet3) + + rc = 0 + + sock.close() +finally: + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde) + +exit(rc) + diff --git a/test/broker/02-subpub-qos2-receive-maximum-2.py b/test/broker/02-subpub-qos2-receive-maximum-2.py new file mode 100755 index 00000000..66de557d --- /dev/null +++ b/test/broker/02-subpub-qos2-receive-maximum-2.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python + +# Does the broker respect receive maximum==2? +# MQTT v5 + +from mosq_test_helper import * + +rc = 1 +keepalive = 60 +props = mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_RECEIVE_MAXIMUM, 2) +connect_packet = mosq_test.gen_connect("subpub-qos2-test", keepalive=keepalive, proto_ver=5, properties=props) +connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) + +mid = 1 +subscribe_packet = mosq_test.gen_subscribe(mid, "subpub/qos2", 2, proto_ver=5) +suback_packet = mosq_test.gen_suback(mid, 2, proto_ver=5) + +mid = 1 +publish_packet1 = mosq_test.gen_publish("subpub/qos2", qos=2, mid=mid, payload="message1", proto_ver=5) +pubrec_packet1 = mosq_test.gen_pubrec(mid, proto_ver=5) +pubrel_packet1 = mosq_test.gen_pubrel(mid, proto_ver=5) +pubcomp_packet1 = mosq_test.gen_pubcomp(mid, proto_ver=5) + +mid = 2 +publish_packet2 = mosq_test.gen_publish("subpub/qos2", qos=2, mid=mid, payload="message2", proto_ver=5) +pubrec_packet2 = mosq_test.gen_pubrec(mid, proto_ver=5) +pubrel_packet2 = mosq_test.gen_pubrel(mid, proto_ver=5) +pubcomp_packet2 = mosq_test.gen_pubcomp(mid, proto_ver=5) + +mid = 3 +publish_packet3 = mosq_test.gen_publish("subpub/qos2", qos=2, mid=mid, payload="message3", proto_ver=5) +pubrec_packet3 = mosq_test.gen_pubrec(mid, proto_ver=5) +pubrel_packet3 = mosq_test.gen_pubrel(mid, proto_ver=5) +pubcomp_packet3 = mosq_test.gen_pubcomp(mid, 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, timeout=20, port=port) + + mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") + + pub = subprocess.Popen(['./02-subpub-qos2-receive-maximum-helper.py', str(port)], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + pub.wait() + (stdo, stde) = pub.communicate() + + if mosq_test.expect_packet(sock, "publish1", publish_packet1): + if mosq_test.expect_packet(sock, "publish2", publish_packet2): + mosq_test.do_send_receive(sock, pubrec_packet1, pubrel_packet1, "pubrel1") + sock.send(pubcomp_packet1) + + if mosq_test.expect_packet(sock, "publish3", publish_packet3): + mosq_test.do_send_receive(sock, pubrec_packet2, pubrel_packet2, "pubrel2") + sock.send(pubcomp_packet2) + + mosq_test.do_send_receive(sock, pubrec_packet3, pubrel_packet3, "pubrel3") + sock.send(pubcomp_packet3) + + rc = 0 + + sock.close() +finally: + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde) + +exit(rc) + diff --git a/test/broker/02-subpub-qos2-receive-maximum-helper.py b/test/broker/02-subpub-qos2-receive-maximum-helper.py new file mode 100755 index 00000000..01eb91a6 --- /dev/null +++ b/test/broker/02-subpub-qos2-receive-maximum-helper.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python + +# Test whether a client subscribed to a topic receives its own message sent to that topic. +# MQTT v5 + +from mosq_test_helper import * + +rc = 1 +keepalive = 60 +connect_packet = mosq_test.gen_connect("subpub-qos2-test-helper", keepalive=keepalive, proto_ver=5) +connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) + +mid = 1 +publish_packet = mosq_test.gen_publish("subpub/qos2", qos=2, mid=mid, payload="message1", proto_ver=5) +pubrec_packet = mosq_test.gen_pubrec(mid, proto_ver=5) +pubrel_packet = mosq_test.gen_pubrel(mid, proto_ver=5) +pubcomp_packet = mosq_test.gen_pubcomp(mid, proto_ver=5) + +mid = 2 +publish_packet2 = mosq_test.gen_publish("subpub/qos2", qos=2, mid=mid, payload="message2", proto_ver=5) +pubrec_packet2 = mosq_test.gen_pubrec(mid, proto_ver=5) +pubrel_packet2 = mosq_test.gen_pubrel(mid, proto_ver=5) +pubcomp_packet2 = mosq_test.gen_pubcomp(mid, proto_ver=5) + +mid = 3 +publish_packet3 = mosq_test.gen_publish("subpub/qos2", qos=2, mid=mid, payload="message3", proto_ver=5) +pubrec_packet3 = mosq_test.gen_pubrec(mid, proto_ver=5) +pubrel_packet3 = mosq_test.gen_pubrel(mid, proto_ver=5) +pubcomp_packet3 = mosq_test.gen_pubcomp(mid, proto_ver=5) + + +port = mosq_test.get_port() + +try: + sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, 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, publish_packet2, pubrec_packet2, "pubrec2") + mosq_test.do_send_receive(sock, pubrel_packet2, pubcomp_packet2, "pubcomp2") + + mosq_test.do_send_receive(sock, publish_packet3, pubrec_packet3, "pubrec3") + mosq_test.do_send_receive(sock, pubrel_packet3, pubcomp_packet3, "pubcomp3") + rc = 0 + + sock.close() +finally: + if rc: + print(stde) + +exit(rc) + diff --git a/test/broker/02-subpub-qos2-v5.py b/test/broker/02-subpub-qos2-v5.py new file mode 100755 index 00000000..32feaed7 --- /dev/null +++ b/test/broker/02-subpub-qos2-v5.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python + +# Test whether a client subscribed to a topic receives its own message sent to that topic. +# MQTT v5 + +from mosq_test_helper import * + +rc = 1 +mid = 530 +keepalive = 60 +connect_packet = mosq_test.gen_connect("subpub-qos2-test", keepalive=keepalive, proto_ver=5) +connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) + +subscribe_packet = mosq_test.gen_subscribe(mid, "subpub/qos2", 2, proto_ver=5) +suback_packet = mosq_test.gen_suback(mid, 2, proto_ver=5) + +mid = 301 +publish_packet = mosq_test.gen_publish("subpub/qos2", qos=2, mid=mid, payload="message", proto_ver=5) +pubrec_packet = mosq_test.gen_pubrec(mid, proto_ver=5) +pubrel_packet = mosq_test.gen_pubrel(mid, proto_ver=5) +pubcomp_packet = mosq_test.gen_pubcomp(mid, proto_ver=5) + +mid = 1 +publish_packet2 = mosq_test.gen_publish("subpub/qos2", qos=2, mid=mid, payload="message", proto_ver=5) +pubrec_packet2 = mosq_test.gen_pubrec(mid, proto_ver=5) +pubrel_packet2 = mosq_test.gen_pubrel(mid, proto_ver=5) +pubcomp_packet2 = mosq_test.gen_pubcomp(mid, 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, timeout=20, port=port) + + mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") + mosq_test.do_send_receive(sock, publish_packet, pubrec_packet, "pubrec") + mosq_test.do_send_receive(sock, pubrel_packet, pubcomp_packet, "pubcomp") + + if mosq_test.expect_packet(sock, "publish2", publish_packet2): + mosq_test.do_send_receive(sock, pubrec_packet2, pubrel_packet2, "pubrel2") + # Broker side of flow complete so can quit here. + rc = 0 + + sock.close() +finally: + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde) + +exit(rc) + diff --git a/test/broker/02-subpub-qos2.py b/test/broker/02-subpub-qos2.py index 610e7c57..f9f941be 100755 --- a/test/broker/02-subpub-qos2.py +++ b/test/broker/02-subpub-qos2.py @@ -2,13 +2,7 @@ # Test whether a client subscribed to a topic receives its own message sent to that topic. -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * rc = 1 mid = 530 diff --git a/test/broker/02-subscribe-invalid-utf8.py b/test/broker/02-subscribe-invalid-utf8.py index 00bed94b..f9ae5b39 100755 --- a/test/broker/02-subscribe-invalid-utf8.py +++ b/test/broker/02-subscribe-invalid-utf8.py @@ -2,15 +2,7 @@ # Test whether a SUBSCRIBE to a topic with an invalid UTF-8 topic fails -import time -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import struct -import mosq_test +from mosq_test_helper import * rc = 1 mid = 53 diff --git a/test/broker/02-subscribe-persistence-flipflop.py b/test/broker/02-subscribe-persistence-flipflop.py index 36b91099..26a627ce 100755 --- a/test/broker/02-subscribe-persistence-flipflop.py +++ b/test/broker/02-subscribe-persistence-flipflop.py @@ -22,14 +22,7 @@ # # sub does not receive the message - -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * rc = 1 keepalive = 60 diff --git a/test/broker/02-subscribe-qos0.py b/test/broker/02-subscribe-qos0.py index a3ec5803..434a8704 100755 --- a/test/broker/02-subscribe-qos0.py +++ b/test/broker/02-subscribe-qos0.py @@ -2,14 +2,7 @@ # Test whether a SUBSCRIBE to a topic with QoS 0 results in the correct SUBACK packet. -import time -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * rc = 1 mid = 53 diff --git a/test/broker/02-subscribe-qos1.py b/test/broker/02-subscribe-qos1.py index 03b326f3..a15290ee 100755 --- a/test/broker/02-subscribe-qos1.py +++ b/test/broker/02-subscribe-qos1.py @@ -2,13 +2,7 @@ # Test whether a SUBSCRIBE to a topic with QoS 1 results in the correct SUBACK packet. -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * rc = 1 mid = 79 diff --git a/test/broker/02-subscribe-qos2.py b/test/broker/02-subscribe-qos2.py index c3e34256..9931583c 100755 --- a/test/broker/02-subscribe-qos2.py +++ b/test/broker/02-subscribe-qos2.py @@ -2,13 +2,7 @@ # Test whether a SUBSCRIBE to a topic with QoS 2 results in the correct SUBACK packet. -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * rc = 1 mid = 3 diff --git a/test/broker/02-unsubscribe-invalid-no-topic.py b/test/broker/02-unsubscribe-invalid-no-topic.py index b5a34152..3cc1f401 100755 --- a/test/broker/02-unsubscribe-invalid-no-topic.py +++ b/test/broker/02-unsubscribe-invalid-no-topic.py @@ -2,20 +2,12 @@ # Test whether a UNSUBSCRIBE with no topic results in a disconnect. MQTT-3.10.3-2 -import inspect, os, sys -import struct +from mosq_test_helper import * def gen_unsubscribe_invalid_no_topic(mid): pack_format = "!BBH" return struct.pack(pack_format, 162, 2, mid) -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test - rc = 1 mid = 3 keepalive = 60 diff --git a/test/broker/02-unsubscribe-qos0.py b/test/broker/02-unsubscribe-qos0.py index 99b85a94..0d824bd3 100755 --- a/test/broker/02-unsubscribe-qos0.py +++ b/test/broker/02-unsubscribe-qos0.py @@ -3,13 +3,7 @@ # Test whether a UNSUBSCRIBE to a topic with QoS 0 results in the correct UNSUBACK packet. # This doesn't assume a subscription exists. -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * rc = 1 mid = 53 diff --git a/test/broker/02-unsubscribe-qos1.py b/test/broker/02-unsubscribe-qos1.py index 45dfdf5f..4fd25d69 100755 --- a/test/broker/02-unsubscribe-qos1.py +++ b/test/broker/02-unsubscribe-qos1.py @@ -2,13 +2,7 @@ # Test whether a SUBSCRIBE to a topic with QoS 1 results in the correct SUBACK packet. -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * rc = 1 mid = 79 diff --git a/test/broker/02-unsubscribe-qos2-multiple-v5.py b/test/broker/02-unsubscribe-qos2-multiple-v5.py new file mode 100755 index 00000000..bbbc40cd --- /dev/null +++ b/test/broker/02-unsubscribe-qos2-multiple-v5.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python + +# Test whether a v5 UNSUBSCRIBE to multiple topics with QoS 2 results in the +# correct UNSUBACK packet, when one subscription exists and the other does not. + +from mosq_test_helper import * + +rc = 1 +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) + +mid = 1 +subscribe_packet = mosq_test.gen_subscribe(mid, "qos2/two", 2, proto_ver=5) +suback_packet = mosq_test.gen_suback(mid, 2, proto_ver=5) + +mid = 3 +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=[17, 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, subscribe_packet, suback_packet, "suback") + 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) + diff --git a/test/broker/02-unsubscribe-qos2-multiple.py b/test/broker/02-unsubscribe-qos2-multiple.py new file mode 100755 index 00000000..b7851f9f --- /dev/null +++ b/test/broker/02-unsubscribe-qos2-multiple.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python + +# Test whether a SUBSCRIBE to multiple topics with QoS 2 results in the correct SUBACK packet. + +from mosq_test_helper import * + +rc = 1 +mid = 3 +keepalive = 60 +connect_packet = mosq_test.gen_connect("unsubscribe-qos2-test", keepalive=keepalive) +connack_packet = mosq_test.gen_connack(rc=0) + +unsubscribe_packet = mosq_test.gen_unsubscribe_multiple(mid, ["qos2/one", "qos2/two"]) +unsuback_packet = mosq_test.gen_unsuback(mid) + +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) + diff --git a/test/broker/02-unsubscribe-qos2-v5.py b/test/broker/02-unsubscribe-qos2-v5.py new file mode 100755 index 00000000..2936c33b --- /dev/null +++ b/test/broker/02-unsubscribe-qos2-v5.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python + +# Test whether a SUBSCRIBE to a topic with QoS 2 results in the correct SUBACK packet. +# MQTT 5 + +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(mid, "qos2/test", proto_ver=5) +unsuback_packet = mosq_test.gen_unsuback(mid, proto_ver=5, reason_code=17) + +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) + diff --git a/test/broker/02-unsubscribe-qos2.py b/test/broker/02-unsubscribe-qos2.py index e4767518..2262c195 100755 --- a/test/broker/02-unsubscribe-qos2.py +++ b/test/broker/02-unsubscribe-qos2.py @@ -2,13 +2,7 @@ # Test whether a SUBSCRIBE to a topic with QoS 2 results in the correct SUBACK packet. -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * rc = 1 mid = 3 diff --git a/test/broker/03-pattern-matching-helper.py b/test/broker/03-pattern-matching-helper.py index c7dc0561..6a1f4168 100755 --- a/test/broker/03-pattern-matching-helper.py +++ b/test/broker/03-pattern-matching-helper.py @@ -1,12 +1,6 @@ #!/usr/bin/env python -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * port = int(sys.argv[2]) diff --git a/test/broker/03-pattern-matching.py b/test/broker/03-pattern-matching.py index 02754fa4..66ff2ef2 100755 --- a/test/broker/03-pattern-matching.py +++ b/test/broker/03-pattern-matching.py @@ -1,15 +1,6 @@ #!/usr/bin/env python -import subprocess -import time - -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * def pattern_test(sub_topic, pub_topic): rc = 1 @@ -29,11 +20,9 @@ def pattern_test(sub_topic, pub_topic): unsuback_packet = mosq_test.gen_unsuback(mid) port = mosq_test.get_port() - broker = subprocess.Popen(['../../src/mosquitto', '-p', str(port)], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: - time.sleep(0.5) - sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") @@ -57,7 +46,7 @@ def pattern_test(sub_topic, pub_topic): if rc: print(stde) print(stdo) - raise + sys.exit(rc) return rc diff --git a/test/broker/03-publish-b2c-disconnect-qos1-helper.py b/test/broker/03-publish-b2c-disconnect-qos1-helper.py index f6c9766f..f86ed175 100755 --- a/test/broker/03-publish-b2c-disconnect-qos1-helper.py +++ b/test/broker/03-publish-b2c-disconnect-qos1-helper.py @@ -1,12 +1,6 @@ #!/usr/bin/env python -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * port = mosq_test.get_port() diff --git a/test/broker/03-publish-b2c-disconnect-qos1.py b/test/broker/03-publish-b2c-disconnect-qos1.py index 14e1ed17..b95cd576 100755 --- a/test/broker/03-publish-b2c-disconnect-qos1.py +++ b/test/broker/03-publish-b2c-disconnect-qos1.py @@ -1,15 +1,6 @@ #!/usr/bin/env python -import subprocess -import socket - -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * port = mosq_test.get_port() @@ -17,8 +8,8 @@ rc = 1 mid = 3265 keepalive = 60 connect_packet = mosq_test.gen_connect("pub-qos1-disco-test", keepalive=keepalive, clean_session=False) -connack1_packet = mosq_test.gen_connack(resv=0, rc=0) -connack2_packet = mosq_test.gen_connack(resv=1, rc=0) +connack1_packet = mosq_test.gen_connack(flags=0, rc=0) +connack2_packet = mosq_test.gen_connack(flags=1, rc=0) subscribe_packet = mosq_test.gen_subscribe(mid, "qos1/disconnect/test", 1) suback_packet = mosq_test.gen_suback(mid, 1) diff --git a/test/broker/03-publish-b2c-disconnect-qos2-helper.py b/test/broker/03-publish-b2c-disconnect-qos2-helper.py index aa99fd4d..f0c0dcce 100755 --- a/test/broker/03-publish-b2c-disconnect-qos2-helper.py +++ b/test/broker/03-publish-b2c-disconnect-qos2-helper.py @@ -2,13 +2,7 @@ # Test whether a PUBLISH to a topic with QoS 2 results in the correct packet flow. -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * port = mosq_test.get_port() diff --git a/test/broker/03-publish-b2c-disconnect-qos2.py b/test/broker/03-publish-b2c-disconnect-qos2.py index 8d40f5ae..30227655 100755 --- a/test/broker/03-publish-b2c-disconnect-qos2.py +++ b/test/broker/03-publish-b2c-disconnect-qos2.py @@ -1,21 +1,13 @@ #!/usr/bin/env python -import subprocess - -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * rc = 1 mid = 3265 keepalive = 60 connect_packet = mosq_test.gen_connect("pub-qos2-disco-test", keepalive=keepalive, clean_session=False) -connack1_packet = mosq_test.gen_connack(resv=0, rc=0) -connack2_packet = mosq_test.gen_connack(resv=1, rc=0) +connack1_packet = mosq_test.gen_connack(flags=0, rc=0) +connack2_packet = mosq_test.gen_connack(flags=1, rc=0) subscribe_packet = mosq_test.gen_subscribe(mid, "qos2/disconnect/test", 2) suback_packet = mosq_test.gen_suback(mid, 2) 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..7be55611 --- /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(flags=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..28623a02 --- /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(flags=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-b2c-timeout-qos1-helper.py b/test/broker/03-publish-b2c-timeout-qos1-helper.py index 5ed91aba..a1d4c829 100755 --- a/test/broker/03-publish-b2c-timeout-qos1-helper.py +++ b/test/broker/03-publish-b2c-timeout-qos1-helper.py @@ -2,13 +2,7 @@ # Test whether a PUBLISH to a topic with QoS 2 results in the correct packet flow. -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * rc = 1 keepalive = 60 diff --git a/test/broker/03-publish-b2c-timeout-qos1.py b/test/broker/03-publish-b2c-timeout-qos1.py index a08dd44b..f5aae3bb 100755 --- a/test/broker/03-publish-b2c-timeout-qos1.py +++ b/test/broker/03-publish-b2c-timeout-qos1.py @@ -2,15 +2,7 @@ # Test whether a SUBSCRIBE to a topic with QoS 2 results in the correct SUBACK packet. -import subprocess - -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * rc = 1 mid = 3265 diff --git a/test/broker/03-publish-b2c-timeout-qos2-helper.py b/test/broker/03-publish-b2c-timeout-qos2-helper.py index a6e3f789..66d5384f 100755 --- a/test/broker/03-publish-b2c-timeout-qos2-helper.py +++ b/test/broker/03-publish-b2c-timeout-qos2-helper.py @@ -2,13 +2,7 @@ # Test whether a PUBLISH to a topic with QoS 2 results in the correct packet flow. -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * rc = 1 keepalive = 60 diff --git a/test/broker/03-publish-b2c-timeout-qos2.py b/test/broker/03-publish-b2c-timeout-qos2.py index aceef579..295b1e20 100755 --- a/test/broker/03-publish-b2c-timeout-qos2.py +++ b/test/broker/03-publish-b2c-timeout-qos2.py @@ -2,15 +2,7 @@ # Test whether a SUBSCRIBE to a topic with QoS 2 results in the correct SUBACK packet. -import subprocess - -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * rc = 1 mid = 3265 diff --git a/test/broker/03-publish-c2b-disconnect-qos2.py b/test/broker/03-publish-c2b-disconnect-qos2.py index 057690fc..d45ffadc 100755 --- a/test/broker/03-publish-c2b-disconnect-qos2.py +++ b/test/broker/03-publish-c2b-disconnect-qos2.py @@ -1,18 +1,12 @@ #!/usr/bin/env python -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * rc = 1 mid = 3265 keepalive = 60 connect_packet = mosq_test.gen_connect("pub-qos2-disco-test", keepalive=keepalive, clean_session=False, proto_ver=3) -connack_packet = mosq_test.gen_connack(resv=0, rc=0) +connack_packet = mosq_test.gen_connack(flags=0, rc=0) subscribe_packet = mosq_test.gen_subscribe(mid, "qos2/disconnect/test", 2) suback_packet = mosq_test.gen_suback(mid, 2) 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..5f9957fe --- /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(flags=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/03-publish-c2b-timeout-qos2.py b/test/broker/03-publish-c2b-timeout-qos2.py index 2c7f6f79..c5e3e8df 100755 --- a/test/broker/03-publish-c2b-timeout-qos2.py +++ b/test/broker/03-publish-c2b-timeout-qos2.py @@ -4,13 +4,7 @@ # flow. This test introduces delays into the flow in order to force the broker # to send duplicate PUBREC and PUBCOMP messages. -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * rc = 1 keepalive = 600 diff --git a/test/broker/03-publish-dollar.py b/test/broker/03-publish-dollar.py index 4b2d3787..35b716cf 100755 --- a/test/broker/03-publish-dollar.py +++ b/test/broker/03-publish-dollar.py @@ -2,13 +2,7 @@ # Test whether a PUBLISH to a topic starting with $ succeeds -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * rc = 1 mid = 19 diff --git a/test/broker/03-publish-invalid-utf8.py b/test/broker/03-publish-invalid-utf8.py index 811b356a..fb3788b1 100755 --- a/test/broker/03-publish-invalid-utf8.py +++ b/test/broker/03-publish-invalid-utf8.py @@ -2,15 +2,7 @@ # Test whether a PUBLISH to a topic with an invalid UTF-8 topic fails -import time -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import struct -import mosq_test +from mosq_test_helper import * rc = 1 mid = 53 diff --git a/test/broker/03-publish-qos1-queued-bytes.py b/test/broker/03-publish-qos1-queued-bytes.py index e2f4c32d..f57e381f 100755 --- a/test/broker/03-publish-qos1-queued-bytes.py +++ b/test/broker/03-publish-qos1-queued-bytes.py @@ -17,13 +17,7 @@ except ImportError: exit(0) -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * rc = 1 diff --git a/test/broker/03-publish-qos1-retain-disabled.py b/test/broker/03-publish-qos1-retain-disabled.py new file mode 100755 index 00000000..bb86fe61 --- /dev/null +++ b/test/broker/03-publish-qos1-retain-disabled.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python + +# Test whether a PUBLISH with a retain set when retains are disabled results in +# the correct DISCONNECT. + +from mosq_test_helper import * + +def write_config(filename, port): + with open(filename, 'w') as f: + f.write("port %d\n" % (port)) + f.write("retain_available false\n") + +port = mosq_test.get_port() +conf_file = os.path.basename(__file__).replace('.py', '.conf') +write_config(conf_file, port) + +rc = 1 +mid = 1 +keepalive = 60 +connect_packet = mosq_test.gen_connect("pub-qos1-test", keepalive=keepalive, proto_ver=5) + +props = mqtt5_props.gen_byte_prop(mqtt5_props.PROP_RETAIN_AVAILABLE, 0) +connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props) + +publish_packet = mosq_test.gen_publish("pub/qos1/test", qos=1, mid=mid, payload="message", retain=True, proto_ver=5) +puback_packet = mosq_test.gen_puback(mid, proto_ver=5) + +disconnect_packet = mosq_test.gen_disconnect(reason_code=154, proto_ver=5) + +broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) + +try: + sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) + mosq_test.do_send_receive(sock, publish_packet, disconnect_packet, "disconnect") + + rc = 0 + + sock.close() +finally: + os.remove(conf_file) + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde) + +exit(rc) + diff --git a/test/broker/03-publish-qos1.py b/test/broker/03-publish-qos1.py index 5bcebd47..dabf3358 100755 --- a/test/broker/03-publish-qos1.py +++ b/test/broker/03-publish-qos1.py @@ -2,13 +2,7 @@ # Test whether a PUBLISH to a topic with QoS 1 results in the correct PUBACK packet. -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * rc = 1 mid = 19 diff --git a/test/broker/03-publish-qos2.py b/test/broker/03-publish-qos2.py index c6ae3c69..2a8ebd47 100755 --- a/test/broker/03-publish-qos2.py +++ b/test/broker/03-publish-qos2.py @@ -2,13 +2,7 @@ # Test whether a PUBLISH to a topic with QoS 2 results in the correct packet flow. -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * rc = 1 keepalive = 60 diff --git a/test/broker/04-retain-check-source-persist-diff-port.py b/test/broker/04-retain-check-source-persist-diff-port.py new file mode 100755 index 00000000..c26ebe5f --- /dev/null +++ b/test/broker/04-retain-check-source-persist-diff-port.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python + +# Test for CVE-2018-12546, with the broker being stopped to write the persistence file, plus subscriber on different port. + +import inspect, os, sys +# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder +cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) +if cmd_subfolder not in sys.path: + sys.path.insert(0, cmd_subfolder) + +import mosq_test +import signal + +def write_config(filename, port1, port2, per_listener): + with open(filename, 'w') as f: + f.write("per_listener_settings %s\n" % (per_listener)) + f.write("check_retain_source true\n") + f.write("port %d\n" % (port1)) + f.write("acl_file %s\n" % (filename.replace('.conf', '.acl'))) + f.write("persistence true\n") + f.write("persistence_file %s\n" % (filename.replace('.conf', '.db'))) + f.write("listener %d\n" % (port2)) + +def write_acl_1(filename, username): + with open(filename, 'w') as f: + if username is not None: + f.write('user %s\n' % (username)) + f.write('topic readwrite test/topic\n') + +def write_acl_2(filename, username): + with open(filename, 'w') as f: + if username is not None: + f.write('user %s\n' % (username)) + f.write('topic read test/topic\n') + + +def do_test(per_listener, username): + conf_file = os.path.basename(__file__).replace('.py', '.conf') + write_config(conf_file, port1, port2, per_listener) + + persistence_file = os.path.basename(__file__).replace('.py', '.db') + try: + os.remove(persistence_file) + except OSError: + pass + + acl_file = os.path.basename(__file__).replace('.py', '.acl') + write_acl_1(acl_file, username) + + + rc = 1 + keepalive = 60 + connect_packet = mosq_test.gen_connect("retain-check", keepalive=keepalive, username=username) + connack_packet = mosq_test.gen_connack(rc=0) + + if per_listener == "true": + u = None + else: + # If per listener is false, then the second client will be denied + # unless we provide a username + u = username + + connect2_packet = mosq_test.gen_connect("retain-recv", keepalive=keepalive, username=u) + connack2_packet = mosq_test.gen_connack(rc=0) + + mid = 1 + publish_packet = mosq_test.gen_publish("test/topic", qos=0, payload="retained message", retain=True) + subscribe_packet = mosq_test.gen_subscribe(mid, "test/topic", 0) + suback_packet = mosq_test.gen_suback(mid, 0) + + pingreq_packet = mosq_test.gen_pingreq() + pingresp_packet = mosq_test.gen_pingresp() + + broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port1) + + try: + sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port1) + sock.send(publish_packet) + sock.close() + + sock = mosq_test.do_client_connect(connect2_packet, connack2_packet, port=port2) + mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback 1") + + if mosq_test.expect_packet(sock, "publish", publish_packet): + sock.close() + + # Remove "write" ability + write_acl_2(acl_file, username) + broker.terminate() + broker.wait() + + broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port1) + + sock = mosq_test.do_client_connect(connect2_packet, connack2_packet, port=port2) + mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback 2") + # If we receive the retained message here, it is a failure. + mosq_test.do_send_receive(sock, pingreq_packet, pingresp_packet, "pingresp") + rc = 0 + + sock.close() + finally: + broker.terminate() + broker.wait() + os.remove(conf_file) + os.remove(acl_file) + os.remove(persistence_file) + (stdo, stde) = broker.communicate() + if rc: + print(stde) + exit(rc) + + +(port1, port2) = mosq_test.get_port(2) +do_test("true", username=None) +do_test("true", username="test") +do_test("false", username=None) +do_test("false", username="test") diff --git a/test/broker/04-retain-check-source-persist.py b/test/broker/04-retain-check-source-persist.py new file mode 100755 index 00000000..d383908e --- /dev/null +++ b/test/broker/04-retain-check-source-persist.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python + +# Test for CVE-2018-12546, with the broker being stopped to write the persistence file. + +import inspect, os, sys +# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder +cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) +if cmd_subfolder not in sys.path: + sys.path.insert(0, cmd_subfolder) + +import mosq_test +import signal + +def write_config(filename, port, per_listener): + with open(filename, 'w') as f: + f.write("per_listener_settings %s\n" % (per_listener)) + f.write("check_retain_source true\n") + f.write("port %d\n" % (port)) + f.write("acl_file %s\n" % (filename.replace('.conf', '.acl'))) + f.write("persistence true\n") + f.write("persistence_file %s\n" % (filename.replace('.conf', '.db'))) + +def write_acl_1(filename, username): + with open(filename, 'w') as f: + if username is not None: + f.write('user %s\n' % (username)) + f.write('topic readwrite test/topic\n') + +def write_acl_2(filename, username): + with open(filename, 'w') as f: + if username is not None: + f.write('user %s\n' % (username)) + f.write('topic read test/topic\n') + + +def do_test(per_listener, username): + conf_file = os.path.basename(__file__).replace('.py', '.conf') + write_config(conf_file, port, per_listener) + + persistence_file = os.path.basename(__file__).replace('.py', '.db') + try: + os.remove(persistence_file) + except OSError: + pass + + acl_file = os.path.basename(__file__).replace('.py', '.acl') + write_acl_1(acl_file, username) + + + rc = 1 + keepalive = 60 + connect_packet = mosq_test.gen_connect("retain-check", keepalive=keepalive, username=username) + connack_packet = mosq_test.gen_connack(rc=0) + + mid = 1 + publish_packet = mosq_test.gen_publish("test/topic", qos=0, payload="retained message", retain=True) + subscribe_packet = mosq_test.gen_subscribe(mid, "test/topic", 0) + suback_packet = mosq_test.gen_suback(mid, 0) + + pingreq_packet = mosq_test.gen_pingreq() + pingresp_packet = mosq_test.gen_pingresp() + + broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) + + try: + sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) + sock.send(publish_packet) + sock.close() + + sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) + mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback 1") + + if mosq_test.expect_packet(sock, "publish", publish_packet): + sock.close() + + # Remove "write" ability + write_acl_2(acl_file, username) + broker.terminate() + broker.wait() + + broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) + + sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) + mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback 2") + # If we receive the retained message here, it is a failure. + mosq_test.do_send_receive(sock, pingreq_packet, pingresp_packet, "pingresp") + rc = 0 + + sock.close() + finally: + broker.terminate() + broker.wait() + os.remove(conf_file) + os.remove(acl_file) + os.remove(persistence_file) + (stdo, stde) = broker.communicate() + if rc: + print(stde) + exit(rc) + + +port = mosq_test.get_port() +do_test("true", username=None) +do_test("true", username="test") +do_test("false", username=None) +do_test("false", username="test") diff --git a/test/broker/04-retain-check-source.py b/test/broker/04-retain-check-source.py new file mode 100755 index 00000000..4ef51275 --- /dev/null +++ b/test/broker/04-retain-check-source.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python + +# Test for CVE-2018-12546 + +import inspect, os, sys +# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder +cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) +if cmd_subfolder not in sys.path: + sys.path.insert(0, cmd_subfolder) + +import mosq_test +import signal + +def write_config(filename, port, per_listener): + with open(filename, 'w') as f: + f.write("per_listener_settings %s\n" % (per_listener)) + f.write("check_retain_source true\n") + f.write("port %d\n" % (port)) + f.write("acl_file %s\n" % (filename.replace('.conf', '.acl'))) + +def write_acl_1(filename): + with open(filename, 'w') as f: + f.write('topic readwrite test/topic\n') + +def write_acl_2(filename): + with open(filename, 'w') as f: + f.write('topic read test/topic\n') + + +def do_test(per_listener): + conf_file = os.path.basename(__file__).replace('.py', '.conf') + write_config(conf_file, port, per_listener) + + acl_file = os.path.basename(__file__).replace('.py', '.acl') + write_acl_1(acl_file) + + + rc = 1 + keepalive = 60 + connect_packet = mosq_test.gen_connect("retain-check", keepalive=keepalive) + connack_packet = mosq_test.gen_connack(rc=0) + + mid = 1 + publish_packet = mosq_test.gen_publish("test/topic", qos=0, payload="retained message", retain=True) + subscribe_packet = mosq_test.gen_subscribe(mid, "test/topic", 0) + suback_packet = mosq_test.gen_suback(mid, 0) + + pingreq_packet = mosq_test.gen_pingreq() + pingresp_packet = mosq_test.gen_pingresp() + + broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) + + try: + sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) + sock.send(publish_packet) + sock.close() + + sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) + mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback 1") + + if mosq_test.expect_packet(sock, "publish", publish_packet): + sock.close() + + # Remove "write" ability + write_acl_2(acl_file) + broker.send_signal(signal.SIGHUP) + + sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) + mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback 2") + # If we receive the retained message here, it is a failure. + mosq_test.do_send_receive(sock, pingreq_packet, pingresp_packet, "pingresp") + rc = 0 + + sock.close() + finally: + os.remove(conf_file) + os.remove(acl_file) + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde) + exit(rc) + +port = mosq_test.get_port() +do_test("true") +do_test("false") diff --git a/test/broker/04-retain-qos0-clear.py b/test/broker/04-retain-qos0-clear.py index 90cdf6c3..ea6d122c 100755 --- a/test/broker/04-retain-qos0-clear.py +++ b/test/broker/04-retain-qos0-clear.py @@ -3,15 +3,7 @@ # Test whether a retained PUBLISH is cleared when a zero length retained # message is published to a topic. -import socket - -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * rc = 1 keepalive = 60 diff --git a/test/broker/04-retain-qos0-fresh.py b/test/broker/04-retain-qos0-fresh.py index c2445b82..41f21a26 100755 --- a/test/broker/04-retain-qos0-fresh.py +++ b/test/broker/04-retain-qos0-fresh.py @@ -3,13 +3,7 @@ # Test whether a retained PUBLISH to a topic with QoS 0 is sent with # retain=false to an already subscribed client. -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * rc = 1 keepalive = 60 diff --git a/test/broker/04-retain-qos0-repeated.py b/test/broker/04-retain-qos0-repeated.py index 7276248a..54fc0e9e 100755 --- a/test/broker/04-retain-qos0-repeated.py +++ b/test/broker/04-retain-qos0-repeated.py @@ -3,13 +3,7 @@ # Test whether a retained PUBLISH to a topic with QoS 0 is actually retained # and delivered when multiple sub/unsub operations are carried out. -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * rc = 1 keepalive = 60 diff --git a/test/broker/04-retain-qos0.py b/test/broker/04-retain-qos0.py index 2d698671..d6b21902 100755 --- a/test/broker/04-retain-qos0.py +++ b/test/broker/04-retain-qos0.py @@ -2,13 +2,7 @@ # Test whether a retained PUBLISH to a topic with QoS 0 is actually retained. -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * rc = 1 keepalive = 60 diff --git a/test/broker/04-retain-qos1-qos0.py b/test/broker/04-retain-qos1-qos0.py index dd452652..3fa96a08 100755 --- a/test/broker/04-retain-qos1-qos0.py +++ b/test/broker/04-retain-qos1-qos0.py @@ -4,13 +4,7 @@ # Subscription is made with QoS 0 so the retained message should also have QoS # 0. -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * rc = 1 keepalive = 60 diff --git a/test/broker/04-retain-upgrade-outgoing-qos.py b/test/broker/04-retain-upgrade-outgoing-qos.py index 597e9828..bccff1d7 100755 --- a/test/broker/04-retain-upgrade-outgoing-qos.py +++ b/test/broker/04-retain-upgrade-outgoing-qos.py @@ -3,13 +3,7 @@ # Test whether a retained PUBLISH to a topic with QoS 0 is sent with subscriber QoS # when upgrade_outgoing_qos is true -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: diff --git a/test/broker/05-clean-session-qos1-helper.py b/test/broker/05-clean-session-qos1-helper.py index c1e62444..28abd803 100755 --- a/test/broker/05-clean-session-qos1-helper.py +++ b/test/broker/05-clean-session-qos1-helper.py @@ -2,13 +2,7 @@ # Test whether a clean session client has a QoS 1 message queued for it. -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * rc = 1 keepalive = 60 diff --git a/test/broker/05-clean-session-qos1.py b/test/broker/05-clean-session-qos1.py index 7ae6cdc8..17265460 100755 --- a/test/broker/05-clean-session-qos1.py +++ b/test/broker/05-clean-session-qos1.py @@ -2,22 +2,14 @@ # Test whether a clean session client has a QoS 1 message queued for it. -import subprocess - -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * rc = 1 mid = 109 keepalive = 60 connect_packet = mosq_test.gen_connect("clean-qos2-test", keepalive=keepalive, clean_session=False) -connack1_packet = mosq_test.gen_connack(resv=0, rc=0) -connack2_packet = mosq_test.gen_connack(resv=1, rc=0) +connack1_packet = mosq_test.gen_connack(flags=0, rc=0) +connack2_packet = mosq_test.gen_connack(flags=1, rc=0) disconnect_packet = mosq_test.gen_disconnect() diff --git a/test/broker/06-bridge-b2br-disconnect-qos1.py b/test/broker/06-bridge-b2br-disconnect-qos1.py index 91c7fe6c..e742bd73 100755 --- a/test/broker/06-bridge-b2br-disconnect-qos1.py +++ b/test/broker/06-bridge-b2br-disconnect-qos1.py @@ -2,15 +2,7 @@ # Does a bridge resend a QoS=1 message correctly after a disconnect? -import socket - -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * def write_config(filename, port1, port2): with open(filename, 'w') as f: diff --git a/test/broker/06-bridge-b2br-disconnect-qos2.py b/test/broker/06-bridge-b2br-disconnect-qos2.py index 11f19700..38c86c23 100755 --- a/test/broker/06-bridge-b2br-disconnect-qos2.py +++ b/test/broker/06-bridge-b2br-disconnect-qos2.py @@ -2,15 +2,7 @@ # Does a bridge resend a QoS=1 message correctly after a disconnect? -import socket - -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * def write_config(filename, port1, port2): with open(filename, 'w') as f: diff --git a/test/broker/06-bridge-b2br-remapping.py b/test/broker/06-bridge-b2br-remapping.py index 4c344354..3d81e0c2 100755 --- a/test/broker/06-bridge-b2br-remapping.py +++ b/test/broker/06-bridge-b2br-remapping.py @@ -2,15 +2,7 @@ # Test remapping of topic name for incoming message -import socket - -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * def write_config(filename, port1, port2): with open(filename, 'w') as f: diff --git a/test/broker/06-bridge-br2b-disconnect-qos1-helper.py b/test/broker/06-bridge-br2b-disconnect-qos1-helper.py index f010cd33..0e50f9ef 100755 --- a/test/broker/06-bridge-br2b-disconnect-qos1-helper.py +++ b/test/broker/06-bridge-br2b-disconnect-qos1-helper.py @@ -1,12 +1,6 @@ #!/usr/bin/env python -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * port = mosq_test.get_port() diff --git a/test/broker/06-bridge-br2b-disconnect-qos1.py b/test/broker/06-bridge-br2b-disconnect-qos1.py index ae43970a..00fdba0f 100755 --- a/test/broker/06-bridge-br2b-disconnect-qos1.py +++ b/test/broker/06-bridge-br2b-disconnect-qos1.py @@ -2,16 +2,7 @@ # Does a bridge resend a QoS=1 message correctly after a disconnect? -import subprocess -import socket - -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * def write_config(filename, port1, port2): with open(filename, 'w') as f: diff --git a/test/broker/06-bridge-br2b-disconnect-qos2-helper.py b/test/broker/06-bridge-br2b-disconnect-qos2-helper.py index 80b12e17..bcb28d43 100755 --- a/test/broker/06-bridge-br2b-disconnect-qos2-helper.py +++ b/test/broker/06-bridge-br2b-disconnect-qos2-helper.py @@ -1,12 +1,6 @@ #!/usr/bin/env python -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * port = mosq_test.get_port() diff --git a/test/broker/06-bridge-br2b-disconnect-qos2.py b/test/broker/06-bridge-br2b-disconnect-qos2.py index 24610aba..d7469288 100755 --- a/test/broker/06-bridge-br2b-disconnect-qos2.py +++ b/test/broker/06-bridge-br2b-disconnect-qos2.py @@ -2,16 +2,7 @@ # Does a bridge resend a QoS=1 message correctly after a disconnect? -import subprocess -import socket - -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * def write_config(filename, port1, port2): with open(filename, 'w') as f: diff --git a/test/broker/06-bridge-br2b-remapping.py b/test/broker/06-bridge-br2b-remapping.py index 51ddf2bb..716b2e5a 100755 --- a/test/broker/06-bridge-br2b-remapping.py +++ b/test/broker/06-bridge-br2b-remapping.py @@ -2,15 +2,7 @@ # Test remapping of topic name for outgoing message -import socket - -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * def write_config(filename, port1, port2): with open(filename, 'w') as f: diff --git a/test/broker/06-bridge-fail-persist-resend-qos1.py b/test/broker/06-bridge-fail-persist-resend-qos1.py index f288561d..ece86f99 100755 --- a/test/broker/06-bridge-fail-persist-resend-qos1.py +++ b/test/broker/06-bridge-fail-persist-resend-qos1.py @@ -2,17 +2,7 @@ # Test whether a bridge can cope with an unknown PUBACK -import socket -import subprocess -import time - -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * def write_config(filename, port1, port2): with open(filename, 'w') as f: diff --git a/test/broker/06-bridge-fail-persist-resend-qos2.py b/test/broker/06-bridge-fail-persist-resend-qos2.py index 1d1aad1d..7c2df3e2 100755 --- a/test/broker/06-bridge-fail-persist-resend-qos2.py +++ b/test/broker/06-bridge-fail-persist-resend-qos2.py @@ -2,17 +2,7 @@ # Test whether a bridge can cope with an unknown PUBACK -import socket -import subprocess -import time - -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * def write_config(filename, port1, port2): with open(filename, 'w') as f: diff --git a/test/broker/06-bridge-per-listener-settings.py b/test/broker/06-bridge-per-listener-settings.py index 06758a75..4e6e90ad 100755 --- a/test/broker/06-bridge-per-listener-settings.py +++ b/test/broker/06-bridge-per-listener-settings.py @@ -2,15 +2,7 @@ # Test remapping of topic name for incoming message -import socket - -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * def write_config(filename, port1, port2, port3): with open(filename, 'w') as f: diff --git a/test/broker/06-bridge-reconnect-local-out-helper.py b/test/broker/06-bridge-reconnect-local-out-helper.py index cf46147b..66eb425d 100755 --- a/test/broker/06-bridge-reconnect-local-out-helper.py +++ b/test/broker/06-bridge-reconnect-local-out-helper.py @@ -1,12 +1,6 @@ #!/usr/bin/env python -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * port = mosq_test.get_port() diff --git a/test/broker/06-bridge-reconnect-local-out.py b/test/broker/06-bridge-reconnect-local-out.py index 9edde871..0b043e52 100755 --- a/test/broker/06-bridge-reconnect-local-out.py +++ b/test/broker/06-bridge-reconnect-local-out.py @@ -3,16 +3,7 @@ # Test whether a bridge topics work correctly after reconnection. # Important point here is that persistence is enabled. -import subprocess -import time - -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * def write_config(filename, port1, port2): with open(filename, 'w') as f: diff --git a/test/broker/07-will-invalid-utf8.py b/test/broker/07-will-invalid-utf8.py index b3059e6c..c4c1f1a2 100755 --- a/test/broker/07-will-invalid-utf8.py +++ b/test/broker/07-will-invalid-utf8.py @@ -2,15 +2,7 @@ # Test whether a will topic with invalid UTF-8 fails -import time -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import struct -import mosq_test +from mosq_test_helper import * rc = 1 mid = 53 diff --git a/test/broker/07-will-no-flag.py b/test/broker/07-will-no-flag.py index e2333231..0b4960e5 100755 --- a/test/broker/07-will-no-flag.py +++ b/test/broker/07-will-no-flag.py @@ -3,14 +3,7 @@ # Test whether a connection is disconnected if it sets the will flag but does # not provide a will payload. -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import struct -import mosq_test +from mosq_test_helper import * rc = 1 keepalive = 10 diff --git a/test/broker/07-will-null-helper.py b/test/broker/07-will-null-helper.py index 9ea56e59..2500185e 100755 --- a/test/broker/07-will-null-helper.py +++ b/test/broker/07-will-null-helper.py @@ -2,13 +2,7 @@ # Connect a client with a will, then disconnect without DISCONNECT. -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * rc = 1 keepalive = 60 diff --git a/test/broker/07-will-null-topic.py b/test/broker/07-will-null-topic.py index 69c5dcbc..b9c15943 100755 --- a/test/broker/07-will-null-topic.py +++ b/test/broker/07-will-null-topic.py @@ -2,13 +2,7 @@ import struct -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * rc = 1 keepalive = 60 diff --git a/test/broker/07-will-null.py b/test/broker/07-will-null.py index 0a76fdca..50c00d4e 100755 --- a/test/broker/07-will-null.py +++ b/test/broker/07-will-null.py @@ -2,14 +2,7 @@ # Test whether a client will is transmitted correctly with a null character in the middle. -import subprocess -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * rc = 1 mid = 53 diff --git a/test/broker/07-will-qos0-helper.py b/test/broker/07-will-qos0-helper.py index e6c46eb7..f9d42c64 100755 --- a/test/broker/07-will-qos0-helper.py +++ b/test/broker/07-will-qos0-helper.py @@ -2,13 +2,7 @@ # Connect a client with a will, then disconnect without DISCONNECT. -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * rc = 1 keepalive = 60 diff --git a/test/broker/07-will-qos0.py b/test/broker/07-will-qos0.py index 4c7ef7d4..990e40b4 100755 --- a/test/broker/07-will-qos0.py +++ b/test/broker/07-will-qos0.py @@ -2,15 +2,7 @@ # Test whether a client will is transmitted correctly. -import subprocess - -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * rc = 1 mid = 53 diff --git a/test/broker/08-ssl-bridge-helper.py b/test/broker/08-ssl-bridge-helper.py index e67c398b..9ca462f4 100755 --- a/test/broker/08-ssl-bridge-helper.py +++ b/test/broker/08-ssl-bridge-helper.py @@ -1,12 +1,6 @@ #!/usr/bin/env python -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * port = mosq_test.get_port() diff --git a/test/broker/08-ssl-bridge.py b/test/broker/08-ssl-bridge.py index d84bc5b6..cb997371 100755 --- a/test/broker/08-ssl-bridge.py +++ b/test/broker/08-ssl-bridge.py @@ -1,16 +1,6 @@ #!/usr/bin/env python -import subprocess -import socket -import ssl - -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * def write_config(filename, port1, port2): with open(filename, 'w') as f: diff --git a/test/broker/08-ssl-connect-cert-auth-crl.py b/test/broker/08-ssl-connect-cert-auth-crl.py index 492b7d8d..b68bee05 100755 --- a/test/broker/08-ssl-connect-cert-auth-crl.py +++ b/test/broker/08-ssl-connect-cert-auth-crl.py @@ -1,21 +1,11 @@ #!/usr/bin/env python -import socket -import ssl -import sys +from mosq_test_helper import * if sys.version < '2.7': print("WARNING: SSL not supported on Python 2.6") exit(0) -import inspect, os -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test - def write_config(filename, port1, port2): with open(filename, 'w') as f: f.write("port %d\n" % (port2)) diff --git a/test/broker/08-ssl-connect-cert-auth-expired.py b/test/broker/08-ssl-connect-cert-auth-expired.py index 4de59705..836a9b82 100755 --- a/test/broker/08-ssl-connect-cert-auth-expired.py +++ b/test/broker/08-ssl-connect-cert-auth-expired.py @@ -3,23 +3,12 @@ # Test whether a valid CONNECT results in the correct CONNACK packet using an # SSL connection with client certificates required. -import socket -import ssl -import sys -import time +from mosq_test_helper import * if sys.version < '2.7': print("WARNING: SSL not supported on Python 2.6") exit(0) -import inspect, os -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test - def write_config(filename, port1, port2): with open(filename, 'w') as f: f.write("port %d\n" % (port2)) @@ -37,7 +26,6 @@ write_config(conf_file, port1, port2) rc = 1 keepalive = 10 connect_packet = mosq_test.gen_connect("connect-success-test", keepalive=keepalive) -connack_packet = mosq_test.gen_connack(rc=0) broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True) @@ -47,6 +35,7 @@ try: ssock.settimeout(20) try: ssock.connect(("localhost", port1)) + mosq_test.do_send_receive(ssock, connect_packet, "", "connack") except ssl.SSLError as err: if err.errno == 1: rc = 0 diff --git a/test/broker/08-ssl-connect-cert-auth-revoked.py b/test/broker/08-ssl-connect-cert-auth-revoked.py index 76b6bfbe..97b8f432 100755 --- a/test/broker/08-ssl-connect-cert-auth-revoked.py +++ b/test/broker/08-ssl-connect-cert-auth-revoked.py @@ -1,22 +1,11 @@ #!/usr/bin/env python -import socket -import ssl -import sys -import time +from mosq_test_helper import * if sys.version < '2.7': print("WARNING: SSL not supported on Python 2.6") exit(0) -import inspect, os -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test - def write_config(filename, port1, port2): with open(filename, 'w') as f: f.write("port %d\n" % (port2)) @@ -34,7 +23,6 @@ write_config(conf_file, port1, port2) rc = 1 keepalive = 10 connect_packet = mosq_test.gen_connect("connect-revoked-test", keepalive=keepalive) -connack_packet = mosq_test.gen_connack(rc=0) broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True) @@ -44,6 +32,7 @@ try: ssock.settimeout(20) try: ssock.connect(("localhost", port1)) + mosq_test.do_send_receive(ssock, connect_packet, "", "connack") except ssl.SSLError as err: if err.errno == 1 and "certificate revoked" in err.strerror: rc = 0 diff --git a/test/broker/08-ssl-connect-cert-auth-without.py b/test/broker/08-ssl-connect-cert-auth-without.py index 2f7dbf83..f31141e7 100755 --- a/test/broker/08-ssl-connect-cert-auth-without.py +++ b/test/broker/08-ssl-connect-cert-auth-without.py @@ -2,24 +2,12 @@ # Test whether a client can connect without an SSL certificate if one is required. -import errno -import socket -import ssl -import sys -import time +from mosq_test_helper import * if sys.version < '2.7': print("WARNING: SSL not supported on Python 2.6") exit(0) -import inspect, os -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test - def write_config(filename, port1, port2): with open(filename, 'w') as f: f.write("port %d\n" % (port2)) @@ -36,7 +24,6 @@ write_config(conf_file, port1, port2) rc = 1 keepalive = 10 connect_packet = mosq_test.gen_connect("connect-cert-test", keepalive=keepalive) -connack_packet = mosq_test.gen_connack(rc=0) broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True) @@ -45,6 +32,7 @@ ssock = ssl.wrap_socket(sock, ca_certs="../ssl/test-root-ca.crt", cert_reqs=ssl. ssock.settimeout(20) try: ssock.connect(("localhost", port1)) + mosq_test.do_send_receive(ssock, connect_packet, "", "connack") except ssl.SSLError as err: if err.errno == 1: rc = 0 diff --git a/test/broker/08-ssl-connect-cert-auth.py b/test/broker/08-ssl-connect-cert-auth.py index 04ca171c..1f0872d3 100755 --- a/test/broker/08-ssl-connect-cert-auth.py +++ b/test/broker/08-ssl-connect-cert-auth.py @@ -2,22 +2,12 @@ # Test whether a valid CONNECT results in the correct CONNACK packet using an SSL connection. -import socket -import ssl -import sys +from mosq_test_helper import * if sys.version < '2.7': print("WARNING: SSL not supported on Python 2.6") exit(0) -import inspect, os -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test - def write_config(filename, port1, port2): with open(filename, 'w') as f: f.write("port %d\n" % (port2)) diff --git a/test/broker/08-ssl-connect-identity.py b/test/broker/08-ssl-connect-identity.py index b73c2d12..4b7dd280 100755 --- a/test/broker/08-ssl-connect-identity.py +++ b/test/broker/08-ssl-connect-identity.py @@ -1,23 +1,13 @@ #!/usr/bin/env python # Client connects with a certificate to a server that has use_identity_as_username=true. Shouldn't be rejected. -import socket -import ssl -import sys -import time + +from mosq_test_helper import * if sys.version < '2.7': print("WARNING: SSL not supported on Python 2.6") exit(0) -import inspect, os -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test - def write_config(filename, port1, port2): with open(filename, 'w') as f: f.write("port %d\n" % (port2)) diff --git a/test/broker/08-ssl-connect-no-auth-wrong-ca.py b/test/broker/08-ssl-connect-no-auth-wrong-ca.py index 9f84a28c..f8cb88e9 100755 --- a/test/broker/08-ssl-connect-no-auth-wrong-ca.py +++ b/test/broker/08-ssl-connect-no-auth-wrong-ca.py @@ -2,23 +2,12 @@ # Test whether a valid CONNECT results in the correct CONNACK packet using an SSL connection. -import socket -import ssl -import sys -import time +from mosq_test_helper import * if sys.version < '2.7': print("WARNING: SSL not supported on Python 2.6") exit(0) -import inspect, os -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test - def write_config(filename, port1, port2): with open(filename, 'w') as f: f.write("port %d\n" % (port2)) diff --git a/test/broker/08-ssl-connect-no-auth.py b/test/broker/08-ssl-connect-no-auth.py index 67c3a877..ee0acb0a 100755 --- a/test/broker/08-ssl-connect-no-auth.py +++ b/test/broker/08-ssl-connect-no-auth.py @@ -2,22 +2,12 @@ # Test whether a valid CONNECT results in the correct CONNACK packet using an SSL connection. -import socket -import ssl -import sys +from mosq_test_helper import * if sys.version < '2.7': print("WARNING: SSL not supported on Python 2.6") exit(0) -import inspect, os -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test - def write_config(filename, port1, port2): with open(filename, 'w') as f: f.write("port %d\n" % (port2)) diff --git a/test/broker/08-ssl-connect-no-identity.py b/test/broker/08-ssl-connect-no-identity.py index 70a0a27c..6f65fd0c 100755 --- a/test/broker/08-ssl-connect-no-identity.py +++ b/test/broker/08-ssl-connect-no-identity.py @@ -1,23 +1,13 @@ #!/usr/bin/env python # Client connects without a certificate to a server that has use_identity_as_username=true. Should be rejected. -import socket -import ssl -import sys -import time + +from mosq_test_helper import * if sys.version < '2.7': print("WARNING: SSL not supported on Python 2.6") exit(0) -import inspect, os -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test - def write_config(filename, port1, port2): with open(filename, 'w') as f: f.write("port %d\n" % (port2)) diff --git a/test/broker/08-tls-psk-bridge.py b/test/broker/08-tls-psk-bridge.py index b0d2cfb9..8a184a27 100755 --- a/test/broker/08-tls-psk-bridge.py +++ b/test/broker/08-tls-psk-bridge.py @@ -1,23 +1,11 @@ #!/usr/bin/env python -import subprocess -import ssl -import sys -import time +from mosq_test_helper import * if sys.version < '2.7': print("WARNING: SSL not supported on Python 2.6") exit(0) - -import inspect, os -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test - def write_config1(filename, port1, port2): with open(filename, 'w') as f: f.write("allow_anonymous true\n") diff --git a/test/broker/08-tls-psk-pub.py b/test/broker/08-tls-psk-pub.py index 34994416..383014a6 100755 --- a/test/broker/08-tls-psk-pub.py +++ b/test/broker/08-tls-psk-pub.py @@ -1,22 +1,12 @@ #!/usr/bin/env python -import subprocess -import ssl -import sys +from mosq_test_helper import * if sys.version < '2.7': print("WARNING: SSL not supported on Python 2.6") exit(0) -import inspect, os -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test - def write_config(filename, port1, port2): with open(filename, 'w') as f: f.write("allow_anonymous true\n") diff --git a/test/broker/09-acl-empty-file.py b/test/broker/09-acl-empty-file.py new file mode 100755 index 00000000..a6d71cf6 --- /dev/null +++ b/test/broker/09-acl-empty-file.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python + +# Test for CVE-2018-xxxxx + +import inspect, os, sys +# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder +cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) +if cmd_subfolder not in sys.path: + sys.path.insert(0, cmd_subfolder) + +import mosq_test +import signal + +def write_config(filename, port, per_listener): + with open(filename, 'w') as f: + f.write("per_listener_settings %s\n" % (per_listener)) + f.write("port %d\n" % (port)) + f.write("acl_file %s\n" % (filename.replace('.conf', '.acl'))) + +def write_acl(filename): + with open(filename, 'w') as f: + f.write('#comment\n') + f.write('\n') + + +def do_test(port, per_listener): + conf_file = os.path.basename(__file__).replace('.py', '.conf') + write_config(conf_file, port, per_listener) + + acl_file = os.path.basename(__file__).replace('.py', '.acl') + write_acl(acl_file) + + rc = 1 + keepalive = 60 + connect_packet = mosq_test.gen_connect("acl-check", keepalive=keepalive) + connack_packet = mosq_test.gen_connack(rc=0) + + mid = 1 + publish_packet = mosq_test.gen_publish("test/topic", qos=0, payload="message") + subscribe_packet = mosq_test.gen_subscribe(mid, "test/topic", 0) + suback_packet = mosq_test.gen_suback(mid, 0) + + pingreq_packet = mosq_test.gen_pingreq() + pingresp_packet = mosq_test.gen_pingresp() + + broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, 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") + + sock.send(publish_packet) + + # If we receive the message, this will fail. + mosq_test.do_send_receive(sock, pingreq_packet, pingresp_packet, "pingresp") + rc = 0 + + sock.close() + finally: + os.remove(conf_file) + os.remove(acl_file) + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde) + exit(rc) + +port = mosq_test.get_port() +do_test(port, "false") +do_test(port, "true") diff --git a/test/broker/09-plugin-auth-acl-sub.py b/test/broker/09-plugin-auth-acl-sub.py index 838705bb..111d5477 100755 --- a/test/broker/09-plugin-auth-acl-sub.py +++ b/test/broker/09-plugin-auth-acl-sub.py @@ -2,13 +2,7 @@ # Test topic subscription. All topic are allowed but not using wildcard in subscribe. -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: diff --git a/test/broker/09-plugin-auth-context-params.py b/test/broker/09-plugin-auth-context-params.py index bedbec39..bb780d64 100755 --- a/test/broker/09-plugin-auth-context-params.py +++ b/test/broker/09-plugin-auth-context-params.py @@ -2,13 +2,7 @@ # Test whether message parameters are passed to the plugin acl check function. -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: diff --git a/test/broker/09-plugin-auth-defer-unpwd-fail.py b/test/broker/09-plugin-auth-defer-unpwd-fail.py index 0cc78aa4..07521a87 100755 --- a/test/broker/09-plugin-auth-defer-unpwd-fail.py +++ b/test/broker/09-plugin-auth-defer-unpwd-fail.py @@ -2,13 +2,7 @@ # Test whether a connection fail when using a auth_plugin that defer authentication. -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: diff --git a/test/broker/09-plugin-auth-defer-unpwd-success.py b/test/broker/09-plugin-auth-defer-unpwd-success.py index 3aa90468..77bf0d72 100755 --- a/test/broker/09-plugin-auth-defer-unpwd-success.py +++ b/test/broker/09-plugin-auth-defer-unpwd-success.py @@ -3,13 +3,7 @@ # Test whether a connection is successful with correct username and password # when using a two auth_plugin (first will defer, second will accept). -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: diff --git a/test/broker/09-plugin-auth-msg-params.py b/test/broker/09-plugin-auth-msg-params.py index 5aa008e1..38a4ecc6 100755 --- a/test/broker/09-plugin-auth-msg-params.py +++ b/test/broker/09-plugin-auth-msg-params.py @@ -2,13 +2,7 @@ # Test whether message parameters are passed to the plugin acl check function. -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: diff --git a/test/broker/09-plugin-auth-unpwd-fail.py b/test/broker/09-plugin-auth-unpwd-fail.py index a9b32a83..8e50fca3 100755 --- a/test/broker/09-plugin-auth-unpwd-fail.py +++ b/test/broker/09-plugin-auth-unpwd-fail.py @@ -3,13 +3,7 @@ # Test whether a connection is successful with correct username and password # when using a simple auth_plugin. -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: diff --git a/test/broker/09-plugin-auth-unpwd-success.py b/test/broker/09-plugin-auth-unpwd-success.py index cff805f5..27908509 100755 --- a/test/broker/09-plugin-auth-unpwd-success.py +++ b/test/broker/09-plugin-auth-unpwd-success.py @@ -3,13 +3,7 @@ # Test whether a connection is successful with correct username and password # when using a simple auth_plugin. -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: diff --git a/test/broker/09-plugin-auth-v2-unpwd-fail.py b/test/broker/09-plugin-auth-v2-unpwd-fail.py index a24bb8a7..65837917 100755 --- a/test/broker/09-plugin-auth-v2-unpwd-fail.py +++ b/test/broker/09-plugin-auth-v2-unpwd-fail.py @@ -3,13 +3,7 @@ # Test whether a connection is successful with correct username and password # when using a simple auth_plugin. -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: diff --git a/test/broker/09-plugin-auth-v2-unpwd-success.py b/test/broker/09-plugin-auth-v2-unpwd-success.py index 7133a691..3cc6d7b9 100755 --- a/test/broker/09-plugin-auth-v2-unpwd-success.py +++ b/test/broker/09-plugin-auth-v2-unpwd-success.py @@ -3,13 +3,7 @@ # Test whether a connection is successful with correct username and password # when using a simple auth_plugin. -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: diff --git a/test/broker/09-pwfile-parse-invalid.py b/test/broker/09-pwfile-parse-invalid.py new file mode 100755 index 00000000..e1781075 --- /dev/null +++ b/test/broker/09-pwfile-parse-invalid.py @@ -0,0 +1,169 @@ +#!/usr/bin/env python + +# Test for CVE-2018-xxxxx. + +import inspect, os, sys +# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder +cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) +if cmd_subfolder not in sys.path: + sys.path.insert(0, cmd_subfolder) + +import mosq_test +import signal + +def write_config(filename, port, per_listener): + with open(filename, 'w') as f: + f.write("per_listener_settings %s\n" % (per_listener)) + f.write("port %d\n" % (port)) + f.write("password_file %s\n" % (filename.replace('.conf', '.pwfile'))) + f.write("allow_anonymous false") + +def write_pwfile(filename, bad_line1, bad_line2): + with open(filename, 'w') as f: + if bad_line1 is not None: + f.write('%s\n' % (bad_line1)) + # Username test, password test + f.write('test:$6$njERlZMi/7DzNB9E$iiavfuXvUm8iyDZArTy7smTxh07GXXOrOsqxfW6gkOYVXHGk+W+i/8d3xDxrMwEPygEBhoA8A/gjQC0N2M4Lkw==\n') + # Username empty, password 0 length + f.write('empty:$6$o+53eGXtmlfHeYrg$FY7X9DNQ4uU1j0NiPmGOOSU05ZSzhqNmNhXIof/0nLpVb1zDhcRHdaC72E3YryH7dtTiG/r6jH6C8J+30cZBgA==\n') + if bad_line2 is not None: + f.write('%s\n' % (bad_line2)) + +def do_test(port, connack_rc, username, password): + rc = 1 + keepalive = 60 + connect_packet = mosq_test.gen_connect("username-password-check", keepalive=keepalive, username=username, password=password) + connack_packet = mosq_test.gen_connack(rc=connack_rc) + + try: + sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) + rc = 0 + sock.close() + finally: + if rc: + exit(rc) + + +def username_password_tests(port): + broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) + + try: + do_test(port, connack_rc=0, username='test', password='test') + do_test(port, connack_rc=5, username='test', password='bad') + do_test(port, connack_rc=5, username='test', password='') + do_test(port, connack_rc=5, username='test', password=None) + do_test(port, connack_rc=5, username='empty', password='test') + do_test(port, connack_rc=5, username='empty', password='bad') + do_test(port, connack_rc=5, username='empty', password='') + do_test(port, connack_rc=5, username='empty', password=None) + do_test(port, connack_rc=5, username='bad', password='test') + do_test(port, connack_rc=5, username='bad', password='bad') + do_test(port, connack_rc=5, username='bad', password='') + do_test(port, connack_rc=5, username='bad', password=None) + do_test(port, connack_rc=5, username='', password='test') + do_test(port, connack_rc=5, username='', password='bad') + do_test(port, connack_rc=5, username='', password='') + do_test(port, connack_rc=5, username='', password=None) + do_test(port, connack_rc=5, username=None, password='test') + do_test(port, connack_rc=5, username=None, password='bad') + do_test(port, connack_rc=5, username=None, password='') + do_test(port, connack_rc=5, username=None, password=None) + except ValueError: + pass + finally: + broker.terminate() + broker.wait() + + +def all_tests(port): + # Valid file, single user + write_pwfile(pw_file, bad_line1=None, bad_line2=None) + username_password_tests(port) + + # Invalid file, first line blank + write_pwfile(pw_file, bad_line1='', bad_line2=None) + username_password_tests(port) + + # Invalid file, last line blank + write_pwfile(pw_file, bad_line1=None, bad_line2='') + username_password_tests(port) + + # Invalid file, first and last line blank + write_pwfile(pw_file, bad_line1='', bad_line2='') + username_password_tests(port) + + # Invalid file, first line 'comment' + write_pwfile(pw_file, bad_line1='#comment', bad_line2=None) + username_password_tests(port) + + # Invalid file, last line 'comment' + write_pwfile(pw_file, bad_line1=None, bad_line2='#comment') + username_password_tests(port) + + # Invalid file, first and last line 'comment' + write_pwfile(pw_file, bad_line1='#comment', bad_line2='#comment') + username_password_tests(port) + + # Invalid file, first line blank and last line 'comment' + write_pwfile(pw_file, bad_line1='', bad_line2='#comment') + username_password_tests(port) + + # Invalid file, first line incomplete + write_pwfile(pw_file, bad_line1='bad:', bad_line2=None) + username_password_tests(port) + + # Invalid file, first line incomplete, but with "password" + write_pwfile(pw_file, bad_line1='bad:bad', bad_line2=None) + username_password_tests(port) + + # Invalid file, first line incomplete, partial password hash + write_pwfile(pw_file, bad_line1='bad:$', bad_line2=None) + username_password_tests(port) + + # Invalid file, first line incomplete, partial password hash + write_pwfile(pw_file, bad_line1='bad:$6', bad_line2=None) + username_password_tests(port) + + # Invalid file, first line incomplete, partial password hash + write_pwfile(pw_file, bad_line1='bad:$6$', bad_line2=None) + username_password_tests(port) + + # Valid file, first line incomplete, has valid salt but no password hash + write_pwfile(pw_file, bad_line1='bad:$6$njERlZMi/7DzNB9E', bad_line2=None) + username_password_tests(port) + + # Valid file, first line incomplete, has valid salt but no password hash + write_pwfile(pw_file, bad_line1='bad:$6$njERlZMi/7DzNB9E$', bad_line2=None) + username_password_tests(port) + + # Valid file, first line has invalid hash designator + write_pwfile(pw_file, bad_line1='bad:$5$njERlZMi/7DzNB9E$iiavfuXvUm8iyDZArTy7smTxh07GXXOrOsqxfW6gkOYVXHGk+W+i/8d3xDxrMwEPygEBhoA8A/gjQC0N2M4Lkw==', bad_line2=None) + username_password_tests(port) + + # Invalid file, missing username but valid password hash + write_pwfile(pw_file, bad_line1=':$6$njERlZMi/7DzNB9E$iiavfuXvUm8iyDZArTy7smTxh07GXXOrOsqxfW6gkOYVXHGk+W+i/8d3xDxrMwEPygEBhoA8A/gjQC0N2M4Lkw==', bad_line2=None) + username_password_tests(port) + + # Valid file, valid username but password salt not base64 + write_pwfile(pw_file, bad_line1='bad:$6$njER{ZMi/7DzNB9E$iiavfuXvUm8iyDZArTy7smTxh07GXXOrOsqxfW6gkOYVXHGk+W+i/8d3xDxrMwEPygEBhoA8A/gjQC0N2M4Lkw==', bad_line2=None) + username_password_tests(port) + + # Valid file, valid username but password hash not base64 + write_pwfile(pw_file, bad_line1='bad:$6$njERlZMi/7DzNB9E$iiavfuXv{}8iyDZArTy7smTxh07GXXOrOsqxfW6gkOYVXHGk+W+i/8d3xDxrMwEPygEBhoA8A/gjQC0N2M4Lkw==', bad_line2=None) + username_password_tests(port) + + + +port = mosq_test.get_port() +conf_file = os.path.basename(__file__).replace('.py', '.conf') +pw_file = os.path.basename(__file__).replace('.py', '.pwfile') + +try: + write_config(conf_file, port, "false") + all_tests(port) + + write_config(conf_file, port, "true") + all_tests(port) +finally: + os.remove(conf_file) + os.remove(pw_file) diff --git a/test/broker/10-listener-mount-point-helper.py b/test/broker/10-listener-mount-point-helper.py index 602bc4a5..a6a91259 100755 --- a/test/broker/10-listener-mount-point-helper.py +++ b/test/broker/10-listener-mount-point-helper.py @@ -1,12 +1,6 @@ #!/usr/bin/env python -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * port = mosq_test.get_port() diff --git a/test/broker/10-listener-mount-point.py b/test/broker/10-listener-mount-point.py index 47fd0766..f190770b 100755 --- a/test/broker/10-listener-mount-point.py +++ b/test/broker/10-listener-mount-point.py @@ -1,14 +1,6 @@ #!/usr/bin/env python -import subprocess - -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * def write_config(filename, port1, port2): with open(filename, 'w') as f: diff --git a/test/broker/11-persistent-subscription.py b/test/broker/11-persistent-subscription.py index 492dcfcc..e64cf2f8 100755 --- a/test/broker/11-persistent-subscription.py +++ b/test/broker/11-persistent-subscription.py @@ -2,13 +2,7 @@ # Test whether a client subscribed to a topic receives its own message sent to that topic. -import inspect, os, sys -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: @@ -27,7 +21,7 @@ connect_packet = mosq_test.gen_connect( "persitent-subscription-test", keepalive=keepalive, clean_session=False, ) connack_packet = mosq_test.gen_connack(rc=0) -connack_packet2 = mosq_test.gen_connack(rc=0, resv=1) # session present +connack_packet2 = mosq_test.gen_connack(rc=0, flags=1) # session present subscribe_packet = mosq_test.gen_subscribe(mid, "subpub/qos1", 1) suback_packet = mosq_test.gen_suback(mid, 1) diff --git a/test/broker/12-prop-assigned-client-identifier.py b/test/broker/12-prop-assigned-client-identifier.py new file mode 100755 index 00000000..2e85c278 --- /dev/null +++ b/test/broker/12-prop-assigned-client-identifier.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python + +# Test whether sending a non zero session expiry interval in DISCONNECT after +# having sent a zero session expiry interval is treated correctly in MQTT v5. + +from mosq_test_helper import * + +rc = 1 + +keepalive = 10 + + +connect_packet = mosq_test.gen_connect(None, proto_ver=5, keepalive=keepalive) + +props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_ASSIGNED_CLIENT_IDENTIFIER, "auto-00000000-0000-0000-0000-000000000000") +connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props) + +props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_SESSION_EXPIRY_INTERVAL, 1) +disconnect_client_packet = mosq_test.gen_disconnect(proto_ver=5, properties=props) + +disconnect_server_packet = mosq_test.gen_disconnect(proto_ver=5, reason_code=130) + +port = mosq_test.get_port() +broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) + +try: + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.settimeout(10) + sock.connect(("localhost", port)) + + sock.send(connect_packet) + connack_recvd = sock.recv(len(connack_packet)) + + if connack_recvd[0:12] == connack_packet[0:12]: + # FIXME - this test could be tightened up a lot + rc = 0 + + sock.close() +finally: + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde) + +exit(rc) + diff --git a/test/broker/12-prop-maximum-packet-size-broker.py b/test/broker/12-prop-maximum-packet-size-broker.py new file mode 100755 index 00000000..7d76d868 --- /dev/null +++ b/test/broker/12-prop-maximum-packet-size-broker.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python + +# Check whether the broker disconnects a client nicely when they send a too large packet. + +from mosq_test_helper import * + +def write_config(filename, port): + with open(filename, 'w') as f: + f.write("port %d\n" % (port)) + f.write("max_packet_size 30\n") + +port = mosq_test.get_port() +conf_file = os.path.basename(__file__).replace('.py', '.conf') +write_config(conf_file, port) + +rc = 1 + +keepalive = 10 +connect_packet = mosq_test.gen_connect("test", proto_ver=5, keepalive=keepalive) +props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_MAXIMUM_PACKET_SIZE, 30) +connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props) + +publish_packet = mosq_test.gen_publish("test/topic", qos=0, payload="0123456789012345678901234567890", proto_ver=5) +disconnect_packet = mosq_test.gen_disconnect(reason_code=149, proto_ver=5) + +broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port, use_conf=True) + +try: + sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) + mosq_test.do_send_receive(sock, publish_packet, disconnect_packet, "disconnect") + rc = 0 +finally: + broker.terminate() + broker.wait() + os.remove(conf_file) + (stdo, stde) = broker.communicate() + if rc: + print(stde) + +exit(rc) + diff --git a/test/broker/12-prop-maximum-packet-size-connect.py b/test/broker/12-prop-maximum-packet-size-connect.py new file mode 100755 index 00000000..11936088 --- /dev/null +++ b/test/broker/12-prop-maximum-packet-size-connect.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python + +# Test whether setting maximum packet size to smaller than a CONNACK packet +# results in the CONNECT being rejected. +# MQTTv5 + +from mosq_test_helper import * + +rc = 1 + +keepalive = 10 +props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_MAXIMUM_PACKET_SIZE, 2) +connect_packet = mosq_test.gen_connect("test", proto_ver=5, keepalive=keepalive, properties=props) + +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, "", port=port) + # Exception occurs if connack packet returned + rc = 0 +finally: + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde) + +exit(rc) + diff --git a/test/broker/12-prop-maximum-packet-size-publish-qos1.py b/test/broker/12-prop-maximum-packet-size-publish-qos1.py new file mode 100755 index 00000000..87388bdb --- /dev/null +++ b/test/broker/12-prop-maximum-packet-size-publish-qos1.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python + +# Test whether maximum packet size is honoured on a PUBLISH to a client +# MQTTv5 + +from mosq_test_helper import * + +rc = 1 + +keepalive = 10 +props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_MAXIMUM_PACKET_SIZE, 20) +connect_packet = mosq_test.gen_connect("test", proto_ver=5, keepalive=keepalive, properties=props) +connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) + +mid = 1 +subscribe_packet = mosq_test.gen_subscribe(mid, "test/topic", 1, proto_ver=5) +suback_packet = mosq_test.gen_suback(mid, 1, proto_ver=5) + +mid=1 +publish1_packet = mosq_test.gen_publish(topic="test/topic", mid=mid, qos=1, payload="12345678901234567890", proto_ver=5) +puback1_packet = mosq_test.gen_puback(mid, proto_ver=5) + +mid=2 +publish2_packet = mosq_test.gen_publish(topic="test/topic", mid=mid, qos=1, payload="7890", proto_ver=5) +puback2_packet = mosq_test.gen_puback(mid, proto_ver=5) + +pingreq_packet = mosq_test.gen_pingreq() +pingresp_packet = mosq_test.gen_pingresp() + +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, subscribe_packet, suback_packet) + + mosq_test.do_send_receive(sock, publish1_packet, puback1_packet, "puback 1") + + # We shouldn't receive the publish here because it is > MAXIMUM_PACKET_SIZE + mosq_test.do_send_receive(sock, pingreq_packet, pingresp_packet) + + mosq_test.do_send_receive(sock, publish2_packet, puback2_packet, "puback 2") + + if mosq_test.expect_packet(sock, "publish2", publish2_packet): + rc = 0 +finally: + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde) + +exit(rc) + diff --git a/test/broker/12-prop-maximum-packet-size-publish-qos2.py b/test/broker/12-prop-maximum-packet-size-publish-qos2.py new file mode 100755 index 00000000..05e38e8d --- /dev/null +++ b/test/broker/12-prop-maximum-packet-size-publish-qos2.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python + +# Test whether maximum packet size is honoured on a PUBLISH to a client +# MQTTv5 + +from mosq_test_helper import * + +rc = 1 + +keepalive = 10 +props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_MAXIMUM_PACKET_SIZE, 20) +connect_packet = mosq_test.gen_connect("test", proto_ver=5, keepalive=keepalive, properties=props) +connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) + +mid = 1 +subscribe_packet = mosq_test.gen_subscribe(mid, "test/topic", 2, proto_ver=5) +suback_packet = mosq_test.gen_suback(mid, 2, proto_ver=5) + +mid=1 +publish1_packet = mosq_test.gen_publish(topic="test/topic", mid=mid, qos=2, payload="12345678901234567890", proto_ver=5) +pubrec1_packet = mosq_test.gen_pubrec(mid, proto_ver=5) +pubrel1_packet = mosq_test.gen_pubrel(mid, proto_ver=5) +pubcomp1_packet = mosq_test.gen_pubcomp(mid, proto_ver=5) + +mid=2 +publish2_packet = mosq_test.gen_publish(topic="test/topic", mid=mid, qos=2, payload="7890", proto_ver=5) +pubrec2_packet = mosq_test.gen_pubrec(mid, proto_ver=5) +pubrel2_packet = mosq_test.gen_pubrel(mid, proto_ver=5) +pubcomp2_packet = mosq_test.gen_pubcomp(mid, proto_ver=5) + +pingreq_packet = mosq_test.gen_pingreq() +pingresp_packet = mosq_test.gen_pingresp() + +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, subscribe_packet, suback_packet) + + mosq_test.do_send_receive(sock, publish1_packet, pubrec1_packet, "pubrec 1") + mosq_test.do_send_receive(sock, pubrel1_packet, pubcomp1_packet, "pubcomp 1") + + # We shouldn't receive the publish here because it is > MAXIMUM_PACKET_SIZE + mosq_test.do_send_receive(sock, pingreq_packet, pingresp_packet) + + mosq_test.do_send_receive(sock, publish2_packet, pubrec2_packet, "pubrec 2") + mosq_test.do_send_receive(sock, pubrel2_packet, pubcomp2_packet, "pubcomp 2") + + if mosq_test.expect_packet(sock, "publish2", publish2_packet): + rc = 0 +finally: + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde) + +exit(rc) + diff --git a/test/broker/12-prop-maximum-packet-size-publish.py b/test/broker/12-prop-maximum-packet-size-publish.py new file mode 100755 index 00000000..9db5ad39 --- /dev/null +++ b/test/broker/12-prop-maximum-packet-size-publish.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python + +# Test whether maximum packet size is honoured on a PUBLISH to a client +# MQTTv5 + +from mosq_test_helper import * + +rc = 1 + +keepalive = 10 +props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_MAXIMUM_PACKET_SIZE, 20) +connect_packet = mosq_test.gen_connect("test", proto_ver=5, keepalive=keepalive, properties=props) +connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) + +mid = 1 +subscribe_packet = mosq_test.gen_subscribe(mid, "test/topic", 0, proto_ver=5) +suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) + +publish1_packet = mosq_test.gen_publish(topic="test/topic", qos=0, payload="12345678901234567890", proto_ver=5) +publish2_packet = mosq_test.gen_publish(topic="test/topic", qos=0, payload="67890", proto_ver=5) + +pingreq_packet = mosq_test.gen_pingreq() +pingresp_packet = mosq_test.gen_pingresp() + +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, subscribe_packet, suback_packet) + sock.send(publish1_packet) + # We shouldn't receive the publish here because it is > MAXIMUM_PACKET_SIZE + mosq_test.do_send_receive(sock, pingreq_packet, pingresp_packet) + mosq_test.do_send_receive(sock, publish2_packet, publish2_packet) + mosq_test.do_send_receive(sock, pingreq_packet, pingresp_packet) + rc = 0 +finally: + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde) + +exit(rc) + diff --git a/test/broker/12-prop-response-topic-correlation-data.py b/test/broker/12-prop-response-topic-correlation-data.py new file mode 100755 index 00000000..13a64760 --- /dev/null +++ b/test/broker/12-prop-response-topic-correlation-data.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python + +# client 1 subscribes to normal-topic +# client 2 susbscribes to response-topic +# client 2 publishes message to normal-topic with response-topic property and correlation-data property +# client 1 receives message, publishes a response on response-topic +# client 2 receives message, checks payload + +from mosq_test_helper import * + +rc = 1 + +keepalive = 10 + +connect_packet1 = mosq_test.gen_connect("client1", proto_ver=5, keepalive=keepalive) +connect_packet2 = mosq_test.gen_connect("client2", proto_ver=5, keepalive=keepalive) +connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) + +subscribe_packet1 = mosq_test.gen_subscribe(mid=1, topic="normal/topic", qos=0, proto_ver=5) +subscribe_packet2 = mosq_test.gen_subscribe(mid=1, topic="response/topic", qos=0, proto_ver=5) +suback_packet = mosq_test.gen_suback(mid=1, qos=0, proto_ver=5) + +props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_RESPONSE_TOPIC, "response/topic") +props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_CORRELATION_DATA, "45vyvynq30q3vt4 nuy893b4v3") +publish_packet2 = mosq_test.gen_publish(topic="normal/topic", qos=0, payload="2", proto_ver=5, properties=props) + +publish_packet1 = mosq_test.gen_publish(topic="response/topic", qos=0, payload="22", proto_ver=5) + +disconnect_client_packet = mosq_test.gen_disconnect(proto_ver=5, properties=props) + +disconnect_server_packet = mosq_test.gen_disconnect(proto_ver=5, reason_code=130) + +port = mosq_test.get_port() +broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) + +try: + sock1 = mosq_test.do_client_connect(connect_packet1, connack_packet, port=port) + sock2 = mosq_test.do_client_connect(connect_packet2, connack_packet, port=port) + + mosq_test.do_send_receive(sock1, subscribe_packet1, suback_packet, "subscribe1") + mosq_test.do_send_receive(sock2, subscribe_packet2, suback_packet, "subscribe2") + + sock2.send(publish_packet2) + if mosq_test.expect_packet(sock1, "publish1", publish_packet2): + # FIXME - it would be better to extract the property and payload, even though we know them + sock1.send(publish_packet1) + if mosq_test.expect_packet(sock2, "publish2", publish_packet1): + rc = 0 + + sock1.close() + sock2.close() +finally: + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde) + +exit(rc) + diff --git a/test/broker/12-prop-response-topic.py b/test/broker/12-prop-response-topic.py new file mode 100755 index 00000000..ddfbf936 --- /dev/null +++ b/test/broker/12-prop-response-topic.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python + +# client 1 subscribes to normal-topic +# client 2 susbscribes to response-topic +# client 2 publishes message to normal-topic with response-topic property +# client 1 receives message, publishes a response on response-topic +# client 2 receives message, checks payload + +from mosq_test_helper import * + +rc = 1 + +keepalive = 10 + +connect_packet1 = mosq_test.gen_connect("client1", proto_ver=5, keepalive=keepalive) +connect_packet2 = mosq_test.gen_connect("client2", proto_ver=5, keepalive=keepalive) +connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) + +subscribe_packet1 = mosq_test.gen_subscribe(mid=1, topic="normal/topic", qos=0, proto_ver=5) +subscribe_packet2 = mosq_test.gen_subscribe(mid=1, topic="response/topic", qos=0, proto_ver=5) +suback_packet = mosq_test.gen_suback(mid=1, qos=0, proto_ver=5) + +props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_RESPONSE_TOPIC, "response/topic") +publish_packet2 = mosq_test.gen_publish(topic="normal/topic", qos=0, payload="2", proto_ver=5, properties=props) + +publish_packet1 = mosq_test.gen_publish(topic="response/topic", qos=0, payload="22", proto_ver=5) + +disconnect_client_packet = mosq_test.gen_disconnect(proto_ver=5, properties=props) + +disconnect_server_packet = mosq_test.gen_disconnect(proto_ver=5, reason_code=130) + +port = mosq_test.get_port() +broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) + +try: + sock1 = mosq_test.do_client_connect(connect_packet1, connack_packet, port=port) + sock2 = mosq_test.do_client_connect(connect_packet2, connack_packet, port=port) + + mosq_test.do_send_receive(sock1, subscribe_packet1, suback_packet, "subscribe1") + mosq_test.do_send_receive(sock2, subscribe_packet2, suback_packet, "subscribe2") + + sock2.send(publish_packet2) + if mosq_test.expect_packet(sock1, "publish1", publish_packet2): + # FIXME - it would be better to extract the property and payload, even though we know them + sock1.send(publish_packet1) + if mosq_test.expect_packet(sock2, "publish2", publish_packet1): + rc = 0 + + sock1.close() + sock2.close() +finally: + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde) + +exit(rc) + diff --git a/test/broker/12-prop-server-keepalive.py b/test/broker/12-prop-server-keepalive.py new file mode 100755 index 00000000..5073f28c --- /dev/null +++ b/test/broker/12-prop-server-keepalive.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python + +# Test whether sending a non zero session expiry interval in DISCONNECT after +# having sent a zero session expiry interval is treated correctly in MQTT v5. + +from mosq_test_helper import * + +def write_config(filename, port): + with open(filename, 'w') as f: + f.write("port %d\n" % (port)) + f.write("\n") + f.write("max_keepalive 60\n") + +port = mosq_test.get_port(1) +conf_file = os.path.basename(__file__).replace('.py', '.conf') +write_config(conf_file, port) + + +rc = 1 + +keepalive = 61 +connect_packet = mosq_test.gen_connect("test", proto_ver=5, keepalive=keepalive) + +props = mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_SERVER_KEEP_ALIVE, 60) +connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props) + +broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port, use_conf=True) + +try: + sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) + sock.close() + rc = 0 +finally: + os.remove(conf_file) + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde) + +exit(rc) + diff --git a/test/broker/12-prop-session-expiry-invalid.py b/test/broker/12-prop-session-expiry-invalid.py new file mode 100755 index 00000000..81cd7b5e --- /dev/null +++ b/test/broker/12-prop-session-expiry-invalid.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python + +# Test whether sending a non zero session expiry interval in DISCONNECT after +# having sent a zero session expiry interval is treated correctly in MQTT v5. + +from mosq_test_helper import * + +rc = 1 + +keepalive = 10 +props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_SESSION_EXPIRY_INTERVAL, 0) +connect_packet = mosq_test.gen_connect("test", proto_ver=5, keepalive=keepalive, properties=props) + +connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) + +props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_SESSION_EXPIRY_INTERVAL, 1) +disconnect_client_packet = mosq_test.gen_disconnect(proto_ver=5, properties=props) + +disconnect_server_packet = mosq_test.gen_disconnect(proto_ver=5, reason_code=130) + +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, disconnect_client_packet, disconnect_server_packet, "disconnect") + sock.close() + rc = 0 +finally: + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde) + +exit(rc) + diff --git a/test/broker/12-prop-subpub-content-type.py b/test/broker/12-prop-subpub-content-type.py new file mode 100755 index 00000000..d7106d15 --- /dev/null +++ b/test/broker/12-prop-subpub-content-type.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python + +# Test whether a client subscribed to a topic receives its own message sent to that topic. +# Does the Content Type property get sent through? +# MQTT v5 + +import prop_subpub_helper as helper +from mosq_test_helper import * + +props_out = mqtt5_props.gen_string_prop(mqtt5_props.PROP_CONTENT_TYPE, "text") +props_out = props_out+mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_TOPIC_ALIAS, 1) + +props_in = mqtt5_props.gen_string_prop(mqtt5_props.PROP_CONTENT_TYPE, "text") + +helper.prop_subpub_helper(props_out, props_in) diff --git a/test/broker/12-prop-subpub-payload-format.py b/test/broker/12-prop-subpub-payload-format.py new file mode 100755 index 00000000..8cbd90e1 --- /dev/null +++ b/test/broker/12-prop-subpub-payload-format.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python + +# Test whether a client subscribed to a topic receives its own message sent to that topic. +# Does the Payload Format Indicator property get sent through? +# MQTT v5 + +import prop_subpub_helper as helper +from mosq_test_helper import * + +props_out = mqtt5_props.gen_byte_prop(mqtt5_props.PROP_PAYLOAD_FORMAT_INDICATOR, 0xed) +props_out = props_out+mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_TOPIC_ALIAS, 1) + +props_in = mqtt5_props.gen_byte_prop(mqtt5_props.PROP_PAYLOAD_FORMAT_INDICATOR, 0xed) + +helper.prop_subpub_helper(props_out, props_in) diff --git a/test/broker/Makefile b/test/broker/Makefile index c70f1f7d..440c38f2 100644 --- a/test/broker/Makefile +++ b/test/broker/Makefile @@ -13,12 +13,17 @@ test-compile : $(MAKE) -C c ptest : test-compile - ./ptest.py + ./test.py -test : test-compile 01 02 03 04 05 06 07 08 09 10 11 +test : test-compile 01 02 03 04 05 06 07 08 09 10 11 12 01 : + ./01-connect-bad-packet.py + ./01-connect-duplicate.py + ./01-connect-duplicate-v5.py ./01-connect-success.py + ./01-connect-success-v5.py + ./01-connect-disconnect-v5.py ./01-connect-invalid-protonum.py ./01-connect-invalid-id-0.py ./01-connect-invalid-id-0-311.py @@ -45,10 +50,33 @@ endif ./02-subpub-qos0.py ./02-subpub-qos1.py ./02-subpub-qos2.py + ./02-subpub-qos1-bad-pubrec.py + ./02-subpub-qos1-bad-pubcomp.py + ./02-subpub-qos2-bad-puback-1.py + ./02-subpub-qos2-bad-puback-2.py + ./02-subpub-qos2-bad-pubcomp.py + ./02-subpub-qos0-v5.py + ./02-subpub-qos1-v5.py + ./02-subpub-qos2-v5.py + ./02-subpub-qos1-message-expiry.py + ./02-subpub-qos1-message-expiry-retain.py + ./02-subpub-qos1-message-expiry-will.py + ./02-subpub-qos1-nolocal.py + ./02-subpub-qos0-retain-as-publish.py + ./02-subpub-qos0-send-retain.py + ./02-subpub-qos0-subscription-id.py + ./02-subpub-qos2-receive-maximum-1.py + ./02-subpub-qos2-receive-maximum-2.py + ./02-subpub-qos2-pubrec-error.py + ./02-subpub-qos0-topic-alias.py + ./02-subpub-qos0-topic-alias-unknown.py ./02-unsubscribe-qos0.py ./02-unsubscribe-qos1.py ./02-unsubscribe-qos2.py + ./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 @@ -59,10 +87,14 @@ 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 ./03-publish-dollar.py + ./03-publish-qos1-retain-disabled.py 04 : ./04-retain-qos0.py @@ -71,6 +103,9 @@ endif ./04-retain-qos1-qos0.py ./04-retain-qos0-clear.py ./04-retain-upgrade-outgoing-qos.py + ./04-retain-check-source.py + ./04-retain-check-source-persist.py + ./04-retain-check-source-persist-diff-port.py 05 : ./05-clean-session-qos1.py @@ -113,6 +148,7 @@ endif endif 09 : + ./09-acl-empty-file.py ./09-plugin-auth-unpwd-success.py ./09-plugin-auth-unpwd-fail.py ./09-plugin-auth-acl-sub.py @@ -123,9 +159,24 @@ endif ./09-plugin-auth-defer-unpwd-fail.py ./09-plugin-auth-msg-params.py ./09-plugin-auth-context-params.py + ./09-pwfile-parse-invalid.py 10 : ./10-listener-mount-point.py 11 : ./11-persistent-subscription.py + +12 : + ./12-prop-session-expiry-invalid.py + ./12-prop-subpub-payload-format.py + ./12-prop-subpub-content-type.py + ./12-prop-assigned-client-identifier.py + ./12-prop-server-keepalive.py + ./12-prop-response-topic.py + ./12-prop-response-topic-correlation-data.py + ./12-prop-maximum-packet-size-broker.py + ./12-prop-maximum-packet-size-connect.py + ./12-prop-maximum-packet-size-publish.py + ./12-prop-maximum-packet-size-publish-qos1.py + ./12-prop-maximum-packet-size-publish-qos2.py diff --git a/test/broker/c/Makefile b/test/broker/c/Makefile index 0ce17768..32631899 100644 --- a/test/broker/c/Makefile +++ b/test/broker/c/Makefile @@ -1,47 +1,33 @@ -.PHONY: all test clean reallyclean 08 +.PHONY: all test clean reallyclean CFLAGS=-I../../../lib -I../../../src -Wall -Werror -PLUGINS= \ - auth_plugin.so \ - auth_plugin_pwd.so \ - auth_plugin_acl.so \ - auth_plugin_v2.so \ - auth_plugin_msg_params.so \ - auth_plugin_context_params.so \ - auth_plugin_acl_sub_denied.so +PLUGIN_SRC = \ + auth_plugin.c \ + auth_plugin_pwd.c \ + auth_plugin_acl.c \ + auth_plugin_acl_sub_denied.c \ + auth_plugin_v2.c \ + auth_plugin_context_params.c \ + auth_plugin_msg_params.c + +PLUGINS = ${PLUGIN_SRC:.c=.so} + +SRC = \ + 08-tls-psk-pub.c \ + 08-tls-psk-bridge.c + +TESTS = ${SRC:.c=.test} -all : ${PLUGINS} 08 +all : ${PLUGINS} ${TESTS} -08 : 08-tls-psk-pub.test 08-tls-psk-bridge.test +${PLUGINS} : %.so: %.c + $(CC) ${CFLAGS} -fPIC -shared $< -o $@ -auth_plugin.so : auth_plugin.c - $(CC) ${CFLAGS} -fPIC -shared $^ -o $@ -auth_plugin_pwd.so : auth_plugin_pwd.c - $(CC) ${CFLAGS} -fPIC -shared $^ -o $@ - -auth_plugin_acl.so : auth_plugin_acl.c - $(CC) ${CFLAGS} -fPIC -shared $^ -o $@ - -auth_plugin_v2.so : auth_plugin_v2.c - $(CC) ${CFLAGS} -fPIC -shared $^ -o $@ - -auth_plugin_context_params.so : auth_plugin_context_params.c - $(CC) ${CFLAGS} -fPIC -shared $^ -o $@ - -auth_plugin_msg_params.so : auth_plugin_msg_params.c - $(CC) ${CFLAGS} -fPIC -shared $^ -o $@ - -auth_plugin_acl_sub_denied.so : auth_plugin_acl_sub_denied.c - $(CC) ${CFLAGS} -fPIC -shared $^ -o $@ - -08-tls-psk-pub.test : 08-tls-psk-pub.c - $(CC) ${CFLAGS} $^ -o $@ ../../../lib/libmosquitto.so.1 - -08-tls-psk-bridge.test : 08-tls-psk-bridge.c - $(CC) ${CFLAGS} $^ -o $@ ../../../lib/libmosquitto.so.1 +${TESTS} : %.test: %.c + $(CC) ${CFLAGS} $< -o $@ ../../../lib/libmosquitto.so.1 reallyclean : clean diff --git a/test/broker/mosq_test_helper.py b/test/broker/mosq_test_helper.py new file mode 100644 index 00000000..9b8f2113 --- /dev/null +++ b/test/broker/mosq_test_helper.py @@ -0,0 +1,16 @@ +import inspect, os, sys + +# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder +cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) +if cmd_subfolder not in sys.path: + sys.path.insert(0, cmd_subfolder) + +import mosq_test +import mqtt5_opts +import mqtt5_props + +import socket +import ssl +import struct +import subprocess +import time diff --git a/test/broker/prop_subpub_helper.py b/test/broker/prop_subpub_helper.py new file mode 100755 index 00000000..212e4d7e --- /dev/null +++ b/test/broker/prop_subpub_helper.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python + +# Test whether a client subscribed to a topic receives its own message sent to that topic. +# Does a given property get sent through? +# MQTT v5 + +from mosq_test_helper import * + +def prop_subpub_helper(props_out, props_in): + rc = 1 + mid = 53 + keepalive = 60 + connect_packet = mosq_test.gen_connect("subpub-qos0-test", keepalive=keepalive, proto_ver=5) + connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) + + subscribe_packet = mosq_test.gen_subscribe(mid, "subpub/qos0", 0, proto_ver=5) + suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) + + publish_packet_out = mosq_test.gen_publish("subpub/qos0", qos=0, payload="message", proto_ver=5, properties=props_out) + + publish_packet_expected = mosq_test.gen_publish("subpub/qos0", qos=0, payload="message", proto_ver=5, properties=props_in) + + 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, timeout=20, port=port) + + mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") + mosq_test.do_send_receive(sock, publish_packet_out, publish_packet_expected, "publish") + + rc = 0 + + sock.close() + finally: + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde) + + exit(rc) + diff --git a/test/broker/readme.txt b/test/broker/readme.txt index 82eff277..c31535de 100644 --- a/test/broker/readme.txt +++ b/test/broker/readme.txt @@ -12,3 +12,8 @@ Numbering is as follows: 05: Clean session tests 06: Bridge tests 07: Will tests +08: TLS tests +09: Auth plugin tests +10: Listener tests +11: Persistence tests +12: Property tests diff --git a/test/broker/ptest.py b/test/broker/test.py similarity index 60% rename from test/broker/ptest.py rename to test/broker/test.py index f39049e1..f93d9c31 100755 --- a/test/broker/ptest.py +++ b/test/broker/test.py @@ -1,13 +1,16 @@ #!/usr/bin/env python3 -import subprocess -import time -import sys +import mosq_test_helper +import ptest -max_running = 10 tests = [ #(ports required, 'path'), + (1, './01-connect-bad-packet.py'), + (1, './01-connect-duplicate.py'), + (1, './01-connect-duplicate-v5.py'), (1, './01-connect-success.py'), + (1, './01-connect-success-v5.py'), + (1, './01-connect-disconnect-v5.py'), (1, './01-connect-invalid-protonum.py'), (1, './01-connect-invalid-id-0.py'), (1, './01-connect-invalid-id-0-311.py'), @@ -28,10 +31,33 @@ tests = [ (1, './02-subpub-qos0.py'), (1, './02-subpub-qos1.py'), (1, './02-subpub-qos2.py'), + (1, './02-subpub-qos1-bad-pubrec.py'), + (1, './02-subpub-qos1-bad-pubcomp.py'), + (1, './02-subpub-qos2-bad-puback-1.py'), + (1, './02-subpub-qos2-bad-puback-2.py'), + (1, './02-subpub-qos2-bad-pubcomp.py'), + (1, './02-subpub-qos0-v5.py'), + (1, './02-subpub-qos1-v5.py'), + (1, './02-subpub-qos2-v5.py'), + (1, './02-subpub-qos1-message-expiry.py'), + (1, './02-subpub-qos1-message-expiry-retain.py'), + (1, './02-subpub-qos1-message-expiry-will.py'), + (1, './02-subpub-qos1-nolocal.py'), + (1, './02-subpub-qos0-retain-as-publish.py'), + (1, './02-subpub-qos0-send-retain.py'), + (1, './02-subpub-qos0-subscription-id.py'), + (1, './02-subpub-qos2-receive-maximum-1.py'), + (1, './02-subpub-qos2-receive-maximum-2.py'), + (1, './02-subpub-qos2-pubrec-error.py'), + (1, './02-subpub-qos0-topic-alias.py'), + (1, './02-subpub-qos0-topic-alias-unknown.py'), (1, './02-unsubscribe-qos0.py'), (1, './02-unsubscribe-qos1.py'), (1, './02-unsubscribe-qos2.py'), + (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'), @@ -41,10 +67,14 @@ 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'), (1, './03-publish-dollar.py'), + (1, './03-publish-qos1-retain-disabled.py'), (1, './04-retain-qos0.py'), (1, './04-retain-qos0-fresh.py'), @@ -52,6 +82,9 @@ tests = [ (1, './04-retain-qos1-qos0.py'), (1, './04-retain-qos0-clear.py'), (1, './04-retain-upgrade-outgoing-qos.py'), + (1, './04-retain-check-source.py'), + (1, './04-retain-check-source-persist.py'), + (2, './04-retain-check-source-persist-diff-port.py'), (1, './05-clean-session-qos1.py'), @@ -85,6 +118,7 @@ tests = [ (2, './08-tls-psk-pub.py'), (3, './08-tls-psk-bridge.py'), + (1, './09-acl-empty-file.py'), (1, './09-plugin-auth-unpwd-success.py'), (1, './09-plugin-auth-unpwd-fail.py'), (1, './09-plugin-auth-acl-sub.py'), @@ -95,89 +129,24 @@ tests = [ (1, './09-plugin-auth-defer-unpwd-fail.py'), (1, './09-plugin-auth-msg-params.py'), (1, './09-plugin-auth-context-params.py'), + (1, './09-pwfile-parse-invalid.py'), (2, './10-listener-mount-point.py'), (1, './11-persistent-subscription.py'), + + (1, './12-prop-session-expiry-invalid.py'), + (1, './12-prop-subpub-payload-format.py'), + (1, './12-prop-subpub-content-type.py'), + (1, './12-prop-assigned-client-identifier.py'), + (1, './12-prop-server-keepalive.py'), + (1, './12-prop-response-topic.py'), + (1, './12-prop-response-topic-correlation-data.py'), + (1, './12-prop-maximum-packet-size-broker.py'), + (1, './12-prop-maximum-packet-size-connect.py'), + (1, './12-prop-maximum-packet-size-publish.py'), + (1, './12-prop-maximum-packet-size-publish-qos1.py'), + (1, './12-prop-maximum-packet-size-publish-qos2.py'), ] -minport = 1888 -ports = list(range(minport, minport+max_running+1)) - -def next_test(tests, ports): - if len(tests) == 0 or len(ports) == 0: - return - - test = tests.pop() - if test[0] == 1: - port = ports.pop() - p = subprocess.Popen([test[1], str(port)]) - p.mosq_port = port - return p - elif test[0] == 2: - if len(ports) < 2: - tests.insert(0,test) - return None - else: - port1 = ports.pop() - port2 = ports.pop() - - p = subprocess.Popen([test[1], str(port1), str(port2)]) - p.mosq_port = (port1, port2) - return p - elif test[0] == 3: - if len(ports) < 3: - tests.insert(0,test) - return None - else: - port1 = ports.pop() - port2 = ports.pop() - port3 = ports.pop() - - p = subprocess.Popen([test[1], str(port1), str(port2), str(port3)]) - p.mosq_port = (port1, port2, port3) - return p - else: - return None - - -def run_tests(tests, ports): - passed = 0 - failed = 0 - - failed_tests = [] - - running_tests = [] - while len(tests) > 0 or len(running_tests) > 0: - if len(running_tests) <= max_running: - t = next_test(tests, ports) - if t is None: - time.sleep(0.1) - else: - running_tests.append(t) - - for t in running_tests: - t.poll() - if t.returncode is not None: - running_tests.remove(t) - if isinstance(t.mosq_port, tuple): - for portret in t.mosq_port: - ports.append(portret) - else: - ports.append(t.mosq_port) - t.terminate() - t.wait() - #(stdo, stde) = t.communicate() - if t.returncode == 1: - print("\033[31m" + t.args[0] + "\033[0m") - failed = failed + 1 - else: - passed = passed + 1 - print("\033[32m" + t.args[0] + "\033[0m") - - print("Passed: %d\nFailed: %d\nTotal: %d" % (passed, failed, passed+failed)) - if failed > 0: - sys.exit(1) - - -run_tests(tests, ports) +ptest.run_tests(tests) diff --git a/test/lib/01-con-discon-success.py b/test/lib/01-con-discon-success.py index bb5f4ce2..6838c587 100755 --- a/test/lib/01-con-discon-success.py +++ b/test/lib/01-con-discon-success.py @@ -8,17 +8,7 @@ # the CONNACK and verifying that rc=0, the client should send a DISCONNECT # message. If rc!=0, the client should exit with an error. -import inspect -import os -import socket -import sys - -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * port = mosq_test.get_lib_port() diff --git a/test/lib/01-keepalive-pingreq.py b/test/lib/01-keepalive-pingreq.py index 8e95b5db..59129cfe 100755 --- a/test/lib/01-keepalive-pingreq.py +++ b/test/lib/01-keepalive-pingreq.py @@ -7,18 +7,7 @@ # The client should send a PINGREQ message after the appropriate amount of time # (4 seconds after no traffic). -import inspect -import os -import socket -import sys -import time - -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * port = mosq_test.get_lib_port() diff --git a/test/lib/01-no-clean-session.py b/test/lib/01-no-clean-session.py index d5f46eb3..481b1190 100755 --- a/test/lib/01-no-clean-session.py +++ b/test/lib/01-no-clean-session.py @@ -5,17 +5,7 @@ # The client should connect to port 1888 with keepalive=60, clean session not # set, and client id 01-no-clean-session. -import inspect -import os -import socket -import sys - -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * port = mosq_test.get_lib_port() diff --git a/test/lib/01-server-keepalive-pingreq.py b/test/lib/01-server-keepalive-pingreq.py new file mode 100755 index 00000000..2d6fa9c2 --- /dev/null +++ b/test/lib/01-server-keepalive-pingreq.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python + +# Test whether a client sends a pingreq after the keepalive time +# Client sets a keepalive of 60 seconds, but receives a server keepalive to set +# it back to 4 seconds. + +from mosq_test_helper import * + +port = mosq_test.get_lib_port() + +rc = 1 +keepalive = 60 +server_keepalive = 4 +connect_packet = mosq_test.gen_connect("01-server-keepalive-pingreq", keepalive=keepalive, proto_ver=5) + +props = mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_SERVER_KEEP_ALIVE, server_keepalive) +connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props) + +pingreq_packet = mosq_test.gen_pingreq() +pingresp_packet = mosq_test.gen_pingresp() + +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(server_keepalive+10) + + if mosq_test.expect_packet(conn, "connect", connect_packet): + conn.send(connack_packet) + + if mosq_test.expect_packet(conn, "pingreq", pingreq_packet): + time.sleep(1.0) + conn.send(pingresp_packet) + + if mosq_test.expect_packet(conn, "pingreq", pingreq_packet): + rc = 0 + + conn.close() +finally: + client.terminate() + client.wait() + sock.close() + +exit(rc) + diff --git a/test/lib/01-unpwd-set.py b/test/lib/01-unpwd-set.py index 454e417a..6dd0d4d5 100755 --- a/test/lib/01-unpwd-set.py +++ b/test/lib/01-unpwd-set.py @@ -5,17 +5,7 @@ # The client should connect to port 1888 with keepalive=60, clean session set, # client id 01-unpwd-set, username set to uname and password set to ;'[08gn=# -import inspect -import os -import socket -import sys - -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * port = mosq_test.get_lib_port() diff --git a/test/lib/01-will-set.py b/test/lib/01-will-set.py index 63a8e2ea..c43f1f08 100755 --- a/test/lib/01-will-set.py +++ b/test/lib/01-will-set.py @@ -7,17 +7,7 @@ # client id 01-will-set will topic set to topic/on/unexpected/disconnect , will # payload set to "will message", will qos set to 1 and will retain set. -import inspect -import os -import socket -import sys - -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * port = mosq_test.get_lib_port() diff --git a/test/lib/01-will-unpwd-set.py b/test/lib/01-will-unpwd-set.py index 2a64bef7..e9132376 100755 --- a/test/lib/01-will-unpwd-set.py +++ b/test/lib/01-will-unpwd-set.py @@ -7,17 +7,7 @@ # set to "will message", will qos=2, will retain not set, username set to # "oibvvwqw" and password set to "#'^2hg9a&nm38*us". -import inspect -import os -import socket -import sys - -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * port = mosq_test.get_lib_port() diff --git a/test/lib/02-subscribe-qos0.py b/test/lib/02-subscribe-qos0.py index f5f60d2f..737efa77 100755 --- a/test/lib/02-subscribe-qos0.py +++ b/test/lib/02-subscribe-qos0.py @@ -12,17 +12,7 @@ # SUBACK message with the accepted QoS set to 0. On receiving the SUBACK # message, the client should send a DISCONNECT message. -import inspect -import os -import socket -import sys - -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * port = mosq_test.get_lib_port() diff --git a/test/lib/02-subscribe-qos1.py b/test/lib/02-subscribe-qos1.py index 68ffa39d..5201bf0e 100755 --- a/test/lib/02-subscribe-qos1.py +++ b/test/lib/02-subscribe-qos1.py @@ -12,17 +12,7 @@ # SUBACK message with the accepted QoS set to 1. On receiving the SUBACK # message, the client should send a DISCONNECT message. -import inspect -import os -import socket -import sys - -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * port = mosq_test.get_lib_port() diff --git a/test/lib/02-subscribe-qos2.py b/test/lib/02-subscribe-qos2.py index 9a2864a5..180c3b9d 100755 --- a/test/lib/02-subscribe-qos2.py +++ b/test/lib/02-subscribe-qos2.py @@ -12,17 +12,7 @@ # SUBACK message with the accepted QoS set to 2. On receiving the SUBACK # message, the client should send a DISCONNECT message. -import inspect -import os -import socket -import sys - -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * port = mosq_test.get_lib_port() diff --git a/test/lib/02-unsubscribe-multiple-v5.py b/test/lib/02-unsubscribe-multiple-v5.py new file mode 100755 index 00000000..af564e6a --- /dev/null +++ b/test/lib/02-unsubscribe-multiple-v5.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python + +# Test whether a v5 client sends a correct UNSUBSCRIBE packet with multiple +# topics, and handles the UNSUBACK. + +from mosq_test_helper import * + +port = mosq_test.get_lib_port() + +keepalive = 60 +connect_packet = mosq_test.gen_connect("unsubscribe-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 +subscribe_packet = mosq_test.gen_subscribe(mid, "unsubscribe/test", 2, proto_ver=5) +suback_packet = mosq_test.gen_suback(mid, 2, proto_ver=5) + +mid = 2 +unsubscribe_packet = mosq_test.gen_unsubscribe_multiple(mid, ["unsubscribe/test", "no-sub"], proto_ver=5) +unsuback_packet = mosq_test.gen_unsuback(mid, reason_code=[0, 17], 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) + +rc = 1 +try: + (conn, address) = sock.accept() + conn.settimeout(10) + + if mosq_test.expect_packet(conn, "connect", connect_packet): + conn.send(connack_packet) + + if mosq_test.expect_packet(conn, "subscribe", subscribe_packet): + conn.send(suback_packet) + + if mosq_test.expect_packet(conn, "unsubscribe", unsubscribe_packet): + conn.send(unsuback_packet) + + if mosq_test.expect_packet(conn, "disconnect", disconnect_packet): + rc = 0 + + conn.close() +finally: + client.terminate() + client.wait() + sock.close() + +exit(rc) diff --git a/test/lib/02-unsubscribe-v5.py b/test/lib/02-unsubscribe-v5.py new file mode 100755 index 00000000..9b55d276 --- /dev/null +++ b/test/lib/02-unsubscribe-v5.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python + +# Test whether a v5 client sends a correct UNSUBSCRIBE packet, and handles the UNSUBACK. + +from mosq_test_helper import * + +port = mosq_test.get_lib_port() + +keepalive = 60 +connect_packet = mosq_test.gen_connect("unsubscribe-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 +unsubscribe_packet = mosq_test.gen_unsubscribe(mid, "unsubscribe/test", proto_ver=5) +unsuback_packet = mosq_test.gen_unsuback(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(10) + + if mosq_test.expect_packet(conn, "connect", connect_packet): + conn.send(connack_packet) + + if mosq_test.expect_packet(conn, "unsubscribe", unsubscribe_packet): + conn.send(unsuback_packet) + + if mosq_test.expect_packet(conn, "disconnect", disconnect_packet): + rc = 0 + + conn.close() +finally: + client.terminate() + client.wait() + sock.close() + +exit(rc) diff --git a/test/lib/02-unsubscribe.py b/test/lib/02-unsubscribe.py index 8d9560cf..abffc00a 100755 --- a/test/lib/02-unsubscribe.py +++ b/test/lib/02-unsubscribe.py @@ -2,17 +2,7 @@ # Test whether a client sends a correct UNSUBSCRIBE packet. -import inspect -import os -import socket -import sys - -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * port = mosq_test.get_lib_port() diff --git a/test/lib/03-publish-b2c-qos1.py b/test/lib/03-publish-b2c-qos1.py index 4d6c475a..942ca4f7 100755 --- a/test/lib/03-publish-b2c-qos1.py +++ b/test/lib/03-publish-b2c-qos1.py @@ -11,18 +11,7 @@ # should handle this as per the spec by sending a PUBACK message. # The client should then exit with return code==0. -import inspect -import os -import socket -import sys -import time - -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * port = mosq_test.get_lib_port() 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-b2c-qos2.py b/test/lib/03-publish-b2c-qos2.py index a970ed55..8600c187 100755 --- a/test/lib/03-publish-b2c-qos2.py +++ b/test/lib/03-publish-b2c-qos2.py @@ -16,18 +16,7 @@ # PUBREL message. The client should respond to this with the correct PUBCOMP # message and then exit with return code=0. -import inspect -import os -import socket -import sys -import time - -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * port = mosq_test.get_lib_port() diff --git a/test/lib/03-publish-c2b-qos1-disconnect.py b/test/lib/03-publish-c2b-qos1-disconnect.py index 2111ae7c..87aa79ba 100755 --- a/test/lib/03-publish-c2b-qos1-disconnect.py +++ b/test/lib/03-publish-c2b-qos1-disconnect.py @@ -2,17 +2,7 @@ # Test whether a client sends a correct PUBLISH to a topic with QoS 1, then responds correctly to a disconnect. -import inspect -import os -import socket -import sys - -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * port = mosq_test.get_lib_port() 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-qos1-receive-maximum.py b/test/lib/03-publish-c2b-qos1-receive-maximum.py new file mode 100755 index 00000000..b55860ea --- /dev/null +++ b/test/lib/03-publish-c2b-qos1-receive-maximum.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python + +# Test whether a client responds correctly to multiple PUBLISH with QoS 1, with +# receive maximum set to 3. + +from mosq_test_helper import * + +port = mosq_test.get_lib_port() + +rc = 1 +keepalive = 60 +connect_packet = mosq_test.gen_connect("publish-qos1-test", keepalive=keepalive, proto_ver=5) + +props = mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_RECEIVE_MAXIMUM, 3) +connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props) + +disconnect_packet = mosq_test.gen_disconnect(proto_ver=5) + +mid = 1 +publish_1_packet = mosq_test.gen_publish("topic", qos=1, mid=mid, payload="12345", proto_ver=5) +puback_1_packet = mosq_test.gen_puback(mid, proto_ver=5) + +mid = 2 +publish_2_packet = mosq_test.gen_publish("topic", qos=1, mid=mid, payload="12345", proto_ver=5) +puback_2_packet = mosq_test.gen_puback(mid, proto_ver=5) + +mid = 3 +publish_3_packet = mosq_test.gen_publish("topic", qos=1, mid=mid, payload="12345", proto_ver=5) +puback_3_packet = mosq_test.gen_puback(mid, proto_ver=5) + +mid = 4 +publish_4_packet = mosq_test.gen_publish("topic", qos=1, mid=mid, payload="12345", proto_ver=5) +puback_4_packet = mosq_test.gen_puback(mid, proto_ver=5) + +mid = 5 +publish_5_packet = mosq_test.gen_publish("topic", qos=1, mid=mid, payload="12345", proto_ver=5) +puback_5_packet = mosq_test.gen_puback(mid, proto_ver=5) + +mid = 6 +publish_6_packet = mosq_test.gen_publish("topic", qos=1, mid=mid, payload="12345", proto_ver=5) +puback_6_packet = mosq_test.gen_puback(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(10) + + if mosq_test.expect_packet(conn, "connect", connect_packet): + conn.send(connack_packet) + + if mosq_test.expect_packet(conn, "publish 1", publish_1_packet): + if mosq_test.expect_packet(conn, "publish 2", publish_2_packet): + if mosq_test.expect_packet(conn, "publish 3", publish_3_packet): + conn.send(puback_1_packet) + conn.send(puback_2_packet) + + if mosq_test.expect_packet(conn, "publish 4", publish_4_packet): + if mosq_test.expect_packet(conn, "publish 5", publish_5_packet): + conn.send(puback_3_packet) + + if mosq_test.expect_packet(conn, "publish 6", publish_6_packet): + conn.send(puback_4_packet) + conn.send(puback_5_packet) + conn.send(puback_6_packet) + rc = 0 + + conn.close() +finally: + for i in range(0, 5): + if client.returncode != None: + break + time.sleep(0.1) + + try: + client.terminate() + except OSError: + pass + + client.wait() + sock.close() + if client.returncode != 0: + exit(1) + +exit(rc) diff --git a/test/lib/03-publish-c2b-qos1-timeout.py b/test/lib/03-publish-c2b-qos1-timeout.py index 7cea7005..104961aa 100755 --- a/test/lib/03-publish-c2b-qos1-timeout.py +++ b/test/lib/03-publish-c2b-qos1-timeout.py @@ -16,17 +16,7 @@ # PUBACK response. On receiving the correct PUBACK response, the client should # send a DISCONNECT message. -import inspect -import os -import socket -import sys - -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * port = mosq_test.get_lib_port() diff --git a/test/lib/03-publish-c2b-qos2-disconnect.py b/test/lib/03-publish-c2b-qos2-disconnect.py index c9a5d526..dbd843db 100755 --- a/test/lib/03-publish-c2b-qos2-disconnect.py +++ b/test/lib/03-publish-c2b-qos2-disconnect.py @@ -2,17 +2,7 @@ # Test whether a client sends a correct PUBLISH to a topic with QoS 2 and responds to a disconnect. -import inspect -import os -import socket -import sys - -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * port = mosq_test.get_lib_port() 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/03-publish-c2b-qos2-maximum-qos-0.py b/test/lib/03-publish-c2b-qos2-maximum-qos-0.py new file mode 100755 index 00000000..430df3a2 --- /dev/null +++ b/test/lib/03-publish-c2b-qos2-maximum-qos-0.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python + +# Test whether a client correctly handles sending a message with QoS > maximum QoS. + +from mosq_test_helper import * + +port = mosq_test.get_lib_port() + +rc = 1 +keepalive = 60 +connect_packet = mosq_test.gen_connect("publish-qos2-test", keepalive=keepalive, proto_ver=5) + +props = mqtt5_props.gen_byte_prop(mqtt5_props.PROP_MAXIMUM_QOS, 0) +connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props) + +disconnect_packet = mosq_test.gen_disconnect(proto_ver=5) + +publish_1_packet = mosq_test.gen_publish("maximum/qos/qos0", qos=0, payload="message", proto_ver=5) + +disconnect_packet = mosq_test.gen_disconnect(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(10) + + if mosq_test.expect_packet(conn, "connect", connect_packet): + conn.send(connack_packet) + + if mosq_test.expect_packet(conn, "publish 1", publish_1_packet): + if mosq_test.expect_packet(conn, "disconnect", disconnect_packet): + rc = 0 + + conn.close() +finally: + for i in range(0, 5): + if client.returncode != None: + break + time.sleep(0.1) + + try: + client.terminate() + except OSError: + pass + + client.wait() + sock.close() + if client.returncode != 0: + exit(1) + +exit(rc) diff --git a/test/lib/03-publish-c2b-qos2-maximum-qos-1.py b/test/lib/03-publish-c2b-qos2-maximum-qos-1.py new file mode 100755 index 00000000..ec29af36 --- /dev/null +++ b/test/lib/03-publish-c2b-qos2-maximum-qos-1.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python + +# Test whether a client correctly handles sending a message with QoS > maximum QoS. + +from mosq_test_helper import * + +port = mosq_test.get_lib_port() + +rc = 1 +keepalive = 60 +connect_packet = mosq_test.gen_connect("publish-qos2-test", keepalive=keepalive, proto_ver=5) + +props = mqtt5_props.gen_byte_prop(mqtt5_props.PROP_MAXIMUM_QOS, 1) +connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props) + +disconnect_packet = mosq_test.gen_disconnect(proto_ver=5) + +mid = 1 +publish_1_packet = mosq_test.gen_publish("maximum/qos/qos1", qos=1, mid=mid, payload="message", proto_ver=5) +puback_1_packet = mosq_test.gen_puback(mid, proto_ver=5) + +publish_2_packet = mosq_test.gen_publish("maximum/qos/qos0", qos=0, payload="message", proto_ver=5) + +disconnect_packet = mosq_test.gen_disconnect(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(10) + + if mosq_test.expect_packet(conn, "connect", connect_packet): + conn.send(connack_packet) + + if mosq_test.expect_packet(conn, "publish 1", publish_1_packet): + conn.send(puback_1_packet) + if mosq_test.expect_packet(conn, "publish 2", publish_2_packet): + if mosq_test.expect_packet(conn, "disconnect", disconnect_packet): + rc = 0 + + conn.close() +finally: + for i in range(0, 5): + if client.returncode != None: + break + time.sleep(0.1) + + try: + client.terminate() + except OSError: + pass + + client.wait() + sock.close() + if client.returncode != 0: + exit(1) + +exit(rc) diff --git a/test/lib/03-publish-c2b-qos2-pubrec-error.py b/test/lib/03-publish-c2b-qos2-pubrec-error.py new file mode 100755 index 00000000..58021bd4 --- /dev/null +++ b/test/lib/03-publish-c2b-qos2-pubrec-error.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python + +# Test whether a client responds correctly when sending multiple PUBLISH with +# QoS 2, with the broker rejecting the first PUBLISH by setting the reason code +# in PUBACK to >= 0x80. + +from mosq_test_helper import * + +port = mosq_test.get_lib_port() + +rc = 1 +keepalive = 60 +connect_packet = mosq_test.gen_connect("publish-qos2-test", keepalive=keepalive, proto_ver=5) + +props = mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_RECEIVE_MAXIMUM, 1) +connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props) + +disconnect_packet = mosq_test.gen_disconnect(proto_ver=5) + +mid = 1 +publish_1_packet = mosq_test.gen_publish("topic", qos=2, mid=mid, payload="rejected", proto_ver=5) +pubrec_1_packet = mosq_test.gen_pubrec(mid, proto_ver=5, reason_code=0x80) + +mid = 2 +publish_2_packet = mosq_test.gen_publish("topic", qos=2, mid=mid, payload="accepted", proto_ver=5) +pubrec_2_packet = mosq_test.gen_pubrec(mid, proto_ver=5) +pubrel_2_packet = mosq_test.gen_pubrel(mid, proto_ver=5) +pubcomp_2_packet = mosq_test.gen_pubcomp(mid, proto_ver=5) + +disconnect_packet = mosq_test.gen_disconnect(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(10) + + if mosq_test.expect_packet(conn, "connect", connect_packet): + conn.send(connack_packet) + + if mosq_test.expect_packet(conn, "publish 1", publish_1_packet): + conn.send(pubrec_1_packet) + + if mosq_test.expect_packet(conn, "publish 2", publish_2_packet): + conn.send(pubrec_2_packet) + + if mosq_test.expect_packet(conn, "pubrel 2", pubrel_2_packet): + conn.send(pubcomp_2_packet) + rc = 0 + + conn.close() +finally: + for i in range(0, 5): + if client.returncode != None: + break + time.sleep(0.1) + + try: + client.terminate() + except OSError: + pass + + client.wait() + sock.close() + if client.returncode != 0: + exit(1) + +exit(rc) diff --git a/test/lib/03-publish-c2b-qos2-receive-maximum-1.py b/test/lib/03-publish-c2b-qos2-receive-maximum-1.py new file mode 100755 index 00000000..cb27cf55 --- /dev/null +++ b/test/lib/03-publish-c2b-qos2-receive-maximum-1.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python + +# Test whether a client responds correctly to multiple PUBLISH with QoS 2, with +# receive maximum set to 1. + +from mosq_test_helper import * + +port = mosq_test.get_lib_port() + +rc = 1 +keepalive = 60 +connect_packet = mosq_test.gen_connect("publish-qos2-test", keepalive=keepalive, proto_ver=5) + +props = mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_RECEIVE_MAXIMUM, 1) +connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props) + +disconnect_packet = mosq_test.gen_disconnect(proto_ver=5) + +mid = 1 +publish_1_packet = mosq_test.gen_publish("topic", qos=2, mid=mid, payload="12345", proto_ver=5) +pubrec_1_packet = mosq_test.gen_pubrec(mid, proto_ver=5) +pubrel_1_packet = mosq_test.gen_pubrel(mid, proto_ver=5) +pubcomp_1_packet = mosq_test.gen_pubcomp(mid, proto_ver=5) + +mid = 2 +publish_2_packet = mosq_test.gen_publish("topic", qos=2, mid=mid, payload="12345", proto_ver=5) +pubrec_2_packet = mosq_test.gen_pubrec(mid, proto_ver=5) +pubrel_2_packet = mosq_test.gen_pubrel(mid, proto_ver=5) +pubcomp_2_packet = mosq_test.gen_pubcomp(mid, proto_ver=5) + +mid = 3 +publish_3_packet = mosq_test.gen_publish("topic", qos=2, mid=mid, payload="12345", proto_ver=5) +pubrec_3_packet = mosq_test.gen_pubrec(mid, proto_ver=5) +pubrel_3_packet = mosq_test.gen_pubrel(mid, proto_ver=5) +pubcomp_3_packet = mosq_test.gen_pubcomp(mid, proto_ver=5) + +mid = 4 +publish_4_packet = mosq_test.gen_publish("topic", qos=2, mid=mid, payload="12345", proto_ver=5) +pubrec_4_packet = mosq_test.gen_pubrec(mid, proto_ver=5) +pubrel_4_packet = mosq_test.gen_pubrel(mid, proto_ver=5) +pubcomp_4_packet = mosq_test.gen_pubcomp(mid, proto_ver=5) + +mid = 5 +publish_5_packet = mosq_test.gen_publish("topic", qos=2, mid=mid, payload="12345", proto_ver=5) +pubrec_5_packet = mosq_test.gen_pubrec(mid, proto_ver=5) +pubrel_5_packet = mosq_test.gen_pubrel(mid, proto_ver=5) +pubcomp_5_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(10) + + if mosq_test.expect_packet(conn, "connect", connect_packet): + conn.send(connack_packet) + + if mosq_test.expect_packet(conn, "publish 1", publish_1_packet): + conn.send(pubrec_1_packet) + if mosq_test.expect_packet(conn, "pubrel 1", pubrel_1_packet): + conn.send(pubcomp_1_packet) + + if mosq_test.expect_packet(conn, "publish 2", publish_2_packet): + conn.send(pubrec_2_packet) + if mosq_test.expect_packet(conn, "pubrel 2", pubrel_2_packet): + conn.send(pubcomp_2_packet) + + if mosq_test.expect_packet(conn, "publish 3", publish_3_packet): + conn.send(pubrec_3_packet) + if mosq_test.expect_packet(conn, "pubrel 3", pubrel_3_packet): + conn.send(pubcomp_3_packet) + + if mosq_test.expect_packet(conn, "publish 4", publish_4_packet): + conn.send(pubrec_4_packet) + if mosq_test.expect_packet(conn, "pubrel 4", pubrel_4_packet): + conn.send(pubcomp_4_packet) + + if mosq_test.expect_packet(conn, "publish 5", publish_5_packet): + conn.send(pubrec_5_packet) + if mosq_test.expect_packet(conn, "pubrel 5", pubrel_5_packet): + conn.send(pubcomp_5_packet) + rc = 0 + + conn.close() +finally: + for i in range(0, 5): + if client.returncode != None: + break + time.sleep(0.1) + + try: + client.terminate() + except OSError: + pass + + client.wait() + sock.close() + if client.returncode != 0: + exit(1) + +exit(rc) diff --git a/test/lib/03-publish-c2b-qos2-receive-maximum-2.py b/test/lib/03-publish-c2b-qos2-receive-maximum-2.py new file mode 100755 index 00000000..b2335a51 --- /dev/null +++ b/test/lib/03-publish-c2b-qos2-receive-maximum-2.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python + +# Test whether a client responds correctly to multiple PUBLISH with QoS 2, with +# receive maximum set to 2. + +from mosq_test_helper import * + +port = mosq_test.get_lib_port() + +rc = 1 +keepalive = 60 +connect_packet = mosq_test.gen_connect("publish-qos2-test", keepalive=keepalive, proto_ver=5) + +props = mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_RECEIVE_MAXIMUM, 2) +connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props) + +disconnect_packet = mosq_test.gen_disconnect(proto_ver=5) + +mid = 1 +publish_1_packet = mosq_test.gen_publish("topic", qos=2, mid=mid, payload="12345", proto_ver=5) +pubrec_1_packet = mosq_test.gen_pubrec(mid, proto_ver=5) +pubrel_1_packet = mosq_test.gen_pubrel(mid, proto_ver=5) +pubcomp_1_packet = mosq_test.gen_pubcomp(mid, proto_ver=5) + +mid = 2 +publish_2_packet = mosq_test.gen_publish("topic", qos=2, mid=mid, payload="12345", proto_ver=5) +pubrec_2_packet = mosq_test.gen_pubrec(mid, proto_ver=5) +pubrel_2_packet = mosq_test.gen_pubrel(mid, proto_ver=5) +pubcomp_2_packet = mosq_test.gen_pubcomp(mid, proto_ver=5) + +mid = 3 +publish_3_packet = mosq_test.gen_publish("topic", qos=2, mid=mid, payload="12345", proto_ver=5) +pubrec_3_packet = mosq_test.gen_pubrec(mid, proto_ver=5) +pubrel_3_packet = mosq_test.gen_pubrel(mid, proto_ver=5) +pubcomp_3_packet = mosq_test.gen_pubcomp(mid, proto_ver=5) + +mid = 4 +publish_4_packet = mosq_test.gen_publish("topic", qos=2, mid=mid, payload="12345", proto_ver=5) +pubrec_4_packet = mosq_test.gen_pubrec(mid, proto_ver=5) +pubrel_4_packet = mosq_test.gen_pubrel(mid, proto_ver=5) +pubcomp_4_packet = mosq_test.gen_pubcomp(mid, proto_ver=5) + +mid = 5 +publish_5_packet = mosq_test.gen_publish("topic", qos=2, mid=mid, payload="12345", proto_ver=5) +pubrec_5_packet = mosq_test.gen_pubrec(mid, proto_ver=5) +pubrel_5_packet = mosq_test.gen_pubrel(mid, proto_ver=5) +pubcomp_5_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(10) + + if mosq_test.expect_packet(conn, "connect", connect_packet): + conn.send(connack_packet) + + if mosq_test.expect_packet(conn, "publish 1", publish_1_packet): + if mosq_test.expect_packet(conn, "publish 2", publish_2_packet): + conn.send(pubrec_1_packet) + conn.send(pubrec_2_packet) + + if mosq_test.expect_packet(conn, "pubrel 1", pubrel_1_packet): + if mosq_test.expect_packet(conn, "pubrel 2", pubrel_2_packet): + conn.send(pubcomp_1_packet) + conn.send(pubcomp_2_packet) + + if mosq_test.expect_packet(conn, "publish 3", publish_3_packet): + if mosq_test.expect_packet(conn, "publish 4", publish_4_packet): + conn.send(pubrec_3_packet) + conn.send(pubrec_4_packet) + + if mosq_test.expect_packet(conn, "pubrel 3", pubrel_3_packet): + if mosq_test.expect_packet(conn, "pubrel 4", pubrel_4_packet): + conn.send(pubcomp_3_packet) + conn.send(pubcomp_4_packet) + + if mosq_test.expect_packet(conn, "publish 5", publish_5_packet): + conn.send(pubrec_5_packet) + + if mosq_test.expect_packet(conn, "pubrel 5", pubrel_5_packet): + conn.send(pubcomp_5_packet) + rc = 0 + + conn.close() +finally: + for i in range(0, 5): + if client.returncode != None: + break + time.sleep(0.1) + + try: + client.terminate() + except OSError: + pass + + client.wait() + sock.close() + if client.returncode != 0: + exit(1) + +exit(rc) diff --git a/test/lib/03-publish-c2b-qos2-timeout.py b/test/lib/03-publish-c2b-qos2-timeout.py index 37031a9a..68313423 100755 --- a/test/lib/03-publish-c2b-qos2-timeout.py +++ b/test/lib/03-publish-c2b-qos2-timeout.py @@ -20,17 +20,7 @@ # the test will send the correct PUBCOMP response. On receiving the correct # PUBCOMP response, the client should send a DISCONNECT message. -import inspect -import os -import socket -import sys - -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * port = mosq_test.get_lib_port() diff --git a/test/lib/03-publish-c2b-qos2.py b/test/lib/03-publish-c2b-qos2.py index b27b6a06..3a52b767 100755 --- a/test/lib/03-publish-c2b-qos2.py +++ b/test/lib/03-publish-c2b-qos2.py @@ -20,17 +20,7 @@ # the test will send the correct PUBCOMP response. On receiving the correct # PUBCOMP response, the client should send a DISCONNECT message. -import inspect -import os -import socket -import sys - -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * port = mosq_test.get_lib_port() diff --git a/test/lib/03-publish-qos0-no-payload.py b/test/lib/03-publish-qos0-no-payload.py index b2185639..4a851d62 100755 --- a/test/lib/03-publish-qos0-no-payload.py +++ b/test/lib/03-publish-qos0-no-payload.py @@ -10,17 +10,7 @@ # rc!=0, the client should exit with an error. # After sending the PUBLISH message, the client should send a DISCONNECT message. -import inspect -import os -import socket -import sys - -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * port = mosq_test.get_lib_port() diff --git a/test/lib/03-publish-qos0.py b/test/lib/03-publish-qos0.py index 3f7adc9b..dff2cb81 100755 --- a/test/lib/03-publish-qos0.py +++ b/test/lib/03-publish-qos0.py @@ -10,17 +10,7 @@ # client should exit with an error. # After sending the PUBLISH message, the client should send a DISCONNECT message. -import inspect -import os -import socket -import sys - -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * port = mosq_test.get_lib_port() diff --git a/test/lib/03-request-response-correlation.py b/test/lib/03-request-response-correlation.py new file mode 100755 index 00000000..689ddcfc --- /dev/null +++ b/test/lib/03-request-response-correlation.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python + +from mosq_test_helper import * + +port = mosq_test.get_lib_port() + + +resp_topic = "response/topic" +pub_topic = "request/topic" + +rc = 1 +keepalive = 60 +connect1_packet = mosq_test.gen_connect("request-test", keepalive=keepalive, proto_ver=5) +connect2_packet = mosq_test.gen_connect("response-test", keepalive=keepalive, proto_ver=5) +connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) + +mid = 1 +subscribe1_packet = mosq_test.gen_subscribe(mid, resp_topic, 0, proto_ver=5) +subscribe2_packet = mosq_test.gen_subscribe(mid, pub_topic, 0, proto_ver=5) +suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) + + +props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_RESPONSE_TOPIC, resp_topic) +props += mqtt5_props.gen_string_prop(mqtt5_props.PROP_CORRELATION_DATA, "corridor") +publish1_packet_incoming = mosq_test.gen_publish(pub_topic, qos=0, payload="action", proto_ver=5, properties=props) + +props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_RESPONSE_TOPIC, resp_topic) +props += mqtt5_props.gen_string_prop(mqtt5_props.PROP_CORRELATION_DATA, "corridor") +props += mqtt5_props.gen_string_pair_prop(mqtt5_props.PROP_USER_PROPERTY, "user", "data") +publish1_packet_outgoing = mosq_test.gen_publish(pub_topic, qos=0, payload="action", proto_ver=5, properties=props) + +props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_CORRELATION_DATA, "corridor") +publish2_packet = mosq_test.gen_publish(resp_topic, qos=0, payload="a response", proto_ver=5, properties=props) + + +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) + +env = dict(os.environ) +env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' +try: + pp = env['PYTHONPATH'] +except KeyError: + pp = '' +env['PYTHONPATH'] = '../../lib/python:'+pp +client1 = mosq_test.start_client(filename="03-request-response-correlation-1.log", cmd=["c/03-request-response-correlation-1.test"], env=env, port=port) + +try: + (conn1, address) = sock.accept() + conn1.settimeout(10) + + client2 = mosq_test.start_client(filename="03-request-response-2.log", cmd=["c/03-request-response-2.test"], env=env, port=port) + (conn2, address) = sock.accept() + conn2.settimeout(10) + + if mosq_test.expect_packet(conn1, "connect1", connect1_packet): + conn1.send(connack_packet) + + if mosq_test.expect_packet(conn2, "connect2", connect2_packet): + conn2.send(connack_packet) + + if mosq_test.expect_packet(conn1, "subscribe1", subscribe1_packet): + conn1.send(suback_packet) + + if mosq_test.expect_packet(conn2, "subscribe2", subscribe2_packet): + conn2.send(suback_packet) + + if mosq_test.expect_packet(conn1, "publish1", publish1_packet_incoming): + conn2.send(publish1_packet_outgoing) + + if mosq_test.expect_packet(conn2, "publish2", publish2_packet): + rc = 0 + + conn1.close() + conn2.close() +finally: + client1.terminate() + client1.wait() + client2.terminate() + client2.wait() + if rc: + (stdo, stde) = client1.communicate() + print(stde) + (stdo, stde) = client2.communicate() + print(stde) + sock.close() + +exit(rc) diff --git a/test/lib/03-request-response.py b/test/lib/03-request-response.py new file mode 100755 index 00000000..fe0bf456 --- /dev/null +++ b/test/lib/03-request-response.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python + +from mosq_test_helper import * + +port = mosq_test.get_lib_port() + + +resp_topic = "response/topic" +pub_topic = "request/topic" + +rc = 1 +keepalive = 60 +connect1_packet = mosq_test.gen_connect("request-test", keepalive=keepalive, proto_ver=5) +connect2_packet = mosq_test.gen_connect("response-test", keepalive=keepalive, proto_ver=5) +connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) + +mid = 1 +subscribe1_packet = mosq_test.gen_subscribe(mid, resp_topic, 0, proto_ver=5) +subscribe2_packet = mosq_test.gen_subscribe(mid, pub_topic, 0, proto_ver=5) +suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) + + +props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_RESPONSE_TOPIC, resp_topic) +publish1_packet = mosq_test.gen_publish(pub_topic, qos=0, payload="action", proto_ver=5, properties=props) + +publish2_packet = mosq_test.gen_publish(resp_topic, qos=0, payload="a response", 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) + +env = dict(os.environ) +env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' +try: + pp = env['PYTHONPATH'] +except KeyError: + pp = '' +env['PYTHONPATH'] = '../../lib/python:'+pp +client1 = mosq_test.start_client(filename="03-request-response-1.log", cmd=["c/03-request-response-1.test"], env=env, port=port) + +try: + (conn1, address) = sock.accept() + conn1.settimeout(10) + + client2 = mosq_test.start_client(filename="03-request-response-2.log", cmd=["c/03-request-response-2.test"], env=env, port=port) + (conn2, address) = sock.accept() + conn2.settimeout(10) + + if mosq_test.expect_packet(conn1, "connect1", connect1_packet): + conn1.send(connack_packet) + + if mosq_test.expect_packet(conn2, "connect2", connect2_packet): + conn2.send(connack_packet) + + if mosq_test.expect_packet(conn1, "subscribe1", subscribe1_packet): + conn1.send(suback_packet) + + if mosq_test.expect_packet(conn2, "subscribe2", subscribe2_packet): + conn2.send(suback_packet) + + if mosq_test.expect_packet(conn1, "publish1", publish1_packet): + conn2.send(publish1_packet) + + if mosq_test.expect_packet(conn2, "publish2", publish2_packet): + rc = 0 + + conn1.close() + conn2.close() +finally: + client1.terminate() + client1.wait() + client2.terminate() + client2.wait() + if rc: + (stdo, stde) = client1.communicate() + print(stde) + (stdo, stde) = client2.communicate() + print(stde) + sock.close() + +exit(rc) diff --git a/test/lib/04-retain-qos0.py b/test/lib/04-retain-qos0.py index cb495df1..485d7712 100755 --- a/test/lib/04-retain-qos0.py +++ b/test/lib/04-retain-qos0.py @@ -2,17 +2,7 @@ # Test whether a client sends a correct retained PUBLISH to a topic with QoS 0. -import inspect -import os -import socket -import sys - -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * port = mosq_test.get_lib_port() diff --git a/test/lib/08-ssl-bad-cacert.py b/test/lib/08-ssl-bad-cacert.py index 20dc6fed..025f66b9 100755 --- a/test/lib/08-ssl-bad-cacert.py +++ b/test/lib/08-ssl-bad-cacert.py @@ -1,15 +1,6 @@ #!/usr/bin/env python -import inspect -import os -import sys - -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * if sys.version < '2.7': print("WARNING: SSL not supported on Python 2.6") diff --git a/test/lib/08-ssl-connect-cert-auth-enc.py b/test/lib/08-ssl-connect-cert-auth-enc.py index d49540fa..2d321fc4 100755 --- a/test/lib/08-ssl-connect-cert-auth-enc.py +++ b/test/lib/08-ssl-connect-cert-auth-enc.py @@ -10,18 +10,7 @@ # the CONNACK and verifying that rc=0, the client should send a DISCONNECT # message. If rc!=0, the client should exit with an error. -import inspect -import os -import socket -import ssl -import sys - -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * port = mosq_test.get_lib_port() diff --git a/test/lib/08-ssl-connect-cert-auth.py b/test/lib/08-ssl-connect-cert-auth.py index f1485bb9..8f5f31d5 100755 --- a/test/lib/08-ssl-connect-cert-auth.py +++ b/test/lib/08-ssl-connect-cert-auth.py @@ -10,18 +10,7 @@ # the CONNACK and verifying that rc=0, the client should send a DISCONNECT # message. If rc!=0, the client should exit with an error. -import inspect -import os -import socket -import ssl -import sys - -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * port = mosq_test.get_lib_port() diff --git a/test/lib/08-ssl-connect-no-auth.py b/test/lib/08-ssl-connect-no-auth.py index 55c81962..2499c31c 100755 --- a/test/lib/08-ssl-connect-no-auth.py +++ b/test/lib/08-ssl-connect-no-auth.py @@ -9,18 +9,7 @@ # the CONNACK and verifying that rc=0, the client should send a DISCONNECT # message. If rc!=0, the client should exit with an error. -import inspect -import os -import socket -import ssl -import sys - -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * port = mosq_test.get_lib_port() diff --git a/test/lib/08-ssl-fake-cacert.py b/test/lib/08-ssl-fake-cacert.py index 48967fd2..367cb479 100755 --- a/test/lib/08-ssl-fake-cacert.py +++ b/test/lib/08-ssl-fake-cacert.py @@ -1,18 +1,6 @@ #!/usr/bin/env python -import inspect -import os -import socket -import ssl -import sys -import time - -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) - -import mosq_test +from mosq_test_helper import * port = mosq_test.get_lib_port() diff --git a/test/lib/09-util-topic-matching.py b/test/lib/09-util-topic-matching.py deleted file mode 100755 index 3dc19e0b..00000000 --- a/test/lib/09-util-topic-matching.py +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env python - -import inspect -import os -import sys - -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) -import mosq_test - -rc = 1 - -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) -client.wait() -if client.returncode: - (stdo, stde) = client.communicate() - print(stdo) -exit(client.returncode) diff --git a/test/lib/09-util-topic-tokenise.py b/test/lib/09-util-topic-tokenise.py index 6f8dc122..88974c2e 100755 --- a/test/lib/09-util-topic-tokenise.py +++ b/test/lib/09-util-topic-tokenise.py @@ -1,14 +1,6 @@ #!/usr/bin/env python -import inspect -import os -import sys - -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) -import mosq_test +from mosq_test_helper import * rc = 1 diff --git a/test/lib/09-util-utf8-validate.py b/test/lib/09-util-utf8-validate.py deleted file mode 100755 index 99e2543b..00000000 --- a/test/lib/09-util-utf8-validate.py +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env python - -import inspect -import os -import subprocess -import sys -import time - -# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder -cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) -if cmd_subfolder not in sys.path: - sys.path.insert(0, cmd_subfolder) -import mosq_test - -rc = 1 - -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) -client.wait() -if client.returncode: - (stdo, stde) = client.communicate() - print(stdo) -exit(client.returncode) - diff --git a/test/lib/11-prop-oversize-packet.py b/test/lib/11-prop-oversize-packet.py new file mode 100755 index 00000000..946b7139 --- /dev/null +++ b/test/lib/11-prop-oversize-packet.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python + +# Test whether a client publishing an oversize packet correctly. +# The client should try to publish a message that is too big, then the one below which is ok. +# It should also try to subscribe with a too large topic + +from mosq_test_helper import * + +port = mosq_test.get_lib_port() + +rc = 1 +keepalive = 60 +connect_packet = mosq_test.gen_connect("publish-qos0-test", keepalive=keepalive, proto_ver=5) +props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_MAXIMUM_PACKET_SIZE, 30) +connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props) + +bad_publish_packet = mosq_test.gen_publish("pub/test", qos=0, payload="0123456789012345678", proto_ver=5) +publish_packet = mosq_test.gen_publish("pub/test", qos=0, payload="012345678901234567", proto_ver=5) + +disconnect_packet = mosq_test.gen_disconnect() + + +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(10) + + if mosq_test.expect_packet(conn, "connect", connect_packet): + conn.send(connack_packet) + + if mosq_test.expect_packet(conn, "publish", publish_packet): + if mosq_test.expect_packet(conn, "disconnect", disconnect_packet): + rc = 0 + + conn.close() +finally: + client.terminate() + client.wait() + if rc: + (stdo, stde) = client.communicate() + print(stde) + sock.close() + +exit(rc) diff --git a/test/lib/11-prop-send-content-type.py b/test/lib/11-prop-send-content-type.py new file mode 100755 index 00000000..9a433f1c --- /dev/null +++ b/test/lib/11-prop-send-content-type.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python + +from mosq_test_helper import * + +port = mosq_test.get_lib_port() + +rc = 1 +keepalive = 60 +connect_packet = mosq_test.gen_connect("prop-test", keepalive=keepalive, proto_ver=5) +connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) + +props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_CONTENT_TYPE, "application/json") +publish_packet = mosq_test.gen_publish("prop/qos0", qos=0, payload="message", proto_ver=5, properties=props) + +disconnect_packet = mosq_test.gen_disconnect(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(10) + + if mosq_test.expect_packet(conn, "connect", connect_packet): + conn.send(connack_packet) + + if mosq_test.expect_packet(conn, "publish", publish_packet): + if mosq_test.expect_packet(conn, "disconnect", disconnect_packet): + rc = 0 + + conn.close() +finally: + client.terminate() + client.wait() + if rc: + (stdo, stde) = client.communicate() + print(stde) + sock.close() + +exit(rc) diff --git a/test/lib/11-prop-send-payload-format.py b/test/lib/11-prop-send-payload-format.py new file mode 100755 index 00000000..701d55ba --- /dev/null +++ b/test/lib/11-prop-send-payload-format.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python + +# Test whether a client sends a correct PUBLISH to a topic with QoS 0. + +# The client should connect to port 1888 with keepalive=60, clean session set, +# and client id publish-qos0-test +# The test will send a CONNACK message to the client with rc=0. Upon receiving +# the CONNACK and verifying that rc=0, the client should send a PUBLISH message +# to topic "pub/qos0/test" with payload "message" and QoS=0. If rc!=0, the +# client should exit with an error. +# After sending the PUBLISH message, the client should send a DISCONNECT message. + +from mosq_test_helper import * + +port = mosq_test.get_lib_port() + +rc = 1 +keepalive = 60 +connect_packet = mosq_test.gen_connect("prop-test", keepalive=keepalive, proto_ver=5) +connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) + +props = mqtt5_props.gen_byte_prop(mqtt5_props.PROP_PAYLOAD_FORMAT_INDICATOR, 0x01) +publish_packet = mosq_test.gen_publish("prop/qos0", qos=0, payload="message", proto_ver=5, properties=props) + +disconnect_packet = mosq_test.gen_disconnect(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(10) + + if mosq_test.expect_packet(conn, "connect", connect_packet): + conn.send(connack_packet) + + if mosq_test.expect_packet(conn, "publish", publish_packet): + if mosq_test.expect_packet(conn, "disconnect", disconnect_packet): + rc = 0 + + conn.close() +finally: + client.terminate() + client.wait() + if rc: + (stdo, stde) = client.communicate() + print(stde) + sock.close() + +exit(rc) diff --git a/test/lib/Makefile b/test/lib/Makefile index 229eb641..b9d9b96b 100644 --- a/test/lib/Makefile +++ b/test/lib/Makefile @@ -8,7 +8,7 @@ LD_LIBRARY_PATH=../../lib all : ptest : test-compile - ./ptest.py + ./test.py test : c cpp @@ -20,24 +20,38 @@ test-compile-c : test-compile-cpp : $(MAKE) -C cpp -c cpp : test-compile +c : test-compile ./01-con-discon-success.py $@/01-con-discon-success.test ./01-will-set.py $@/01-will-set.test ./01-unpwd-set.py $@/01-unpwd-set.test ./01-will-unpwd-set.py $@/01-will-unpwd-set.test ./01-no-clean-session.py $@/01-no-clean-session.test ./01-keepalive-pingreq.py $@/01-keepalive-pingreq.test + ./01-server-keepalive-pingreq.py $@/01-server-keepalive-pingreq.test ./02-subscribe-qos0.py $@/02-subscribe-qos0.test ./02-subscribe-qos1.py $@/02-subscribe-qos1.test ./02-subscribe-qos2.py $@/02-subscribe-qos2.test ./02-unsubscribe.py $@/02-unsubscribe.test + ./02-unsubscribe-v5.py $@/02-unsubscribe-v5.test + ./02-unsubscribe-multiple-v5.py $@/02-unsubscribe-multiple-v5.test ./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 + ./03-publish-c2b-qos2-receive-maximum-1.py $@/03-publish-c2b-qos2-receive-maximum-1.test + ./03-publish-c2b-qos2-receive-maximum-2.py $@/03-publish-c2b-qos2-receive-maximum-2.test + ./03-publish-c2b-qos2-pubrec-error.py $@/03-publish-c2b-qos2-pubrec-error.test + ./03-publish-c2b-qos2-maximum-qos-0.py $@/03-publish-c2b-qos2-maximum-qos-0.test + ./03-publish-c2b-qos2-maximum-qos-1.py $@/03-publish-c2b-qos2-maximum-qos-1.test ./03-publish-b2c-qos1.py $@/03-publish-b2c-qos1.test ./03-publish-b2c-qos2.py $@/03-publish-b2c-qos2.test + ./03-request-response.py $@/03-request-response.test + ./03-request-response-correlation.py $@/03-request-response-correlation.test ./04-retain-qos0.py $@/04-retain-qos0.test ifeq ($(WITH_TLS),yes) ./08-ssl-connect-no-auth.py $@/08-ssl-connect-no-auth.test @@ -46,9 +60,10 @@ ifeq ($(WITH_TLS),yes) ./08-ssl-bad-cacert.py $@/08-ssl-bad-cacert.test #./08-ssl-fake-cacert.py $@/08-ssl-fake-cacert.test endif - ./09-util-topic-matching.py $@/09-util-topic-matching.test ./09-util-topic-tokenise.py $@/09-util-topic-tokenise.test - ./09-util-utf8-validate.py $@/09-util-utf8-validate.test + ./11-prop-oversize-packet.py $@/11-prop-oversize-packet.test + ./11-prop-send-payload-format.py $@/11-prop-send-payload-format.test + ./11-prop-send-content-type.py $@/11-prop-send-content-type.test clean : $(MAKE) -C c clean diff --git a/test/lib/c/01-server-keepalive-pingreq.c b/test/lib/c/01-server-keepalive-pingreq.c new file mode 100644 index 00000000..99da6f69 --- /dev/null +++ b/test/lib/c/01-server-keepalive-pingreq.c @@ -0,0 +1,37 @@ +#include +#include +#include +#include + +static int run = -1; + +void on_connect(struct mosquitto *mosq, void *obj, int rc) +{ + if(rc){ + exit(1); + } +} + +int main(int argc, char *argv[]) +{ + int rc; + struct mosquitto *mosq; + + int port = atoi(argv[1]); + + mosquitto_lib_init(); + + mosq = mosquitto_new("01-server-keepalive-pingreq", true, NULL); + mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5); + mosquitto_connect_callback_set(mosq, on_connect); + + rc = mosquitto_connect(mosq, "localhost", port, 60); + + while(run == -1){ + mosquitto_loop(mosq, -1, 1); + } + + mosquitto_destroy(mosq); + mosquitto_lib_cleanup(); + return run; +} diff --git a/test/lib/c/02-unsubscribe-multiple-v5.c b/test/lib/c/02-unsubscribe-multiple-v5.c new file mode 100644 index 00000000..7403a633 --- /dev/null +++ b/test/lib/c/02-unsubscribe-multiple-v5.c @@ -0,0 +1,59 @@ +#include +#include +#include +#include + +static int run = -1; + +void on_connect(struct mosquitto *mosq, void *obj, int rc) +{ + if(rc){ + exit(1); + }else{ + mosquitto_subscribe(mosq, NULL, "unsubscribe/test", 2); + } +} + +void on_subscribe(struct mosquitto *mosq, void *obj, int mid, int sub_count, const int *subs) +{ + char *unsubs[] = {"unsubscribe/test", "no-sub"}; + + mosquitto_unsubscribe_multiple(mosq, NULL, 2, unsubs, NULL); +} + +void on_disconnect(struct mosquitto *mosq, void *obj, int rc) +{ + run = rc; +} + +void on_unsubscribe(struct mosquitto *mosq, void *obj, int mid) +{ + mosquitto_disconnect(mosq); +} + + +int main(int argc, char *argv[]) +{ + int rc; + struct mosquitto *mosq; + + int port = atoi(argv[1]); + + mosquitto_lib_init(); + + mosq = mosquitto_new("unsubscribe-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_subscribe_callback_set(mosq, on_subscribe); + mosquitto_unsubscribe_callback_set(mosq, on_unsubscribe); + + rc = mosquitto_connect(mosq, "localhost", port, 60); + + while(run == -1){ + mosquitto_loop(mosq, -1, 1); + } + + mosquitto_lib_cleanup(); + return run; +} diff --git a/test/lib/c/02-unsubscribe-v5.c b/test/lib/c/02-unsubscribe-v5.c new file mode 100644 index 00000000..50e7377b --- /dev/null +++ b/test/lib/c/02-unsubscribe-v5.c @@ -0,0 +1,50 @@ +#include +#include +#include +#include + +static int run = -1; + +void on_connect(struct mosquitto *mosq, void *obj, int rc) +{ + if(rc){ + exit(1); + }else{ + mosquitto_unsubscribe(mosq, NULL, "unsubscribe/test"); + } +} + +void on_disconnect(struct mosquitto *mosq, void *obj, int rc) +{ + run = rc; +} + +void on_unsubscribe(struct mosquitto *mosq, void *obj, int mid) +{ + mosquitto_disconnect(mosq); +} + +int main(int argc, char *argv[]) +{ + int rc; + struct mosquitto *mosq; + + int port = atoi(argv[1]); + + mosquitto_lib_init(); + + mosq = mosquitto_new("unsubscribe-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_unsubscribe_callback_set(mosq, on_unsubscribe); + + rc = mosquitto_connect(mosq, "localhost", port, 60); + + while(run == -1){ + mosquitto_loop(mosq, -1, 1); + } + + mosquitto_lib_cleanup(); + return run; +} 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-qos1-receive-maximum.c b/test/lib/c/03-publish-c2b-qos1-receive-maximum.c new file mode 100644 index 00000000..563da395 --- /dev/null +++ b/test/lib/c/03-publish-c2b-qos1-receive-maximum.c @@ -0,0 +1,52 @@ +#include +#include +#include +#include +#include + +static int run = -1; + +void on_connect(struct mosquitto *mosq, void *obj, int rc, int flags, const mosquitto_property *properties) +{ + if(rc){ + exit(1); + } + + for(int i=0; i<6; i++){ + mosquitto_publish_v5(mosq, NULL, "topic", 5, "12345", 1, false, NULL); + } +} + +void on_publish(struct mosquitto *mosq, void *obj, int mid, int reason_code, const mosquitto_property *properties) +{ + if(mid == 6){ + mosquitto_disconnect(mosq); + run = 0; + } +} + +int main(int argc, char *argv[]) +{ + int rc; + struct mosquitto *mosq; + mosquitto_property *props = NULL; + + int port = atoi(argv[1]); + + mosquitto_lib_init(); + + mosq = mosquitto_new("publish-qos1-test", true, &run); + mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5); + + mosquitto_connect_v5_callback_set(mosq, on_connect); + mosquitto_publish_v5_callback_set(mosq, on_publish); + + rc = mosquitto_connect_bind_v5(mosq, "localhost", port, 60, NULL, NULL); + + 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/03-publish-c2b-qos2-maximum-qos-0.c b/test/lib/c/03-publish-c2b-qos2-maximum-qos-0.c new file mode 100644 index 00000000..cfe7ab1d --- /dev/null +++ b/test/lib/c/03-publish-c2b-qos2-maximum-qos-0.c @@ -0,0 +1,58 @@ +#include +#include +#include +#include +#include + +static int run = -1; + +void on_connect(struct mosquitto *mosq, void *obj, int rc) +{ + if(rc){ + exit(1); + }else{ + rc = mosquitto_publish(mosq, NULL, "maximum/qos/qos2", strlen("message"), "message", 2, false); + if(rc != MOSQ_ERR_QOS_NOT_SUPPORTED) run = 1; + rc = mosquitto_publish(mosq, NULL, "maximum/qos/qos1", strlen("message"), "message", 1, false); + if(rc != MOSQ_ERR_QOS_NOT_SUPPORTED) run = 1; + rc = mosquitto_publish(mosq, NULL, "maximum/qos/qos0", strlen("message"), "message", 0, false); + if(rc != MOSQ_ERR_SUCCESS) run = 1; + } +} + +void on_publish(struct mosquitto *mosq, void *obj, int mid) +{ + if(mid == 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, 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); + + rc = mosquitto_connect(mosq, "localhost", port, 60); + + while(run == -1){ + mosquitto_loop(mosq, 50, 1); + } + + mosquitto_lib_cleanup(); + return run; +} diff --git a/test/lib/c/03-publish-c2b-qos2-maximum-qos-1.c b/test/lib/c/03-publish-c2b-qos2-maximum-qos-1.c new file mode 100644 index 00000000..84e56692 --- /dev/null +++ b/test/lib/c/03-publish-c2b-qos2-maximum-qos-1.c @@ -0,0 +1,58 @@ +#include +#include +#include +#include +#include + +static int run = -1; + +void on_connect(struct mosquitto *mosq, void *obj, int rc) +{ + if(rc){ + exit(1); + }else{ + rc = mosquitto_publish(mosq, NULL, "maximum/qos/qos2", strlen("message"), "message", 2, false); + if(rc != MOSQ_ERR_QOS_NOT_SUPPORTED) run = 1; + rc = mosquitto_publish(mosq, NULL, "maximum/qos/qos1", strlen("message"), "message", 1, false); + if(rc != MOSQ_ERR_SUCCESS) run = 1; + rc = mosquitto_publish(mosq, NULL, "maximum/qos/qos0", strlen("message"), "message", 0, false); + if(rc != MOSQ_ERR_SUCCESS) run = 1; + } +} + +void on_publish(struct mosquitto *mosq, void *obj, int mid) +{ + if(mid == 2){ + 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_callback_set(mosq, on_publish); + + rc = mosquitto_connect(mosq, "localhost", port, 60); + + while(run == -1){ + mosquitto_loop(mosq, 50, 1); + } + + mosquitto_lib_cleanup(); + return run; +} diff --git a/test/lib/c/03-publish-c2b-qos2-pubrec-error.c b/test/lib/c/03-publish-c2b-qos2-pubrec-error.c new file mode 100644 index 00000000..f7a64b17 --- /dev/null +++ b/test/lib/c/03-publish-c2b-qos2-pubrec-error.c @@ -0,0 +1,49 @@ +#include +#include +#include +#include +#include + +static int run = -1; + +void on_connect(struct mosquitto *mosq, void *obj, int rc, int flags, const mosquitto_property *properties) +{ + if(rc){ + exit(1); + } + mosquitto_publish_v5(mosq, NULL, "topic", strlen("rejected"), "rejected", 2, false, NULL); + mosquitto_publish_v5(mosq, NULL, "topic", strlen("accepted"), "accepted", 2, false, NULL); +} + +void on_publish(struct mosquitto *mosq, void *obj, int mid, int reason_code, const mosquitto_property *properties) +{ + if(mid == 2){ + run = 0; + } +} + +int main(int argc, char *argv[]) +{ + int rc; + struct mosquitto *mosq; + mosquitto_property *props = NULL; + + 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_v5_callback_set(mosq, on_connect); + mosquitto_publish_v5_callback_set(mosq, on_publish); + + rc = mosquitto_connect_bind_v5(mosq, "localhost", port, 60, NULL, NULL); + + while(run == -1){ + mosquitto_loop(mosq, 100, 1); + } + + mosquitto_lib_cleanup(); + return run; +} diff --git a/test/lib/c/03-publish-c2b-qos2-receive-maximum-1.c b/test/lib/c/03-publish-c2b-qos2-receive-maximum-1.c new file mode 100644 index 00000000..d0f427ae --- /dev/null +++ b/test/lib/c/03-publish-c2b-qos2-receive-maximum-1.c @@ -0,0 +1,52 @@ +#include +#include +#include +#include +#include + +static int run = -1; + +void on_connect(struct mosquitto *mosq, void *obj, int rc, int flags, const mosquitto_property *properties) +{ + if(rc){ + exit(1); + } + + for(int i=0; i<5; i++){ + mosquitto_publish_v5(mosq, NULL, "topic", 5, "12345", 2, false, NULL); + } +} + +void on_publish(struct mosquitto *mosq, void *obj, int mid, int reason_code, const mosquitto_property *properties) +{ + if(mid == 5){ + mosquitto_disconnect(mosq); + run = 0; + } +} + +int main(int argc, char *argv[]) +{ + int rc; + struct mosquitto *mosq; + mosquitto_property *props = NULL; + + 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_v5_callback_set(mosq, on_connect); + mosquitto_publish_v5_callback_set(mosq, on_publish); + + rc = mosquitto_connect_bind_v5(mosq, "localhost", port, 60, NULL, NULL); + + while(run == -1){ + mosquitto_loop(mosq, 300, 1); + } + + mosquitto_lib_cleanup(); + return run; +} diff --git a/test/lib/c/03-publish-c2b-qos2-receive-maximum-2.c b/test/lib/c/03-publish-c2b-qos2-receive-maximum-2.c new file mode 100644 index 00000000..d0f427ae --- /dev/null +++ b/test/lib/c/03-publish-c2b-qos2-receive-maximum-2.c @@ -0,0 +1,52 @@ +#include +#include +#include +#include +#include + +static int run = -1; + +void on_connect(struct mosquitto *mosq, void *obj, int rc, int flags, const mosquitto_property *properties) +{ + if(rc){ + exit(1); + } + + for(int i=0; i<5; i++){ + mosquitto_publish_v5(mosq, NULL, "topic", 5, "12345", 2, false, NULL); + } +} + +void on_publish(struct mosquitto *mosq, void *obj, int mid, int reason_code, const mosquitto_property *properties) +{ + if(mid == 5){ + mosquitto_disconnect(mosq); + run = 0; + } +} + +int main(int argc, char *argv[]) +{ + int rc; + struct mosquitto *mosq; + mosquitto_property *props = NULL; + + 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_v5_callback_set(mosq, on_connect); + mosquitto_publish_v5_callback_set(mosq, on_publish); + + rc = mosquitto_connect_bind_v5(mosq, "localhost", port, 60, NULL, NULL); + + while(run == -1){ + mosquitto_loop(mosq, 300, 1); + } + + mosquitto_lib_cleanup(); + return run; +} diff --git a/test/lib/c/03-request-response-1.c b/test/lib/c/03-request-response-1.c new file mode 100644 index 00000000..1cdd4079 --- /dev/null +++ b/test/lib/c/03-request-response-1.c @@ -0,0 +1,61 @@ +#include +#include +#include +#include +#include +#include + +static int run = -1; +static int sent_mid = -1; + +void on_connect(struct mosquitto *mosq, void *obj, int rc) +{ + if(rc){ + exit(1); + }else{ + mosquitto_subscribe(mosq, NULL, "response/topic", 0); + } +} + +void on_subscribe(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos) +{ + mosquitto_property *props = NULL; + mosquitto_property_add_string(&props, MQTT_PROP_RESPONSE_TOPIC, "response/topic"); + mosquitto_publish_v5(mosq, NULL, "request/topic", 6, "action", 0, 0, props); + mosquitto_property_free_all(&props); +} + +void on_message(struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg) +{ + if(!strcmp(msg->payload, "a response")){ + run = 0; + }else{ + run = 1; + } +} + +int main(int argc, char *argv[]) +{ + int rc; + struct mosquitto *mosq; + int ver = PROTOCOL_VERSION_v5; + + int port = atoi(argv[1]); + + mosquitto_lib_init(); + + mosq = mosquitto_new("request-test", true, NULL); + mosquitto_opts_set(mosq, MOSQ_OPT_PROTOCOL_VERSION, &ver); + mosquitto_connect_callback_set(mosq, on_connect); + mosquitto_subscribe_callback_set(mosq, on_subscribe); + mosquitto_message_callback_set(mosq, on_message); + + rc = mosquitto_connect(mosq, "localhost", port, 60); + + while(run == -1){ + rc = mosquitto_loop(mosq, -1, 1); + } + + mosquitto_lib_cleanup(); + return run; +} diff --git a/test/lib/c/03-request-response-2.c b/test/lib/c/03-request-response-2.c new file mode 100644 index 00000000..3419f395 --- /dev/null +++ b/test/lib/c/03-request-response-2.c @@ -0,0 +1,66 @@ +#include +#include +#include +#include +#include +#include + +static int run = -1; +static int sent_mid = -1; + +void on_connect(struct mosquitto *mosq, void *obj, int rc) +{ + if(rc){ + exit(1); + }else{ + mosquitto_subscribe(mosq, NULL, "request/topic", 0); + } +} + +void on_message_v5(struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg, const mosquitto_property *props) +{ + const mosquitto_property *p_resp, *p_corr = NULL; + char *resp_topic = NULL; + int rc; + + if(!strcmp(msg->topic, "request/topic")){ + p_resp = mosquitto_property_read_string(props, MQTT_PROP_RESPONSE_TOPIC, &resp_topic, false); + if(p_resp){ + p_corr = mosquitto_property_read_binary(props, MQTT_PROP_CORRELATION_DATA, NULL, NULL, false); + rc = mosquitto_publish_v5(mosq, NULL, resp_topic, strlen("a response"), "a response", 0, false, p_corr); + free(resp_topic); + } + } +} + +void on_publish(struct mosquitto *mosq, void *obj, int mid) +{ + run = 0; +} + + +int main(int argc, char *argv[]) +{ + int rc; + struct mosquitto *mosq; + int ver = PROTOCOL_VERSION_v5; + + int port = atoi(argv[1]); + + mosquitto_lib_init(); + + mosq = mosquitto_new("response-test", true, NULL); + mosquitto_opts_set(mosq, MOSQ_OPT_PROTOCOL_VERSION, &ver); + mosquitto_connect_callback_set(mosq, on_connect); + mosquitto_publish_callback_set(mosq, on_publish); + mosquitto_message_v5_callback_set(mosq, on_message_v5); + + rc = mosquitto_connect(mosq, "localhost", port, 60); + + while(run == -1){ + rc = mosquitto_loop(mosq, -1, 1); + } + + mosquitto_lib_cleanup(); + return run; +} diff --git a/test/lib/c/03-request-response-correlation-1.c b/test/lib/c/03-request-response-correlation-1.c new file mode 100644 index 00000000..35190af5 --- /dev/null +++ b/test/lib/c/03-request-response-correlation-1.c @@ -0,0 +1,62 @@ +#include +#include +#include +#include +#include +#include + +static int run = -1; +static int sent_mid = -1; + +void on_connect(struct mosquitto *mosq, void *obj, int rc) +{ + if(rc){ + exit(1); + }else{ + mosquitto_subscribe(mosq, NULL, "response/topic", 0); + } +} + +void on_subscribe(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos) +{ + mosquitto_property *props = NULL; + mosquitto_property_add_string(&props, MQTT_PROP_RESPONSE_TOPIC, "response/topic"); + mosquitto_property_add_binary(&props, MQTT_PROP_CORRELATION_DATA, "corridor", 8); + mosquitto_publish_v5(mosq, NULL, "request/topic", 6, "action", 0, 0, props); + mosquitto_property_free_all(&props); +} + +void on_message(struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg) +{ + if(!strcmp(msg->payload, "a response")){ + run = 0; + }else{ + run = 1; + } +} + +int main(int argc, char *argv[]) +{ + int rc; + struct mosquitto *mosq; + int ver = PROTOCOL_VERSION_v5; + + int port = atoi(argv[1]); + + mosquitto_lib_init(); + + mosq = mosquitto_new("request-test", true, NULL); + mosquitto_opts_set(mosq, MOSQ_OPT_PROTOCOL_VERSION, &ver); + mosquitto_connect_callback_set(mosq, on_connect); + mosquitto_subscribe_callback_set(mosq, on_subscribe); + mosquitto_message_callback_set(mosq, on_message); + + rc = mosquitto_connect(mosq, "localhost", port, 60); + + while(run == -1){ + rc = mosquitto_loop(mosq, -1, 1); + } + + mosquitto_lib_cleanup(); + return run; +} diff --git a/test/lib/c/09-util-topic-matching.c b/test/lib/c/09-util-topic-matching.c deleted file mode 100644 index 12c512fd..00000000 --- a/test/lib/c/09-util-topic-matching.c +++ /dev/null @@ -1,63 +0,0 @@ -#include -#include -#include - -#define EXPECT_MATCH(A, B) do_check((A), (B), false) -#define EXPECT_NOMATCH(A, B) do_check((A), (B), true) - -void do_check(const char *sub, const char *topic, bool bad_res) -{ - bool match; - - mosquitto_topic_matches_sub(sub, topic, &match); - - if(match == bad_res){ - printf("s: %s t: %s\n", sub, topic); - exit(1); - } -} - -int main(int argc, char *argv[]) -{ - EXPECT_MATCH("foo/#", "foo/"); - EXPECT_NOMATCH("foo#", "foo"); - EXPECT_NOMATCH("fo#o/", "foo"); - EXPECT_NOMATCH("foo#", "fooa"); - EXPECT_NOMATCH("foo+", "foo"); - EXPECT_NOMATCH("foo+", "fooa"); - - EXPECT_NOMATCH("test/6/#", "test/3"); - EXPECT_MATCH("foo/bar", "foo/bar"); - EXPECT_MATCH("foo/+", "foo/bar"); - EXPECT_MATCH("foo/+/baz", "foo/bar/baz"); - - EXPECT_MATCH("A/B/+/#", "A/B/B/C"); - - EXPECT_MATCH("foo/+/#", "foo/bar/baz"); - EXPECT_MATCH("foo/+/#", "foo/bar"); - EXPECT_MATCH("#", "foo/bar/baz"); - EXPECT_MATCH("#", "foo/bar/baz"); - - EXPECT_NOMATCH("foo/bar", "foo"); - EXPECT_NOMATCH("foo/+", "foo/bar/baz"); - EXPECT_NOMATCH("foo/+/baz", "foo/bar/bar"); - - EXPECT_NOMATCH("foo/+/#", "fo2/bar/baz"); - - EXPECT_MATCH("#", "/foo/bar"); - EXPECT_MATCH("/#", "/foo/bar"); - EXPECT_NOMATCH("/#", "foo/bar"); - - - EXPECT_MATCH("foo//bar", "foo//bar"); - EXPECT_MATCH("foo//+", "foo//bar"); - EXPECT_MATCH("foo/+/+/baz", "foo///baz"); - EXPECT_MATCH("foo/bar/+", "foo/bar/"); - - EXPECT_MATCH("$SYS/bar", "$SYS/bar"); - EXPECT_NOMATCH("#", "$SYS/bar"); - EXPECT_NOMATCH("$BOB/bar", "$SYS/bar"); - - return 0; -} - diff --git a/test/lib/c/09-util-utf8-validate.c b/test/lib/c/09-util-utf8-validate.c deleted file mode 100644 index 8a8e85b4..00000000 --- a/test/lib/c/09-util-utf8-validate.c +++ /dev/null @@ -1,252 +0,0 @@ -#include -#include -#include -#include - -/* Test data taken from - * http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt but modified for - * updated standard (no 5, 6 byte lengths) */ - -void assert_invalid(const char *str) -{ - if(mosquitto_validate_utf8(str, strlen(str)) == 0){ - printf("ERROR assert_invalid: %s\n", str); - exit(1); - } -} - -void assert_valid_len(const char *str, int len) -{ - if(mosquitto_validate_utf8(str, strlen(str)) != 0){ - printf("ERROR assert_valid: %s\n", str); - exit(1); - } -} - -void assert_valid(const char *str) -{ - assert_valid_len(str, strlen(str)); -} - -int main(int argc, char *argv[]) -{ - /* 1 Some correct UTF-8 text */ - assert_valid("You should see the Greek word 'kosme': \"κόσμε\""); - - /* 2 Boundary condition test cases */ - /* 2.1 First possible sequence of a certain length */ - assert_valid_len("2.1.1 1 byte (U-00000000): \"\0\"", 39); - assert_valid("2.1.2 2 bytes (U-00000080): \"€\""); - assert_valid("2.1.3 3 bytes (U-00000800): \"ࠀ\""); - assert_valid("2.1.4 4 bytes (U-00010000): \"𐀀\""); - - /* 2.2 Last possible sequence of a certain length */ - - assert_valid("2.2.1 1 byte (U-0000007F): \"\""); - assert_valid("2.2.2 2 bytes (U-000007FF): \"߿\""); - assert_valid("2.2.3 3 bytes (U-0000FFFF): \"￿\""); - // FIXME assert_valid("2.2.4 4 bytes (U-001FFFFF): \"\""); - - /* 2.3 Other boundary conditions */ - - assert_valid("2.3.1 U-0000D7FF = ed 9f bf = \"퟿\""); - assert_valid("2.3.2 U-0000E000 = ee 80 80 = \"\""); - assert_valid("2.3.3 U-0000FFFD = ef bf bd = \"�\""); - assert_valid("2.3.4 U-0010FFFF = f4 8f bf bf = \"􏿿\""); - /* This used to be valid in pre-2003 utf-8 */ - assert_invalid("2.3.5 U-00110000 = f4 90 80 80 = \"\""); - - /* 3 Malformed sequences */ - /* 3.1 Unexpected continuation bytes */ - assert_invalid("3.1.1 First continuation byte 0x80: \"\""); - assert_invalid("3.1.2 Last continuation byte 0xbf: \"\""); - assert_invalid("3.1.3 2 continuation bytes: \"\""); - assert_invalid("3.1.4 3 continuation bytes: \"\""); - assert_invalid("3.1.5 4 continuation bytes: \"\""); - assert_invalid("3.1.6 5 continuation bytes: \"\""); - assert_invalid("3.1.7 6 continuation bytes: \"\""); - assert_invalid("3.1.8 7 continuation bytes: \"\""); - - /* 3.1.9 Sequence of all 64 possible continuation bytes (0x80-0xbf): */ - assert_invalid(""); - assert_invalid(""); - assert_invalid(""); - assert_invalid("\""); - - /* 3.2 Lonely start characters */ - - /* 3.2.1 All 32 first bytes of 2-byte sequences (0xc0-0xdf), - each followed by a space character: */ - assert_invalid("\" \""); - - /* 3.2.2 All 16 first bytes of 3-byte sequences (0xe0-0xef), - each followed by a space character: */ - assert_invalid("\" \""); - - /* 3.2.3 All 8 first bytes of 4-byte sequences (0xf0-0xf7), - each followed by a space character: */ - assert_invalid("\" \""); - - /* 3.2.4 All 4 first bytes of 5-byte sequences (0xf8-0xfb), - each followed by a space character: */ - assert_invalid("\" \""); - - /* 3.2.5 All 2 first bytes of 6-byte sequences (0xfc-0xfd), - each followed by a space character: */ - assert_invalid("\" \""); - - /* 3.3 Sequences with last continuation byte missing - - All bytes of an incomplete sequence should be signalled as a single - malformed sequence, i.e., you should see only a single replacement - character in each of the next 10 tests. (Characters as in section 2) */ - - assert_invalid("3.3.1 2-byte sequence with last byte missing (U+0000): \"\""); - assert_invalid("3.3.2 3-byte sequence with last byte missing (U+0000): \"\""); - assert_invalid("3.3.3 4-byte sequence with last byte missing (U+0000): \"\""); - assert_invalid("3.3.4 5-byte sequence with last byte missing (U+0000): \"\""); - assert_invalid("3.3.5 6-byte sequence with last byte missing (U+0000): \"\""); - assert_invalid("3.3.6 2-byte sequence with last byte missing (U-000007FF): \"\""); - assert_invalid("3.3.7 3-byte sequence with last byte missing (U-0000FFFF): \"\""); - assert_invalid("3.3.8 4-byte sequence with last byte missing (U-001FFFFF): \"\""); - assert_invalid("3.3.9 5-byte sequence with last byte missing (U-03FFFFFF): \"\""); - assert_invalid("3.3.10 6-byte sequence with last byte missing (U-7FFFFFFF): \"\""); - - /* 3.4 Concatenation of incomplete sequences - - All the 10 sequences of 3.3 concatenated, you should see 10 malformed - sequences being signalled:*/ - - assert_invalid("\"\""); - - /* 3.5 Impossible bytes - - The following two bytes cannot appear in a correct UTF-8 string */ - - assert_invalid("3.5.1 fe = \"\""); - assert_invalid("3.5.2 ff = \"\""); - assert_invalid("3.5.3 fe fe ff ff = \"\""); - - /* 4 Overlong sequences - - The following sequences are not malformed according to the letter of - the Unicode 2.0 standard. However, they are longer then necessary and - a correct UTF-8 encoder is not allowed to produce them. A "safe UTF-8 - decoder" should reject them just like malformed sequences for two - reasons: (1) It helps to debug applications if overlong sequences are - not treated as valid representations of characters, because this helps - to spot problems more quickly. (2) Overlong sequences provide - alternative representations of characters, that could maliciously be - used to bypass filters that check only for ASCII characters. For - instance, a 2-byte encoded line feed (LF) would not be caught by a - line counter that counts only 0x0a bytes, but it would still be - processed as a line feed by an unsafe UTF-8 decoder later in the - pipeline. From a security point of view, ASCII compatibility of UTF-8 - sequences means also, that ASCII characters are *only* allowed to be - represented by ASCII bytes in the range 0x00-0x7f. To ensure this - aspect of ASCII compatibility, use only "safe UTF-8 decoders" that - reject overlong UTF-8 sequences for which a shorter encoding exists. */ - - /* 4.1 Examples of an overlong ASCII character - - With a safe UTF-8 decoder, all of the following five overlong - representations of the ASCII character slash ("/") should be rejected - like a malformed UTF-8 sequence, for instance by substituting it with - a replacement character. If you see a slash below, you do not have a - safe UTF-8 decoder! */ - - assert_invalid("4.1.1 U+002F = c0 af = \"\""); - assert_invalid("4.1.2 U+002F = e0 80 af = \"\""); - assert_invalid("4.1.3 U+002F = f0 80 80 af = \"\""); - assert_invalid("4.1.4 U+002F = f8 80 80 80 af = \"\""); - assert_invalid("4.1.5 U+002F = fc 80 80 80 80 af = \"\""); - - /* 4.2 Maximum overlong sequences - - Below you see the highest Unicode value that is still resulting in an - overlong sequence if represented with the given number of bytes. This - is a boundary test for safe UTF-8 decoders. All five characters should - be rejected like malformed UTF-8 sequences. */ - - assert_invalid("4.2.1 U-0000007F = c1 bf = \"\""); - assert_invalid("4.2.2 U-000007FF = e0 9f bf = \"\""); - assert_invalid("4.2.3 U-0000FFFF = f0 8f bf bf = \"\""); - assert_invalid("4.2.4 U-001FFFFF = f8 87 bf bf bf = \"\""); - assert_invalid("4.2.5 U-03FFFFFF = fc 83 bf bf bf bf = \"\""); - - /* 4.3 Overlong representation of the NUL character - - The following five sequences should also be rejected like malformed - UTF-8 sequences and should not be treated like the ASCII NUL - character. */ - - assert_invalid("4.3.1 U+0000 = c0 80 = \"\""); - assert_invalid("4.3.2 U+0000 = e0 80 80 = \"\""); - assert_invalid("4.3.3 U+0000 = f0 80 80 80 = \"\""); - assert_invalid("4.3.4 U+0000 = f8 80 80 80 80 = \"\""); - assert_invalid("4.3.5 U+0000 = fc 80 80 80 80 80 = \"\""); - - /* 5 Illegal code positions - - The following UTF-8 sequences should be rejected like malformed - sequences, because they never represent valid ISO 10646 characters and - a UTF-8 decoder that accepts them might introduce security problems - comparable to overlong UTF-8 sequences. */ - - /* 5.1 Single UTF-16 surrogates */ - - assert_invalid("5.1.1 U+D800 = ed a0 80 = \"\""); - assert_invalid("5.1.2 U+DB7F = ed ad bf = \"\""); - assert_invalid("5.1.3 U+DB80 = ed ae 80 = \"\""); - assert_invalid("5.1.4 U+DBFF = ed af bf = \"\""); - assert_invalid("5.1.5 U+DC00 = ed b0 80 = \"\""); - assert_invalid("5.1.6 U+DF80 = ed be 80 = \"\""); - assert_invalid("5.1.7 U+DFFF = ed bf bf = \"\""); - - /* 5.2 Paired UTF-16 surrogates */ - - assert_invalid("5.2.1 U+D800 U+DC00 = ed a0 80 ed b0 80 = \"\""); - assert_invalid("5.2.2 U+D800 U+DFFF = ed a0 80 ed bf bf = \"\""); - assert_invalid("5.2.3 U+DB7F U+DC00 = ed ad bf ed b0 80 = \"\""); - assert_invalid("5.2.4 U+DB7F U+DFFF = ed ad bf ed bf bf = \"\""); - assert_invalid("5.2.5 U+DB80 U+DC00 = ed ae 80 ed b0 80 = \"\""); - assert_invalid("5.2.6 U+DB80 U+DFFF = ed ae 80 ed bf bf = \"\""); - assert_invalid("5.2.7 U+DBFF U+DC00 = ed af bf ed b0 80 = \"\""); - assert_invalid("5.2.8 U+DBFF U+DFFF = ed af bf ed bf bf = \"\""); - - /* 5.3 Noncharacter code positions - - The following "noncharacters" are "reserved for internal use" by - applications, and according to older versions of the Unicode Standard - "should never be interchanged". Unicode Corrigendum #9 dropped the - latter restriction. Nevertheless, their presence in incoming UTF-8 data - can remain a potential security risk, depending on what use is made of - these codes subsequently. Examples of such internal use: - - - Some file APIs with 16-bit characters may use the integer value -1 - = U+FFFF to signal an end-of-file (EOF) or error condition. - - - In some UTF-16 receivers, code point U+FFFE might trigger a - byte-swap operation (to convert between UTF-16LE and UTF-16BE). - - With such internal use of noncharacters, it may be desirable and safer - to block those code points in UTF-8 decoders, as they should never - occur legitimately in incoming UTF-8 data, and could trigger unsafe - behaviour in subsequent processing. - - Particularly problematic noncharacters in 16-bit applications: */ - - assert_valid("5.3.1 U+FFFE = ef bf be = \"￾\""); - assert_valid("5.3.2 U+FFFF = ef bf bf = \"￿\""); - - /* Other noncharacters: */ - - assert_valid("5.3.3 U+FDD0 .. U+FDEF = \"﷐﷑﷒﷓﷔﷕﷖﷗﷘﷙﷚﷛﷜﷝﷞﷟﷠﷡﷢﷣﷤﷥﷦﷧﷨﷩﷪﷫﷬﷭﷮﷯\""); - - /* 5.3.4 U+nFFFE U+nFFFF (for n = 1..10) */ - - assert_valid("\"🿾🿿𯿾𯿿𿿾𿿿񏿾񏿿񟿾񟿿񯿾񯿿񿿾񿿿򏿾򏿿򟿾򟿿򯿾򯿿򿿾򿿿󏿾󏿿󟿾󟿿󯿾󯿿󿿾󿿿􏿾􏿿\""); - - - return 0; -} diff --git a/test/lib/c/11-prop-oversize-packet.c b/test/lib/c/11-prop-oversize-packet.c new file mode 100644 index 00000000..c96526f5 --- /dev/null +++ b/test/lib/c/11-prop-oversize-packet.c @@ -0,0 +1,72 @@ +#include +#include +#include +#include +#include + +static int run = -1; +static int sent_mid = -1; + +void on_connect(struct mosquitto *mosq, void *obj, int rc) +{ + if(rc){ + exit(1); + }else{ + rc = mosquitto_subscribe(mosq, NULL, "0123456789012345678901234567890", 0); + if(rc != MOSQ_ERR_OVERSIZE_PACKET){ + printf("Fail on subscribe\n"); + exit(1); + } + + rc = mosquitto_unsubscribe(mosq, NULL, "0123456789012345678901234567890"); + if(rc != MOSQ_ERR_OVERSIZE_PACKET){ + printf("Fail on unsubscribe\n"); + exit(1); + } + + rc = mosquitto_publish(mosq, &sent_mid, "pub/test", strlen("0123456789012345678"), "0123456789012345678", 0, false); + if(rc != MOSQ_ERR_OVERSIZE_PACKET){ + printf("Fail on publish 1\n"); + exit(1); + } + rc = mosquitto_publish(mosq, &sent_mid, "pub/test", strlen("012345678901234567"), "012345678901234567", 0, false); + if(rc != MOSQ_ERR_SUCCESS){ + printf("Fail on publish 2\n"); + exit(1); + } + } +} + +void on_publish(struct mosquitto *mosq, void *obj, int mid) +{ + if(mid == sent_mid){ + mosquitto_disconnect(mosq); + run = 0; + }else{ + exit(1); + } +} + +int main(int argc, char *argv[]) +{ + int rc; + struct mosquitto *mosq; + + int port = atoi(argv[1]); + + mosquitto_lib_init(); + + mosq = mosquitto_new("publish-qos0-test", true, NULL); + mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5); + mosquitto_connect_callback_set(mosq, on_connect); + mosquitto_publish_callback_set(mosq, on_publish); + + rc = mosquitto_connect(mosq, "localhost", port, 60); + + while(run == -1){ + rc = mosquitto_loop(mosq, -1, 1); + } + + mosquitto_lib_cleanup(); + return run; +} diff --git a/test/lib/c/11-prop-send-content-type.c b/test/lib/c/11-prop-send-content-type.c new file mode 100644 index 00000000..d60c8b5a --- /dev/null +++ b/test/lib/c/11-prop-send-content-type.c @@ -0,0 +1,58 @@ +#include +#include +#include +#include +#include +#include + +static int run = -1; +static int sent_mid = -1; + +void on_connect(struct mosquitto *mosq, void *obj, int rc) +{ + int rc2; + mosquitto_property *proplist = NULL; + + if(rc){ + exit(1); + }else{ + rc2 = mosquitto_property_add_string(&proplist, MQTT_PROP_CONTENT_TYPE, "application/json"); + mosquitto_publish_v5(mosq, &sent_mid, "prop/qos0", strlen("message"), "message", 0, false, proplist); + } +} + +void on_publish(struct mosquitto *mosq, void *obj, int mid) +{ + if(mid == sent_mid){ + mosquitto_disconnect(mosq); + run = 0; + }else{ + exit(1); + } +} + +int main(int argc, char *argv[]) +{ + int rc; + int tmp; + struct mosquitto *mosq; + + int port = atoi(argv[1]); + + mosquitto_lib_init(); + + mosq = mosquitto_new("prop-test", true, NULL); + mosquitto_connect_callback_set(mosq, on_connect); + mosquitto_publish_callback_set(mosq, on_publish); + tmp = MQTT_PROTOCOL_V5; + mosquitto_opts_set(mosq, MOSQ_OPT_PROTOCOL_VERSION, &tmp); + + rc = mosquitto_connect(mosq, "localhost", port, 60); + + while(run == -1){ + rc = mosquitto_loop(mosq, -1, 1); + } + + mosquitto_lib_cleanup(); + return run; +} diff --git a/test/lib/c/11-prop-send-payload-format.c b/test/lib/c/11-prop-send-payload-format.c new file mode 100644 index 00000000..516d86d4 --- /dev/null +++ b/test/lib/c/11-prop-send-payload-format.c @@ -0,0 +1,58 @@ +#include +#include +#include +#include +#include +#include + +static int run = -1; +static int sent_mid = -1; + +void on_connect(struct mosquitto *mosq, void *obj, int rc) +{ + int rc2; + mosquitto_property *proplist = NULL; + + if(rc){ + exit(1); + }else{ + rc2 = mosquitto_property_add_byte(&proplist, MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, 1); + mosquitto_publish_v5(mosq, &sent_mid, "prop/qos0", strlen("message"), "message", 0, false, proplist); + } +} + +void on_publish(struct mosquitto *mosq, void *obj, int mid) +{ + if(mid == sent_mid){ + mosquitto_disconnect(mosq); + run = 0; + }else{ + exit(1); + } +} + +int main(int argc, char *argv[]) +{ + int rc; + int tmp; + struct mosquitto *mosq; + + int port = atoi(argv[1]); + + mosquitto_lib_init(); + + mosq = mosquitto_new("prop-test", true, NULL); + mosquitto_connect_callback_set(mosq, on_connect); + mosquitto_publish_callback_set(mosq, on_publish); + tmp = MQTT_PROTOCOL_V5; + mosquitto_opts_set(mosq, MOSQ_OPT_PROTOCOL_VERSION, &tmp); + + rc = mosquitto_connect(mosq, "localhost", port, 60); + + while(run == -1){ + rc = mosquitto_loop(mosq, -1, 1); + } + + mosquitto_lib_cleanup(); + return run; +} diff --git a/test/lib/c/Makefile b/test/lib/c/Makefile index 323d23ed..4eed7da0 100644 --- a/test/lib/c/Makefile +++ b/test/lib/c/Makefile @@ -1,100 +1,59 @@ -.PHONY: all test 01 02 03 04 08 09 clean reallyclean +.PHONY: all clean reallyclean CFLAGS=-I../../../lib -Werror LIBS=../../../lib/libmosquitto.so.1 -all : 01 02 03 04 08 09 +SRC = \ + 01-con-discon-success.c \ + 01-will-set.c \ + 01-unpwd-set.c \ + 01-will-unpwd-set.c \ + 01-no-clean-session.c \ + 01-keepalive-pingreq.c \ + 01-server-keepalive-pingreq.c \ + 02-subscribe-qos0.c \ + 02-subscribe-qos1.c \ + 02-subscribe-qos2.c \ + 02-unsubscribe.c \ + 02-unsubscribe-v5.c \ + 02-unsubscribe-multiple-v5.c \ + 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 \ + 03-publish-c2b-qos2-pubrec-error.c \ + 03-publish-c2b-qos2-maximum-qos-0.c \ + 03-publish-c2b-qos2-maximum-qos-1.c \ + 03-publish-b2c-qos1.c \ + 03-publish-b2c-qos2.c \ + 03-request-response-1.c \ + 03-request-response-2.c \ + 03-request-response-correlation-1.c \ + 04-retain-qos0.c \ + 08-ssl-connect-no-auth.c \ + 08-ssl-connect-cert-auth.c \ + 08-ssl-connect-cert-auth-enc.c \ + 08-ssl-bad-cacert.c \ + 08-ssl-fake-cacert.c \ + 09-util-topic-tokenise.c \ + 11-prop-oversize-packet.c \ + 11-prop-send-payload-format.c \ + 11-prop-send-content-type.c -01-con-discon-success.test : 01-con-discon-success.c +TESTS = ${SRC:.c=.test} + +all : ${TESTS} + +${TESTS} : %.test: %.c $(CC) $< -o $@ $(CFLAGS) $(LIBS) -01-will-set.test : 01-will-set.c - $(CC) $< -o $@ $(CFLAGS) $(LIBS) - -01-unpwd-set.test : 01-unpwd-set.c - $(CC) $< -o $@ $(CFLAGS) $(LIBS) - -01-will-unpwd-set.test : 01-will-unpwd-set.c - $(CC) $< -o $@ $(CFLAGS) $(LIBS) - -01-no-clean-session.test : 01-no-clean-session.c - $(CC) $< -o $@ $(CFLAGS) $(LIBS) - -01-keepalive-pingreq.test : 01-keepalive-pingreq.c - $(CC) $< -o $@ $(CFLAGS) $(LIBS) - -02-subscribe-qos0.test : 02-subscribe-qos0.c - $(CC) $< -o $@ $(CFLAGS) $(LIBS) - -02-subscribe-qos1.test : 02-subscribe-qos1.c - $(CC) $< -o $@ $(CFLAGS) $(LIBS) - -02-subscribe-qos2.test : 02-subscribe-qos2.c - $(CC) $< -o $@ $(CFLAGS) $(LIBS) - -02-unsubscribe.test : 02-unsubscribe.c - $(CC) $< -o $@ $(CFLAGS) $(LIBS) - -03-publish-qos0.test : 03-publish-qos0.c - $(CC) $< -o $@ $(CFLAGS) $(LIBS) - -03-publish-qos0-no-payload.test : 03-publish-qos0-no-payload.c - $(CC) $< -o $@ $(CFLAGS) $(LIBS) - -03-publish-c2b-qos1-disconnect.test : 03-publish-c2b-qos1-disconnect.c - $(CC) $< -o $@ $(CFLAGS) $(LIBS) - -03-publish-c2b-qos2.test : 03-publish-c2b-qos2.c - $(CC) $< -o $@ $(CFLAGS) $(LIBS) - -03-publish-c2b-qos2-disconnect.test : 03-publish-c2b-qos2-disconnect.c - $(CC) $< -o $@ $(CFLAGS) $(LIBS) - -03-publish-b2c-qos1.test : 03-publish-b2c-qos1.c - $(CC) $< -o $@ $(CFLAGS) $(LIBS) - -03-publish-b2c-qos2.test : 03-publish-b2c-qos2.c - $(CC) $< -o $@ $(CFLAGS) $(LIBS) - -04-retain-qos0.test : 04-retain-qos0.c - $(CC) $< -o $@ $(CFLAGS) $(LIBS) - -08-ssl-connect-no-auth.test : 08-ssl-connect-no-auth.c - $(CC) $< -o $@ $(CFLAGS) $(LIBS) - -08-ssl-connect-cert-auth.test : 08-ssl-connect-cert-auth.c - $(CC) $< -o $@ $(CFLAGS) $(LIBS) - -08-ssl-connect-cert-auth-enc.test : 08-ssl-connect-cert-auth-enc.c - $(CC) $< -o $@ $(CFLAGS) $(LIBS) - -08-ssl-bad-cacert.test : 08-ssl-bad-cacert.c - $(CC) $< -o $@ $(CFLAGS) $(LIBS) - -08-ssl-fake-cacert.test : 08-ssl-fake-cacert.c - $(CC) $< -o $@ $(CFLAGS) $(LIBS) - -09-util-topic-matching.test : 09-util-topic-matching.c - $(CC) $< -o $@ $(CFLAGS) $(LIBS) - -09-util-topic-tokenise.test : 09-util-topic-tokenise.c - $(CC) $< -o $@ $(CFLAGS) $(LIBS) - -09-util-utf8-validate.test : 09-util-utf8-validate.c - $(CC) $< -o $@ $(CFLAGS) $(LIBS) - -01 : 01-con-discon-success.test 01-will-set.test 01-unpwd-set.test 01-will-unpwd-set.test 01-no-clean-session.test 01-keepalive-pingreq.test - -02 : 02-subscribe-qos0.test 02-subscribe-qos1.test 02-subscribe-qos2.test 02-unsubscribe.test - -03 : 03-publish-qos0.test 03-publish-qos0-no-payload.test 03-publish-c2b-qos1-disconnect.test 03-publish-c2b-qos2.test 03-publish-c2b-qos2-disconnect.test 03-publish-b2c-qos1.test 03-publish-b2c-qos2.test - -04 : 04-retain-qos0.test - -08 : 08-ssl-connect-no-auth.test 08-ssl-connect-cert-auth.test 08-ssl-connect-cert-auth-enc.test 08-ssl-bad-cacert.test 08-ssl-fake-cacert.test - -09 : 09-util-topic-matching.test 09-util-topic-tokenise.test 09-util-utf8-validate.test - reallyclean : clean -rm -f *.orig diff --git a/test/lib/cpp/09-util-topic-matching.cpp b/test/lib/cpp/09-util-topic-matching.cpp deleted file mode 100644 index 4b1776aa..00000000 --- a/test/lib/cpp/09-util-topic-matching.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include -#include - -void do_check(const char *sub, const char *topic, bool bad_res) -{ - bool match; - - mosqpp::topic_matches_sub(sub, topic, &match); - - if(match == bad_res){ - printf("s: %s t: %s\n", sub, topic); - exit(1); - } -} - -int main(int argc, char *argv[]) -{ - do_check("foo/bar", "foo/bar", false); - do_check("foo/+", "foo/bar", false); - do_check("foo/+/baz", "foo/bar/baz", false); - - do_check("foo/+/#", "foo/bar/baz", false); - do_check("#", "foo/bar/baz", false); - - do_check("foo/bar", "foo", true); - do_check("foo/+", "foo/bar/baz", true); - do_check("foo/+/baz", "foo/bar/bar", true); - - do_check("foo/+/#", "fo2/bar/baz", true); - - do_check("#", "/foo/bar", false); - do_check("/#", "/foo/bar", false); - do_check("/#", "foo/bar", true); - - - do_check("foo//bar", "foo//bar", false); - do_check("foo//+", "foo//bar", false); - do_check("foo/+/+/baz", "foo///baz", false); - do_check("foo/bar/+", "foo/bar/", false); - - do_check("$SYS/bar", "$SYS/bar", false); - do_check("#", "$SYS/bar", true); - do_check("$BOB/bar", "$SYS/bar", true); - - return 0; -} - diff --git a/test/lib/cpp/09-util-utf8-validate.cpp b/test/lib/cpp/09-util-utf8-validate.cpp deleted file mode 100644 index 2decafcb..00000000 --- a/test/lib/cpp/09-util-utf8-validate.cpp +++ /dev/null @@ -1,252 +0,0 @@ -#include -#include -#include -#include - -/* Test data taken from - * http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt but modified for - * updated standard (no 5, 6 byte lengths) */ - -void assert_invalid(const char *str) -{ - if(mosqpp::validate_utf8(str, strlen(str)) == 0){ - printf("ERROR assert_invalid: %s\n", str); - exit(1); - } -} - -void assert_valid_len(const char *str, int len) -{ - if(mosqpp::validate_utf8(str, strlen(str)) != 0){ - printf("ERROR assert_valid: %s\n", str); - exit(1); - } -} - -void assert_valid(const char *str) -{ - assert_valid_len(str, strlen(str)); -} - -int main(int argc, char *argv[]) -{ - /* 1 Some correct UTF-8 text */ - assert_valid("You should see the Greek word 'kosme': \"κόσμε\""); - - /* 2 Boundary condition test cases */ - /* 2.1 First possible sequence of a certain length */ - assert_valid_len("2.1.1 1 byte (U-00000000): \"\0\"", 39); - assert_valid("2.1.2 2 bytes (U-00000080): \"€\""); - assert_valid("2.1.3 3 bytes (U-00000800): \"ࠀ\""); - assert_valid("2.1.4 4 bytes (U-00010000): \"𐀀\""); - - /* 2.2 Last possible sequence of a certain length */ - - assert_valid("2.2.1 1 byte (U-0000007F): \"\""); - assert_valid("2.2.2 2 bytes (U-000007FF): \"߿\""); - assert_valid("2.2.3 3 bytes (U-0000FFFF): \"￿\""); - // FIXME assert_valid("2.2.4 4 bytes (U-001FFFFF): \"\""); - - /* 2.3 Other boundary conditions */ - - assert_valid("2.3.1 U-0000D7FF = ed 9f bf = \"퟿\""); - assert_valid("2.3.2 U-0000E000 = ee 80 80 = \"\""); - assert_valid("2.3.3 U-0000FFFD = ef bf bd = \"�\""); - assert_valid("2.3.4 U-0010FFFF = f4 8f bf bf = \"􏿿\""); - /* This used to be valid in pre-2003 utf-8 */ - assert_invalid("2.3.5 U-00110000 = f4 90 80 80 = \"\""); - - /* 3 Malformed sequences */ - /* 3.1 Unexpected continuation bytes */ - assert_invalid("3.1.1 First continuation byte 0x80: \"\""); - assert_invalid("3.1.2 Last continuation byte 0xbf: \"\""); - assert_invalid("3.1.3 2 continuation bytes: \"\""); - assert_invalid("3.1.4 3 continuation bytes: \"\""); - assert_invalid("3.1.5 4 continuation bytes: \"\""); - assert_invalid("3.1.6 5 continuation bytes: \"\""); - assert_invalid("3.1.7 6 continuation bytes: \"\""); - assert_invalid("3.1.8 7 continuation bytes: \"\""); - - /* 3.1.9 Sequence of all 64 possible continuation bytes (0x80-0xbf): */ - assert_invalid(""); - assert_invalid(""); - assert_invalid(""); - assert_invalid("\""); - - /* 3.2 Lonely start characters */ - - /* 3.2.1 All 32 first bytes of 2-byte sequences (0xc0-0xdf), - each followed by a space character: */ - assert_invalid("\" \""); - - /* 3.2.2 All 16 first bytes of 3-byte sequences (0xe0-0xef), - each followed by a space character: */ - assert_invalid("\" \""); - - /* 3.2.3 All 8 first bytes of 4-byte sequences (0xf0-0xf7), - each followed by a space character: */ - assert_invalid("\" \""); - - /* 3.2.4 All 4 first bytes of 5-byte sequences (0xf8-0xfb), - each followed by a space character: */ - assert_invalid("\" \""); - - /* 3.2.5 All 2 first bytes of 6-byte sequences (0xfc-0xfd), - each followed by a space character: */ - assert_invalid("\" \""); - - /* 3.3 Sequences with last continuation byte missing - - All bytes of an incomplete sequence should be signalled as a single - malformed sequence, i.e., you should see only a single replacement - character in each of the next 10 tests. (Characters as in section 2) */ - - assert_invalid("3.3.1 2-byte sequence with last byte missing (U+0000): \"\""); - assert_invalid("3.3.2 3-byte sequence with last byte missing (U+0000): \"\""); - assert_invalid("3.3.3 4-byte sequence with last byte missing (U+0000): \"\""); - assert_invalid("3.3.4 5-byte sequence with last byte missing (U+0000): \"\""); - assert_invalid("3.3.5 6-byte sequence with last byte missing (U+0000): \"\""); - assert_invalid("3.3.6 2-byte sequence with last byte missing (U-000007FF): \"\""); - assert_invalid("3.3.7 3-byte sequence with last byte missing (U-0000FFFF): \"\""); - assert_invalid("3.3.8 4-byte sequence with last byte missing (U-001FFFFF): \"\""); - assert_invalid("3.3.9 5-byte sequence with last byte missing (U-03FFFFFF): \"\""); - assert_invalid("3.3.10 6-byte sequence with last byte missing (U-7FFFFFFF): \"\""); - - /* 3.4 Concatenation of incomplete sequences - - All the 10 sequences of 3.3 concatenated, you should see 10 malformed - sequences being signalled:*/ - - assert_invalid("\"\""); - - /* 3.5 Impossible bytes - - The following two bytes cannot appear in a correct UTF-8 string */ - - assert_invalid("3.5.1 fe = \"\""); - assert_invalid("3.5.2 ff = \"\""); - assert_invalid("3.5.3 fe fe ff ff = \"\""); - - /* 4 Overlong sequences - - The following sequences are not malformed according to the letter of - the Unicode 2.0 standard. However, they are longer then necessary and - a correct UTF-8 encoder is not allowed to produce them. A "safe UTF-8 - decoder" should reject them just like malformed sequences for two - reasons: (1) It helps to debug applications if overlong sequences are - not treated as valid representations of characters, because this helps - to spot problems more quickly. (2) Overlong sequences provide - alternative representations of characters, that could maliciously be - used to bypass filters that check only for ASCII characters. For - instance, a 2-byte encoded line feed (LF) would not be caught by a - line counter that counts only 0x0a bytes, but it would still be - processed as a line feed by an unsafe UTF-8 decoder later in the - pipeline. From a security point of view, ASCII compatibility of UTF-8 - sequences means also, that ASCII characters are *only* allowed to be - represented by ASCII bytes in the range 0x00-0x7f. To ensure this - aspect of ASCII compatibility, use only "safe UTF-8 decoders" that - reject overlong UTF-8 sequences for which a shorter encoding exists. */ - - /* 4.1 Examples of an overlong ASCII character - - With a safe UTF-8 decoder, all of the following five overlong - representations of the ASCII character slash ("/") should be rejected - like a malformed UTF-8 sequence, for instance by substituting it with - a replacement character. If you see a slash below, you do not have a - safe UTF-8 decoder! */ - - assert_invalid("4.1.1 U+002F = c0 af = \"\""); - assert_invalid("4.1.2 U+002F = e0 80 af = \"\""); - assert_invalid("4.1.3 U+002F = f0 80 80 af = \"\""); - assert_invalid("4.1.4 U+002F = f8 80 80 80 af = \"\""); - assert_invalid("4.1.5 U+002F = fc 80 80 80 80 af = \"\""); - - /* 4.2 Maximum overlong sequences - - Below you see the highest Unicode value that is still resulting in an - overlong sequence if represented with the given number of bytes. This - is a boundary test for safe UTF-8 decoders. All five characters should - be rejected like malformed UTF-8 sequences. */ - - assert_invalid("4.2.1 U-0000007F = c1 bf = \"\""); - assert_invalid("4.2.2 U-000007FF = e0 9f bf = \"\""); - assert_invalid("4.2.3 U-0000FFFF = f0 8f bf bf = \"\""); - assert_invalid("4.2.4 U-001FFFFF = f8 87 bf bf bf = \"\""); - assert_invalid("4.2.5 U-03FFFFFF = fc 83 bf bf bf bf = \"\""); - - /* 4.3 Overlong representation of the NUL character - - The following five sequences should also be rejected like malformed - UTF-8 sequences and should not be treated like the ASCII NUL - character. */ - - assert_invalid("4.3.1 U+0000 = c0 80 = \"\""); - assert_invalid("4.3.2 U+0000 = e0 80 80 = \"\""); - assert_invalid("4.3.3 U+0000 = f0 80 80 80 = \"\""); - assert_invalid("4.3.4 U+0000 = f8 80 80 80 80 = \"\""); - assert_invalid("4.3.5 U+0000 = fc 80 80 80 80 80 = \"\""); - - /* 5 Illegal code positions - - The following UTF-8 sequences should be rejected like malformed - sequences, because they never represent valid ISO 10646 characters and - a UTF-8 decoder that accepts them might introduce security problems - comparable to overlong UTF-8 sequences. */ - - /* 5.1 Single UTF-16 surrogates */ - - assert_invalid("5.1.1 U+D800 = ed a0 80 = \"\""); - assert_invalid("5.1.2 U+DB7F = ed ad bf = \"\""); - assert_invalid("5.1.3 U+DB80 = ed ae 80 = \"\""); - assert_invalid("5.1.4 U+DBFF = ed af bf = \"\""); - assert_invalid("5.1.5 U+DC00 = ed b0 80 = \"\""); - assert_invalid("5.1.6 U+DF80 = ed be 80 = \"\""); - assert_invalid("5.1.7 U+DFFF = ed bf bf = \"\""); - - /* 5.2 Paired UTF-16 surrogates */ - - assert_invalid("5.2.1 U+D800 U+DC00 = ed a0 80 ed b0 80 = \"\""); - assert_invalid("5.2.2 U+D800 U+DFFF = ed a0 80 ed bf bf = \"\""); - assert_invalid("5.2.3 U+DB7F U+DC00 = ed ad bf ed b0 80 = \"\""); - assert_invalid("5.2.4 U+DB7F U+DFFF = ed ad bf ed bf bf = \"\""); - assert_invalid("5.2.5 U+DB80 U+DC00 = ed ae 80 ed b0 80 = \"\""); - assert_invalid("5.2.6 U+DB80 U+DFFF = ed ae 80 ed bf bf = \"\""); - assert_invalid("5.2.7 U+DBFF U+DC00 = ed af bf ed b0 80 = \"\""); - assert_invalid("5.2.8 U+DBFF U+DFFF = ed af bf ed bf bf = \"\""); - - /* 5.3 Noncharacter code positions - - The following "noncharacters" are "reserved for internal use" by - applications, and according to older versions of the Unicode Standard - "should never be interchanged". Unicode Corrigendum #9 dropped the - latter restriction. Nevertheless, their presence in incoming UTF-8 data - can remain a potential security risk, depending on what use is made of - these codes subsequently. Examples of such internal use: - - - Some file APIs with 16-bit characters may use the integer value -1 - = U+FFFF to signal an end-of-file (EOF) or error condition. - - - In some UTF-16 receivers, code point U+FFFE might trigger a - byte-swap operation (to convert between UTF-16LE and UTF-16BE). - - With such internal use of noncharacters, it may be desirable and safer - to block those code points in UTF-8 decoders, as they should never - occur legitimately in incoming UTF-8 data, and could trigger unsafe - behaviour in subsequent processing. - - Particularly problematic noncharacters in 16-bit applications: */ - - assert_valid("5.3.1 U+FFFE = ef bf be = \"￾\""); - assert_valid("5.3.2 U+FFFF = ef bf bf = \"￿\""); - - /* Other noncharacters: */ - - assert_valid("5.3.3 U+FDD0 .. U+FDEF = \"﷐﷑﷒﷓﷔﷕﷖﷗﷘﷙﷚﷛﷜﷝﷞﷟﷠﷡﷢﷣﷤﷥﷦﷧﷨﷩﷪﷫﷬﷭﷮﷯\""); - - /* 5.3.4 U+nFFFE U+nFFFF (for n = 1..10) */ - - assert_valid("\"🿾🿿𯿾𯿿𿿾𿿿񏿾񏿿񟿾񟿿񯿾񯿿񿿾񿿿򏿾򏿿򟿾򟿿򯿾򯿿򿿾򿿿󏿾󏿿󟿾󟿿󯿾󯿿󿿾󿿿􏿾􏿿\""); - - - return 0; -} diff --git a/test/lib/cpp/Makefile b/test/lib/cpp/Makefile index e5a85e5f..5761d9d4 100644 --- a/test/lib/cpp/Makefile +++ b/test/lib/cpp/Makefile @@ -74,15 +74,9 @@ all : 01 02 03 04 08 09 08-ssl-fake-cacert.test : 08-ssl-fake-cacert.cpp $(CXX) $< -o $@ $(CFLAGS) $(LIBS) -09-util-topic-matching.test : 09-util-topic-matching.cpp - $(CXX) $< -o $@ $(CFLAGS) $(LIBS) - 09-util-topic-tokenise.test : 09-util-topic-tokenise.cpp $(CXX) $< -o $@ $(CFLAGS) $(LIBS) -09-util-utf8-validate.test : 09-util-utf8-validate.cpp - $(CXX) $< -o $@ $(CFLAGS) $(LIBS) - 01 : 01-con-discon-success.test 01-will-set.test 01-unpwd-set.test 01-will-unpwd-set.test 01-no-clean-session.test 01-keepalive-pingreq.test 02 : 02-subscribe-qos0.test 02-subscribe-qos1.test 02-subscribe-qos2.test 02-unsubscribe.test @@ -93,7 +87,7 @@ all : 01 02 03 04 08 09 08 : 08-ssl-connect-no-auth.test 08-ssl-connect-cert-auth.test 08-ssl-connect-cert-auth-enc.test 08-ssl-bad-cacert.test 08-ssl-fake-cacert.test -09 : 09-util-topic-matching.test 09-util-topic-tokenise.test 09-util-utf8-validate.test +09 : 09-util-topic-tokenise.test reallyclean : clean -rm -f *.orig diff --git a/test/lib/mosq_test_helper.py b/test/lib/mosq_test_helper.py new file mode 100644 index 00000000..0e861dba --- /dev/null +++ b/test/lib/mosq_test_helper.py @@ -0,0 +1,15 @@ +import inspect, os, sys + +# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder +cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) +if cmd_subfolder not in sys.path: + sys.path.insert(0, cmd_subfolder) + +import mosq_test +import mqtt5_props + +import socket +import ssl +import struct +import subprocess +import time diff --git a/test/lib/ptest.py b/test/lib/ptest.py deleted file mode 100755 index 42821379..00000000 --- a/test/lib/ptest.py +++ /dev/null @@ -1,114 +0,0 @@ -#!/usr/bin/env python3 - -import subprocess -import time -import sys - -max_running = 10 -tests = [ - ('./01-con-discon-success.py', 'c/01-con-discon-success.test'), - ('./01-keepalive-pingreq.py', 'c/01-keepalive-pingreq.test'), - ('./01-no-clean-session.py', 'c/01-no-clean-session.test'), - ('./01-unpwd-set.py', 'c/01-unpwd-set.test'), - ('./01-will-set.py', 'c/01-will-set.test'), - ('./01-will-unpwd-set.py', 'c/01-will-unpwd-set.test'), - ('./02-subscribe-qos0.py', 'c/02-subscribe-qos0.test'), - ('./02-subscribe-qos1.py', 'c/02-subscribe-qos1.test'), - ('./02-subscribe-qos2.py', 'c/02-subscribe-qos2.test'), - ('./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-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'), - ('./03-publish-qos0-no-payload.py', 'c/03-publish-qos0-no-payload.test'), - ('./03-publish-qos0.py', 'c/03-publish-qos0.test'), - ('./04-retain-qos0.py', 'c/04-retain-qos0.test'), - ('./08-ssl-bad-cacert.py', 'c/08-ssl-bad-cacert.test'), - ('./08-ssl-connect-cert-auth-enc.py', 'c/08-ssl-connect-cert-auth-enc.test'), - ('./08-ssl-connect-cert-auth.py', 'c/08-ssl-connect-cert-auth.test'), - ('./08-ssl-connect-no-auth.py', 'c/08-ssl-connect-no-auth.test'), - ('./09-util-topic-matching.py', 'c/09-util-topic-matching.test'), - ('./09-util-topic-tokenise.py', 'c/09-util-topic-tokenise.test'), - ('./09-util-utf8-validate.py', 'c/09-util-utf8-validate.test'), - - ('./01-con-discon-success.py', 'cpp/01-con-discon-success.test'), - ('./01-keepalive-pingreq.py', 'cpp/01-keepalive-pingreq.test'), - ('./01-no-clean-session.py', 'cpp/01-no-clean-session.test'), - ('./01-unpwd-set.py', 'cpp/01-unpwd-set.test'), - ('./01-will-set.py', 'cpp/01-will-set.test'), - ('./01-will-unpwd-set.py', 'cpp/01-will-unpwd-set.test'), - ('./02-subscribe-qos0.py', 'cpp/02-subscribe-qos0.test'), - ('./02-subscribe-qos1.py', 'cpp/02-subscribe-qos1.test'), - ('./02-subscribe-qos2.py', 'cpp/02-subscribe-qos2.test'), - ('./02-unsubscribe.py', 'cpp/02-unsubscribe.test'), - ('./03-publish-b2c-qos1.py', 'cpp/03-publish-b2c-qos1.test'), - ('./03-publish-b2c-qos2.py', 'cpp/03-publish-b2c-qos2.test'), - ('./03-publish-c2b-qos1-disconnect.py', 'cpp/03-publish-c2b-qos1-disconnect.test'), - ('./03-publish-c2b-qos2-disconnect.py', 'cpp/03-publish-c2b-qos2-disconnect.test'), - ('./03-publish-c2b-qos2.py', 'cpp/03-publish-c2b-qos2.test'), - ('./03-publish-qos0-no-payload.py', 'cpp/03-publish-qos0-no-payload.test'), - ('./03-publish-qos0.py', 'cpp/03-publish-qos0.test'), - ('./04-retain-qos0.py', 'cpp/04-retain-qos0.test'), - ('./08-ssl-bad-cacert.py', 'cpp/08-ssl-bad-cacert.test'), - ('./08-ssl-connect-cert-auth-enc.py', 'cpp/08-ssl-connect-cert-auth-enc.test'), - ('./08-ssl-connect-cert-auth.py', 'cpp/08-ssl-connect-cert-auth.test'), - ('./08-ssl-connect-no-auth.py', 'cpp/08-ssl-connect-no-auth.test'), - ('./09-util-topic-matching.py', 'cpp/09-util-topic-matching.test'), - ('./09-util-topic-tokenise.py', 'cpp/09-util-topic-tokenise.test'), - ('./09-util-utf8-validate.py', 'cpp/09-util-utf8-validate.test'), - ] - -minport = 1888 -ports = list(range(minport, minport+max_running+1)) - -def next_test(tests, ports): - if len(tests) == 0 or len(ports) == 0: - return - - test = tests.pop() - port = ports.pop() - p = subprocess.Popen([test[0], test[1], str(port)]) - p.mosq_port = port - return p - -def run_tests(tests, ports): - passed = 0 - failed = 0 - - failed_tests = [] - - running_tests = [] - while len(tests) > 0 or len(running_tests) > 0: - if len(running_tests) <= max_running: - t = next_test(tests, ports) - if t is None: - time.sleep(0.1) - else: - running_tests.append(t) - - for t in running_tests: - t.poll() - if t.returncode is not None: - running_tests.remove(t) - if isinstance(t.mosq_port, tuple): - for portret in t.mosq_port: - ports.append(portret) - else: - ports.append(t.mosq_port) - t.terminate() - t.wait() - #(stdo, stde) = t.communicate() - if t.returncode == 1: - print("\033[31m %s %s\033[0m" % (t.args[0], t.args[1])) - failed = failed + 1 - else: - passed = passed + 1 - print("\033[32m %s %s\033[0m" % (t.args[0], t.args[1])) - - print("Passed: %d\nFailed: %d\nTotal: %d" % (passed, failed, passed+failed)) - if failed > 0: - sys.exit(1) - - -run_tests(tests, ports) diff --git a/test/lib/test.py b/test/lib/test.py new file mode 100755 index 00000000..b6520ef5 --- /dev/null +++ b/test/lib/test.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python3 + +import mosq_test_helper +import ptest + +tests = [ + (1, ['./01-con-discon-success.py', 'c/01-con-discon-success.test']), + (1, ['./01-keepalive-pingreq.py', 'c/01-keepalive-pingreq.test']), + (1, ['./01-server-keepalive-pingreq.py', 'c/01-server-keepalive-pingreq.test']), + (1, ['./01-no-clean-session.py', 'c/01-no-clean-session.test']), + (1, ['./01-unpwd-set.py', 'c/01-unpwd-set.test']), + (1, ['./01-will-set.py', 'c/01-will-set.test']), + (1, ['./01-will-unpwd-set.py', 'c/01-will-unpwd-set.test']), + (1, ['./02-subscribe-qos0.py', 'c/02-subscribe-qos0.test']), + (1, ['./02-subscribe-qos1.py', 'c/02-subscribe-qos1.test']), + (1, ['./02-subscribe-qos2.py', 'c/02-subscribe-qos2.test']), + (1, ['./02-unsubscribe.py', 'c/02-unsubscribe.test']), + (1, ['./02-unsubscribe-v5.py', 'c/02-unsubscribe-v5.test']), + (1, ['./02-unsubscribe-multiple-v5.py', 'c/02-unsubscribe-multiple-v5.test']), + (1, ['./03-publish-b2c-qos1.py', 'c/03-publish-b2c-qos1.test']), + (1, ['./03-publish-b2c-qos2.py', 'c/03-publish-b2c-qos2.test']), + (1, ['./03-publish-c2b-qos1-len.py', 'c/03-publish-c2b-qos1-len.test']), + (1, ['./03-publish-c2b-qos2-len.py', 'c/03-publish-c2b-qos2-len.test']), + (1, ['./03-publish-b2c-qos2-len.py', 'c/03-publish-b2c-qos2-len.test']), + (1, ['./03-publish-c2b-qos1-disconnect.py', 'c/03-publish-c2b-qos1-disconnect.test']), + (1, ['./03-publish-c2b-qos2-disconnect.py', 'c/03-publish-c2b-qos2-disconnect.test']), + (1, ['./03-publish-c2b-qos2.py', 'c/03-publish-c2b-qos2.test']), + (1, ['./03-publish-c2b-qos1-receive-maximum.py', 'c/03-publish-c2b-qos1-receive-maximum.test']), + (1, ['./03-publish-c2b-qos2-receive-maximum-1.py', 'c/03-publish-c2b-qos2-receive-maximum-1.test']), + (1, ['./03-publish-c2b-qos2-receive-maximum-2.py', 'c/03-publish-c2b-qos2-receive-maximum-2.test']), + (1, ['./03-publish-c2b-qos2-pubrec-error.py', 'c/03-publish-c2b-qos2-pubrec-error.test']), + (1, ['./03-publish-c2b-qos2-maximum-qos-0.py', 'c/03-publish-c2b-qos2-maximum-qos-0.test']), + (1, ['./03-publish-c2b-qos2-maximum-qos-1.py', 'c/03-publish-c2b-qos2-maximum-qos-1.test']), + (1, ['./03-publish-qos0-no-payload.py', 'c/03-publish-qos0-no-payload.test']), + (1, ['./03-publish-qos0.py', 'c/03-publish-qos0.test']), + (1, ['./03-request-response.py', 'c/03-request-response.test']), + (1, ['./03-request-response-correlation.py', 'c/03-request-response-correlation.test']), + (1, ['./04-retain-qos0.py', 'c/04-retain-qos0.test']), + (1, ['./08-ssl-bad-cacert.py', 'c/08-ssl-bad-cacert.test']), + (1, ['./08-ssl-connect-cert-auth-enc.py', 'c/08-ssl-connect-cert-auth-enc.test']), + (1, ['./08-ssl-connect-cert-auth.py', 'c/08-ssl-connect-cert-auth.test']), + (1, ['./08-ssl-connect-no-auth.py', 'c/08-ssl-connect-no-auth.test']), + (1, ['./09-util-topic-tokenise.py', 'c/09-util-topic-tokenise.test']), + (1, ['./11-prop-oversize-packet.py', 'c/11-prop-oversize-packet.test']), + (1, ['./11-prop-send-payload-format.py', 'c/11-prop-send-payload-format.test']), + (1, ['./11-prop-send-content-type.py', 'c/11-prop-send-content-type.test']), + + (1, ['./01-con-discon-success.py', 'cpp/01-con-discon-success.test']), + (1, ['./01-keepalive-pingreq.py', 'cpp/01-keepalive-pingreq.test']), + (1, ['./01-no-clean-session.py', 'cpp/01-no-clean-session.test']), + (1, ['./01-unpwd-set.py', 'cpp/01-unpwd-set.test']), + (1, ['./01-will-set.py', 'cpp/01-will-set.test']), + (1, ['./01-will-unpwd-set.py', 'cpp/01-will-unpwd-set.test']), + (1, ['./02-subscribe-qos0.py', 'cpp/02-subscribe-qos0.test']), + (1, ['./02-subscribe-qos1.py', 'cpp/02-subscribe-qos1.test']), + (1, ['./02-subscribe-qos2.py', 'cpp/02-subscribe-qos2.test']), + (1, ['./02-unsubscribe.py', 'cpp/02-unsubscribe.test']), + (1, ['./03-publish-b2c-qos1.py', 'cpp/03-publish-b2c-qos1.test']), + (1, ['./03-publish-b2c-qos2.py', 'cpp/03-publish-b2c-qos2.test']), + (1, ['./03-publish-c2b-qos1-disconnect.py', 'cpp/03-publish-c2b-qos1-disconnect.test']), + (1, ['./03-publish-c2b-qos2-disconnect.py', 'cpp/03-publish-c2b-qos2-disconnect.test']), + (1, ['./03-publish-c2b-qos2.py', 'cpp/03-publish-c2b-qos2.test']), + (1, ['./03-publish-qos0-no-payload.py', 'cpp/03-publish-qos0-no-payload.test']), + (1, ['./03-publish-qos0.py', 'cpp/03-publish-qos0.test']), + (1, ['./04-retain-qos0.py', 'cpp/04-retain-qos0.test']), + (1, ['./08-ssl-bad-cacert.py', 'cpp/08-ssl-bad-cacert.test']), + (1, ['./08-ssl-connect-cert-auth-enc.py', 'cpp/08-ssl-connect-cert-auth-enc.test']), + (1, ['./08-ssl-connect-cert-auth.py', 'cpp/08-ssl-connect-cert-auth.test']), + (1, ['./08-ssl-connect-no-auth.py', 'cpp/08-ssl-connect-no-auth.test']), + (1, ['./09-util-topic-tokenise.py', 'cpp/09-util-topic-tokenise.test']), + ] + + +ptest.run_tests(tests) diff --git a/test/mosq_test.py b/test/mosq_test.py index a28f2eb1..bb5e9147 100644 --- a/test/mosq_test.py +++ b/test/mosq_test.py @@ -6,7 +6,9 @@ import struct import sys import time -def start_broker(filename, cmd=None, port=0, use_conf=False): +import mqtt5_props + +def start_broker(filename, cmd=None, port=0, use_conf=False, expect_fail=False): delay = 0.1 if use_conf == True: @@ -43,7 +45,11 @@ def start_broker(filename, cmd=None, port=0, use_conf=False): c.close() time.sleep(delay) return broker - raise IOError + + if expect_fail == False: + raise IOError + else: + return None def start_client(filename, cmd, env, port=1888): if cmd is None: @@ -72,10 +78,14 @@ def packet_matches(name, recvd, expected): print("Received: "+to_string(recvd)) except struct.error: print("Received (not decoded, len=%d): %s" % (len(recvd), recvd)) + for i in range(0, len(recvd)): + print('%c'%(recvd[i]),) try: print("Expected: "+to_string(expected)) except struct.error: print("Expected (not decoded, len=%d): %s" % (len(expected), expected)) + for i in range(0, len(expected)): + print('%c'%(expected[i]),) return 0 else: @@ -292,16 +302,18 @@ def to_string(packet): # Reserved return "0xF0" -def gen_connect(client_id, clean_session=True, keepalive=60, username=None, password=None, will_topic=None, will_qos=0, will_retain=False, will_payload="", proto_ver=4, connect_reserved=False): +def gen_connect(client_id, clean_session=True, keepalive=60, username=None, password=None, will_topic=None, will_qos=0, will_retain=False, will_payload="", proto_ver=4, connect_reserved=False, properties="", will_properties=""): if (proto_ver&0x7F) == 3 or proto_ver == 0: remaining_length = 12 - elif (proto_ver&0x7F) == 4: + elif (proto_ver&0x7F) == 4 or proto_ver == 5: remaining_length = 10 else: raise ValueError if client_id != None: remaining_length = remaining_length + 2+len(client_id) + else: + remaining_length = remaining_length + 2 connect_flags = 0 @@ -311,11 +323,20 @@ def gen_connect(client_id, clean_session=True, keepalive=60, username=None, pass if clean_session: connect_flags = connect_flags | 0x02 + if proto_ver == 5: + if properties == "": + properties += mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_RECEIVE_MAXIMUM, 20) + properties = mqtt5_props.prop_finalise(properties) + remaining_length += len(properties) + if will_topic != None: remaining_length = remaining_length + 2+len(will_topic) + 2+len(will_payload) connect_flags = connect_flags | 0x04 | ((will_qos&0x03) << 3) if will_retain: connect_flags = connect_flags | 32 + if proto_ver == 5: + will_properties = mqtt5_props.prop_finalise(will_properties) + remaining_length += len(will_properties) if username != None: remaining_length = remaining_length + 2+len(username) @@ -328,13 +349,19 @@ def gen_connect(client_id, clean_session=True, keepalive=60, username=None, pass packet = struct.pack("!B"+str(len(rl))+"s", 0x10, rl) if (proto_ver&0x7F) == 3 or proto_ver == 0: packet = packet + struct.pack("!H6sBBH", len("MQIsdp"), "MQIsdp", proto_ver, connect_flags, keepalive) - elif (proto_ver&0x7F) == 4: + elif (proto_ver&0x7F) == 4 or proto_ver == 5: packet = packet + struct.pack("!H4sBBH", len("MQTT"), "MQTT", proto_ver, connect_flags, keepalive) + if proto_ver == 5: + packet += properties + if client_id != None: packet = packet + struct.pack("!H"+str(len(client_id))+"s", len(client_id), client_id) + else: + packet = packet + struct.pack("!H", 0) if will_topic != None: + packet += will_properties packet = packet + struct.pack("!H"+str(len(will_topic))+"s", len(will_topic), will_topic) if len(will_payload) > 0: packet = packet + struct.pack("!H"+str(len(will_payload))+"s", len(will_payload), will_payload) @@ -347,15 +374,30 @@ def gen_connect(client_id, clean_session=True, keepalive=60, username=None, pass packet = packet + struct.pack("!H"+str(len(password))+"s", len(password), password) return packet -def gen_connack(resv=0, rc=0): - return struct.pack('!BBBB', 32, 2, resv, rc); +def gen_connack(flags=0, rc=0, proto_ver=4, properties=""): + if proto_ver == 5: + properties += mqtt5_props.gen_byte_prop(mqtt5_props.PROP_SHARED_SUB_AVAILABLE, 0) + properties = mqtt5_props.prop_finalise(properties) -def gen_publish(topic, qos, payload=None, retain=False, dup=False, mid=0): + packet = struct.pack('!BBBB', 32, 2+len(properties), flags, rc) + properties + else: + packet = struct.pack('!BBBB', 32, 2, flags, rc); + + return packet + +def gen_publish(topic, qos, payload=None, retain=False, dup=False, mid=0, proto_ver=4, properties=""): rl = 2+len(topic) pack_format = "H"+str(len(topic))+"s" if qos > 0: rl = rl + 2 pack_format = pack_format + "H" + + if proto_ver == 5: + properties = mqtt5_props.prop_finalise(properties) + rl += len(properties) + # This will break if len(properties) > 127 + pack_format = pack_format + "%ds"%(len(properties)) + if payload != None: rl = rl + len(payload) pack_format = pack_format + str(len(payload))+"s" @@ -370,40 +412,104 @@ def gen_publish(topic, qos, payload=None, retain=False, dup=False, mid=0): if dup: cmd = cmd + 8 - if qos > 0: - return struct.pack("!B" + str(len(rlpacked))+"s" + pack_format, cmd, rlpacked, len(topic), topic, mid, payload) + if proto_ver == 5: + if qos > 0: + return struct.pack("!B" + str(len(rlpacked))+"s" + pack_format, cmd, rlpacked, len(topic), topic, mid, properties, payload) + else: + return struct.pack("!B" + str(len(rlpacked))+"s" + pack_format, cmd, rlpacked, len(topic), topic, properties, payload) else: - return struct.pack("!B" + str(len(rlpacked))+"s" + pack_format, cmd, rlpacked, len(topic), topic, payload) + if qos > 0: + return struct.pack("!B" + str(len(rlpacked))+"s" + pack_format, cmd, rlpacked, len(topic), topic, mid, payload) + else: + return struct.pack("!B" + str(len(rlpacked))+"s" + pack_format, cmd, rlpacked, len(topic), topic, payload) -def gen_puback(mid): - return struct.pack('!BBH', 64, 2, mid) +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 -def gen_pubrec(mid): - return struct.pack('!BBH', 80, 2, mid) + 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_pubrel(mid, dup=False): +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=-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=-1, properties=None): if dup: cmd = 96+8+2 else: cmd = 96+2 - return struct.pack('!BBH', cmd, 2, mid) + return _gen_command_with_mid(cmd, mid, proto_ver, reason_code, properties) -def gen_pubcomp(mid): - return struct.pack('!BBH', 112, 2, mid) +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): - pack_format = "!BBHH"+str(len(topic))+"sB" - return struct.pack(pack_format, 130, 2+2+len(topic)+1, mid, len(topic), topic, qos) +def gen_subscribe(mid, topic, qos, proto_ver=4, properties=""): + if proto_ver == 5: + if properties == "": + pack_format = "!BBHBH"+str(len(topic))+"sB" + return struct.pack(pack_format, 130, 2+1+2+len(topic)+1, mid, 0, len(topic), topic, qos) + else: + properties = mqtt5_props.prop_finalise(properties) + pack_format = "!BBH"+str(len(properties))+"s"+"H"+str(len(topic))+"sB" + return struct.pack(pack_format, 130, 2+1+2+len(topic)+len(properties), mid, properties, len(topic), topic, qos) + else: + pack_format = "!BBHH"+str(len(topic))+"sB" + return struct.pack(pack_format, 130, 2+2+len(topic)+1, mid, len(topic), topic, qos) -def gen_suback(mid, qos): - return struct.pack('!BBHB', 144, 2+1, mid, qos) +def gen_suback(mid, qos, proto_ver=4): + if proto_ver == 5: + return struct.pack('!BBHBB', 144, 2+1+1, mid, 0, qos) + else: + return struct.pack('!BBHB', 144, 2+1, mid, qos) -def gen_unsubscribe(mid, topic): - pack_format = "!BBHH"+str(len(topic))+"s" - return struct.pack(pack_format, 162, 2+2+len(topic), mid, len(topic), topic) +def gen_unsubscribe(mid, topic, proto_ver=4): + if proto_ver == 5: + pack_format = "!BBHBH"+str(len(topic))+"s" + return struct.pack(pack_format, 162, 2+2+len(topic)+1, mid, 0, len(topic), topic) + else: + pack_format = "!BBHH"+str(len(topic))+"s" + return struct.pack(pack_format, 162, 2+2+len(topic), mid, len(topic), topic) -def gen_unsuback(mid): - return struct.pack('!BBH', 176, 2, mid) +def gen_unsubscribe_multiple(mid, topics, proto_ver=4): + packet = "" + remaining_length = 0 + for t in topics: + remaining_length += 2+len(t) + packet += struct.pack("!H"+str(len(t))+"s", len(t), t) + + if proto_ver == 5: + remaining_length += 2+1 + + return struct.pack("!BBHB", 162, remaining_length, mid, 0) + packet + else: + remaining_length += 2 + + return struct.pack("!BBH", 162, remaining_length, mid) + packet + +def gen_unsuback(mid, proto_ver=4, reason_code=0): + if proto_ver == 5: + 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) def gen_pingreq(): return struct.pack('!BB', 192, 0) @@ -411,8 +517,20 @@ def gen_pingreq(): def gen_pingresp(): return struct.pack('!BB', 208, 0) -def gen_disconnect(): - return struct.pack('!BB', 224, 0) +def gen_disconnect(reason_code=-1, proto_ver=4, 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('!BBB', 224, 1, reason_code) + elif properties == "": + return struct.pack('!BBBB', 224, 2, reason_code, 0) + else: + properties = mqtt5_props.prop_finalise(properties) + return struct.pack("!BBB", 224, 1+len(properties), reason_code) + properties + else: + return struct.pack('!BB', 224, 0) def pack_remaining_length(remaining_length): s = "" diff --git a/test/mqtt5_opts.py b/test/mqtt5_opts.py new file mode 100644 index 00000000..55675a73 --- /dev/null +++ b/test/mqtt5_opts.py @@ -0,0 +1,5 @@ +MQTT_SUB_OPT_NO_LOCAL = 0x04 +MQTT_SUB_OPT_RETAIN_AS_PUBLISHED = 0x08 +MQTT_SUB_OPT_SEND_RETAIN_ALWAYS = 0x00 +MQTT_SUB_OPT_SEND_RETAIN_NEW = 0x10 +MQTT_SUB_OPT_SEND_RETAIN_NEVER = 0x20 diff --git a/test/mqtt5_props.py b/test/mqtt5_props.py new file mode 100644 index 00000000..9f307324 --- /dev/null +++ b/test/mqtt5_props.py @@ -0,0 +1,70 @@ +import struct + +PROP_PAYLOAD_FORMAT_INDICATOR = 1 +PROP_MESSAGE_EXPIRY_INTERVAL = 2 +PROP_CONTENT_TYPE = 3 +PROP_RESPONSE_TOPIC = 8 +PROP_CORRELATION_DATA = 9 +PROP_SUBSCRIPTION_IDENTIFIER = 11 +PROP_SESSION_EXPIRY_INTERVAL = 17 +PROP_ASSIGNED_CLIENT_IDENTIFIER = 18 +PROP_SERVER_KEEP_ALIVE = 19 +PROP_AUTHENTICATION_METHOD = 21 +PROP_AUTHENTICATION_DATA = 22 +PROP_REQUEST_PROBLEM_INFO = 23 +PROP_WILL_DELAY_INTERVAL = 24 +PROP_REQUEST_RESPONSE_INFO = 25 +PROP_RESPONSE_INFO = 26 +PROP_SERVER_REFERENCE = 28 +PROP_REASON_STRING = 31 +PROP_RECEIVE_MAXIMUM = 33 +PROP_TOPIC_ALIAS_MAXIMUM = 34 +PROP_TOPIC_ALIAS = 35 +PROP_MAXIMUM_QOS = 36 +PROP_RETAIN_AVAILABLE = 37 +PROP_USER_PROPERTY = 38 +PROP_MAXIMUM_PACKET_SIZE = 39 +PROP_WILDCARD_SUB_AVAILABLE = 40 +PROP_SUBSCRIPTION_ID_AVAILABLE = 41 +PROP_SHARED_SUB_AVAILABLE = 42 + +def gen_byte_prop(identifier, byte): + prop = struct.pack('BB', identifier, byte) + return prop + +def gen_uint16_prop(identifier, word): + prop = struct.pack('!BH', identifier, word) + return prop + +def gen_uint32_prop(identifier, word): + prop = struct.pack('!BI', identifier, word) + return prop + +def gen_string_prop(identifier, s): + prop = struct.pack('!BH%ds'%(len(s)), identifier, len(s), s) + return prop + +def gen_string_pair_prop(identifier, s1, s2): + prop = struct.pack('!BH%dsH%ds'%(len(s1), len(s2)), identifier, len(s1), s1, len(s2), s2) + return prop + +def gen_varint_prop(identifier, val): + v = pack_varint(val) + return struct.pack("!B"+str(len(v))+"s", identifier, v) + +def pack_varint(varint): + s = "" + while True: + byte = varint % 128 + varint = varint // 128 + # If there are more digits to encode, set the top bit of this digit + if varint > 0: + byte = byte | 0x80 + + s = s + struct.pack("!B", byte) + if varint == 0: + return s + +def prop_finalise(props): + return pack_varint(len(props)) + props + diff --git a/test/ptest.py b/test/ptest.py new file mode 100755 index 00000000..233f609e --- /dev/null +++ b/test/ptest.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 + +import subprocess +import time +import sys + +def next_test(tests, ports): + if len(tests) == 0 or len(ports) == 0: + return + + test = tests.pop() + proc_ports = () + + if len(ports) < test[0]: + tests.insert(0, test) + return None + else: + if isinstance(test[1], (list,)): + args = test[1] + else: + args = [test[1]] + + for i in range(0, test[0]): + proc_port = ports.pop() + proc_ports = proc_ports + (proc_port,) + args.append(str(proc_port)) + + proc = subprocess.Popen(args) + proc.start_time = time.time() + proc.mosq_port = proc_ports + return proc + + +def run_tests(tests, minport=1888, max_running=20): + ports = list(range(minport, minport+max_running+1)) + start_time = time.time() + passed = 0 + failed = 0 + + failed_tests = [] + + running_tests = [] + while len(tests) > 0 or len(running_tests) > 0: + if len(running_tests) <= max_running: + t = next_test(tests, ports) + if t is None: + time.sleep(0.1) + else: + running_tests.append(t) + + for t in running_tests: + t.poll() + if t.returncode is not None: + running_tests.remove(t) + if isinstance(t.mosq_port, tuple): + for portret in t.mosq_port: + ports.append(portret) + else: + ports.append(t.mosq_port) + t.terminate() + t.wait() + runtime = time.time() - t.start_time + #(stdo, stde) = t.communicate() + if t.returncode == 1: + print("%0.3fs : \033[31m%s\033[0m" % (runtime, t.args[0])) + failed = failed + 1 + failed_tests.append(t.args[0]) + else: + passed = passed + 1 + print("%0.3fs : \033[32m%s\033[0m" % (runtime, t.args[0])) + + print("Passed: %d\nFailed: %d\nTotal: %d\nTotal time: %0.2f" % (passed, failed, passed+failed, time.time()-start_time)) + if failed > 0: + print("Failing tests:") + for f in failed_tests: + print(f) + sys.exit(1) + diff --git a/test/unit/Makefile b/test/unit/Makefile new file mode 100644 index 00000000..b627cbde --- /dev/null +++ b/test/unit/Makefile @@ -0,0 +1,53 @@ +include ../../config.mk + +.PHONY: all test clean coverage + +CFLAGS=-I../.. -I../../lib -coverage -Wall -ggdb +TEST_LDFLAGS=-lcunit -coverage + +TEST_OBJS = test.o \ + datatype_read.o \ + datatype_write.o \ + property_add.o \ + property_read.o \ + property_user_read.o \ + property_write.o \ + stubs.o \ + util_topic_test.o \ + utf8.o + +LIB_OBJS = memory_mosq.o \ + packet_datatypes.o \ + property_mosq.o \ + util_topic.o \ + utf8_mosq.o + +all : test + +mosq_test : ${TEST_OBJS} ${LIB_OBJS} + $(CROSS_COMPILE)$(CC) -o $@ $^ ${TEST_LDFLAGS} + +memory_mosq.o : ../../lib/memory_mosq.c + $(CROSS_COMPILE)$(CC) $(CFLAGS) -c -o $@ $^ + +packet_datatypes.o : ../../lib/packet_datatypes.c + $(CROSS_COMPILE)$(CC) $(CFLAGS) -c -o $@ $^ + +property_mosq.o : ../../lib/property_mosq.c + $(CROSS_COMPILE)$(CC) $(CFLAGS) -c -o $@ $^ + +util_topic.o : ../../lib/util_topic.c + $(CROSS_COMPILE)$(CC) $(CFLAGS) -c -o $@ $^ + +utf8_mosq.o : ../../lib/utf8_mosq.c + $(CROSS_COMPILE)$(CC) $(CFLAGS) -c -o $@ $^ + +test : mosq_test + ./mosq_test + +clean : + -rm -rf mosq_test *.o *.gcda *.gcno coverage.info out/ + +coverage : + lcov --capture --directory . --output-file coverage.info + genhtml coverage.info --output-directory out diff --git a/test/unit/datatype_read.c b/test/unit/datatype_read.c new file mode 100644 index 00000000..f0234bf4 --- /dev/null +++ b/test/unit/datatype_read.c @@ -0,0 +1,827 @@ +#include +#include + +#include "packet_mosq.h" + +static void byte_read_helper( + uint8_t *payload, + int remaining_length, + int rc_expected, + uint8_t value_expected) +{ + struct mosquitto__packet packet; + uint8_t value = 0; + int rc; + + memset(&packet, 0, sizeof(struct mosquitto__packet)); + packet.payload = payload; + packet.remaining_length = remaining_length; + rc = packet__read_byte(&packet, &value); + CU_ASSERT_EQUAL(rc, rc_expected); + CU_ASSERT_EQUAL(value, value_expected); +} + + +static void uint16_read_helper( + uint8_t *payload, + int remaining_length, + int rc_expected, + uint16_t value_expected) +{ + struct mosquitto__packet packet; + uint16_t value = 0; + int rc; + + memset(&packet, 0, sizeof(struct mosquitto__packet)); + packet.payload = payload; + packet.remaining_length = remaining_length; + rc = packet__read_uint16(&packet, &value); + CU_ASSERT_EQUAL(rc, rc_expected); + CU_ASSERT_EQUAL(value, value_expected); +} + + +static void uint32_read_helper( + uint8_t *payload, + int remaining_length, + int rc_expected, + uint32_t value_expected) +{ + struct mosquitto__packet packet; + uint32_t value = 0; + int rc; + + memset(&packet, 0, sizeof(struct mosquitto__packet)); + packet.payload = payload; + packet.remaining_length = remaining_length; + rc = packet__read_uint32(&packet, &value); + CU_ASSERT_EQUAL(rc, rc_expected); + CU_ASSERT_EQUAL(value, value_expected); +} + + +static void varint_read_helper( + uint8_t *payload, + int remaining_length, + int rc_expected, + int32_t value_expected, + int8_t bytes_expected) +{ + struct mosquitto__packet packet; + int32_t value = -1; + int8_t bytes = -1; + int rc; + + memset(&packet, 0, sizeof(struct mosquitto__packet)); + packet.payload = payload; + packet.remaining_length = remaining_length; + rc = packet__read_varint(&packet, &value, &bytes); + CU_ASSERT_EQUAL(rc, rc_expected); + CU_ASSERT_EQUAL(value, value_expected); + CU_ASSERT_EQUAL(bytes, bytes_expected); +} + + +static void binary_read_helper( + uint8_t *payload, + int remaining_length, + int rc_expected, + const uint8_t *value_expected, + int length_expected) +{ + struct mosquitto__packet packet; + uint8_t *value = NULL; + int length = -1; + int rc; + + memset(&packet, 0, sizeof(struct mosquitto__packet)); + packet.payload = payload; + packet.remaining_length = remaining_length; + rc = packet__read_binary(&packet, (uint8_t **)&value, &length); + CU_ASSERT_EQUAL(rc, rc_expected); + if(value_expected){ + /* FIXME - this should be a memcmp */ + CU_ASSERT_NSTRING_EQUAL(value, value_expected, length_expected); + }else{ + CU_ASSERT_EQUAL(value, NULL); + } + CU_ASSERT_EQUAL(length, length_expected); + free(value); +} + + +static void string_read_helper( + uint8_t *payload, + int remaining_length, + int rc_expected, + const uint8_t *value_expected, + int length_expected) +{ + struct mosquitto__packet packet; + uint8_t *value = NULL; + int length = -1; + int rc; + + memset(&packet, 0, sizeof(struct mosquitto__packet)); + packet.payload = payload; + packet.remaining_length = remaining_length; + rc = packet__read_string(&packet, (char **)&value, &length); + CU_ASSERT_EQUAL(rc, rc_expected); + if(value_expected){ + CU_ASSERT_NSTRING_EQUAL(value, value_expected, length_expected); + }else{ + CU_ASSERT_EQUAL(value, NULL); + } + CU_ASSERT_EQUAL(length, length_expected); + free(value); +} + + +static void bytes_read_helper( + uint8_t *payload, + int remaining_length, + int rc_expected, + const uint8_t *value_expected, + int count) +{ + struct mosquitto__packet packet; + uint8_t value[count]; + int rc; + int i; + + memset(&packet, 0, sizeof(struct mosquitto__packet)); + packet.payload = payload; + packet.remaining_length = remaining_length; + rc = packet__read_bytes(&packet, value, count); + CU_ASSERT_EQUAL(rc, rc_expected); + if(rc == MOSQ_ERR_SUCCESS){ + CU_ASSERT_EQUAL(packet.pos, count); + } + if(value_expected){ + for(i=0; i +#include + +#include + +#include "packet_mosq.h" + +/* ======================================================================== + * BYTE TESTS + * ======================================================================== */ + +/* This tests writing a Byte to an incoming packet. */ +static void TEST_byte_write(void) +{ + uint8_t payload[260]; + struct mosquitto__packet packet; + int i; + + memset(&packet, 0, sizeof(struct mosquitto__packet)); + memset(payload, 0, sizeof(payload)); + packet.payload = payload; + packet.packet_length = 256; + + for(i=0; i<256; i++){ + packet__write_byte(&packet, 255-i); + } + + CU_ASSERT_EQUAL(packet.pos, 256); + for(i=0; i<256; i++){ + CU_ASSERT_EQUAL(payload[i], 255-i); + } +} + + +/* ======================================================================== + * TWO BYTE INTEGER TESTS + * ======================================================================== */ + +/* This tests writing a Two Byte Integer to an incoming packet. */ +static void TEST_uint16_write(void) +{ + uint8_t payload[650]; + uint16_t *payload16; + struct mosquitto__packet packet; + int i; + + memset(&packet, 0, sizeof(struct mosquitto__packet)); + memset(payload, 0, sizeof(payload)); + packet.payload = payload; + packet.packet_length = 650; + + for(i=0; i<325; i++){ + packet__write_uint16(&packet, 100*i); + } + + CU_ASSERT_EQUAL(packet.pos, 650); + payload16 = (uint16_t *)payload; + for(i=0; i<325; i++){ + CU_ASSERT_EQUAL(payload16[i], htons(100*i)); + } +} + + +/* ======================================================================== + * FOUR BYTE INTEGER TESTS + * ======================================================================== */ + +/* This tests writing a Four Byte Integer to an incoming packet. */ +static void TEST_uint32_write(void) +{ + uint8_t *payload; + uint32_t *payload32; + struct mosquitto__packet packet; + int i; + + payload = calloc(42000, sizeof(uint32_t)); + if(!payload){ + CU_FAIL_FATAL("Out of memory"); + } + + memset(&packet, 0, sizeof(struct mosquitto__packet)); + packet.payload = payload; + packet.packet_length = 42000; + + for(i=0; i<10500; i++){ + packet__write_uint32(&packet, 1000*i); + } + + CU_ASSERT_EQUAL(packet.pos, 42000); + payload32 = (uint32_t *)payload; + for(i=0; i<10500; i++){ + CU_ASSERT_EQUAL(payload32[i], htonl(1000*i)); + } + free(payload); +} + + +/* ======================================================================== + * UTF-8 STRING TESTS + * ======================================================================== */ + +/* This tests writing a UTF-8 String to an incoming packet. */ +static void TEST_string_write(void) +{ + uint8_t payload[100]; + struct mosquitto__packet packet; + + memset(&packet, 0, sizeof(struct mosquitto__packet)); + memset(payload, 0, 100); + + packet.payload = payload; + packet.packet_length = 100; + + packet__write_string(&packet, "first test", strlen("first test")); + packet__write_string(&packet, "second test", strlen("second test")); + + CU_ASSERT_EQUAL(packet.pos, 2+10+2+11); + CU_ASSERT_EQUAL(payload[0], 0); + CU_ASSERT_EQUAL(payload[1], 10); + CU_ASSERT_NSTRING_EQUAL(payload+2, "first test", 10); + CU_ASSERT_EQUAL(payload[2+10+0], 0); + CU_ASSERT_EQUAL(payload[2+10+1], 11); + CU_ASSERT_NSTRING_EQUAL(payload+2+10+2, "second test", 11); +} + + +/* ======================================================================== + * TEST SUITE SETUP + * ======================================================================== */ + +int init_datatype_write_tests(void) +{ + CU_pSuite test_suite = NULL; + + test_suite = CU_add_suite("Datatype write", NULL, NULL); + if(!test_suite){ + printf("Error adding CUnit test suite.\n"); + return 1; + } + + if(0 + || !CU_add_test(test_suite, "Byte write", TEST_byte_write) + || !CU_add_test(test_suite, "Two Byte Integer write", TEST_uint16_write) + || !CU_add_test(test_suite, "Four Byte Integer write", TEST_uint32_write) + || !CU_add_test(test_suite, "UTF-8 String write", TEST_string_write) + ){ + + printf("Error adding Datatype write CUnit tests.\n"); + return 1; + } + + return 0; +} diff --git a/test/unit/property_add.c b/test/unit/property_add.c new file mode 100644 index 00000000..341c0080 --- /dev/null +++ b/test/unit/property_add.c @@ -0,0 +1,614 @@ +#include +#include + +#include "mqtt_protocol.h" +#include "property_mosq.h" +#include "packet_mosq.h" + +/* ======================================================================== + * BAD IDENTIFIER + * ======================================================================== */ + +static void bad_add_byte_helper(int identifier) +{ + mosquitto_property *proplist = NULL; + int rc; + + rc = mosquitto_property_add_byte(&proplist, identifier, 1); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); + CU_ASSERT_PTR_NULL(proplist); +} + +static void bad_add_int16_helper(int identifier) +{ + mosquitto_property *proplist = NULL; + int rc; + + rc = mosquitto_property_add_int16(&proplist, identifier, 1); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); + CU_ASSERT_PTR_NULL(proplist); +} + +static void bad_add_int32_helper(int identifier) +{ + mosquitto_property *proplist = NULL; + int rc; + + rc = mosquitto_property_add_int32(&proplist, identifier, 1); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); + CU_ASSERT_PTR_NULL(proplist); +} + +static void bad_add_varint_helper(int identifier) +{ + mosquitto_property *proplist = NULL; + int rc; + + rc = mosquitto_property_add_varint(&proplist, identifier, 1); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); + CU_ASSERT_PTR_NULL(proplist); +} + +static void bad_add_binary_helper(int identifier) +{ + mosquitto_property *proplist = NULL; + int rc; + + rc = mosquitto_property_add_binary(&proplist, identifier, "test", 4); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); + CU_ASSERT_PTR_NULL(proplist); +} + +static void bad_add_string_helper(int identifier) +{ + mosquitto_property *proplist = NULL; + int rc; + + rc = mosquitto_property_add_string(&proplist, identifier, "test"); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); + CU_ASSERT_PTR_NULL(proplist); +} + +static void bad_add_string_pair_helper(int identifier) +{ + mosquitto_property *proplist = NULL; + int rc; + + rc = mosquitto_property_add_string_pair(&proplist, identifier, "key", "value"); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); + CU_ASSERT_PTR_NULL(proplist); +} + +static void TEST_add_bad_byte(void) +{ + bad_add_byte_helper(MQTT_PROP_MESSAGE_EXPIRY_INTERVAL); + bad_add_byte_helper(MQTT_PROP_CONTENT_TYPE); + bad_add_byte_helper(MQTT_PROP_RESPONSE_TOPIC); + bad_add_byte_helper(MQTT_PROP_CORRELATION_DATA); + bad_add_byte_helper(MQTT_PROP_SUBSCRIPTION_IDENTIFIER); + bad_add_byte_helper(MQTT_PROP_SESSION_EXPIRY_INTERVAL); + bad_add_byte_helper(MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER); + bad_add_byte_helper(MQTT_PROP_SERVER_KEEP_ALIVE); + bad_add_byte_helper(MQTT_PROP_AUTHENTICATION_METHOD); + bad_add_byte_helper(MQTT_PROP_AUTHENTICATION_DATA); + bad_add_byte_helper(MQTT_PROP_WILL_DELAY_INTERVAL); + bad_add_byte_helper(MQTT_PROP_RESPONSE_INFORMATION); + bad_add_byte_helper(MQTT_PROP_SERVER_REFERENCE); + bad_add_byte_helper(MQTT_PROP_REASON_STRING); + bad_add_byte_helper(MQTT_PROP_RECEIVE_MAXIMUM); + bad_add_byte_helper(MQTT_PROP_TOPIC_ALIAS_MAXIMUM); + bad_add_byte_helper(MQTT_PROP_TOPIC_ALIAS); + bad_add_byte_helper(MQTT_PROP_USER_PROPERTY); + bad_add_byte_helper(MQTT_PROP_MAXIMUM_PACKET_SIZE); +} + +static void TEST_add_bad_int16(void) +{ + bad_add_int16_helper(MQTT_PROP_PAYLOAD_FORMAT_INDICATOR); + bad_add_int16_helper(MQTT_PROP_MESSAGE_EXPIRY_INTERVAL); + bad_add_int16_helper(MQTT_PROP_CONTENT_TYPE); + bad_add_int16_helper(MQTT_PROP_RESPONSE_TOPIC); + bad_add_int16_helper(MQTT_PROP_CORRELATION_DATA); + bad_add_int16_helper(MQTT_PROP_SUBSCRIPTION_IDENTIFIER); + bad_add_int16_helper(MQTT_PROP_SESSION_EXPIRY_INTERVAL); + bad_add_int16_helper(MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER); + bad_add_int16_helper(MQTT_PROP_AUTHENTICATION_METHOD); + bad_add_int16_helper(MQTT_PROP_AUTHENTICATION_DATA); + bad_add_int16_helper(MQTT_PROP_REQUEST_PROBLEM_INFORMATION); + bad_add_int16_helper(MQTT_PROP_WILL_DELAY_INTERVAL); + bad_add_int16_helper(MQTT_PROP_REQUEST_RESPONSE_INFORMATION); + bad_add_int16_helper(MQTT_PROP_RESPONSE_INFORMATION); + bad_add_int16_helper(MQTT_PROP_SERVER_REFERENCE); + bad_add_int16_helper(MQTT_PROP_REASON_STRING); + bad_add_int16_helper(MQTT_PROP_MAXIMUM_QOS); + bad_add_int16_helper(MQTT_PROP_RETAIN_AVAILABLE); + bad_add_int16_helper(MQTT_PROP_USER_PROPERTY); + bad_add_int16_helper(MQTT_PROP_MAXIMUM_PACKET_SIZE); + bad_add_int16_helper(MQTT_PROP_WILDCARD_SUB_AVAILABLE); + bad_add_int16_helper(MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE); + bad_add_int16_helper(MQTT_PROP_SHARED_SUB_AVAILABLE); +} + +static void TEST_add_bad_int32(void) +{ + bad_add_int32_helper(MQTT_PROP_PAYLOAD_FORMAT_INDICATOR); + bad_add_int32_helper(MQTT_PROP_CONTENT_TYPE); + bad_add_int32_helper(MQTT_PROP_RESPONSE_TOPIC); + bad_add_int32_helper(MQTT_PROP_CORRELATION_DATA); + bad_add_int32_helper(MQTT_PROP_SUBSCRIPTION_IDENTIFIER); + bad_add_int32_helper(MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER); + bad_add_int32_helper(MQTT_PROP_SERVER_KEEP_ALIVE); + bad_add_int32_helper(MQTT_PROP_AUTHENTICATION_METHOD); + bad_add_int32_helper(MQTT_PROP_AUTHENTICATION_DATA); + bad_add_int32_helper(MQTT_PROP_REQUEST_PROBLEM_INFORMATION); + bad_add_int32_helper(MQTT_PROP_REQUEST_RESPONSE_INFORMATION); + bad_add_int32_helper(MQTT_PROP_RESPONSE_INFORMATION); + bad_add_int32_helper(MQTT_PROP_SERVER_REFERENCE); + bad_add_int32_helper(MQTT_PROP_REASON_STRING); + bad_add_int32_helper(MQTT_PROP_RECEIVE_MAXIMUM); + bad_add_int32_helper(MQTT_PROP_TOPIC_ALIAS_MAXIMUM); + bad_add_int32_helper(MQTT_PROP_TOPIC_ALIAS); + bad_add_int32_helper(MQTT_PROP_MAXIMUM_QOS); + bad_add_int32_helper(MQTT_PROP_RETAIN_AVAILABLE); + bad_add_int32_helper(MQTT_PROP_USER_PROPERTY); + bad_add_int32_helper(MQTT_PROP_WILDCARD_SUB_AVAILABLE); + bad_add_int32_helper(MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE); + bad_add_int32_helper(MQTT_PROP_SHARED_SUB_AVAILABLE); +} + +static void TEST_add_bad_varint(void) +{ + bad_add_varint_helper(MQTT_PROP_PAYLOAD_FORMAT_INDICATOR); + bad_add_varint_helper(MQTT_PROP_MESSAGE_EXPIRY_INTERVAL); + bad_add_varint_helper(MQTT_PROP_CONTENT_TYPE); + bad_add_varint_helper(MQTT_PROP_RESPONSE_TOPIC); + bad_add_varint_helper(MQTT_PROP_CORRELATION_DATA); + bad_add_varint_helper(MQTT_PROP_SESSION_EXPIRY_INTERVAL); + bad_add_varint_helper(MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER); + bad_add_varint_helper(MQTT_PROP_SERVER_KEEP_ALIVE); + bad_add_varint_helper(MQTT_PROP_AUTHENTICATION_METHOD); + bad_add_varint_helper(MQTT_PROP_AUTHENTICATION_DATA); + bad_add_varint_helper(MQTT_PROP_REQUEST_PROBLEM_INFORMATION); + bad_add_varint_helper(MQTT_PROP_WILL_DELAY_INTERVAL); + bad_add_varint_helper(MQTT_PROP_REQUEST_RESPONSE_INFORMATION); + bad_add_varint_helper(MQTT_PROP_RESPONSE_INFORMATION); + bad_add_varint_helper(MQTT_PROP_SERVER_REFERENCE); + bad_add_varint_helper(MQTT_PROP_REASON_STRING); + bad_add_varint_helper(MQTT_PROP_RECEIVE_MAXIMUM); + bad_add_varint_helper(MQTT_PROP_TOPIC_ALIAS_MAXIMUM); + bad_add_varint_helper(MQTT_PROP_TOPIC_ALIAS); + bad_add_varint_helper(MQTT_PROP_MAXIMUM_QOS); + bad_add_varint_helper(MQTT_PROP_RETAIN_AVAILABLE); + bad_add_varint_helper(MQTT_PROP_USER_PROPERTY); + bad_add_varint_helper(MQTT_PROP_MAXIMUM_PACKET_SIZE); + bad_add_varint_helper(MQTT_PROP_WILDCARD_SUB_AVAILABLE); + bad_add_varint_helper(MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE); + bad_add_varint_helper(MQTT_PROP_SHARED_SUB_AVAILABLE); +} + +static void TEST_add_bad_binary(void) +{ + bad_add_binary_helper(MQTT_PROP_PAYLOAD_FORMAT_INDICATOR); + bad_add_binary_helper(MQTT_PROP_MESSAGE_EXPIRY_INTERVAL); + bad_add_binary_helper(MQTT_PROP_CONTENT_TYPE); + bad_add_binary_helper(MQTT_PROP_RESPONSE_TOPIC); + bad_add_binary_helper(MQTT_PROP_SUBSCRIPTION_IDENTIFIER); + bad_add_binary_helper(MQTT_PROP_SESSION_EXPIRY_INTERVAL); + bad_add_binary_helper(MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER); + bad_add_binary_helper(MQTT_PROP_SERVER_KEEP_ALIVE); + bad_add_binary_helper(MQTT_PROP_AUTHENTICATION_METHOD); + bad_add_binary_helper(MQTT_PROP_REQUEST_PROBLEM_INFORMATION); + bad_add_binary_helper(MQTT_PROP_WILL_DELAY_INTERVAL); + bad_add_binary_helper(MQTT_PROP_REQUEST_RESPONSE_INFORMATION); + bad_add_binary_helper(MQTT_PROP_RESPONSE_INFORMATION); + bad_add_binary_helper(MQTT_PROP_SERVER_REFERENCE); + bad_add_binary_helper(MQTT_PROP_REASON_STRING); + bad_add_binary_helper(MQTT_PROP_RECEIVE_MAXIMUM); + bad_add_binary_helper(MQTT_PROP_TOPIC_ALIAS_MAXIMUM); + bad_add_binary_helper(MQTT_PROP_TOPIC_ALIAS); + bad_add_binary_helper(MQTT_PROP_MAXIMUM_QOS); + bad_add_binary_helper(MQTT_PROP_RETAIN_AVAILABLE); + bad_add_binary_helper(MQTT_PROP_USER_PROPERTY); + bad_add_binary_helper(MQTT_PROP_MAXIMUM_PACKET_SIZE); + bad_add_binary_helper(MQTT_PROP_WILDCARD_SUB_AVAILABLE); + bad_add_binary_helper(MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE); + bad_add_binary_helper(MQTT_PROP_SHARED_SUB_AVAILABLE); +} + +static void TEST_add_bad_string(void) +{ + bad_add_string_helper(MQTT_PROP_PAYLOAD_FORMAT_INDICATOR); + bad_add_string_helper(MQTT_PROP_MESSAGE_EXPIRY_INTERVAL); + bad_add_string_helper(MQTT_PROP_CORRELATION_DATA); + bad_add_string_helper(MQTT_PROP_SUBSCRIPTION_IDENTIFIER); + bad_add_string_helper(MQTT_PROP_SESSION_EXPIRY_INTERVAL); + bad_add_string_helper(MQTT_PROP_SERVER_KEEP_ALIVE); + bad_add_string_helper(MQTT_PROP_AUTHENTICATION_DATA); + bad_add_string_helper(MQTT_PROP_REQUEST_PROBLEM_INFORMATION); + bad_add_string_helper(MQTT_PROP_WILL_DELAY_INTERVAL); + bad_add_string_helper(MQTT_PROP_REQUEST_RESPONSE_INFORMATION); + bad_add_string_helper(MQTT_PROP_RECEIVE_MAXIMUM); + bad_add_string_helper(MQTT_PROP_TOPIC_ALIAS_MAXIMUM); + bad_add_string_helper(MQTT_PROP_TOPIC_ALIAS); + bad_add_string_helper(MQTT_PROP_MAXIMUM_QOS); + bad_add_string_helper(MQTT_PROP_RETAIN_AVAILABLE); + bad_add_string_helper(MQTT_PROP_USER_PROPERTY); + bad_add_string_helper(MQTT_PROP_MAXIMUM_PACKET_SIZE); + bad_add_string_helper(MQTT_PROP_WILDCARD_SUB_AVAILABLE); + bad_add_string_helper(MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE); + bad_add_string_helper(MQTT_PROP_SHARED_SUB_AVAILABLE); +} + +static void TEST_add_bad_string_pair(void) +{ + bad_add_string_pair_helper(MQTT_PROP_PAYLOAD_FORMAT_INDICATOR); + bad_add_string_pair_helper(MQTT_PROP_MESSAGE_EXPIRY_INTERVAL); + bad_add_string_pair_helper(MQTT_PROP_CONTENT_TYPE); + bad_add_string_pair_helper(MQTT_PROP_RESPONSE_TOPIC); + bad_add_string_pair_helper(MQTT_PROP_CORRELATION_DATA); + bad_add_string_pair_helper(MQTT_PROP_SUBSCRIPTION_IDENTIFIER); + bad_add_string_pair_helper(MQTT_PROP_SESSION_EXPIRY_INTERVAL); + bad_add_string_pair_helper(MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER); + bad_add_string_pair_helper(MQTT_PROP_SERVER_KEEP_ALIVE); + bad_add_string_pair_helper(MQTT_PROP_AUTHENTICATION_METHOD); + bad_add_string_pair_helper(MQTT_PROP_AUTHENTICATION_DATA); + bad_add_string_pair_helper(MQTT_PROP_REQUEST_PROBLEM_INFORMATION); + bad_add_string_pair_helper(MQTT_PROP_WILL_DELAY_INTERVAL); + bad_add_string_pair_helper(MQTT_PROP_REQUEST_RESPONSE_INFORMATION); + bad_add_string_pair_helper(MQTT_PROP_RESPONSE_INFORMATION); + bad_add_string_pair_helper(MQTT_PROP_SERVER_REFERENCE); + bad_add_string_pair_helper(MQTT_PROP_REASON_STRING); + bad_add_string_pair_helper(MQTT_PROP_RECEIVE_MAXIMUM); + bad_add_string_pair_helper(MQTT_PROP_TOPIC_ALIAS_MAXIMUM); + bad_add_string_pair_helper(MQTT_PROP_TOPIC_ALIAS); + bad_add_string_pair_helper(MQTT_PROP_MAXIMUM_QOS); + bad_add_string_pair_helper(MQTT_PROP_RETAIN_AVAILABLE); + bad_add_string_pair_helper(MQTT_PROP_MAXIMUM_PACKET_SIZE); + bad_add_string_pair_helper(MQTT_PROP_WILDCARD_SUB_AVAILABLE); + bad_add_string_pair_helper(MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE); + bad_add_string_pair_helper(MQTT_PROP_SHARED_SUB_AVAILABLE); +} + +/* ======================================================================== + * SINGLE ADD + * ======================================================================== */ + +static void single_add_byte_helper(int identifier) +{ + mosquitto_property *proplist = NULL; + int rc; + + rc = mosquitto_property_add_byte(&proplist, identifier, 1); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_NOT_NULL(proplist); + if(proplist){ + CU_ASSERT_EQUAL(proplist->identifier, identifier); + CU_ASSERT_EQUAL(proplist->value.i8, 1); + CU_ASSERT_PTR_NULL(proplist->next); + + mosquitto_property_free_all(&proplist); + } +} + +static void single_add_int16_helper(int identifier) +{ + mosquitto_property *proplist = NULL; + int rc; + + rc = mosquitto_property_add_int16(&proplist, identifier, 11234); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_NOT_NULL(proplist); + if(proplist){ + CU_ASSERT_EQUAL(proplist->identifier, identifier); + CU_ASSERT_EQUAL(proplist->value.i16, 11234); + CU_ASSERT_PTR_NULL(proplist->next); + + mosquitto_property_free_all(&proplist); + } +} + +static void single_add_int32_helper(int identifier) +{ + mosquitto_property *proplist = NULL; + int rc; + + rc = mosquitto_property_add_int32(&proplist, identifier, 765432); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_NOT_NULL(proplist); + if(proplist){ + CU_ASSERT_EQUAL(proplist->identifier, identifier); + CU_ASSERT_EQUAL(proplist->value.i32, 765432); + CU_ASSERT_PTR_NULL(proplist->next); + + mosquitto_property_free_all(&proplist); + } +} + +static void single_add_varint_helper(int identifier) +{ + mosquitto_property *proplist = NULL; + int rc; + + rc = mosquitto_property_add_varint(&proplist, identifier, 139123999); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_NOT_NULL(proplist); + if(proplist){ + CU_ASSERT_EQUAL(proplist->identifier, identifier); + CU_ASSERT_EQUAL(proplist->value.varint, 139123999); + CU_ASSERT_PTR_NULL(proplist->next); + + mosquitto_property_free_all(&proplist); + } +} + +static void single_add_binary_helper(int identifier) +{ + mosquitto_property *proplist = NULL; + int rc; + + rc = mosquitto_property_add_binary(&proplist, identifier, "test", 4); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_NOT_NULL(proplist); + if(proplist){ + CU_ASSERT_EQUAL(proplist->identifier, identifier); + CU_ASSERT_EQUAL(proplist->value.bin.len, 4); + CU_ASSERT_NSTRING_EQUAL(proplist->value.bin.v, "test", 4); + CU_ASSERT_PTR_NULL(proplist->next); + + mosquitto_property_free_all(&proplist); + } +} + +static void single_add_string_helper(int identifier) +{ + mosquitto_property *proplist = NULL; + int rc; + + rc = mosquitto_property_add_string(&proplist, identifier, "string"); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_NOT_NULL(proplist); + if(proplist){ + CU_ASSERT_EQUAL(proplist->identifier, identifier); + CU_ASSERT_STRING_EQUAL(proplist->value.s.v, "string"); + CU_ASSERT_EQUAL(proplist->value.s.len, strlen("string")); + CU_ASSERT_PTR_NULL(proplist->next); + + mosquitto_property_free_all(&proplist); + } +} + +static void single_add_string_pair_helper(int identifier) +{ + mosquitto_property *proplist = NULL; + int rc; + + rc = mosquitto_property_add_string_pair(&proplist, identifier, "key", "value"); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_NOT_NULL(proplist); + if(proplist){ + CU_ASSERT_EQUAL(proplist->identifier, identifier); + CU_ASSERT_STRING_EQUAL(proplist->name.v, "key"); + CU_ASSERT_EQUAL(proplist->name.len, strlen("key")); + CU_ASSERT_STRING_EQUAL(proplist->value.s.v, "value"); + CU_ASSERT_EQUAL(proplist->value.s.len, strlen("value")); + CU_ASSERT_PTR_NULL(proplist->next); + + mosquitto_property_free_all(&proplist); + } +} + +static void TEST_add_single_byte(void) +{ + single_add_byte_helper(MQTT_PROP_PAYLOAD_FORMAT_INDICATOR); + single_add_byte_helper(MQTT_PROP_REQUEST_PROBLEM_INFORMATION); + single_add_byte_helper(MQTT_PROP_REQUEST_RESPONSE_INFORMATION); + single_add_byte_helper(MQTT_PROP_MAXIMUM_QOS); + single_add_byte_helper(MQTT_PROP_RETAIN_AVAILABLE); + single_add_byte_helper(MQTT_PROP_WILDCARD_SUB_AVAILABLE); + single_add_byte_helper(MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE); + single_add_byte_helper(MQTT_PROP_SHARED_SUB_AVAILABLE); +} + +static void TEST_add_single_int16(void) +{ + single_add_int16_helper(MQTT_PROP_SERVER_KEEP_ALIVE); + single_add_int16_helper(MQTT_PROP_RECEIVE_MAXIMUM); + single_add_int16_helper(MQTT_PROP_TOPIC_ALIAS_MAXIMUM); + single_add_int16_helper(MQTT_PROP_TOPIC_ALIAS); +} + +static void TEST_add_single_int32(void) +{ + single_add_int32_helper(MQTT_PROP_MESSAGE_EXPIRY_INTERVAL); + single_add_int32_helper(MQTT_PROP_SESSION_EXPIRY_INTERVAL); + single_add_int32_helper(MQTT_PROP_WILL_DELAY_INTERVAL); + single_add_int32_helper(MQTT_PROP_MAXIMUM_PACKET_SIZE); +} + +static void TEST_add_single_varint(void) +{ + single_add_varint_helper(MQTT_PROP_SUBSCRIPTION_IDENTIFIER); +} + +static void TEST_add_single_binary(void) +{ + single_add_binary_helper(MQTT_PROP_CORRELATION_DATA); + single_add_binary_helper(MQTT_PROP_AUTHENTICATION_DATA); +} + +static void TEST_add_single_string(void) +{ + single_add_string_helper(MQTT_PROP_CONTENT_TYPE); + single_add_string_helper(MQTT_PROP_RESPONSE_TOPIC); + single_add_string_helper(MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER); + single_add_string_helper(MQTT_PROP_AUTHENTICATION_METHOD); + single_add_string_helper(MQTT_PROP_RESPONSE_INFORMATION); + single_add_string_helper(MQTT_PROP_SERVER_REFERENCE); + single_add_string_helper(MQTT_PROP_REASON_STRING); +} + +static void TEST_add_single_string_pair(void) +{ + single_add_string_pair_helper(MQTT_PROP_USER_PROPERTY); +} + +/* ======================================================================== + * ADD ALL PROPERTIES FOR A COMMAND + * ======================================================================== */ + +static void TEST_add_all_connect(void) +{ + mosquitto_property *proplist = NULL; + mosquitto_property *p; + int count; + int rc; + + rc = mosquitto_property_add_int32(&proplist, MQTT_PROP_SESSION_EXPIRY_INTERVAL, 86400); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_NOT_NULL(proplist); + rc = mosquitto_property_add_string(&proplist, MQTT_PROP_AUTHENTICATION_METHOD, "basic"); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_NOT_NULL(proplist); + rc = mosquitto_property_add_binary(&proplist, MQTT_PROP_AUTHENTICATION_DATA, "password", strlen("password")); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_NOT_NULL(proplist); + rc = mosquitto_property_add_byte(&proplist, MQTT_PROP_REQUEST_PROBLEM_INFORMATION, 1); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_NOT_NULL(proplist); + rc = mosquitto_property_add_byte(&proplist, MQTT_PROP_REQUEST_RESPONSE_INFORMATION, 1); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_NOT_NULL(proplist); + rc = mosquitto_property_add_int16(&proplist, MQTT_PROP_RECEIVE_MAXIMUM, 1024); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_NOT_NULL(proplist); + rc = mosquitto_property_add_int16(&proplist, MQTT_PROP_TOPIC_ALIAS_MAXIMUM, 64); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_NOT_NULL(proplist); + rc = mosquitto_property_add_string_pair(&proplist, MQTT_PROP_USER_PROPERTY, "user-agent", "mosquitto/test"); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_NOT_NULL(proplist); + rc = mosquitto_property_add_int32(&proplist, MQTT_PROP_MAXIMUM_PACKET_SIZE, 200000000); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_NOT_NULL(proplist); + + p = proplist; + count = 0; + while(p){ + count++; + p = p->next; + } + CU_ASSERT_EQUAL(count, 9); + + mosquitto_property_free_all(&proplist); +} + + +static void TEST_add_all_connack(void) +{ + mosquitto_property *proplist = NULL; + mosquitto_property *p; + int count; + int rc; + + rc = mosquitto_property_add_int32(&proplist, MQTT_PROP_SESSION_EXPIRY_INTERVAL, 86400); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_NOT_NULL(proplist); + rc = mosquitto_property_add_string(&proplist, MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER, "clientid"); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_NOT_NULL(proplist); + rc = mosquitto_property_add_int16(&proplist, MQTT_PROP_SERVER_KEEP_ALIVE, 900); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_NOT_NULL(proplist); + rc = mosquitto_property_add_string(&proplist, MQTT_PROP_AUTHENTICATION_METHOD, "basic"); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_NOT_NULL(proplist); + rc = mosquitto_property_add_binary(&proplist, MQTT_PROP_AUTHENTICATION_DATA, "password", strlen("password")); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_NOT_NULL(proplist); + rc = mosquitto_property_add_string(&proplist, MQTT_PROP_RESPONSE_INFORMATION, "response"); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_NOT_NULL(proplist); + rc = mosquitto_property_add_string(&proplist, MQTT_PROP_SERVER_REFERENCE, "localhost"); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_NOT_NULL(proplist); + rc = mosquitto_property_add_string(&proplist, MQTT_PROP_REASON_STRING, "reason"); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_NOT_NULL(proplist); + rc = mosquitto_property_add_int16(&proplist, MQTT_PROP_RECEIVE_MAXIMUM, 1024); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_NOT_NULL(proplist); + rc = mosquitto_property_add_int16(&proplist, MQTT_PROP_TOPIC_ALIAS_MAXIMUM, 64); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_NOT_NULL(proplist); + rc = mosquitto_property_add_byte(&proplist, MQTT_PROP_MAXIMUM_QOS, 1); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_NOT_NULL(proplist); + rc = mosquitto_property_add_byte(&proplist, MQTT_PROP_RETAIN_AVAILABLE, 0); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_NOT_NULL(proplist); + rc = mosquitto_property_add_string_pair(&proplist, MQTT_PROP_USER_PROPERTY, "user-agent", "mosquitto/test"); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_NOT_NULL(proplist); + rc = mosquitto_property_add_int32(&proplist, MQTT_PROP_MAXIMUM_PACKET_SIZE, 200000000); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_NOT_NULL(proplist); + rc = mosquitto_property_add_byte(&proplist, MQTT_PROP_WILDCARD_SUB_AVAILABLE, 0); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_NOT_NULL(proplist); + rc = mosquitto_property_add_byte(&proplist, MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE, 0); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_NOT_NULL(proplist); + rc = mosquitto_property_add_byte(&proplist, MQTT_PROP_SHARED_SUB_AVAILABLE, 0); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_NOT_NULL(proplist); + + p = proplist; + count = 0; + while(p){ + count++; + p = p->next; + } + CU_ASSERT_EQUAL(count, 17); + + mosquitto_property_free_all(&proplist); +} + +/* ======================================================================== + * TEST SUITE SETUP + * ======================================================================== */ + +int init_property_add_tests(void) +{ + CU_pSuite test_suite = NULL; + + test_suite = CU_add_suite("Property add", NULL, NULL); + if(!test_suite){ + printf("Error adding CUnit Property add test suite.\n"); + return 1; + } + + if(0 + || !CU_add_test(test_suite, "Add bad byte", TEST_add_bad_byte) + || !CU_add_test(test_suite, "Add bad int16", TEST_add_bad_int16) + || !CU_add_test(test_suite, "Add bad int32", TEST_add_bad_int32) + || !CU_add_test(test_suite, "Add bad varint", TEST_add_bad_varint) + || !CU_add_test(test_suite, "Add bad binary", TEST_add_bad_binary) + || !CU_add_test(test_suite, "Add bad string", TEST_add_bad_string) + || !CU_add_test(test_suite, "Add bad string pair", TEST_add_bad_string_pair) + || !CU_add_test(test_suite, "Add single byte", TEST_add_single_byte) + || !CU_add_test(test_suite, "Add single int16", TEST_add_single_int16) + || !CU_add_test(test_suite, "Add single int32", TEST_add_single_int32) + || !CU_add_test(test_suite, "Add single varint", TEST_add_single_varint) + || !CU_add_test(test_suite, "Add single binary", TEST_add_single_binary) + || !CU_add_test(test_suite, "Add single string", TEST_add_single_string) + || !CU_add_test(test_suite, "Add single string pair", TEST_add_single_string_pair) + || !CU_add_test(test_suite, "Add all CONNECT", TEST_add_all_connect) + || !CU_add_test(test_suite, "Add all CONNACK", TEST_add_all_connack) + ){ + + printf("Error adding Property Add CUnit tests.\n"); + return 1; + } + + return 0; +} diff --git a/test/unit/property_read.c b/test/unit/property_read.c new file mode 100644 index 00000000..5982ae32 --- /dev/null +++ b/test/unit/property_read.c @@ -0,0 +1,1783 @@ +#include +#include + +#include "mqtt_protocol.h" +#include "property_mosq.h" +#include "packet_mosq.h" + +static void byte_prop_read_helper( + int command, + uint8_t *payload, + int remaining_length, + int rc_expected, + int identifier, + uint8_t value_expected) +{ + struct mosquitto__packet packet; + mosquitto_property *properties; + int rc; + + memset(&packet, 0, sizeof(struct mosquitto__packet)); + packet.payload = payload; + packet.remaining_length = remaining_length; + rc = property__read_all(command, &packet, &properties); + + CU_ASSERT_EQUAL(rc, rc_expected); + CU_ASSERT_EQUAL(packet.pos, remaining_length); + if(properties){ + CU_ASSERT_EQUAL(properties->identifier, identifier); + CU_ASSERT_EQUAL(properties->value.i8, value_expected); + CU_ASSERT_PTR_EQUAL(properties->next, NULL); + CU_ASSERT_EQUAL(property__get_length_all(properties), 2); + mosquitto_property_free_all(&properties); + } + CU_ASSERT_PTR_EQUAL(properties, NULL); +} + +static void duplicate_byte_helper(int command, int identifier) +{ + uint8_t payload[20]; + + memset(&payload, 0, sizeof(payload)); + payload[0] = 4; /* Proplen = (Identifier + byte)*2 */ + payload[1] = identifier; + payload[2] = 1; + payload[3] = identifier; + payload[4] = 0; + + byte_prop_read_helper(command, payload, 5, MOSQ_ERR_DUPLICATE_PROPERTY, identifier, 1); +} + +static void bad_byte_helper(int command, int identifier) +{ + uint8_t payload[20]; + + memset(&payload, 0, sizeof(payload)); + payload[0] = 2; /* Proplen = Identifier + byte */ + payload[1] = identifier; + payload[2] = 2; /* 0, 1 are only valid values */ + + byte_prop_read_helper(command, payload, 3, MOSQ_ERR_PROTOCOL, identifier, 0); +} + + +static void int32_prop_read_helper( + int command, + uint8_t *payload, + int remaining_length, + int rc_expected, + int identifier, + uint32_t value_expected) +{ + struct mosquitto__packet packet; + mosquitto_property *properties; + int rc; + + memset(&packet, 0, sizeof(struct mosquitto__packet)); + packet.payload = payload; + packet.remaining_length = remaining_length; + rc = property__read_all(command, &packet, &properties); + + CU_ASSERT_EQUAL(rc, rc_expected); + CU_ASSERT_EQUAL(packet.pos, remaining_length); + if(properties){ + CU_ASSERT_EQUAL(properties->identifier, identifier); + CU_ASSERT_EQUAL(properties->value.i32, value_expected); + CU_ASSERT_PTR_EQUAL(properties->next, NULL); + CU_ASSERT_EQUAL(property__get_length_all(properties), 5); + mosquitto_property_free_all(&properties); + } + CU_ASSERT_PTR_EQUAL(properties, NULL); +} + +static void duplicate_int32_helper(int command, int identifier) +{ + uint8_t payload[20]; + + memset(&payload, 0, sizeof(payload)); + payload[0] = 10; /* Proplen = (Identifier + int32)*2 */ + payload[1] = identifier; + payload[2] = 1; + payload[3] = 1; + payload[4] = 1; + payload[5] = 1; + payload[6] = identifier; + payload[7] = 0; + payload[8] = 0; + payload[9] = 0; + payload[10] = 0; + + int32_prop_read_helper(command, payload, 11, MOSQ_ERR_DUPLICATE_PROPERTY, identifier, 1); +} + + +static void int16_prop_read_helper( + int command, + uint8_t *payload, + int remaining_length, + int rc_expected, + int identifier, + uint16_t value_expected) +{ + struct mosquitto__packet packet; + mosquitto_property *properties; + int rc; + + memset(&packet, 0, sizeof(struct mosquitto__packet)); + packet.payload = payload; + packet.remaining_length = remaining_length; + rc = property__read_all(command, &packet, &properties); + + CU_ASSERT_EQUAL(rc, rc_expected); + CU_ASSERT_EQUAL(packet.pos, remaining_length); + if(properties){ + CU_ASSERT_EQUAL(properties->identifier, identifier); + CU_ASSERT_EQUAL(properties->value.i16, value_expected); + CU_ASSERT_PTR_EQUAL(properties->next, NULL); + CU_ASSERT_EQUAL(property__get_length_all(properties), 3); + mosquitto_property_free_all(&properties); + } + CU_ASSERT_PTR_EQUAL(properties, NULL); +} + +static void duplicate_int16_helper(int command, int identifier) +{ + uint8_t payload[20]; + + memset(&payload, 0, sizeof(payload)); + payload[0] = 6; /* Proplen = (Identifier + int16)*2 */ + payload[1] = identifier; + payload[2] = 1; + payload[3] = 1; + payload[4] = identifier; + payload[5] = 0; + payload[6] = 0; + + int16_prop_read_helper(command, payload, 7, MOSQ_ERR_DUPLICATE_PROPERTY, identifier, 1); +} + +static void string_prop_read_helper( + int command, + uint8_t *payload, + int remaining_length, + int rc_expected, + int identifier, + const char *value_expected) +{ + struct mosquitto__packet packet; + mosquitto_property *properties; + int rc; + + memset(&packet, 0, sizeof(struct mosquitto__packet)); + packet.payload = payload; + packet.remaining_length = remaining_length; + rc = property__read_all(command, &packet, &properties); + + CU_ASSERT_EQUAL(rc, rc_expected); + CU_ASSERT_EQUAL(packet.pos, remaining_length); + if(properties){ + CU_ASSERT_EQUAL(properties->identifier, identifier); + CU_ASSERT_EQUAL(properties->value.s.len, strlen(value_expected)); + CU_ASSERT_STRING_EQUAL(properties->value.s.v, value_expected); + CU_ASSERT_PTR_EQUAL(properties->next, NULL); + CU_ASSERT_EQUAL(property__get_length_all(properties), 1+2+strlen(value_expected)); + mosquitto_property_free_all(&properties); + } + CU_ASSERT_PTR_EQUAL(properties, NULL); +} + +static void duplicate_string_helper(int command, int identifier) +{ + uint8_t payload[20]; + + memset(&payload, 0, sizeof(payload)); + payload[0] = 8; + payload[1] = identifier; + payload[2] = 0; + payload[3] = 1; /* 1 length string */ + payload[4] = 'h'; + payload[5] = identifier; + payload[6] = 0; + payload[7] = 1; + payload[8] = 'h'; + + string_prop_read_helper(command, payload, 9, MOSQ_ERR_DUPLICATE_PROPERTY, identifier, ""); +} + +static void bad_string_helper(int identifier) +{ + uint8_t payload[20]; + + memset(&payload, 0, sizeof(payload)); + payload[0] = 6; + payload[1] = identifier; + payload[2] = 0; + payload[3] = 3; /* 1 length string */ + payload[4] = 'h'; + payload[5] = 0; /* 0 in string not allowed */ + payload[6] = 'h'; + + string_prop_read_helper(CMD_PUBLISH, payload, 7, MOSQ_ERR_MALFORMED_UTF8, identifier, ""); +} + +static void binary_prop_read_helper( + int command, + uint8_t *payload, + int remaining_length, + int rc_expected, + int identifier, + const uint8_t *value_expected, + int len_expected) +{ + struct mosquitto__packet packet; + mosquitto_property *properties; + int rc; + + memset(&packet, 0, sizeof(struct mosquitto__packet)); + packet.payload = payload; + packet.remaining_length = remaining_length; + rc = property__read_all(command, &packet, &properties); + + CU_ASSERT_EQUAL(rc, rc_expected); + CU_ASSERT_EQUAL(packet.pos, remaining_length); + if(properties){ + CU_ASSERT_EQUAL(properties->identifier, identifier); + CU_ASSERT_EQUAL(properties->value.bin.len, len_expected); + CU_ASSERT_EQUAL(memcmp(properties->value.bin.v, value_expected, len_expected), 0); + CU_ASSERT_PTR_EQUAL(properties->next, NULL); + CU_ASSERT_EQUAL(property__get_length_all(properties), 1+2+len_expected); + mosquitto_property_free_all(&properties); + } + CU_ASSERT_PTR_EQUAL(properties, NULL); +} + +static void duplicate_binary_helper(int command, int identifier) +{ + uint8_t payload[20]; + + memset(&payload, 0, sizeof(payload)); + payload[0] = 8; + payload[1] = identifier; + payload[2] = 0; + payload[3] = 1; /* 2 length binary */ + payload[4] = 'h'; + payload[5] = identifier; + payload[6] = 0; + payload[7] = 1; + payload[8] = 'h'; + + string_prop_read_helper(command, payload, 9, MOSQ_ERR_DUPLICATE_PROPERTY, identifier, ""); +} + +static void string_pair_prop_read_helper( + uint8_t *payload, + int remaining_length, + int rc_expected, + int identifier, + const char *name_expected, + const char *value_expected, + bool expect_multiple) +{ + struct mosquitto__packet packet; + mosquitto_property *properties; + int rc; + + memset(&packet, 0, sizeof(struct mosquitto__packet)); + packet.payload = payload; + packet.remaining_length = remaining_length; + rc = property__read_all(CMD_CONNECT, &packet, &properties); + + CU_ASSERT_EQUAL(rc, rc_expected); + CU_ASSERT_EQUAL(packet.pos, remaining_length); + if(properties){ + CU_ASSERT_EQUAL(properties->identifier, identifier); + CU_ASSERT_EQUAL(properties->name.len, strlen(name_expected)); + CU_ASSERT_EQUAL(properties->value.s.len, strlen(value_expected)); + CU_ASSERT_STRING_EQUAL(properties->name.v, name_expected); + CU_ASSERT_STRING_EQUAL(properties->value.s.v, value_expected); + if(expect_multiple){ + CU_ASSERT_PTR_NOT_NULL(properties->next); + }else{ + CU_ASSERT_PTR_NULL(properties->next); + CU_ASSERT_EQUAL(property__get_length_all(properties), 1+2+strlen(name_expected)+2+strlen(value_expected)); + } + mosquitto_property_free_all(&properties); + } + CU_ASSERT_PTR_NULL(properties); +} + +static void varint_prop_read_helper( + uint8_t *payload, + int remaining_length, + int rc_expected, + int identifier, + uint32_t value_expected) +{ + struct mosquitto__packet packet; + mosquitto_property *properties; + int rc; + + memset(&packet, 0, sizeof(struct mosquitto__packet)); + packet.payload = payload; + packet.remaining_length = remaining_length; + rc = property__read_all(CMD_PUBLISH, &packet, &properties); + + CU_ASSERT_EQUAL(rc, rc_expected); + if(properties){ + CU_ASSERT_EQUAL(properties->identifier, identifier); + CU_ASSERT_EQUAL(properties->value.varint, value_expected); + CU_ASSERT_PTR_NULL(properties->next); + CU_ASSERT_EQUAL(property__get_length_all(properties), packet__varint_bytes(value_expected)+1); + mosquitto_property_free_all(&properties); + } + CU_ASSERT_PTR_NULL(properties); +} + +static void packet_helper_reason_string_user_property(int command) +{ + uint8_t payload[24] = {23, + MQTT_PROP_REASON_STRING, 0, 6, 'r', 'e', 'a', 's', 'o', 'n', + MQTT_PROP_USER_PROPERTY, 0, 4, 'n', 'a', 'm', 'e', 0, 5, 'v', 'a', 'l', 'u', 'e'}; + + struct mosquitto__packet packet; + mosquitto_property *properties, *p; + int rc; + + memset(&packet, 0, sizeof(struct mosquitto__packet)); + packet.payload = payload; + packet.remaining_length = sizeof(payload);; + rc = property__read_all(command, &packet, &properties); + + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_NOT_NULL(properties); + if(properties){ + CU_ASSERT_PTR_NOT_NULL(properties->next); + p = properties; + + CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_REASON_STRING); + CU_ASSERT_STRING_EQUAL(p->value.s.v, "reason"); + CU_ASSERT_EQUAL(p->value.s.len, strlen("reason")); + + p = p->next; + CU_ASSERT_PTR_NULL(p->next); + + CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_USER_PROPERTY); + CU_ASSERT_STRING_EQUAL(p->value.s.v, "value"); + CU_ASSERT_EQUAL(p->value.s.len, strlen("value")); + CU_ASSERT_STRING_EQUAL(p->name.v, "name"); + CU_ASSERT_EQUAL(p->name.len, strlen("name")); + + mosquitto_property_free_all(&properties); + } +} + +/* ======================================================================== + * NO PROPERTIES + * ======================================================================== */ + +static void TEST_no_properties(void) +{ + struct mosquitto__packet packet; + mosquitto_property *properties = NULL; + uint8_t payload[5]; + int rc; + + memset(&packet, 0, sizeof(struct mosquitto__packet)); + memset(payload, 0, sizeof(payload)); + packet.payload = payload; + packet.remaining_length = 1; + rc = property__read_all(CMD_CONNECT, &packet, &properties); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_EQUAL(properties, NULL); + CU_ASSERT_EQUAL(packet.pos, 1); +} + +static void TEST_truncated(void) +{ + struct mosquitto__packet packet; + mosquitto_property *properties = NULL; + uint8_t payload[5]; + int rc; + + /* Zero length packet */ + memset(&packet, 0, sizeof(struct mosquitto__packet)); + memset(payload, 0, sizeof(payload)); + packet.payload = payload; + packet.remaining_length = 0; + rc = property__read_all(CMD_CONNECT, &packet, &properties); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_PROTOCOL); + CU_ASSERT_PTR_EQUAL(properties, NULL); + CU_ASSERT_EQUAL(packet.pos, 0); + + /* Proplen > 0 but not enough data */ + memset(&packet, 0, sizeof(struct mosquitto__packet)); + memset(payload, 0, sizeof(payload)); + payload[0] = 2; + packet.payload = payload; + packet.remaining_length = 1; + rc = property__read_all(CMD_CONNECT, &packet, &properties); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_PROTOCOL); + CU_ASSERT_PTR_EQUAL(properties, NULL); + CU_ASSERT_EQUAL(packet.pos, 1); + + /* Proplen > 0 but not enough data */ + memset(&packet, 0, sizeof(struct mosquitto__packet)); + memset(payload, 0, sizeof(payload)); + payload[0] = 4; + payload[1] = MQTT_PROP_PAYLOAD_FORMAT_INDICATOR; + packet.payload = payload; + packet.remaining_length = 2; + rc = property__read_all(CMD_CONNECT, &packet, &properties); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_PROTOCOL); + CU_ASSERT_PTR_EQUAL(properties, NULL); + CU_ASSERT_EQUAL(packet.pos, 2); +} + +/* ======================================================================== + * INVALID PROPERTY ID + * ======================================================================== */ + +static void TEST_invalid_property_id(void) +{ + struct mosquitto__packet packet; + mosquitto_property *properties = NULL; + uint8_t payload[5]; + int rc; + + /* ID = 0 */ + memset(&packet, 0, sizeof(struct mosquitto__packet)); + memset(payload, 0, sizeof(payload)); + payload[0] = 4; + packet.payload = payload; + packet.remaining_length = 2; + rc = property__read_all(CMD_CONNECT, &packet, &properties); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_MALFORMED_PACKET); + CU_ASSERT_PTR_EQUAL(properties, NULL); + CU_ASSERT_EQUAL(packet.pos, 2); + + /* ID = 4 */ + memset(&packet, 0, sizeof(struct mosquitto__packet)); + memset(payload, 0, sizeof(payload)); + payload[0] = 4; + payload[1] = 4; + packet.payload = payload; + packet.remaining_length = 2; + rc = property__read_all(CMD_CONNECT, &packet, &properties); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_MALFORMED_PACKET); + CU_ASSERT_PTR_EQUAL(properties, NULL); + CU_ASSERT_EQUAL(packet.pos, 2); +} + +/* ======================================================================== + * SINGLE PROPERTIES + * ======================================================================== */ + +static void TEST_single_payload_format_indicator(void) +{ + uint8_t payload[20]; + + memset(&payload, 0, sizeof(payload)); + payload[0] = 2; /* Proplen = Identifier + byte */ + payload[1] = MQTT_PROP_PAYLOAD_FORMAT_INDICATOR; + payload[2] = 1; + + byte_prop_read_helper(CMD_PUBLISH, payload, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, 1); +} + +static void TEST_single_request_problem_information(void) +{ + uint8_t payload[20]; + + memset(&payload, 0, sizeof(payload)); + payload[0] = 2; /* Proplen = Identifier + byte */ + payload[1] = MQTT_PROP_REQUEST_PROBLEM_INFORMATION; + payload[2] = 1; + + byte_prop_read_helper(CMD_CONNECT, payload, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_REQUEST_PROBLEM_INFORMATION, 1); +} + +static void TEST_single_request_response_information(void) +{ + uint8_t payload[20]; + + memset(&payload, 0, sizeof(payload)); + payload[0] = 2; /* Proplen = Identifier + byte */ + payload[1] = MQTT_PROP_REQUEST_RESPONSE_INFORMATION; + payload[2] = 1; + + byte_prop_read_helper(CMD_CONNECT, payload, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_REQUEST_RESPONSE_INFORMATION, 1); +} + +static void TEST_single_maximum_qos(void) +{ + uint8_t payload[20]; + + memset(&payload, 0, sizeof(payload)); + payload[0] = 2; /* Proplen = Identifier + byte */ + payload[1] = MQTT_PROP_MAXIMUM_QOS; + payload[2] = 1; + + byte_prop_read_helper(CMD_CONNACK, payload, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_MAXIMUM_QOS, 1); +} + +static void TEST_single_retain_available(void) +{ + uint8_t payload[20]; + + memset(&payload, 0, sizeof(payload)); + payload[0] = 2; /* Proplen = Identifier + byte */ + payload[1] = MQTT_PROP_RETAIN_AVAILABLE; + payload[2] = 1; + + byte_prop_read_helper(CMD_CONNACK, payload, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_RETAIN_AVAILABLE, 1); +} + +static void TEST_single_wildcard_subscription_available(void) +{ + uint8_t payload[20]; + + memset(&payload, 0, sizeof(payload)); + payload[0] = 2; /* Proplen = Identifier + byte */ + payload[1] = MQTT_PROP_WILDCARD_SUB_AVAILABLE; + payload[2] = 0; + + byte_prop_read_helper(CMD_CONNACK, payload, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_WILDCARD_SUB_AVAILABLE, 0); +} + +static void TEST_single_subscription_identifier_available(void) +{ + uint8_t payload[20]; + + memset(&payload, 0, sizeof(payload)); + payload[0] = 2; /* Proplen = Identifier + byte */ + payload[1] = MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE; + payload[2] = 0; + + byte_prop_read_helper(CMD_CONNACK, payload, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE, 0); +} + +static void TEST_single_shared_subscription_available(void) +{ + uint8_t payload[20]; + + memset(&payload, 0, sizeof(payload)); + payload[0] = 2; /* Proplen = Identifier + byte */ + payload[1] = MQTT_PROP_SHARED_SUB_AVAILABLE; + payload[2] = 1; + + byte_prop_read_helper(CMD_CONNACK, payload, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_SHARED_SUB_AVAILABLE, 1); +} + +static void TEST_single_message_expiry_interval(void) +{ + uint8_t payload[20]; + + memset(&payload, 0, sizeof(payload)); + payload[0] = 5; /* Proplen = Identifier + int32 */ + payload[1] = MQTT_PROP_MESSAGE_EXPIRY_INTERVAL; + payload[2] = 0x12; + payload[3] = 0x23; + payload[4] = 0x34; + payload[5] = 0x45; + + int32_prop_read_helper(CMD_WILL, payload, 6, MOSQ_ERR_SUCCESS, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, 0x12233445); +} + +static void TEST_single_session_expiry_interval(void) +{ + uint8_t payload[20]; + + memset(&payload, 0, sizeof(payload)); + payload[0] = 5; /* Proplen = Identifier + int32 */ + payload[1] = MQTT_PROP_SESSION_EXPIRY_INTERVAL; + payload[2] = 0x45; + payload[3] = 0x34; + payload[4] = 0x23; + payload[5] = 0x12; + + int32_prop_read_helper(CMD_CONNACK, payload, 6, MOSQ_ERR_SUCCESS, MQTT_PROP_SESSION_EXPIRY_INTERVAL, 0x45342312); +} + +static void TEST_single_will_delay_interval(void) +{ + uint8_t payload[20]; + + memset(&payload, 0, sizeof(payload)); + payload[0] = 5; /* Proplen = Identifier + int32 */ + payload[1] = MQTT_PROP_WILL_DELAY_INTERVAL; + payload[2] = 0x45; + payload[3] = 0x34; + payload[4] = 0x23; + payload[5] = 0x12; + + int32_prop_read_helper(CMD_WILL, payload, 6, MOSQ_ERR_SUCCESS, MQTT_PROP_WILL_DELAY_INTERVAL, 0x45342312); +} + +static void TEST_single_maximum_packet_size(void) +{ + uint8_t payload[20]; + + memset(&payload, 0, sizeof(payload)); + payload[0] = 5; /* Proplen = Identifier + int32 */ + payload[1] = MQTT_PROP_MAXIMUM_PACKET_SIZE; + payload[2] = 0x45; + payload[3] = 0x34; + payload[4] = 0x23; + payload[5] = 0x12; + + int32_prop_read_helper(CMD_CONNECT, payload, 6, MOSQ_ERR_SUCCESS, MQTT_PROP_MAXIMUM_PACKET_SIZE, 0x45342312); +} + +static void TEST_single_server_keep_alive(void) +{ + uint8_t payload[20]; + + memset(&payload, 0, sizeof(payload)); + payload[0] = 3; /* Proplen = Identifier + int16 */ + payload[1] = MQTT_PROP_SERVER_KEEP_ALIVE; + payload[2] = 0x45; + payload[3] = 0x34; + + int16_prop_read_helper(CMD_CONNACK, payload, 4, MOSQ_ERR_SUCCESS, MQTT_PROP_SERVER_KEEP_ALIVE, 0x4534); +} + +static void TEST_single_receive_maximum(void) +{ + uint8_t payload[20]; + + memset(&payload, 0, sizeof(payload)); + payload[0] = 3; /* Proplen = Identifier + int16 */ + payload[1] = MQTT_PROP_RECEIVE_MAXIMUM; + payload[2] = 0x68; + payload[3] = 0x42; + + int16_prop_read_helper(CMD_CONNACK, payload, 4, MOSQ_ERR_SUCCESS, MQTT_PROP_RECEIVE_MAXIMUM, 0x6842); +} + +static void TEST_single_topic_alias_maximum(void) +{ + uint8_t payload[20]; + + memset(&payload, 0, sizeof(payload)); + payload[0] = 3; /* Proplen = Identifier + int16 */ + payload[1] = MQTT_PROP_TOPIC_ALIAS_MAXIMUM; + payload[2] = 0x68; + payload[3] = 0x42; + + int16_prop_read_helper(CMD_CONNECT, payload, 4, MOSQ_ERR_SUCCESS, MQTT_PROP_TOPIC_ALIAS_MAXIMUM, 0x6842); +} + +static void TEST_single_topic_alias(void) +{ + uint8_t payload[20]; + + memset(&payload, 0, sizeof(payload)); + payload[0] = 3; /* Proplen = Identifier + int16 */ + payload[1] = MQTT_PROP_TOPIC_ALIAS; + payload[2] = 0x68; + payload[3] = 0x42; + + int16_prop_read_helper(CMD_PUBLISH, payload, 4, MOSQ_ERR_SUCCESS, MQTT_PROP_TOPIC_ALIAS, 0x6842); +} + +static void TEST_single_content_type(void) +{ + uint8_t payload[20]; + + memset(&payload, 0, sizeof(payload)); + payload[0] = 8; + payload[1] = MQTT_PROP_CONTENT_TYPE; + payload[2] = 0x00; + payload[3] = 0x05; + payload[4] = 'h'; + payload[5] = 'e'; + payload[6] = 'l'; + payload[7] = 'l'; + payload[8] = 'o'; + + string_prop_read_helper(CMD_PUBLISH, payload, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_CONTENT_TYPE, "hello"); +} + +static void TEST_single_response_topic(void) +{ + uint8_t payload[20]; + + memset(&payload, 0, sizeof(payload)); + payload[0] = 8; + payload[1] = MQTT_PROP_RESPONSE_TOPIC; + payload[2] = 0x00; + payload[3] = 0x05; + payload[4] = 'h'; + payload[5] = 'e'; + payload[6] = 'l'; + payload[7] = 'l'; + payload[8] = 'o'; + + string_prop_read_helper(CMD_WILL, payload, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_RESPONSE_TOPIC, "hello"); +} + +static void TEST_single_assigned_client_identifier(void) +{ + uint8_t payload[20]; + + memset(&payload, 0, sizeof(payload)); + payload[0] = 8; + payload[1] = MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER; + payload[2] = 0x00; + payload[3] = 0x05; + payload[4] = 'h'; + payload[5] = 'e'; + payload[6] = 'l'; + payload[7] = 'l'; + payload[8] = 'o'; + + string_prop_read_helper(CMD_CONNACK, payload, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER, "hello"); +} + +static void TEST_single_authentication_method(void) +{ + uint8_t payload[20]; + + memset(&payload, 0, sizeof(payload)); + payload[0] = 8; + payload[1] = MQTT_PROP_AUTHENTICATION_METHOD; + payload[2] = 0x00; + payload[3] = 0x05; + payload[4] = 'h'; + payload[5] = 'e'; + payload[6] = 'l'; + payload[7] = 'l'; + payload[8] = 'o'; + + string_prop_read_helper(CMD_AUTH, payload, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_AUTHENTICATION_METHOD, "hello"); +} + +static void TEST_single_response_information(void) +{ + uint8_t payload[20]; + + memset(&payload, 0, sizeof(payload)); + payload[0] = 8; + payload[1] = MQTT_PROP_RESPONSE_INFORMATION; + payload[2] = 0x00; + payload[3] = 0x05; + payload[4] = 'h'; + payload[5] = 'e'; + payload[6] = 'l'; + payload[7] = 'l'; + payload[8] = 'o'; + + string_prop_read_helper(CMD_CONNACK, payload, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_RESPONSE_INFORMATION, "hello"); +} + +static void TEST_single_server_reference(void) +{ + uint8_t payload[20]; + + memset(&payload, 0, sizeof(payload)); + payload[0] = 8; + payload[1] = MQTT_PROP_SERVER_REFERENCE; + payload[2] = 0x00; + payload[3] = 0x05; + payload[4] = 'h'; + payload[5] = 'e'; + payload[6] = 'l'; + payload[7] = 'l'; + payload[8] = 'o'; + + string_prop_read_helper(CMD_CONNACK, payload, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_SERVER_REFERENCE, "hello"); +} + +static void TEST_single_reason_string(void) +{ + uint8_t payload[20]; + + memset(&payload, 0, sizeof(payload)); + payload[0] = 8; + payload[1] = MQTT_PROP_REASON_STRING; + payload[2] = 0x00; + payload[3] = 0x05; + payload[4] = 'h'; + payload[5] = 'e'; + payload[6] = 'l'; + payload[7] = 'l'; + payload[8] = 'o'; + + string_prop_read_helper(CMD_PUBCOMP, payload, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_REASON_STRING, "hello"); +} + +static void TEST_single_correlation_data(void) +{ + uint8_t payload[20]; + + memset(&payload, 0, sizeof(payload)); + payload[0] = 8; + payload[1] = MQTT_PROP_CORRELATION_DATA; + payload[2] = 0x00; + payload[3] = 0x05; + payload[4] = 1; + payload[5] = 'e'; + payload[6] = 0; + payload[7] = 'l'; + payload[8] = 9; + + binary_prop_read_helper(CMD_PUBLISH, payload, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_CORRELATION_DATA, &payload[4], 5); +} + +static void TEST_single_authentication_data(void) +{ + uint8_t payload[20]; + + memset(&payload, 0, sizeof(payload)); + payload[0] = 8; + payload[1] = MQTT_PROP_AUTHENTICATION_DATA; + payload[2] = 0x00; + payload[3] = 0x05; + payload[4] = 1; + payload[5] = 'e'; + payload[6] = 0; + payload[7] = 'l'; + payload[8] = 9; + + binary_prop_read_helper(CMD_CONNECT, payload, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_AUTHENTICATION_DATA, &payload[4], 5); +} + +static void TEST_single_user_property(void) +{ + uint8_t payload[20]; + + payload[0] = 9; + payload[1] = MQTT_PROP_USER_PROPERTY; + payload[2] = 0; + payload[3] = 2; + payload[4] = 'z'; + payload[5] = 'a'; + payload[6] = 0; + payload[7] = 2; + payload[8] = 'b'; + payload[9] = 'c'; + + string_pair_prop_read_helper(payload, 10, MOSQ_ERR_SUCCESS, MQTT_PROP_USER_PROPERTY, "za", "bc", false); +} + +static void TEST_single_subscription_identifier(void) +{ + uint8_t payload[20]; + + payload[0] = 2; + payload[1] = MQTT_PROP_SUBSCRIPTION_IDENTIFIER; + payload[2] = 0; + varint_prop_read_helper(payload, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 0); + + payload[0] = 2; + payload[1] = MQTT_PROP_SUBSCRIPTION_IDENTIFIER; + payload[2] = 0x7F; + varint_prop_read_helper(payload, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 127); + + payload[0] = 3; + payload[1] = MQTT_PROP_SUBSCRIPTION_IDENTIFIER; + payload[2] = 0x80; + payload[3] = 0x01; + varint_prop_read_helper(payload, 4, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 128); + + payload[0] = 3; + payload[1] = MQTT_PROP_SUBSCRIPTION_IDENTIFIER; + payload[2] = 0xFF; + payload[3] = 0x7F; + varint_prop_read_helper(payload, 4, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 16383); + + payload[0] = 4; + payload[1] = MQTT_PROP_SUBSCRIPTION_IDENTIFIER; + payload[2] = 0x80; + payload[3] = 0x80; + payload[4] = 0x01; + varint_prop_read_helper(payload, 5, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 16384); + + payload[0] = 4; + payload[1] = MQTT_PROP_SUBSCRIPTION_IDENTIFIER; + payload[2] = 0xFF; + payload[3] = 0xFF; + payload[4] = 0x7F; + varint_prop_read_helper(payload, 5, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 2097151); + + payload[0] = 5; + payload[1] = MQTT_PROP_SUBSCRIPTION_IDENTIFIER; + payload[2] = 0x80; + payload[3] = 0x80; + payload[4] = 0x80; + payload[5] = 0x01; + varint_prop_read_helper(payload, 6, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 2097152); + + + payload[0] = 5; + payload[1] = MQTT_PROP_SUBSCRIPTION_IDENTIFIER; + payload[2] = 0xFF; + payload[3] = 0xFF; + payload[4] = 0xFF; + payload[5] = 0x7F; + varint_prop_read_helper(payload, 6, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 268435455); +} + +/* ======================================================================== + * DUPLICATE PROPERTIES + * ======================================================================== */ + +static void TEST_duplicate_payload_format_indicator(void) +{ + duplicate_byte_helper(CMD_PUBLISH, MQTT_PROP_PAYLOAD_FORMAT_INDICATOR); +} + +static void TEST_duplicate_request_problem_information(void) +{ + duplicate_byte_helper(CMD_CONNECT, MQTT_PROP_REQUEST_PROBLEM_INFORMATION); +} + +static void TEST_duplicate_request_response_information(void) +{ + duplicate_byte_helper(CMD_CONNECT, MQTT_PROP_REQUEST_RESPONSE_INFORMATION); +} + +static void TEST_duplicate_maximum_qos(void) +{ + duplicate_byte_helper(CMD_CONNACK, MQTT_PROP_MAXIMUM_QOS); +} + +static void TEST_duplicate_retain_available(void) +{ + duplicate_byte_helper(CMD_CONNACK, MQTT_PROP_RETAIN_AVAILABLE); +} + +static void TEST_duplicate_wildcard_subscription_available(void) +{ + duplicate_byte_helper(CMD_CONNACK, MQTT_PROP_WILDCARD_SUB_AVAILABLE); +} + +static void TEST_duplicate_subscription_identifier_available(void) +{ + duplicate_byte_helper(CMD_CONNACK, MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE); +} + +static void TEST_duplicate_shared_subscription_available(void) +{ + duplicate_byte_helper(CMD_CONNACK, MQTT_PROP_SHARED_SUB_AVAILABLE); +} + +static void TEST_duplicate_message_expiry_interval(void) +{ + duplicate_int32_helper(CMD_PUBLISH, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL); +} + +static void TEST_duplicate_session_expiry_interval(void) +{ + duplicate_int32_helper(CMD_DISCONNECT, MQTT_PROP_SESSION_EXPIRY_INTERVAL); +} + +static void TEST_duplicate_will_delay_interval(void) +{ + duplicate_int32_helper(CMD_WILL, MQTT_PROP_WILL_DELAY_INTERVAL); +} + +static void TEST_duplicate_maximum_packet_size(void) +{ + duplicate_int32_helper(CMD_CONNECT, MQTT_PROP_MAXIMUM_PACKET_SIZE); +} + +static void TEST_duplicate_server_keep_alive(void) +{ + duplicate_int16_helper(CMD_CONNACK, MQTT_PROP_SERVER_KEEP_ALIVE); +} + +static void TEST_duplicate_receive_maximum(void) +{ + duplicate_int16_helper(CMD_CONNACK, MQTT_PROP_RECEIVE_MAXIMUM); +} + +static void TEST_duplicate_topic_alias_maximum(void) +{ + duplicate_int16_helper(CMD_CONNECT, MQTT_PROP_TOPIC_ALIAS_MAXIMUM); +} + +static void TEST_duplicate_topic_alias(void) +{ + duplicate_int16_helper(CMD_PUBLISH, MQTT_PROP_TOPIC_ALIAS); +} + +static void TEST_duplicate_content_type(void) +{ + duplicate_string_helper(CMD_PUBLISH, MQTT_PROP_CONTENT_TYPE); +} + +static void TEST_duplicate_response_topic(void) +{ + duplicate_string_helper(CMD_PUBLISH, MQTT_PROP_RESPONSE_TOPIC); +} + +static void TEST_duplicate_assigned_client_identifier(void) +{ + duplicate_string_helper(CMD_CONNACK, MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER); +} + +static void TEST_duplicate_authentication_method(void) +{ + duplicate_string_helper(CMD_AUTH, MQTT_PROP_AUTHENTICATION_METHOD); +} + +static void TEST_duplicate_response_information(void) +{ + duplicate_string_helper(CMD_CONNACK, MQTT_PROP_RESPONSE_INFORMATION); +} + +static void TEST_duplicate_server_reference(void) +{ + duplicate_string_helper(CMD_CONNACK, MQTT_PROP_SERVER_REFERENCE); +} + +static void TEST_duplicate_reason_string(void) +{ + duplicate_string_helper(CMD_PUBACK, MQTT_PROP_REASON_STRING); +} + +static void TEST_duplicate_correlation_data(void) +{ + duplicate_binary_helper(CMD_PUBLISH, MQTT_PROP_CORRELATION_DATA); +} + +static void TEST_duplicate_authentication_data(void) +{ + duplicate_binary_helper(CMD_CONNACK, MQTT_PROP_AUTHENTICATION_DATA); +} + +static void TEST_duplicate_user_property(void) +{ + uint8_t payload[20]; + + memset(&payload, 0, sizeof(payload)); + payload[0] = 18; /* Proplen = (Identifier + byte)*2 */ + payload[1] = MQTT_PROP_USER_PROPERTY; + payload[2] = 0; + payload[3] = 2; + payload[4] = 'a'; + payload[5] = 'b'; + payload[6] = 0; + payload[7] = 2; + payload[8] = 'g'; + payload[9] = 'h'; + payload[10] = MQTT_PROP_USER_PROPERTY; + payload[11] = 0; + payload[12] = 2; + payload[13] = 'c'; + payload[14] = 'd'; + payload[15] = 0; + payload[16] = 2; + payload[17] = 'e'; + payload[18] = 'f'; + + string_pair_prop_read_helper(payload, 19, MOSQ_ERR_SUCCESS, MQTT_PROP_USER_PROPERTY, "ab", "gh", true); +} + +static void TEST_duplicate_subscription_identifier(void) +{ + uint8_t payload[20]; + + memset(&payload, 0, sizeof(payload)); + payload[0] = 4; /* Proplen = (Identifier + byte)*2 */ + payload[1] = MQTT_PROP_SUBSCRIPTION_IDENTIFIER; + payload[2] = 0x80; + payload[3] = 0x02; + payload[4] = MQTT_PROP_SUBSCRIPTION_IDENTIFIER; + payload[5] = 0x04; + + varint_prop_read_helper(payload, 5, MOSQ_ERR_PROTOCOL, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 0); +} + +/* ======================================================================== + * BAD PROPERTY VALUES + * ======================================================================== */ + +static void TEST_bad_request_problem_information(void) +{ + bad_byte_helper(CMD_CONNECT, MQTT_PROP_REQUEST_PROBLEM_INFORMATION); +} + +static void TEST_bad_request_response_information(void) +{ + bad_byte_helper(CMD_CONNECT, MQTT_PROP_REQUEST_RESPONSE_INFORMATION); +} + +static void TEST_bad_maximum_qos(void) +{ + bad_byte_helper(CMD_CONNACK, MQTT_PROP_MAXIMUM_QOS); +} + +static void TEST_bad_retain_available(void) +{ + bad_byte_helper(CMD_CONNACK, MQTT_PROP_RETAIN_AVAILABLE); +} + +static void TEST_bad_wildcard_sub_available(void) +{ + bad_byte_helper(CMD_CONNACK, MQTT_PROP_WILDCARD_SUB_AVAILABLE); +} + +static void TEST_bad_subscription_id_available(void) +{ + bad_byte_helper(CMD_CONNACK, MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE); +} + +static void TEST_bad_shared_sub_available(void) +{ + bad_byte_helper(CMD_CONNACK, MQTT_PROP_SHARED_SUB_AVAILABLE); +} + +static void TEST_bad_maximum_packet_size(void) +{ + uint8_t payload[20]; + + memset(&payload, 0, sizeof(payload)); + payload[0] = 5; /* Proplen = Identifier + int32 */ + payload[1] = MQTT_PROP_MAXIMUM_PACKET_SIZE; + payload[2] = 0; + payload[3] = 0; + payload[4] = 0; + payload[5] = 0; /* 0 is invalid */ + + int32_prop_read_helper(CMD_CONNACK, payload, 6, MOSQ_ERR_PROTOCOL, MQTT_PROP_MAXIMUM_PACKET_SIZE, 0); +} + +static void TEST_bad_receive_maximum(void) +{ + uint8_t payload[20]; + + memset(&payload, 0, sizeof(payload)); + payload[0] = 3; /* Proplen = Identifier + int16 */ + payload[1] = MQTT_PROP_RECEIVE_MAXIMUM; + payload[2] = 0; + payload[3] = 0; /* 0 is invalid */ + + int32_prop_read_helper(CMD_CONNECT, payload, 4, MOSQ_ERR_PROTOCOL, MQTT_PROP_RECEIVE_MAXIMUM, 0); +} + +static void TEST_bad_topic_alias(void) +{ + uint8_t payload[20]; + + memset(&payload, 0, sizeof(payload)); + payload[0] = 3; /* Proplen = Identifier + int16 */ + payload[1] = MQTT_PROP_TOPIC_ALIAS; + payload[2] = 0; + payload[3] = 0; /* 0 is invalid */ + + int32_prop_read_helper(CMD_PUBLISH, payload, 4, MOSQ_ERR_PROTOCOL, MQTT_PROP_TOPIC_ALIAS, 0); +} + +static void TEST_bad_content_type(void) +{ + bad_string_helper(MQTT_PROP_CONTENT_TYPE); +} + +static void TEST_bad_subscription_identifier(void) +{ + uint8_t payload[20]; + + memset(&payload, 0, sizeof(payload)); + payload[0] = 6; + payload[1] = MQTT_PROP_SUBSCRIPTION_IDENTIFIER; + payload[2] = 0xFF; + payload[3] = 0xFF; + payload[4] = 0xFF; + payload[5] = 0xFF; + payload[6] = 0x01; + + varint_prop_read_helper(payload, 7, MOSQ_ERR_PROTOCOL, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 0); +} + +/* ======================================================================== + * CONTROL PACKET TESTS + * ======================================================================== */ + +static void TEST_packet_connect(void) +{ + uint8_t payload[] = {0, + MQTT_PROP_SESSION_EXPIRY_INTERVAL, 0x12, 0x45, 0x00, 0x00, + MQTT_PROP_RECEIVE_MAXIMUM, 0x00, 0x05, + MQTT_PROP_MAXIMUM_PACKET_SIZE, 0x12, 0x45, 0x00, 0x00, + MQTT_PROP_TOPIC_ALIAS_MAXIMUM, 0x00, 0x02, + MQTT_PROP_REQUEST_PROBLEM_INFORMATION, 1, + MQTT_PROP_REQUEST_RESPONSE_INFORMATION, 1, + MQTT_PROP_USER_PROPERTY, 0, 4, 'n', 'a', 'm', 'e', 0, 5, 'v', 'a', 'l', 'u', 'e', + MQTT_PROP_AUTHENTICATION_METHOD, 0x00, 0x04, 'n', 'o', 'n', 'e', + MQTT_PROP_AUTHENTICATION_DATA, 0x00, 0x02, 1, 2}; + + struct mosquitto__packet packet; + mosquitto_property *properties, *p; + int rc; + + payload[0] = sizeof(payload)-1; + + memset(&packet, 0, sizeof(struct mosquitto__packet)); + packet.payload = payload; + packet.remaining_length = sizeof(payload);; + rc = property__read_all(CMD_CONNECT, &packet, &properties); + + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_NOT_NULL(properties->next); + p = properties; + + CU_ASSERT_PTR_NOT_NULL(p->next); + CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_SESSION_EXPIRY_INTERVAL); + CU_ASSERT_EQUAL(p->value.i32, 0x12450000); + + p = p->next; + CU_ASSERT_PTR_NOT_NULL(p->next); + CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_RECEIVE_MAXIMUM); + CU_ASSERT_EQUAL(p->value.i16, 0x0005); + + p = p->next; + CU_ASSERT_PTR_NOT_NULL(p->next); + CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_MAXIMUM_PACKET_SIZE); + CU_ASSERT_EQUAL(p->value.i32, 0x12450000); + + p = p->next; + CU_ASSERT_PTR_NOT_NULL(p->next); + CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_TOPIC_ALIAS_MAXIMUM); + CU_ASSERT_EQUAL(p->value.i16, 0x0002); + + p = p->next; + CU_ASSERT_PTR_NOT_NULL(p->next); + CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_REQUEST_PROBLEM_INFORMATION); + CU_ASSERT_EQUAL(p->value.i8, 1); + + p = p->next; + CU_ASSERT_PTR_NOT_NULL(p->next); + CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_REQUEST_RESPONSE_INFORMATION); + CU_ASSERT_EQUAL(p->value.i8, 1); + + p = p->next; + CU_ASSERT_PTR_NOT_NULL(p->next); + CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_USER_PROPERTY); + CU_ASSERT_STRING_EQUAL(p->value.s.v, "value"); + CU_ASSERT_EQUAL(p->value.s.len, strlen("value")); + CU_ASSERT_STRING_EQUAL(p->name.v, "name"); + CU_ASSERT_EQUAL(p->name.len, strlen("name")); + + p = p->next; + CU_ASSERT_PTR_NOT_NULL(p->next); + CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_AUTHENTICATION_METHOD); + CU_ASSERT_STRING_EQUAL(p->value.s.v, "none"); + CU_ASSERT_EQUAL(p->value.s.len, strlen("none")); + + p = p->next; + CU_ASSERT_PTR_NULL(p->next); + CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_AUTHENTICATION_DATA); + CU_ASSERT_EQUAL(p->value.bin.v[0], 1); + CU_ASSERT_EQUAL(p->value.bin.v[1], 2); + CU_ASSERT_EQUAL(p->value.s.len, 2); + + mosquitto_property_free_all(&properties); +} + +static void TEST_packet_connack(void) +{ + uint8_t payload[] = {0, + MQTT_PROP_SESSION_EXPIRY_INTERVAL, 0x12, 0x45, 0x00, 0x00, + MQTT_PROP_RECEIVE_MAXIMUM, 0x00, 0x05, + MQTT_PROP_MAXIMUM_QOS, 1, + MQTT_PROP_RETAIN_AVAILABLE, 0, + MQTT_PROP_MAXIMUM_PACKET_SIZE, 0x12, 0x45, 0x00, 0x00, + MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER, 0x00, 0x02, 'a', 'b', + MQTT_PROP_TOPIC_ALIAS_MAXIMUM, 0x00, 0x02, + MQTT_PROP_REASON_STRING, 0, 6, 'r', 'e', 'a', 's', 'o', 'n', + MQTT_PROP_USER_PROPERTY, 0, 4, 'n', 'a', 'm', 'e', 0, 5, 'v', 'a', 'l', 'u', 'e', + MQTT_PROP_WILDCARD_SUB_AVAILABLE, 0, + MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE, 0, + MQTT_PROP_SHARED_SUB_AVAILABLE, 0, + MQTT_PROP_SERVER_KEEP_ALIVE, 0x00, 0xFF, + MQTT_PROP_RESPONSE_INFORMATION, 0x00, 0x03, 'r', 's', 'p', + MQTT_PROP_SERVER_REFERENCE, 0x00, 0x04, 's', 'e', 'r', 'v', + MQTT_PROP_AUTHENTICATION_METHOD, 0x00, 0x04, 'n', 'o', 'n', 'e', + MQTT_PROP_AUTHENTICATION_DATA, 0x00, 0x02, 1, 2}; + + struct mosquitto__packet packet; + mosquitto_property *properties, *p; + int rc; + + payload[0] = sizeof(payload)-1; + + memset(&packet, 0, sizeof(struct mosquitto__packet)); + packet.payload = payload; + packet.remaining_length = sizeof(payload);; + rc = property__read_all(CMD_CONNACK, &packet, &properties); + + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_NOT_NULL(properties->next); + p = properties; + + CU_ASSERT_PTR_NOT_NULL(p->next); + CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_SESSION_EXPIRY_INTERVAL); + CU_ASSERT_EQUAL(p->value.i32, 0x12450000); + + p = p->next; + CU_ASSERT_PTR_NOT_NULL(p->next); + CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_RECEIVE_MAXIMUM); + CU_ASSERT_EQUAL(p->value.i16, 0x0005); + + p = p->next; + CU_ASSERT_PTR_NOT_NULL(p->next); + CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_MAXIMUM_QOS); + CU_ASSERT_EQUAL(p->value.i8, 1); + + p = p->next; + CU_ASSERT_PTR_NOT_NULL(p->next); + CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_RETAIN_AVAILABLE); + CU_ASSERT_EQUAL(p->value.i8, 0); + + p = p->next; + CU_ASSERT_PTR_NOT_NULL(p->next); + CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_MAXIMUM_PACKET_SIZE); + CU_ASSERT_EQUAL(p->value.i32, 0x12450000); + + p = p->next; + CU_ASSERT_PTR_NOT_NULL(p->next); + CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER); + CU_ASSERT_STRING_EQUAL(p->value.s.v, "ab"); + CU_ASSERT_EQUAL(p->value.s.len, strlen("ab")); + + p = p->next; + CU_ASSERT_PTR_NOT_NULL(p->next); + CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_TOPIC_ALIAS_MAXIMUM); + CU_ASSERT_EQUAL(p->value.i16, 0x0002); + + p = p->next; + CU_ASSERT_PTR_NOT_NULL(p->next); + CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_REASON_STRING); + CU_ASSERT_STRING_EQUAL(p->value.s.v, "reason"); + CU_ASSERT_EQUAL(p->value.s.len, strlen("reason")); + + p = p->next; + CU_ASSERT_PTR_NOT_NULL(p->next); + CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_USER_PROPERTY); + CU_ASSERT_STRING_EQUAL(p->value.s.v, "value"); + CU_ASSERT_EQUAL(p->value.s.len, strlen("value")); + CU_ASSERT_STRING_EQUAL(p->name.v, "name"); + CU_ASSERT_EQUAL(p->name.len, strlen("name")); + + p = p->next; + CU_ASSERT_PTR_NOT_NULL(p->next); + CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_WILDCARD_SUB_AVAILABLE); + CU_ASSERT_EQUAL(p->value.i8, 0); + + p = p->next; + CU_ASSERT_PTR_NOT_NULL(p->next); + CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE); + CU_ASSERT_EQUAL(p->value.i8, 0); + + p = p->next; + CU_ASSERT_PTR_NOT_NULL(p->next); + CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_SHARED_SUB_AVAILABLE); + CU_ASSERT_EQUAL(p->value.i8, 0); + + p = p->next; + CU_ASSERT_PTR_NOT_NULL(p->next); + CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_SERVER_KEEP_ALIVE); + CU_ASSERT_EQUAL(p->value.i16, 0x00FF); + + p = p->next; + CU_ASSERT_PTR_NOT_NULL(p->next); + CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_RESPONSE_INFORMATION); + CU_ASSERT_STRING_EQUAL(p->value.s.v, "rsp"); + CU_ASSERT_EQUAL(p->value.s.len, strlen("rsp")); + + p = p->next; + CU_ASSERT_PTR_NOT_NULL(p->next); + CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_SERVER_REFERENCE); + CU_ASSERT_STRING_EQUAL(p->value.s.v, "serv"); + CU_ASSERT_EQUAL(p->value.s.len, strlen("serv")); + + p = p->next; + CU_ASSERT_PTR_NOT_NULL(p->next); + CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_AUTHENTICATION_METHOD); + CU_ASSERT_STRING_EQUAL(p->value.s.v, "none"); + CU_ASSERT_EQUAL(p->value.s.len, strlen("none")); + + p = p->next; + CU_ASSERT_PTR_NULL(p->next); + CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_AUTHENTICATION_DATA); + CU_ASSERT_EQUAL(p->value.bin.v[0], 1); + CU_ASSERT_EQUAL(p->value.bin.v[1], 2); + CU_ASSERT_EQUAL(p->value.s.len, 2); + + mosquitto_property_free_all(&properties); +} + +static void TEST_packet_publish(void) +{ + uint8_t payload[] = {0, + MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, 1, + MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, 0x12, 0x45, 0x00, 0x00, + MQTT_PROP_TOPIC_ALIAS, 0x00, 0x02, + MQTT_PROP_RESPONSE_TOPIC, 0, 6, 'r', 'e', 's', 'p', 'o', 'n', + MQTT_PROP_CORRELATION_DATA, 0x00, 0x02, 1, 2, + MQTT_PROP_USER_PROPERTY, 0, 4, 'n', 'a', 'm', 'e', 0, 5, 'v', 'a', 'l', 'u', 'e', + MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 0x04, + MQTT_PROP_CONTENT_TYPE, 0, 5, 'e', 'm', 'p', 't', 'y'}; + + struct mosquitto__packet packet; + mosquitto_property *properties, *p; + int rc; + + payload[0] = sizeof(payload)-1; + + memset(&packet, 0, sizeof(struct mosquitto__packet)); + packet.payload = payload; + packet.remaining_length = sizeof(payload);; + rc = property__read_all(CMD_PUBLISH, &packet, &properties); + + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_NOT_NULL(properties->next); + p = properties; + + CU_ASSERT_PTR_NOT_NULL(p->next); + CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_PAYLOAD_FORMAT_INDICATOR); + CU_ASSERT_EQUAL(p->value.i8, 1); + + p = p->next; + CU_ASSERT_PTR_NOT_NULL(p->next); + CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL); + CU_ASSERT_EQUAL(p->value.i32, 0x12450000); + + p = p->next; + CU_ASSERT_PTR_NOT_NULL(p->next); + CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_TOPIC_ALIAS); + CU_ASSERT_EQUAL(p->value.i16, 0x0002); + + p = p->next; + CU_ASSERT_PTR_NOT_NULL(p->next); + CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_RESPONSE_TOPIC); + CU_ASSERT_STRING_EQUAL(p->value.s.v, "respon"); + CU_ASSERT_EQUAL(p->value.s.len, strlen("respon")); + + p = p->next; + CU_ASSERT_PTR_NOT_NULL(p->next); + CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_CORRELATION_DATA); + CU_ASSERT_EQUAL(p->value.bin.v[0], 1); + CU_ASSERT_EQUAL(p->value.bin.v[1], 2); + CU_ASSERT_EQUAL(p->value.bin.len, 2); + + p = p->next; + CU_ASSERT_PTR_NOT_NULL(p->next); + CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_USER_PROPERTY); + CU_ASSERT_STRING_EQUAL(p->value.s.v, "value"); + CU_ASSERT_EQUAL(p->value.s.len, strlen("value")); + CU_ASSERT_STRING_EQUAL(p->name.v, "name"); + CU_ASSERT_EQUAL(p->name.len, strlen("name")); + + p = p->next; + CU_ASSERT_PTR_NOT_NULL(p->next); + CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_SUBSCRIPTION_IDENTIFIER); + CU_ASSERT_EQUAL(p->value.varint, 0x00000004); + + p = p->next; + CU_ASSERT_PTR_NULL(p->next); + CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_CONTENT_TYPE); + CU_ASSERT_STRING_EQUAL(p->value.s.v, "empty"); + CU_ASSERT_EQUAL(p->value.s.len, strlen("empty")); + + mosquitto_property_free_all(&properties); +} + +static void TEST_packet_puback(void) +{ + packet_helper_reason_string_user_property(CMD_PUBACK); +} + +static void TEST_packet_pubrec(void) +{ + packet_helper_reason_string_user_property(CMD_PUBREC); +} + +static void TEST_packet_pubrel(void) +{ + packet_helper_reason_string_user_property(CMD_PUBREL); +} + +static void TEST_packet_pubcomp(void) +{ + packet_helper_reason_string_user_property(CMD_PUBCOMP); +} + +static void TEST_packet_subscribe(void) +{ + uint8_t payload[] = {0, + MQTT_PROP_USER_PROPERTY, 0, 4, 'n', 'a', 'm', 'e', 0, 5, 'v', 'a', 'l', 'u', 'e', + MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 0x04}; + + struct mosquitto__packet packet; + mosquitto_property *properties, *p; + int rc; + + payload[0] = sizeof(payload)-1; + + memset(&packet, 0, sizeof(struct mosquitto__packet)); + packet.payload = payload; + packet.remaining_length = sizeof(payload);; + rc = property__read_all(CMD_SUBSCRIBE, &packet, &properties); + + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_NOT_NULL(properties->next); + p = properties; + + CU_ASSERT_PTR_NOT_NULL(p->next); + CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_USER_PROPERTY); + CU_ASSERT_STRING_EQUAL(p->value.s.v, "value"); + CU_ASSERT_EQUAL(p->value.s.len, strlen("value")); + CU_ASSERT_STRING_EQUAL(p->name.v, "name"); + CU_ASSERT_EQUAL(p->name.len, strlen("name")); + + p = p->next; + CU_ASSERT_PTR_NULL(p->next); + CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_SUBSCRIPTION_IDENTIFIER); + CU_ASSERT_EQUAL(p->value.varint, 0x00000004); + + mosquitto_property_free_all(&properties); +} + +static void TEST_packet_suback(void) +{ + packet_helper_reason_string_user_property(CMD_SUBACK); +} + +static void TEST_packet_unsubscribe(void) +{ + uint8_t payload[] = {0, + MQTT_PROP_USER_PROPERTY, 0, 4, 'n', 'a', 'm', 'e', 0, 5, 'v', 'a', 'l', 'u', 'e'}; + + struct mosquitto__packet packet; + mosquitto_property *properties, *p; + int rc; + + payload[0] = sizeof(payload)-1; + + memset(&packet, 0, sizeof(struct mosquitto__packet)); + packet.payload = payload; + packet.remaining_length = sizeof(payload);; + rc = property__read_all(CMD_UNSUBSCRIBE, &packet, &properties); + + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + p = properties; + + CU_ASSERT_PTR_NULL(p->next); + CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_USER_PROPERTY); + CU_ASSERT_STRING_EQUAL(p->value.s.v, "value"); + CU_ASSERT_EQUAL(p->value.s.len, strlen("value")); + CU_ASSERT_STRING_EQUAL(p->name.v, "name"); + CU_ASSERT_EQUAL(p->name.len, strlen("name")); + + mosquitto_property_free_all(&properties); +} + +static void TEST_packet_unsuback(void) +{ + packet_helper_reason_string_user_property(CMD_UNSUBACK); +} + +static void TEST_packet_disconnect(void) +{ + uint8_t payload[] = {0, + MQTT_PROP_SESSION_EXPIRY_INTERVAL, 0x12, 0x45, 0x00, 0x00, + MQTT_PROP_REASON_STRING, 0, 6, 'r', 'e', 'a', 's', 'o', 'n', + MQTT_PROP_USER_PROPERTY, 0, 4, 'n', 'a', 'm', 'e', 0, 5, 'v', 'a', 'l', 'u', 'e'}; + + struct mosquitto__packet packet; + mosquitto_property *properties, *p; + int rc; + + payload[0] = sizeof(payload)-1; + + memset(&packet, 0, sizeof(struct mosquitto__packet)); + packet.payload = payload; + packet.remaining_length = sizeof(payload);; + rc = property__read_all(CMD_DISCONNECT, &packet, &properties); + + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_NOT_NULL(properties->next); + p = properties; + + CU_ASSERT_PTR_NOT_NULL(p->next); + CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_SESSION_EXPIRY_INTERVAL); + CU_ASSERT_EQUAL(p->value.i32, 0x12450000); + + p = p->next; + CU_ASSERT_PTR_NOT_NULL(p->next); + CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_REASON_STRING); + CU_ASSERT_STRING_EQUAL(p->value.s.v, "reason"); + CU_ASSERT_EQUAL(p->value.s.len, strlen("reason")); + + p = p->next; + CU_ASSERT_PTR_NULL(p->next); + CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_USER_PROPERTY); + CU_ASSERT_STRING_EQUAL(p->value.s.v, "value"); + CU_ASSERT_EQUAL(p->value.s.len, strlen("value")); + CU_ASSERT_STRING_EQUAL(p->name.v, "name"); + CU_ASSERT_EQUAL(p->name.len, strlen("name")); + + mosquitto_property_free_all(&properties); +} + +static void TEST_packet_auth(void) +{ + uint8_t payload[] = {0, + MQTT_PROP_AUTHENTICATION_METHOD, 0x00, 0x04, 'n', 'o', 'n', 'e', + MQTT_PROP_AUTHENTICATION_DATA, 0x00, 0x02, 1, 2, + MQTT_PROP_REASON_STRING, 0, 6, 'r', 'e', 'a', 's', 'o', 'n', + MQTT_PROP_USER_PROPERTY, 0, 4, 'n', 'a', 'm', 'e', 0, 5, 'v', 'a', 'l', 'u', 'e'}; + + struct mosquitto__packet packet; + mosquitto_property *properties, *p; + int rc; + + payload[0] = sizeof(payload)-1; + + memset(&packet, 0, sizeof(struct mosquitto__packet)); + packet.payload = payload; + packet.remaining_length = sizeof(payload);; + rc = property__read_all(CMD_AUTH, &packet, &properties); + + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_NOT_NULL(properties->next); + p = properties; + + CU_ASSERT_PTR_NOT_NULL(p->next); + CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_AUTHENTICATION_METHOD); + CU_ASSERT_STRING_EQUAL(p->value.s.v, "none"); + CU_ASSERT_EQUAL(p->value.s.len, strlen("none")); + + p = p->next; + CU_ASSERT_PTR_NOT_NULL(p->next); + CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_AUTHENTICATION_DATA); + CU_ASSERT_EQUAL(p->value.bin.v[0], 1); + CU_ASSERT_EQUAL(p->value.bin.v[1], 2); + CU_ASSERT_EQUAL(p->value.s.len, 2); + + p = p->next; + CU_ASSERT_PTR_NOT_NULL(p->next); + CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_REASON_STRING); + CU_ASSERT_STRING_EQUAL(p->value.s.v, "reason"); + CU_ASSERT_EQUAL(p->value.s.len, strlen("reason")); + + p = p->next; + CU_ASSERT_PTR_NULL(p->next); + CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_USER_PROPERTY); + CU_ASSERT_STRING_EQUAL(p->value.s.v, "value"); + CU_ASSERT_EQUAL(p->value.s.len, strlen("value")); + CU_ASSERT_STRING_EQUAL(p->name.v, "name"); + CU_ASSERT_EQUAL(p->name.len, strlen("name")); + + mosquitto_property_free_all(&properties); +} + + +/* ======================================================================== + * TEST SUITE SETUP + * ======================================================================== */ + +int init_property_read_tests(void) +{ + CU_pSuite test_suite = NULL; + + test_suite = CU_add_suite("Property read", NULL, NULL); + if(!test_suite){ + printf("Error adding CUnit Property read test suite.\n"); + return 1; + } + + if(0 + || !CU_add_test(test_suite, "Truncated packet", TEST_truncated) + || !CU_add_test(test_suite, "Invalid property ID", TEST_invalid_property_id) + || !CU_add_test(test_suite, "No properties", TEST_no_properties) + || !CU_add_test(test_suite, "Single Payload Format Indicator", TEST_single_payload_format_indicator) + || !CU_add_test(test_suite, "Single Request Problem Information", TEST_single_request_problem_information) + || !CU_add_test(test_suite, "Single Request Response Information", TEST_single_request_response_information) + || !CU_add_test(test_suite, "Single Maximum QoS", TEST_single_maximum_qos) + || !CU_add_test(test_suite, "Single Retain Available", TEST_single_retain_available) + || !CU_add_test(test_suite, "Single Wildcard Subscription Available", TEST_single_wildcard_subscription_available) + || !CU_add_test(test_suite, "Single Subscription Identifier Available", TEST_single_subscription_identifier_available) + || !CU_add_test(test_suite, "Single Shared Subscription Available", TEST_single_shared_subscription_available) + || !CU_add_test(test_suite, "Single Message Expiry Interval", TEST_single_message_expiry_interval) + || !CU_add_test(test_suite, "Single Session Expiry Interval", TEST_single_session_expiry_interval) + || !CU_add_test(test_suite, "Single Will Delay Interval", TEST_single_will_delay_interval) + || !CU_add_test(test_suite, "Single Maximum Packet Size", TEST_single_maximum_packet_size) + || !CU_add_test(test_suite, "Single Server Keep Alive", TEST_single_server_keep_alive) + || !CU_add_test(test_suite, "Single Receive Maximum", TEST_single_receive_maximum) + || !CU_add_test(test_suite, "Single Topic Alias Maximum", TEST_single_topic_alias_maximum) + || !CU_add_test(test_suite, "Single Topic Alias", TEST_single_topic_alias) + || !CU_add_test(test_suite, "Single Content Type", TEST_single_content_type) + || !CU_add_test(test_suite, "Single Response Topic", TEST_single_response_topic) + || !CU_add_test(test_suite, "Single Assigned Client Identifier", TEST_single_assigned_client_identifier) + || !CU_add_test(test_suite, "Single Authentication Method", TEST_single_authentication_method) + || !CU_add_test(test_suite, "Single Response Information", TEST_single_response_information) + || !CU_add_test(test_suite, "Single Server Reference", TEST_single_server_reference) + || !CU_add_test(test_suite, "Single Reason String", TEST_single_reason_string) + || !CU_add_test(test_suite, "Single Correlation Data", TEST_single_correlation_data) + || !CU_add_test(test_suite, "Single Authentication Data", TEST_single_authentication_data) + || !CU_add_test(test_suite, "Single User Property", TEST_single_user_property) + || !CU_add_test(test_suite, "Single Subscription Identifier", TEST_single_subscription_identifier) + || !CU_add_test(test_suite, "Duplicate Payload Format Indicator", TEST_duplicate_payload_format_indicator) + || !CU_add_test(test_suite, "Duplicate Request Problem Information", TEST_duplicate_request_problem_information) + || !CU_add_test(test_suite, "Duplicate Request Response Information", TEST_duplicate_request_response_information) + || !CU_add_test(test_suite, "Duplicate Maximum QoS", TEST_duplicate_maximum_qos) + || !CU_add_test(test_suite, "Duplicate Retain Available", TEST_duplicate_retain_available) + || !CU_add_test(test_suite, "Duplicate Wildcard Subscription Available", TEST_duplicate_wildcard_subscription_available) + || !CU_add_test(test_suite, "Duplicate Subscription Identifier Available", TEST_duplicate_subscription_identifier_available) + || !CU_add_test(test_suite, "Duplicate Shared Subscription Available", TEST_duplicate_shared_subscription_available) + || !CU_add_test(test_suite, "Duplicate Message Expiry Interval", TEST_duplicate_message_expiry_interval) + || !CU_add_test(test_suite, "Duplicate Session Expiry Interval", TEST_duplicate_session_expiry_interval) + || !CU_add_test(test_suite, "Duplicate Will Delay Interval", TEST_duplicate_will_delay_interval) + || !CU_add_test(test_suite, "Duplicate Maximum Packet Size", TEST_duplicate_maximum_packet_size) + || !CU_add_test(test_suite, "Duplicate Server Keep Alive", TEST_duplicate_server_keep_alive) + || !CU_add_test(test_suite, "Duplicate Receive Maximum", TEST_duplicate_receive_maximum) + || !CU_add_test(test_suite, "Duplicate Topic Alias Maximum", TEST_duplicate_topic_alias_maximum) + || !CU_add_test(test_suite, "Duplicate Topic Alias", TEST_duplicate_topic_alias) + || !CU_add_test(test_suite, "Duplicate Content Type", TEST_duplicate_content_type) + || !CU_add_test(test_suite, "Duplicate Response Topic", TEST_duplicate_response_topic) + || !CU_add_test(test_suite, "Duplicate Assigned Client ID", TEST_duplicate_assigned_client_identifier) + || !CU_add_test(test_suite, "Duplicate Authentication Method", TEST_duplicate_authentication_method) + || !CU_add_test(test_suite, "Duplicate Response Information", TEST_duplicate_response_information) + || !CU_add_test(test_suite, "Duplicate Server Reference", TEST_duplicate_server_reference) + || !CU_add_test(test_suite, "Duplicate Reason String", TEST_duplicate_reason_string) + || !CU_add_test(test_suite, "Duplicate Correlation Data", TEST_duplicate_correlation_data) + || !CU_add_test(test_suite, "Duplicate Authentication Data", TEST_duplicate_authentication_data) + || !CU_add_test(test_suite, "Duplicate User Property", TEST_duplicate_user_property) + || !CU_add_test(test_suite, "Duplicate Subscription Identifier", TEST_duplicate_subscription_identifier) + || !CU_add_test(test_suite, "Bad Request Problem Information", TEST_bad_request_problem_information) + || !CU_add_test(test_suite, "Bad Request Response Information", TEST_bad_request_response_information) + || !CU_add_test(test_suite, "Bad Maximum QoS", TEST_bad_maximum_qos) + || !CU_add_test(test_suite, "Bad Retain Available", TEST_bad_retain_available) + || !CU_add_test(test_suite, "Bad Wildcard Subscription Available", TEST_bad_wildcard_sub_available) + || !CU_add_test(test_suite, "Bad Subscription Identifier Available", TEST_bad_subscription_id_available) + || !CU_add_test(test_suite, "Bad Shared Subscription Available", TEST_bad_shared_sub_available) + || !CU_add_test(test_suite, "Bad Maximum Packet Size", TEST_bad_maximum_packet_size) + || !CU_add_test(test_suite, "Bad Receive Maximum", TEST_bad_receive_maximum) + || !CU_add_test(test_suite, "Bad Topic Alias", TEST_bad_topic_alias) + || !CU_add_test(test_suite, "Bad Content Type", TEST_bad_content_type) + || !CU_add_test(test_suite, "Bad Subscription Identifier", TEST_bad_subscription_identifier) + || !CU_add_test(test_suite, "Packet CONNECT", TEST_packet_connect) + || !CU_add_test(test_suite, "Packet CONNACK", TEST_packet_connack) + || !CU_add_test(test_suite, "Packet PUBLISH", TEST_packet_publish) + || !CU_add_test(test_suite, "Packet PUBACK", TEST_packet_puback) + || !CU_add_test(test_suite, "Packet PUBREC", TEST_packet_pubrec) + || !CU_add_test(test_suite, "Packet PUBREL", TEST_packet_pubrel) + || !CU_add_test(test_suite, "Packet PUBCOMP", TEST_packet_pubcomp) + || !CU_add_test(test_suite, "Packet SUBSCRIBE", TEST_packet_subscribe) + || !CU_add_test(test_suite, "Packet SUBACK", TEST_packet_suback) + || !CU_add_test(test_suite, "Packet UNSUBSCRIBE", TEST_packet_unsubscribe) + || !CU_add_test(test_suite, "Packet UNSUBACK", TEST_packet_unsuback) + || !CU_add_test(test_suite, "Packet DISCONNECT", TEST_packet_disconnect) + || !CU_add_test(test_suite, "Packet AUTH", TEST_packet_auth) + ){ + + printf("Error adding Property read CUnit tests.\n"); + return 1; + } + + return 0; +} diff --git a/test/unit/property_user_read.c b/test/unit/property_user_read.c new file mode 100644 index 00000000..42a8c08d --- /dev/null +++ b/test/unit/property_user_read.c @@ -0,0 +1,565 @@ +#include +#include + +#include "mqtt_protocol.h" +#include "property_mosq.h" +#include "packet_mosq.h" + +static void generate_full_proplist(mosquitto_property **proplist) +{ + int rc; + + /* This isn't a valid proplist for sending, because it contains every + * property. Very useful for testing though. */ + rc = mosquitto_property_add_byte(proplist, MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, 1); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + rc = mosquitto_property_add_int32(proplist, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, 3600); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + rc = mosquitto_property_add_string(proplist, MQTT_PROP_CONTENT_TYPE, "application/json"); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + rc = mosquitto_property_add_string(proplist, MQTT_PROP_RESPONSE_TOPIC, "response/topic"); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + rc = mosquitto_property_add_binary(proplist, MQTT_PROP_CORRELATION_DATA, "correlation-data", strlen("correlation-data")); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + rc = mosquitto_property_add_varint(proplist, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 63); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + rc = mosquitto_property_add_int32(proplist, MQTT_PROP_SESSION_EXPIRY_INTERVAL, 86400); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + rc = mosquitto_property_add_string(proplist, MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER, "mosquitto-test"); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + rc = mosquitto_property_add_int16(proplist, MQTT_PROP_SERVER_KEEP_ALIVE, 180); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + rc = mosquitto_property_add_string(proplist, MQTT_PROP_AUTHENTICATION_METHOD, "basic"); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + rc = mosquitto_property_add_binary(proplist, MQTT_PROP_AUTHENTICATION_DATA, "password", strlen("password")); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + rc = mosquitto_property_add_byte(proplist, MQTT_PROP_REQUEST_PROBLEM_INFORMATION, 1); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + rc = mosquitto_property_add_int32(proplist, MQTT_PROP_WILL_DELAY_INTERVAL, 1800); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + rc = mosquitto_property_add_byte(proplist, MQTT_PROP_REQUEST_RESPONSE_INFORMATION, 1); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + rc = mosquitto_property_add_string(proplist, MQTT_PROP_RESPONSE_INFORMATION, "response"); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + rc = mosquitto_property_add_string(proplist, MQTT_PROP_SERVER_REFERENCE, "localhost"); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + rc = mosquitto_property_add_string(proplist, MQTT_PROP_REASON_STRING, "reason"); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + rc = mosquitto_property_add_int16(proplist, MQTT_PROP_RECEIVE_MAXIMUM, 1024); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + rc = mosquitto_property_add_int16(proplist, MQTT_PROP_TOPIC_ALIAS_MAXIMUM, 64); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + rc = mosquitto_property_add_int16(proplist, MQTT_PROP_TOPIC_ALIAS, 15); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + rc = mosquitto_property_add_byte(proplist, MQTT_PROP_MAXIMUM_QOS, 0); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + rc = mosquitto_property_add_byte(proplist, MQTT_PROP_RETAIN_AVAILABLE, 0); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + rc = mosquitto_property_add_string_pair(proplist, MQTT_PROP_USER_PROPERTY, "user-agent", "mosquitto/test"); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + rc = mosquitto_property_add_int32(proplist, MQTT_PROP_MAXIMUM_PACKET_SIZE, 200000000); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + rc = mosquitto_property_add_byte(proplist, MQTT_PROP_WILDCARD_SUB_AVAILABLE, 0); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + rc = mosquitto_property_add_byte(proplist, MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE, 0); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + rc = mosquitto_property_add_byte(proplist, MQTT_PROP_SHARED_SUB_AVAILABLE, 0); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); +} + +static void generate_partial_proplist(mosquitto_property **proplist) +{ + int rc; + + // BYTE MISSING: MQTT_PROP_PAYLOAD_FORMAT_INDICATOR + rc = mosquitto_property_add_int32(proplist, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, 3600); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + // STRING MISSING: MQTT_PROP_CONTENT_TYPE + rc = mosquitto_property_add_string(proplist, MQTT_PROP_RESPONSE_TOPIC, "response/topic"); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + // BINARY MISSING: MQTT_PROP_CORRELATION_DATA + // VARINT MISSING: MQTT_PROP_SUBSCRIPTION_IDENTIFIER + // INT32 MISSING: MQTT_PROP_SESSION_EXPIRY_INTERVAL + rc = mosquitto_property_add_string(proplist, MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER, "mosquitto-test"); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + // INT16 MISSING: MQTT_PROP_SERVER_KEEP_ALIVE + rc = mosquitto_property_add_string(proplist, MQTT_PROP_AUTHENTICATION_METHOD, "basic"); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + rc = mosquitto_property_add_binary(proplist, MQTT_PROP_AUTHENTICATION_DATA, "password", strlen("password")); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + rc = mosquitto_property_add_byte(proplist, MQTT_PROP_REQUEST_PROBLEM_INFORMATION, 1); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + rc = mosquitto_property_add_int32(proplist, MQTT_PROP_WILL_DELAY_INTERVAL, 1800); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + rc = mosquitto_property_add_byte(proplist, MQTT_PROP_REQUEST_RESPONSE_INFORMATION, 1); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + rc = mosquitto_property_add_string(proplist, MQTT_PROP_RESPONSE_INFORMATION, "response"); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + rc = mosquitto_property_add_string(proplist, MQTT_PROP_SERVER_REFERENCE, "localhost"); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + rc = mosquitto_property_add_string(proplist, MQTT_PROP_REASON_STRING, "reason"); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + rc = mosquitto_property_add_int16(proplist, MQTT_PROP_RECEIVE_MAXIMUM, 1024); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + rc = mosquitto_property_add_int16(proplist, MQTT_PROP_TOPIC_ALIAS_MAXIMUM, 64); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + rc = mosquitto_property_add_int16(proplist, MQTT_PROP_TOPIC_ALIAS, 15); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + rc = mosquitto_property_add_byte(proplist, MQTT_PROP_MAXIMUM_QOS, 0); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + rc = mosquitto_property_add_byte(proplist, MQTT_PROP_RETAIN_AVAILABLE, 0); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + // STRING PAIR MISSING: MQTT_PROP_USER_PROPERTY + rc = mosquitto_property_add_int32(proplist, MQTT_PROP_MAXIMUM_PACKET_SIZE, 200000000); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + rc = mosquitto_property_add_byte(proplist, MQTT_PROP_WILDCARD_SUB_AVAILABLE, 0); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + rc = mosquitto_property_add_byte(proplist, MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE, 0); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + rc = mosquitto_property_add_byte(proplist, MQTT_PROP_SHARED_SUB_AVAILABLE, 0); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); +} + +/* ======================================================================== + * SINGLE READ + * ======================================================================== */ + +static void read_byte_helper(const mosquitto_property *proplist, int identifier, uint8_t expected_value) +{ + const mosquitto_property *prop; + uint8_t value; + + prop = mosquitto_property_read_byte(proplist, identifier, &value, false); + CU_ASSERT_PTR_NOT_NULL(prop); + CU_ASSERT_EQUAL(value, expected_value); +} + +static void read_int16_helper(const mosquitto_property *proplist, int identifier, uint16_t expected_value) +{ + const mosquitto_property *prop; + uint16_t value; + + prop = mosquitto_property_read_int16(proplist, identifier, &value, false); + CU_ASSERT_PTR_NOT_NULL(prop); + CU_ASSERT_EQUAL(value, expected_value); +} + +static void read_int32_helper(const mosquitto_property *proplist, int identifier, uint32_t expected_value) +{ + const mosquitto_property *prop; + uint32_t value; + + prop = mosquitto_property_read_int32(proplist, identifier, &value, false); + CU_ASSERT_PTR_NOT_NULL(prop); + CU_ASSERT_EQUAL(value, expected_value); +} + +static void read_varint_helper(const mosquitto_property *proplist, int identifier, uint32_t expected_value) +{ + const mosquitto_property *prop; + uint32_t value; + + prop = mosquitto_property_read_varint(proplist, identifier, &value, false); + CU_ASSERT_PTR_NOT_NULL(prop); + CU_ASSERT_EQUAL(value, expected_value); +} + +static void read_binary_helper(const mosquitto_property *proplist, int identifier, void *expected_value, int expected_length) +{ + const mosquitto_property *prop; + void *value; + int length; + + prop = mosquitto_property_read_binary(proplist, identifier, &value, &length, false); + CU_ASSERT_PTR_NOT_NULL(prop); + CU_ASSERT_EQUAL(length, expected_length); + CU_ASSERT_NSTRING_EQUAL(value, expected_value, expected_length); + free(value); +} + +static void read_string_helper(const mosquitto_property *proplist, int identifier, char *expected_value) +{ + const mosquitto_property *prop; + char *value; + + prop = mosquitto_property_read_string(proplist, identifier, &value, false); + CU_ASSERT_PTR_NOT_NULL(prop); + CU_ASSERT_STRING_EQUAL(value, expected_value); + free(value); +} + +static void read_string_pair_helper(const mosquitto_property *proplist, int identifier, char *expected_key, char *expected_value) +{ + const mosquitto_property *prop; + char *key, *value; + + prop = mosquitto_property_read_string_pair(proplist, identifier, &key, &value, false); + CU_ASSERT_PTR_NOT_NULL(prop); + CU_ASSERT_STRING_EQUAL(key, expected_key); + CU_ASSERT_STRING_EQUAL(value, expected_value); + free(key); + free(value); +} + + +static void TEST_read_single_byte(void) +{ + int rc; + mosquitto_property *proplist = NULL, *proplist_copy = NULL; + + generate_full_proplist(&proplist); + if(!proplist) return; + + read_byte_helper(proplist, MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, 1); + read_byte_helper(proplist, MQTT_PROP_REQUEST_PROBLEM_INFORMATION, 1); + read_byte_helper(proplist, MQTT_PROP_REQUEST_RESPONSE_INFORMATION, 1); + read_byte_helper(proplist, MQTT_PROP_MAXIMUM_QOS, 0); + read_byte_helper(proplist, MQTT_PROP_RETAIN_AVAILABLE, 0); + read_byte_helper(proplist, MQTT_PROP_WILDCARD_SUB_AVAILABLE, 0); + read_byte_helper(proplist, MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE, 0); + read_byte_helper(proplist, MQTT_PROP_SHARED_SUB_AVAILABLE, 0); + + rc = mosquitto_property_copy_all(&proplist_copy, proplist); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_NOT_NULL(proplist_copy); + + read_byte_helper(proplist_copy, MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, 1); + read_byte_helper(proplist_copy, MQTT_PROP_REQUEST_PROBLEM_INFORMATION, 1); + read_byte_helper(proplist_copy, MQTT_PROP_REQUEST_RESPONSE_INFORMATION, 1); + read_byte_helper(proplist_copy, MQTT_PROP_MAXIMUM_QOS, 0); + read_byte_helper(proplist_copy, MQTT_PROP_RETAIN_AVAILABLE, 0); + read_byte_helper(proplist_copy, MQTT_PROP_WILDCARD_SUB_AVAILABLE, 0); + read_byte_helper(proplist_copy, MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE, 0); + read_byte_helper(proplist_copy, MQTT_PROP_SHARED_SUB_AVAILABLE, 0); + + mosquitto_property_free_all(&proplist); + mosquitto_property_free_all(&proplist_copy); +} + +static void TEST_read_single_int16(void) +{ + int rc; + mosquitto_property *proplist = NULL, *proplist_copy = NULL; + + generate_full_proplist(&proplist); + if(!proplist) return; + + read_int16_helper(proplist, MQTT_PROP_SERVER_KEEP_ALIVE, 180); + read_int16_helper(proplist, MQTT_PROP_RECEIVE_MAXIMUM, 1024); + read_int16_helper(proplist, MQTT_PROP_TOPIC_ALIAS_MAXIMUM, 64); + read_int16_helper(proplist, MQTT_PROP_TOPIC_ALIAS, 15); + + rc = mosquitto_property_copy_all(&proplist_copy, proplist); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_NOT_NULL(proplist_copy); + + read_int16_helper(proplist_copy, MQTT_PROP_SERVER_KEEP_ALIVE, 180); + read_int16_helper(proplist_copy, MQTT_PROP_RECEIVE_MAXIMUM, 1024); + read_int16_helper(proplist_copy, MQTT_PROP_TOPIC_ALIAS_MAXIMUM, 64); + read_int16_helper(proplist_copy, MQTT_PROP_TOPIC_ALIAS, 15); + + mosquitto_property_free_all(&proplist); + mosquitto_property_free_all(&proplist_copy); +} + +static void TEST_read_single_int32(void) +{ + int rc; + mosquitto_property *proplist = NULL, *proplist_copy = NULL; + + generate_full_proplist(&proplist); + if(!proplist) return; + + read_int32_helper(proplist, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, 3600); + read_int32_helper(proplist, MQTT_PROP_SESSION_EXPIRY_INTERVAL, 86400); + read_int32_helper(proplist, MQTT_PROP_WILL_DELAY_INTERVAL, 1800); + read_int32_helper(proplist, MQTT_PROP_MAXIMUM_PACKET_SIZE, 200000000); + + rc = mosquitto_property_copy_all(&proplist_copy, proplist); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_NOT_NULL(proplist_copy); + + read_int32_helper(proplist_copy, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, 3600); + read_int32_helper(proplist_copy, MQTT_PROP_SESSION_EXPIRY_INTERVAL, 86400); + read_int32_helper(proplist_copy, MQTT_PROP_WILL_DELAY_INTERVAL, 1800); + read_int32_helper(proplist_copy, MQTT_PROP_MAXIMUM_PACKET_SIZE, 200000000); + + mosquitto_property_free_all(&proplist); + mosquitto_property_free_all(&proplist_copy); +} + +static void TEST_read_single_varint(void) +{ + int rc; + mosquitto_property *proplist = NULL, *proplist_copy = NULL; + + generate_full_proplist(&proplist); + if(!proplist) return; + + read_varint_helper(proplist, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 63); + + rc = mosquitto_property_copy_all(&proplist_copy, proplist); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_NOT_NULL(proplist_copy); + + read_varint_helper(proplist_copy, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 63); + + mosquitto_property_free_all(&proplist); + mosquitto_property_free_all(&proplist_copy); +} + +static void TEST_read_single_binary(void) +{ + int rc; + mosquitto_property *proplist = NULL, *proplist_copy = NULL; + + generate_full_proplist(&proplist); + if(!proplist) return; + + read_binary_helper(proplist, MQTT_PROP_CORRELATION_DATA, "correlation-data", strlen("correlation-data")); + read_binary_helper(proplist, MQTT_PROP_AUTHENTICATION_DATA, "password", strlen("password")); + + rc = mosquitto_property_copy_all(&proplist_copy, proplist); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_NOT_NULL(proplist_copy); + + read_binary_helper(proplist_copy, MQTT_PROP_CORRELATION_DATA, "correlation-data", strlen("correlation-data")); + read_binary_helper(proplist_copy, MQTT_PROP_AUTHENTICATION_DATA, "password", strlen("password")); + + mosquitto_property_free_all(&proplist); + mosquitto_property_free_all(&proplist_copy); +} + +static void TEST_read_single_string(void) +{ + int rc; + mosquitto_property *proplist = NULL, *proplist_copy = NULL; + + generate_full_proplist(&proplist); + if(!proplist) return; + + read_string_helper(proplist, MQTT_PROP_CONTENT_TYPE, "application/json"); + read_string_helper(proplist, MQTT_PROP_RESPONSE_TOPIC, "response/topic"); + read_string_helper(proplist, MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER, "mosquitto-test"); + read_string_helper(proplist, MQTT_PROP_AUTHENTICATION_METHOD, "basic"); + read_string_helper(proplist, MQTT_PROP_RESPONSE_INFORMATION, "response"); + read_string_helper(proplist, MQTT_PROP_SERVER_REFERENCE, "localhost"); + read_string_helper(proplist, MQTT_PROP_REASON_STRING, "reason"); + + rc = mosquitto_property_copy_all(&proplist_copy, proplist); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_NOT_NULL(proplist_copy); + + read_string_helper(proplist_copy, MQTT_PROP_CONTENT_TYPE, "application/json"); + read_string_helper(proplist_copy, MQTT_PROP_RESPONSE_TOPIC, "response/topic"); + read_string_helper(proplist_copy, MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER, "mosquitto-test"); + read_string_helper(proplist_copy, MQTT_PROP_AUTHENTICATION_METHOD, "basic"); + read_string_helper(proplist_copy, MQTT_PROP_RESPONSE_INFORMATION, "response"); + read_string_helper(proplist_copy, MQTT_PROP_SERVER_REFERENCE, "localhost"); + read_string_helper(proplist_copy, MQTT_PROP_REASON_STRING, "reason"); + + mosquitto_property_free_all(&proplist); + mosquitto_property_free_all(&proplist_copy); +} + +static void TEST_read_single_string_pair(void) +{ + int rc; + mosquitto_property *proplist = NULL, *proplist_copy = NULL; + + generate_full_proplist(&proplist); + if(!proplist) return; + + read_string_pair_helper(proplist, MQTT_PROP_USER_PROPERTY, "user-agent", "mosquitto/test"); + + rc = mosquitto_property_copy_all(&proplist_copy, proplist); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_NOT_NULL(proplist_copy); + + read_string_pair_helper(proplist_copy, MQTT_PROP_USER_PROPERTY, "user-agent", "mosquitto/test"); + + mosquitto_property_free_all(&proplist); + mosquitto_property_free_all(&proplist_copy); +} + +/* ======================================================================== + * MISSING READ + * ======================================================================== */ + +static void missing_read_helper(mosquitto_property *proplist) +{ + const mosquitto_property *prop; + uint8_t byte_value; + uint16_t int16_value; + uint32_t int32_value; + char *key, *value; + int length; + + /* MISSING */ + prop = mosquitto_property_read_byte(proplist, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, &byte_value, false); + CU_ASSERT_PTR_NULL(prop); + + /* NOT MISSING */ + prop = mosquitto_property_read_int32(proplist, MQTT_PROP_WILL_DELAY_INTERVAL, &int32_value, false); + CU_ASSERT_PTR_NOT_NULL(prop); + CU_ASSERT_EQUAL(int32_value, 1800); + + /* MISSING */ + prop = mosquitto_property_read_string(proplist, MQTT_PROP_CONTENT_TYPE, &value, false); + CU_ASSERT_PTR_NULL(prop); + + /* NOT MISSING */ + prop = mosquitto_property_read_string(proplist, MQTT_PROP_RESPONSE_TOPIC, &value, false); + CU_ASSERT_PTR_NOT_NULL(prop); + CU_ASSERT_STRING_EQUAL(value, "response/topic"); + free(value); + + /* MISSING */ + prop = mosquitto_property_read_binary(proplist, MQTT_PROP_CORRELATION_DATA, (void **)&value, &length, false); + CU_ASSERT_PTR_NULL(prop); + + /* NOT MISSING */ + prop = mosquitto_property_read_byte(proplist, MQTT_PROP_REQUEST_PROBLEM_INFORMATION, &byte_value, false); + CU_ASSERT_PTR_NOT_NULL(prop); + CU_ASSERT_EQUAL(byte_value, 1); + + /* MISSING */ + prop = mosquitto_property_read_varint(proplist, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, &int32_value, false); + CU_ASSERT_PTR_NULL(prop); + + /* NOT MISSING */ + prop = mosquitto_property_read_string(proplist, MQTT_PROP_SERVER_REFERENCE, &value, false); + CU_ASSERT_PTR_NOT_NULL(prop); + CU_ASSERT_STRING_EQUAL(value, "localhost"); + free(value); + + /* MISSING */ + prop = mosquitto_property_read_int32(proplist, MQTT_PROP_SESSION_EXPIRY_INTERVAL, &int32_value, false); + CU_ASSERT_PTR_NULL(prop); + + /* NOT MISSING */ + prop = mosquitto_property_read_binary(proplist, MQTT_PROP_AUTHENTICATION_DATA, (void **)&value, &length, false); + CU_ASSERT_PTR_NOT_NULL(prop); + CU_ASSERT_NSTRING_EQUAL(value, "password", strlen("password")); + CU_ASSERT_EQUAL(length, strlen("password")); + free(value); + + /* MISSING */ + prop = mosquitto_property_read_int16(proplist, MQTT_PROP_SERVER_KEEP_ALIVE, &int16_value, false); + CU_ASSERT_PTR_NULL(prop); + + /* NOT MISSING */ + prop = mosquitto_property_read_int16(proplist, MQTT_PROP_RECEIVE_MAXIMUM, &int16_value, false); + CU_ASSERT_PTR_NOT_NULL(prop); + CU_ASSERT_EQUAL(int16_value, 1024); + + /* MISSING */ + prop = mosquitto_property_read_string_pair(proplist, MQTT_PROP_USER_PROPERTY, &key, &value, false); + CU_ASSERT_PTR_NULL(prop); +} + + +static void TEST_read_missing(void) +{ + mosquitto_property *proplist = NULL, *proplist_copy = NULL; + int rc; + + generate_partial_proplist(&proplist); + if(!proplist) return; + + missing_read_helper(proplist); + rc = mosquitto_property_copy_all(&proplist_copy, proplist); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_PTR_NOT_NULL(proplist_copy); + missing_read_helper(proplist_copy); + + mosquitto_property_free_all(&proplist); + mosquitto_property_free_all(&proplist_copy); +} + +/* ======================================================================== + * STRING TO PROPERTY INFO + * ======================================================================== */ + +static void string_to_property_info_helper(const char *str, int rc_expected, int identifier_expected, int type_expected) +{ + int rc; + int identifier, type; + + rc = mosquitto_string_to_property_info(str, &identifier, &type); + CU_ASSERT_EQUAL(rc, rc_expected); + if(rc == MOSQ_ERR_SUCCESS){ + CU_ASSERT_EQUAL(identifier, identifier_expected); + CU_ASSERT_EQUAL(type, type_expected); + } +} + +static void TEST_string_to_property_info(void) +{ + string_to_property_info_helper("payload-format-indicator", MOSQ_ERR_SUCCESS, MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, MQTT_PROP_TYPE_BYTE); + string_to_property_info_helper("message-expiry-interval", MOSQ_ERR_SUCCESS, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, MQTT_PROP_TYPE_INT32); + string_to_property_info_helper("content-type", MOSQ_ERR_SUCCESS, MQTT_PROP_CONTENT_TYPE, MQTT_PROP_TYPE_STRING); + string_to_property_info_helper("response-topic", MOSQ_ERR_SUCCESS, MQTT_PROP_RESPONSE_TOPIC, MQTT_PROP_TYPE_STRING); + string_to_property_info_helper("correlation-data", MOSQ_ERR_SUCCESS, MQTT_PROP_CORRELATION_DATA, MQTT_PROP_TYPE_BINARY); + string_to_property_info_helper("subscription-identifier", MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, MQTT_PROP_TYPE_VARINT); + string_to_property_info_helper("session-expiry-interval", MOSQ_ERR_SUCCESS, MQTT_PROP_SESSION_EXPIRY_INTERVAL, MQTT_PROP_TYPE_INT32); + string_to_property_info_helper("assigned-client-identifier", MOSQ_ERR_SUCCESS, MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER, MQTT_PROP_TYPE_STRING); + string_to_property_info_helper("server-keep-alive", MOSQ_ERR_SUCCESS, MQTT_PROP_SERVER_KEEP_ALIVE, MQTT_PROP_TYPE_INT16); + string_to_property_info_helper("authentication-method", MOSQ_ERR_SUCCESS, MQTT_PROP_AUTHENTICATION_METHOD, MQTT_PROP_TYPE_STRING); + string_to_property_info_helper("authentication-data", MOSQ_ERR_SUCCESS, MQTT_PROP_AUTHENTICATION_DATA, MQTT_PROP_TYPE_BINARY); + string_to_property_info_helper("request-problem-information", MOSQ_ERR_SUCCESS, MQTT_PROP_REQUEST_PROBLEM_INFORMATION, MQTT_PROP_TYPE_BYTE); + string_to_property_info_helper("will-delay-interval", MOSQ_ERR_SUCCESS, MQTT_PROP_WILL_DELAY_INTERVAL, MQTT_PROP_TYPE_INT32); + string_to_property_info_helper("request-response-information", MOSQ_ERR_SUCCESS, MQTT_PROP_REQUEST_RESPONSE_INFORMATION, MQTT_PROP_TYPE_BYTE); + string_to_property_info_helper("response-information", MOSQ_ERR_SUCCESS, MQTT_PROP_RESPONSE_INFORMATION, MQTT_PROP_TYPE_STRING); + string_to_property_info_helper("server-reference", MOSQ_ERR_SUCCESS, MQTT_PROP_SERVER_REFERENCE, MQTT_PROP_TYPE_STRING); + string_to_property_info_helper("reason-string", MOSQ_ERR_SUCCESS, MQTT_PROP_REASON_STRING, MQTT_PROP_TYPE_STRING); + string_to_property_info_helper("receive-maximum", MOSQ_ERR_SUCCESS, MQTT_PROP_RECEIVE_MAXIMUM, MQTT_PROP_TYPE_INT16); + string_to_property_info_helper("topic-alias-maximum", MOSQ_ERR_SUCCESS, MQTT_PROP_TOPIC_ALIAS_MAXIMUM, MQTT_PROP_TYPE_INT16); + string_to_property_info_helper("topic-alias", MOSQ_ERR_SUCCESS, MQTT_PROP_TOPIC_ALIAS, MQTT_PROP_TYPE_INT16); + string_to_property_info_helper("maximum-qos", MOSQ_ERR_SUCCESS, MQTT_PROP_MAXIMUM_QOS, MQTT_PROP_TYPE_BYTE); + string_to_property_info_helper("retain-available", MOSQ_ERR_SUCCESS, MQTT_PROP_RETAIN_AVAILABLE, MQTT_PROP_TYPE_BYTE); + string_to_property_info_helper("user-property", MOSQ_ERR_SUCCESS, MQTT_PROP_USER_PROPERTY, MQTT_PROP_TYPE_STRING_PAIR); + string_to_property_info_helper("maximum-packet-size", MOSQ_ERR_SUCCESS, MQTT_PROP_MAXIMUM_PACKET_SIZE, MQTT_PROP_TYPE_INT32); + string_to_property_info_helper("wildcard-subscription-available", MOSQ_ERR_SUCCESS, MQTT_PROP_WILDCARD_SUB_AVAILABLE, MQTT_PROP_TYPE_BYTE); + string_to_property_info_helper("subscription-identifier-available", MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE, MQTT_PROP_TYPE_BYTE); + string_to_property_info_helper("shared-subscription-available", MOSQ_ERR_SUCCESS, MQTT_PROP_SHARED_SUB_AVAILABLE, MQTT_PROP_TYPE_BYTE); + + string_to_property_info_helper("payload-format-indicator1", MOSQ_ERR_INVAL, 0, 0); + string_to_property_info_helper("payload", MOSQ_ERR_INVAL, 0, 0); + string_to_property_info_helper("", MOSQ_ERR_INVAL, 0, 0); + string_to_property_info_helper(NULL, MOSQ_ERR_INVAL, 0, 0); +} + + +/* ======================================================================== + * TEST SUITE SETUP + * ======================================================================== */ + +int init_property_user_read_tests(void) +{ + CU_pSuite test_suite = NULL; + + test_suite = CU_add_suite("Property user read", NULL, NULL); + if(!test_suite){ + printf("Error adding CUnit Property user read test suite.\n"); + return 1; + } + + if(0 + || !CU_add_test(test_suite, "Read single byte", TEST_read_single_byte) + || !CU_add_test(test_suite, "Read single int16", TEST_read_single_int16) + || !CU_add_test(test_suite, "Read single int32", TEST_read_single_int32) + || !CU_add_test(test_suite, "Read single varint", TEST_read_single_varint) + || !CU_add_test(test_suite, "Read single binary", TEST_read_single_binary) + || !CU_add_test(test_suite, "Read single string", TEST_read_single_string) + || !CU_add_test(test_suite, "Read single string pair", TEST_read_single_string_pair) + || !CU_add_test(test_suite, "Read missing", TEST_read_missing) + || !CU_add_test(test_suite, "String to property info", TEST_string_to_property_info) + ){ + + printf("Error adding Property Add CUnit tests.\n"); + return 1; + } + + return 0; +} diff --git a/test/unit/property_write.c b/test/unit/property_write.c new file mode 100644 index 00000000..7a4a3d04 --- /dev/null +++ b/test/unit/property_write.c @@ -0,0 +1,543 @@ +#include +#include + +#include "mqtt_protocol.h" +#include "property_mosq.h" +#include "packet_mosq.h" + +static void byte_prop_write_helper( + int command, + int remaining_length, + int rc_expected, + int identifier, + uint8_t value_expected) +{ + mosquitto_property property; + struct mosquitto__packet packet; + mosquitto_property *properties; + int rc; + + memset(&property, 0, sizeof(mosquitto_property)); + + property.identifier = identifier; + property.value.i8 = value_expected; + + memset(&packet, 0, sizeof(struct mosquitto__packet)); + packet.remaining_length = property__get_length_all(&property)+1; + packet.packet_length = packet.remaining_length+10; + packet.payload = calloc(packet.remaining_length+10, 1); + + property__write_all(&packet, &property, true); + packet.pos = 0; + + rc = property__read_all(command, &packet, &properties); + + CU_ASSERT_EQUAL(rc, rc_expected); + CU_ASSERT_EQUAL(packet.pos, remaining_length); + if(properties){ + CU_ASSERT_EQUAL(properties->identifier, identifier); + CU_ASSERT_EQUAL(properties->value.i8, value_expected); + CU_ASSERT_PTR_EQUAL(properties->next, NULL); + CU_ASSERT_EQUAL(property__get_length_all(properties), 2); + mosquitto_property_free_all(&properties); + } + CU_ASSERT_PTR_EQUAL(properties, NULL); + free(packet.payload); +} + + +static void int32_prop_write_helper( + int command, + int remaining_length, + int rc_expected, + int identifier, + uint32_t value_expected) +{ + mosquitto_property property; + struct mosquitto__packet packet; + mosquitto_property *properties; + int rc; + + memset(&property, 0, sizeof(mosquitto_property)); + + property.identifier = identifier; + property.value.i32 = value_expected; + + memset(&packet, 0, sizeof(struct mosquitto__packet)); + packet.remaining_length = property__get_length_all(&property)+1; + packet.packet_length = packet.remaining_length+10; + packet.payload = calloc(packet.remaining_length+10, 1); + + property__write_all(&packet, &property, true); + packet.pos = 0; + + rc = property__read_all(command, &packet, &properties); + + CU_ASSERT_EQUAL(rc, rc_expected); + CU_ASSERT_EQUAL(packet.pos, remaining_length); + if(properties){ + CU_ASSERT_EQUAL(properties->identifier, identifier); + CU_ASSERT_EQUAL(properties->value.i32, value_expected); + CU_ASSERT_PTR_EQUAL(properties->next, NULL); + CU_ASSERT_EQUAL(property__get_length_all(properties), 5); + mosquitto_property_free_all(&properties); + } + CU_ASSERT_PTR_EQUAL(properties, NULL); + free(packet.payload); +} + + +static void int16_prop_write_helper( + int command, + int remaining_length, + int rc_expected, + int identifier, + uint16_t value_expected) +{ + mosquitto_property property; + struct mosquitto__packet packet; + mosquitto_property *properties; + int rc; + + memset(&property, 0, sizeof(mosquitto_property)); + + property.identifier = identifier; + property.value.i16 = value_expected; + + memset(&packet, 0, sizeof(struct mosquitto__packet)); + packet.remaining_length = property__get_length_all(&property)+1; + packet.packet_length = packet.remaining_length+10; + packet.payload = calloc(packet.remaining_length+10, 1); + + property__write_all(&packet, &property, true); + packet.pos = 0; + + rc = property__read_all(command, &packet, &properties); + + CU_ASSERT_EQUAL(rc, rc_expected); + CU_ASSERT_EQUAL(packet.pos, remaining_length); + if(properties){ + CU_ASSERT_EQUAL(properties->identifier, identifier); + CU_ASSERT_EQUAL(properties->value.i16, value_expected); + CU_ASSERT_PTR_EQUAL(properties->next, NULL); + CU_ASSERT_EQUAL(property__get_length_all(properties), 3); + mosquitto_property_free_all(&properties); + } + CU_ASSERT_PTR_EQUAL(properties, NULL); + free(packet.payload); +} + +static void string_prop_write_helper( + int command, + int remaining_length, + int rc_expected, + int identifier, + const char *value_expected) +{ + mosquitto_property property; + struct mosquitto__packet packet; + mosquitto_property *properties; + int rc; + + memset(&property, 0, sizeof(mosquitto_property)); + + property.identifier = identifier; + property.value.s.v = strdup(value_expected); + property.value.s.len = strlen(value_expected); + + memset(&packet, 0, sizeof(struct mosquitto__packet)); + packet.remaining_length = property__get_length_all(&property)+1; + packet.packet_length = packet.remaining_length+10; + packet.payload = calloc(packet.remaining_length+10, 1); + + property__write_all(&packet, &property, true); + packet.pos = 0; + + rc = property__read_all(command, &packet, &properties); + + CU_ASSERT_EQUAL(rc, rc_expected); + CU_ASSERT_EQUAL(packet.pos, remaining_length); + if(properties){ + CU_ASSERT_EQUAL(properties->identifier, identifier); + CU_ASSERT_EQUAL(properties->value.s.len, strlen(value_expected)); + CU_ASSERT_STRING_EQUAL(properties->value.s.v, value_expected); + CU_ASSERT_PTR_EQUAL(properties->next, NULL); + CU_ASSERT_EQUAL(property__get_length_all(properties), 1+2+strlen(value_expected)); + mosquitto_property_free_all(&properties); + } + CU_ASSERT_PTR_EQUAL(properties, NULL); + free(property.value.s.v); + free(packet.payload); +} + + +static void binary_prop_write_helper( + int command, + int remaining_length, + int rc_expected, + int identifier, + const uint8_t *value_expected, + int len_expected) +{ + mosquitto_property property; + struct mosquitto__packet packet; + mosquitto_property *properties; + int rc; + + memset(&property, 0, sizeof(mosquitto_property)); + + property.identifier = identifier; + property.value.bin.v = malloc(len_expected); + memcpy(property.value.bin.v, value_expected, len_expected); + property.value.bin.len = len_expected; + + memset(&packet, 0, sizeof(struct mosquitto__packet)); + packet.remaining_length = property__get_length_all(&property)+1; + packet.packet_length = packet.remaining_length+10; + packet.payload = calloc(packet.remaining_length+10, 1); + + property__write_all(&packet, &property, true); + packet.pos = 0; + + rc = property__read_all(command, &packet, &properties); + + CU_ASSERT_EQUAL(rc, rc_expected); + CU_ASSERT_EQUAL(packet.pos, remaining_length); + if(properties){ + CU_ASSERT_EQUAL(properties->identifier, identifier); + CU_ASSERT_EQUAL(properties->value.bin.len, len_expected); + CU_ASSERT_EQUAL(memcmp(properties->value.bin.v, value_expected, len_expected), 0); + CU_ASSERT_PTR_EQUAL(properties->next, NULL); + CU_ASSERT_EQUAL(property__get_length_all(properties), 1+2+len_expected); + mosquitto_property_free_all(&properties); + } + CU_ASSERT_PTR_EQUAL(properties, NULL); + free(property.value.bin.v); + free(packet.payload); +} + +static void string_pair_prop_write_helper( + int remaining_length, + int rc_expected, + int identifier, + const char *name_expected, + const char *value_expected, + bool expect_multiple) +{ + mosquitto_property property; + struct mosquitto__packet packet; + mosquitto_property *properties; + int rc; + + memset(&property, 0, sizeof(mosquitto_property)); + + property.identifier = identifier; + property.value.s.v = strdup(value_expected); + property.value.s.len = strlen(value_expected); + property.name.v = strdup(name_expected); + property.name.len = strlen(name_expected); + + memset(&packet, 0, sizeof(struct mosquitto__packet)); + packet.remaining_length = property__get_length_all(&property)+1; + packet.packet_length = packet.remaining_length+10; + packet.payload = calloc(packet.remaining_length+10, 1); + + property__write_all(&packet, &property, true); + packet.pos = 0; + + rc = property__read_all(CMD_CONNECT, &packet, &properties); + + CU_ASSERT_EQUAL(rc, rc_expected); + CU_ASSERT_EQUAL(packet.pos, remaining_length); + if(properties){ + CU_ASSERT_EQUAL(properties->identifier, identifier); + CU_ASSERT_EQUAL(properties->name.len, strlen(name_expected)); + CU_ASSERT_EQUAL(properties->value.s.len, strlen(value_expected)); + CU_ASSERT_STRING_EQUAL(properties->name.v, name_expected); + CU_ASSERT_STRING_EQUAL(properties->value.s.v, value_expected); + if(expect_multiple){ + CU_ASSERT_PTR_NOT_NULL(properties->next); + }else{ + CU_ASSERT_PTR_NULL(properties->next); + CU_ASSERT_EQUAL(property__get_length_all(properties), 1+2+strlen(name_expected)+2+strlen(value_expected)); + } + mosquitto_property_free_all(&properties); + } + CU_ASSERT_PTR_NULL(properties); + free(property.value.s.v); + free(property.name.v); + free(packet.payload); +} + +static void varint_prop_write_helper( + int remaining_length, + int rc_expected, + int identifier, + uint32_t value_expected) +{ + mosquitto_property property; + struct mosquitto__packet packet; + mosquitto_property *properties; + int rc; + + memset(&property, 0, sizeof(mosquitto_property)); + + property.identifier = identifier; + property.value.varint = value_expected; + + memset(&packet, 0, sizeof(struct mosquitto__packet)); + packet.remaining_length = property__get_length_all(&property)+1; + packet.packet_length = packet.remaining_length+10; + packet.payload = calloc(packet.remaining_length+10, 1); + + property__write_all(&packet, &property, true); + packet.pos = 0; + + rc = property__read_all(CMD_PUBLISH, &packet, &properties); + + CU_ASSERT_EQUAL(rc, rc_expected); + if(properties){ + CU_ASSERT_EQUAL(properties->identifier, identifier); + CU_ASSERT_EQUAL(properties->value.varint, value_expected); + CU_ASSERT_PTR_NULL(properties->next); + if(value_expected < 128){ + CU_ASSERT_EQUAL(property__get_length_all(properties), 2); + }else if(value_expected < 16384){ + CU_ASSERT_EQUAL(property__get_length_all(properties), 3); + }else if(value_expected < 2097152){ + CU_ASSERT_EQUAL(property__get_length_all(properties), 4); + }else if(value_expected < 268435456){ + CU_ASSERT_EQUAL(property__get_length_all(properties), 5); + }else{ + CU_FAIL("Incorrect varint value."); + } + mosquitto_property_free_all(&properties); + } + CU_ASSERT_PTR_NULL(properties); + free(packet.payload); +} + +/* ======================================================================== + * BAD IDENTIFIER + * ======================================================================== */ + +static void TEST_bad_identifier(void) +{ + mosquitto_property property; + struct mosquitto__packet packet; + uint8_t payload[10]; + int rc; + + memset(&property, 0, sizeof(mosquitto_property)); + memset(&packet, 0, sizeof(struct mosquitto__packet)); + property.identifier = 0xFFFF; + packet.packet_length = 10; + packet.remaining_length = 8; + packet.payload = payload; + rc = property__write_all(&packet, &property, true); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); +} + + +/* ======================================================================== + * SINGLE PROPERTIES + * ======================================================================== */ + +static void TEST_single_payload_format_indicator(void) +{ + byte_prop_write_helper(CMD_PUBLISH, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, 1); +} + +static void TEST_single_request_problem_information(void) +{ + byte_prop_write_helper(CMD_CONNECT, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_REQUEST_PROBLEM_INFORMATION, 1); +} + +static void TEST_single_request_response_information(void) +{ + byte_prop_write_helper(CMD_CONNECT, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_REQUEST_RESPONSE_INFORMATION, 1); +} + +static void TEST_single_maximum_qos(void) +{ + byte_prop_write_helper(CMD_CONNACK, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_MAXIMUM_QOS, 1); +} + +static void TEST_single_retain_available(void) +{ + byte_prop_write_helper(CMD_CONNACK, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_RETAIN_AVAILABLE, 1); +} + +static void TEST_single_wildcard_subscription_available(void) +{ + byte_prop_write_helper(CMD_CONNACK, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_WILDCARD_SUB_AVAILABLE, 0); +} + +static void TEST_single_subscription_identifier_available(void) +{ + byte_prop_write_helper(CMD_CONNACK, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE, 0); +} + +static void TEST_single_shared_subscription_available(void) +{ + byte_prop_write_helper(CMD_CONNACK, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_SHARED_SUB_AVAILABLE, 1); +} + +static void TEST_single_message_expiry_interval(void) +{ + int32_prop_write_helper(CMD_PUBLISH, 6, MOSQ_ERR_SUCCESS, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, 0x12233445); +} + +static void TEST_single_session_expiry_interval(void) +{ + int32_prop_write_helper(CMD_CONNACK, 6, MOSQ_ERR_SUCCESS, MQTT_PROP_SESSION_EXPIRY_INTERVAL, 0x45342312); +} + +static void TEST_single_will_delay_interval(void) +{ + int32_prop_write_helper(CMD_WILL, 6, MOSQ_ERR_SUCCESS, MQTT_PROP_WILL_DELAY_INTERVAL, 0x45342312); +} + +static void TEST_single_maximum_packet_size(void) +{ + int32_prop_write_helper(CMD_CONNECT, 6, MOSQ_ERR_SUCCESS, MQTT_PROP_MAXIMUM_PACKET_SIZE, 0x45342312); +} + +static void TEST_single_server_keep_alive(void) +{ + int16_prop_write_helper(CMD_CONNACK, 4, MOSQ_ERR_SUCCESS, MQTT_PROP_SERVER_KEEP_ALIVE, 0x4534); +} + +static void TEST_single_receive_maximum(void) +{ + int16_prop_write_helper(CMD_CONNACK, 4, MOSQ_ERR_SUCCESS, MQTT_PROP_RECEIVE_MAXIMUM, 0x6842); +} + +static void TEST_single_topic_alias_maximum(void) +{ + int16_prop_write_helper(CMD_CONNECT, 4, MOSQ_ERR_SUCCESS, MQTT_PROP_TOPIC_ALIAS_MAXIMUM, 0x6842); +} + +static void TEST_single_topic_alias(void) +{ + int16_prop_write_helper(CMD_PUBLISH, 4, MOSQ_ERR_SUCCESS, MQTT_PROP_TOPIC_ALIAS, 0x6842); +} + +static void TEST_single_content_type(void) +{ + string_prop_write_helper(CMD_PUBLISH, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_CONTENT_TYPE, "hello"); +} + +static void TEST_single_response_topic(void) +{ + string_prop_write_helper(CMD_WILL, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_RESPONSE_TOPIC, "hello"); +} + +static void TEST_single_assigned_client_identifier(void) +{ + string_prop_write_helper(CMD_CONNACK, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER, "hello"); +} + +static void TEST_single_authentication_method(void) +{ + string_prop_write_helper(CMD_CONNECT, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_AUTHENTICATION_METHOD, "hello"); +} + +static void TEST_single_response_information(void) +{ + string_prop_write_helper(CMD_CONNACK, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_RESPONSE_INFORMATION, "hello"); +} + +static void TEST_single_server_reference(void) +{ + string_prop_write_helper(CMD_CONNACK, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_SERVER_REFERENCE, "hello"); +} + +static void TEST_single_reason_string(void) +{ + string_prop_write_helper(CMD_PUBREC, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_REASON_STRING, "hello"); +} + +static void TEST_single_correlation_data(void) +{ + uint8_t payload[5] = {1, 'e', 0, 'l', 9}; + + binary_prop_write_helper(CMD_PUBLISH, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_CORRELATION_DATA, payload, 5); +} + +static void TEST_single_authentication_data(void) +{ + uint8_t payload[5] = {1, 'e', 0, 'l', 9}; + + binary_prop_write_helper(CMD_CONNECT, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_AUTHENTICATION_DATA, payload, 5); +} + +static void TEST_single_user_property(void) +{ + string_pair_prop_write_helper(10, MOSQ_ERR_SUCCESS, MQTT_PROP_USER_PROPERTY, "za", "bc", false); +} + +static void TEST_single_subscription_identifier(void) +{ + varint_prop_write_helper(3, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 0); + varint_prop_write_helper(3, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 127); + varint_prop_write_helper(4, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 128); + varint_prop_write_helper(4, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 16383); + varint_prop_write_helper(5, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 16384); + varint_prop_write_helper(5, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 2097151); + varint_prop_write_helper(6, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 2097152); + varint_prop_write_helper(6, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 268435455); +} + + +/* ======================================================================== + * TEST SUITE SETUP + * ======================================================================== */ + +int init_property_write_tests(void) +{ + CU_pSuite test_suite = NULL; + + test_suite = CU_add_suite("Property write", NULL, NULL); + if(!test_suite){ + printf("Error adding CUnit Property write test suite.\n"); + return 1; + } + + if(0 + || !CU_add_test(test_suite, "Bad identifier", TEST_bad_identifier) + || !CU_add_test(test_suite, "Single Payload Format Indicator", TEST_single_payload_format_indicator) + || !CU_add_test(test_suite, "Single Request Problem Information", TEST_single_request_problem_information) + || !CU_add_test(test_suite, "Single Request Response Information", TEST_single_request_response_information) + || !CU_add_test(test_suite, "Single Maximum QoS", TEST_single_maximum_qos) + || !CU_add_test(test_suite, "Single Retain Available", TEST_single_retain_available) + || !CU_add_test(test_suite, "Single Wildcard Subscription Available", TEST_single_wildcard_subscription_available) + || !CU_add_test(test_suite, "Single Subscription Identifier Available", TEST_single_subscription_identifier_available) + || !CU_add_test(test_suite, "Single Shared Subscription Available", TEST_single_shared_subscription_available) + || !CU_add_test(test_suite, "Single Message Expiry Interval", TEST_single_message_expiry_interval) + || !CU_add_test(test_suite, "Single Session Expiry Interval", TEST_single_session_expiry_interval) + || !CU_add_test(test_suite, "Single Will Delay Interval", TEST_single_will_delay_interval) + || !CU_add_test(test_suite, "Single Maximum Packet Size", TEST_single_maximum_packet_size) + || !CU_add_test(test_suite, "Single Server Keep Alive", TEST_single_server_keep_alive) + || !CU_add_test(test_suite, "Single Receive Maximum", TEST_single_receive_maximum) + || !CU_add_test(test_suite, "Single Topic Alias Maximum", TEST_single_topic_alias_maximum) + || !CU_add_test(test_suite, "Single Topic Alias", TEST_single_topic_alias) + || !CU_add_test(test_suite, "Single Content Type", TEST_single_content_type) + || !CU_add_test(test_suite, "Single Response Topic", TEST_single_response_topic) + || !CU_add_test(test_suite, "Single Assigned Client Identifier", TEST_single_assigned_client_identifier) + || !CU_add_test(test_suite, "Single Authentication Method", TEST_single_authentication_method) + || !CU_add_test(test_suite, "Single Response Information", TEST_single_response_information) + || !CU_add_test(test_suite, "Single Server Reference", TEST_single_server_reference) + || !CU_add_test(test_suite, "Single Reason String", TEST_single_reason_string) + || !CU_add_test(test_suite, "Single Correlation Data", TEST_single_correlation_data) + || !CU_add_test(test_suite, "Single Authentication Data", TEST_single_authentication_data) + || !CU_add_test(test_suite, "Single User Property", TEST_single_user_property) + || !CU_add_test(test_suite, "Single Subscription Identifier", TEST_single_subscription_identifier) + ){ + + printf("Error adding Property read CUnit tests.\n"); + return 1; + } + + return 0; +} diff --git a/test/unit/publish_test.c b/test/unit/publish_test.c new file mode 100644 index 00000000..6371db3c --- /dev/null +++ b/test/unit/publish_test.c @@ -0,0 +1,43 @@ +#include +#include + +#include +#include + + +static void TEST_maximum_packet_size(void) +{ + struct mosquitto mosq; + int rc; + + memset(&mosq, 0, sizeof(struct mosquitto)); + + mosq.maximum_packet_size = 5; + rc = mosquitto_publish(&mosq, NULL, "topic/oversize", strlen("payload"), "payload", 0, 0); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_OVERSIZE_PACKET); +} + +/* ======================================================================== + * TEST SUITE SETUP + * ======================================================================== */ + +int init_publish_tests(void) +{ + CU_pSuite test_suite = NULL; + + test_suite = CU_add_suite("Publish", NULL, NULL); + if(!test_suite){ + printf("Error adding CUnit Publish test suite.\n"); + return 1; + } + + if(0 + || !CU_add_test(test_suite, "v5: Maximum packet size", TEST_maximum_packet_size) + ){ + + printf("Error adding Publish CUnit tests.\n"); + return 1; + } + + return 0; +} diff --git a/test/unit/stubs.c b/test/unit/stubs.c new file mode 100644 index 00000000..f4b7c97a --- /dev/null +++ b/test/unit/stubs.c @@ -0,0 +1,6 @@ +#include + +int log__printf(struct mosquitto *mosq, int priority, const char *fmt, ...) +{ + return 0; +} diff --git a/test/unit/test.c b/test/unit/test.c new file mode 100644 index 00000000..17a37023 --- /dev/null +++ b/test/unit/test.c @@ -0,0 +1,44 @@ +#include + +#include +#include + +int init_datatype_read_tests(void); +int init_datatype_write_tests(void); +int init_property_add_tests(void); +int init_property_read_tests(void); +int init_property_user_read_tests(void); +int init_property_write_tests(void); +int init_utf8_tests(void); +int init_util_topic_tests(void); + +int main(int argc, char *argv[]) +{ + + if(CU_initialize_registry() != CUE_SUCCESS){ + printf("Error initializing CUnit registry.\n"); + return 1; + } + + if(0 + || init_utf8_tests() + || init_datatype_read_tests() + || init_datatype_write_tests() + || init_property_add_tests() + || init_property_read_tests() + || init_property_user_read_tests() + || init_property_write_tests() + || init_util_topic_tests() + ){ + + CU_cleanup_registry(); + return 1; + } + + CU_basic_set_mode(CU_BRM_VERBOSE); + CU_basic_run_tests(); + CU_cleanup_registry(); + + return 0; +} + diff --git a/test/unit/utf8.c b/test/unit/utf8.c new file mode 100644 index 00000000..1982da87 --- /dev/null +++ b/test/unit/utf8.c @@ -0,0 +1,482 @@ +#include +#include + +#include "mosquitto.h" + +/* Test data taken from + * http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt but modified for + * updated standard (no 5, 6 byte lengths) */ + +static void utf8_helper_len(const char *text, int len, int expected) +{ + int result; + + result = mosquitto_validate_utf8(text, len); + CU_ASSERT_EQUAL(result, expected); +} + +static void utf8_helper(const char *text, int expected) +{ + utf8_helper_len(text, strlen(text), expected); +} + + +static void TEST_utf8_empty(void) +{ + utf8_helper_len(NULL, 0, MOSQ_ERR_INVAL); +} + + +static void TEST_utf8_valid(void) +{ + /* 1 Some correct UTF-8 text */ + utf8_helper("", MOSQ_ERR_SUCCESS); + utf8_helper("You should see the Greek word 'kosme': \"κόσμε\"", MOSQ_ERR_SUCCESS); +} + + +static void TEST_utf8_truncated(void) +{ + char buf[4]; + + /* As per boundary condition tests, but less one character */ + buf[0] = 0xC2; buf[1] = 0; + utf8_helper(buf, MOSQ_ERR_MALFORMED_UTF8); + + buf[0] = 0xE0; buf[1] = 0xA0; buf[2] = 0; + utf8_helper(buf, MOSQ_ERR_MALFORMED_UTF8); + + buf[0] = 0xF0; buf[1] = 0x90; buf[2] = 0x80; buf[3] = 0; + utf8_helper(buf, MOSQ_ERR_MALFORMED_UTF8); +} + + +static void TEST_utf8_boundary_conditions(void) +{ + /* 2 Boundary condition test cases */ + /* 2.1 First possible sequence of a certain length */ + utf8_helper_len("2.1.1 1 byte (U-00000000): \"\0\"", 39, MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("2.1.2 2 bytes (U-00000080): \"€\"", MOSQ_ERR_MALFORMED_UTF8); /* control char */ + utf8_helper("2.1.3 3 bytes (U-00000800): \"ࠀ\"", MOSQ_ERR_SUCCESS); + utf8_helper("2.1.4 4 bytes (U-00010000): \"𐀀\"", MOSQ_ERR_SUCCESS); + + /* 2.2 Last possible sequence of a certain length */ + + utf8_helper("2.2.1 1 byte (U-0000007F): \"\"", MOSQ_ERR_MALFORMED_UTF8); /* control char */ + utf8_helper("2.2.2 2 bytes (U-000007FF): \"߿\"", MOSQ_ERR_SUCCESS); + /* Non character */ + utf8_helper("2.2.3 3 bytes (U-0000FFFF): \"￿\"", MOSQ_ERR_MALFORMED_UTF8); + /* Non character */ + utf8_helper("2.2.4 4 bytes (U-0010FFFF): \"\"", MOSQ_ERR_MALFORMED_UTF8); + + /* 2.3 Other boundary conditions */ + + utf8_helper("2.3.1 U-0000D7FF = ed 9f bf = \"퟿\"", MOSQ_ERR_SUCCESS); + utf8_helper("2.3.2 U-0000E000 = ee 80 80 = \"\"", MOSQ_ERR_SUCCESS); + utf8_helper("2.3.3 U-0000FFFD = ef bf bd = \"�\"", MOSQ_ERR_SUCCESS); + /* Non character */ + utf8_helper("2.3.4 U-0010FFFF = f4 8f bf bf = \"􏿿\"", MOSQ_ERR_MALFORMED_UTF8); + /* This used to be valid in pre-2003 utf-8 */ + utf8_helper("2.3.5 U-00110000 = f4 90 80 80 = \"\"", MOSQ_ERR_MALFORMED_UTF8); +} + + +static void TEST_utf8_malformed_sequences(void) +{ + char buf[100]; + int i; + /* 3 Malformed sequences */ + /* 3.1 Unexpected continuation bytes */ + utf8_helper("3.1.1 First continuation byte 0x80: \"\"", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("3.1.2 Last continuation byte 0xbf: \"\"", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("3.1.3 2 continuation bytes: \"\"", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("3.1.4 3 continuation bytes: \"\"", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("3.1.5 4 continuation bytes: \"\"", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("3.1.6 5 continuation bytes: \"\"", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("3.1.7 6 continuation bytes: \"\"", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("3.1.8 7 continuation bytes: \"\"", MOSQ_ERR_MALFORMED_UTF8); + + /* 3.1.9 Sequence of all 64 possible continuation bytes (0x80-0xbf): */ + memset(buf, 0, sizeof(buf)); + for(i=0x80; i<0x90; i++){ + buf[i-0x80] = i; + } + utf8_helper(buf, MOSQ_ERR_MALFORMED_UTF8); + memset(buf, 0, sizeof(buf)); + for(i=0x90; i<0xa0; i++){ + buf[i-0x90] = i; + } + utf8_helper(buf, MOSQ_ERR_MALFORMED_UTF8); + + for(i=0x80; i<0xA0; i++){ + buf[0] = i; + buf[1] = 0; + utf8_helper(buf, MOSQ_ERR_MALFORMED_UTF8); + } + + for(i=0xA0; i<0xC0; i++){ + buf[0] = i; + buf[1] = 0; + utf8_helper(buf, MOSQ_ERR_MALFORMED_UTF8); + } + + /* 3.2 Lonely start characters */ + + /* 3.2.1 All 32 first bytes of 2-byte sequences (0xc0-0xdf), + each followed by a space character: */ + utf8_helper(" ", MOSQ_ERR_MALFORMED_UTF8); + for(i=0xC0; i<0xE0; i++){ + buf[0] = i; + buf[1] = ' '; + buf[2] = 0; + utf8_helper(buf, MOSQ_ERR_MALFORMED_UTF8); + } + + /* 3.2.2 All 16 first bytes of 3-byte sequences (0xe0-0xef), + each followed by a space character: */ + utf8_helper("\" \"", MOSQ_ERR_MALFORMED_UTF8); + for(i=0xe0; i<0xf0; i++){ + buf[0] = i; + buf[1] = ' '; + buf[2] = 0; + utf8_helper(buf, MOSQ_ERR_MALFORMED_UTF8); + } + + /* 3.2.3 All 8 first bytes of 4-byte sequences (0xf0-0xf7), + each followed by a space character: */ + utf8_helper("\" \"", MOSQ_ERR_MALFORMED_UTF8); + for(i=0xF0; i<0xF8; i++){ + buf[0] = i; + buf[1] = ' '; + buf[2] = 0; + utf8_helper(buf, MOSQ_ERR_MALFORMED_UTF8); + } + + /* 3.2.4 All 4 first bytes of 5-byte sequences (0xf8-0xfb), + each followed by a space character: */ + utf8_helper("\" \"", MOSQ_ERR_MALFORMED_UTF8); + for(i=0xF8; i<0xFC; i++){ + buf[0] = i; + buf[1] = ' '; + buf[2] = 0; + utf8_helper(buf, MOSQ_ERR_MALFORMED_UTF8); + } + + /* 3.2.5 All 2 first bytes of 6-byte sequences (0xfc-0xfd), + each followed by a space character: */ + utf8_helper("\" \"", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper(" ", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper(" ", MOSQ_ERR_MALFORMED_UTF8); + for(i=0xFC; i<0xFE; i++){ + buf[0] = i; + buf[1] = ' '; + buf[2] = 0; + utf8_helper(buf, MOSQ_ERR_MALFORMED_UTF8); + } + + /* 3.3 Sequences with last continuation byte missing + + All bytes of an incomplete sequence should be signalled as a single + malformed sequence, i.e., you should see only a single replacement + character in each of the next 10 tests. (Characters as in section 2) */ + + utf8_helper("3.3.1 2-byte sequence with last byte missing (U+0000): \"\"", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("3.3.2 3-byte sequence with last byte missing (U+0000): \"\"", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("3.3.3 4-byte sequence with last byte missing (U+0000): \"\"", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("3.3.4 5-byte sequence with last byte missing (U+0000): \"\"", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("3.3.5 6-byte sequence with last byte missing (U+0000): \"\"", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("3.3.6 2-byte sequence with last byte missing (U-000007FF): \"\"", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("3.3.7 3-byte sequence with last byte missing (U-0000FFFF): \"\"", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("3.3.8 4-byte sequence with last byte missing (U-001FFFFF): \"\"", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("3.3.9 5-byte sequence with last byte missing (U-03FFFFFF): \"\"", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("3.3.10 6-byte sequence with last byte missing (U-7FFFFFFF): \"\"", MOSQ_ERR_MALFORMED_UTF8); + + /* 3.4 Concatenation of incomplete sequences + + All the 10 sequences of 3.3 concatenated, you should see 10 malformed + sequences being signalled:*/ + + utf8_helper("\"\"", MOSQ_ERR_MALFORMED_UTF8); + + /* 3.5 Impossible bytes + + The following two bytes cannot appear in a correct UTF-8 string */ + + utf8_helper("3.5.1 fe = \"\"", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("3.5.2 ff = \"\"", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("3.5.3 fe fe ff ff = \"\"", MOSQ_ERR_MALFORMED_UTF8); +} + +static void TEST_utf8_overlong_encoding(void) +{ + /* 4 Overlong sequences + + The following sequences are not malformed according to the letter of + the Unicode 2.0 standard. However, they are longer then necessary and + a correct UTF-8 encoder is not allowed to produce them. A "safe UTF-8 + decoder" should reject them just like malformed sequences for two + reasons: (1) It helps to debug applications if overlong sequences are + not treated as valid representations of characters, because this helps + to spot problems more quickly. (2) Overlong sequences provide + alternative representations of characters, that could maliciously be + used to bypass filters that check only for ASCII characters. For + instance, a 2-byte encoded line feed (LF) would not be caught by a + line counter that counts only 0x0a bytes, but it would still be + processed as a line feed by an unsafe UTF-8 decoder later in the + pipeline. From a security point of view, ASCII compatibility of UTF-8 + sequences means also, that ASCII characters are *only* allowed to be + represented by ASCII bytes in the range 0x00-0x7f. To ensure this + aspect of ASCII compatibility, use only "safe UTF-8 decoders" that + reject overlong UTF-8 sequences for which a shorter encoding exists. */ + + /* 4.1 Examples of an overlong ASCII character + + With a safe UTF-8 decoder, all of the following five overlong + representations of the ASCII character slash ("/") should be rejected + like a malformed UTF-8 sequence, for instance by substituting it with + a replacement character. If you see a slash below, you do not have a + safe UTF-8 decoder! */ + + utf8_helper("4.1.1 U+002F = c0 af = \"\"", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("4.1.2 U+002F = e0 80 af = \"\"", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("4.1.3 U+002F = f0 80 80 af = \"\"", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("4.1.4 U+002F = f8 80 80 80 af = \"\"", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("4.1.5 U+002F = fc 80 80 80 80 af = \"\"", MOSQ_ERR_MALFORMED_UTF8); + + /* 4.2 Maximum overlong sequences + + Below you see the highest Unicode value that is still resulting in an + overlong sequence if represented with the given number of bytes. This + is a boundary test for safe UTF-8 decoders. All five characters should + be rejected like malformed UTF-8 sequences. */ + + utf8_helper("4.2.1 U-0000007F = c1 bf = \"\"", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("4.2.2 U-000007FF = e0 9f bf = \"\"", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("4.2.3 U-0000FFFF = f0 8f bf bf = \"\"", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("4.2.4 U-001FFFFF = f8 87 bf bf bf = \"\"", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("4.2.5 U-03FFFFFF = fc 83 bf bf bf bf = \"\"", MOSQ_ERR_MALFORMED_UTF8); + + /* 4.3 Overlong representation of the NUL character + + The following five sequences should also be rejected like malformed + UTF-8 sequences and should not be treated like the ASCII NUL + character. */ + + utf8_helper("4.3.1 U+0000 = c0 80 = \"\"", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("4.3.2 U+0000 = e0 80 80 = \"\"", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("4.3.3 U+0000 = f0 80 80 80 = \"\"", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("4.3.4 U+0000 = f8 80 80 80 80 = \"\"", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("4.3.5 U+0000 = fc 80 80 80 80 80 = \"\"", MOSQ_ERR_MALFORMED_UTF8); +} + + +static void TEST_utf8_illegal_code_positions(void) +{ + /* 5 Illegal code positions + + The following UTF-8 sequences should be rejected like malformed + sequences, because they never represent valid ISO 10646 characters and + a UTF-8 decoder that accepts them might introduce security problems + comparable to overlong UTF-8 sequences. */ + + /* 5.1 Single UTF-16 surrogates */ + + utf8_helper("5.1.1 U+D800 = ed a0 80 = \"\"", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("5.1.2 U+DB7F = ed ad bf = \"\"", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("5.1.3 U+DB80 = ed ae 80 = \"\"", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("5.1.4 U+DBFF = ed af bf = \"\"", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("5.1.5 U+DC00 = ed b0 80 = \"\"", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("5.1.6 U+DF80 = ed be 80 = \"\"", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("5.1.7 U+DFFF = ed bf bf = \"\"", MOSQ_ERR_MALFORMED_UTF8); + + /* 5.2 Paired UTF-16 surrogates */ + + utf8_helper("5.2.1 U+D800 U+DC00 = ed a0 80 ed b0 80 = \"\"", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("5.2.2 U+D800 U+DFFF = ed a0 80 ed bf bf = \"\"", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("5.2.3 U+DB7F U+DC00 = ed ad bf ed b0 80 = \"\"", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("5.2.4 U+DB7F U+DFFF = ed ad bf ed bf bf = \"\"", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("5.2.5 U+DB80 U+DC00 = ed ae 80 ed b0 80 = \"\"", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("5.2.6 U+DB80 U+DFFF = ed ae 80 ed bf bf = \"\"", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("5.2.7 U+DBFF U+DC00 = ed af bf ed b0 80 = \"\"", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("5.2.8 U+DBFF U+DFFF = ed af bf ed bf bf = \"\"", MOSQ_ERR_MALFORMED_UTF8); + + /* 5.3 Noncharacter code positions + + The following "noncharacters" are "reserved for internal use" by + applications, and according to older versions of the Unicode Standard + "should never be interchanged". Unicode Corrigendum #9 dropped the + latter restriction. Nevertheless, their presence in incoming UTF-8 data + can remain a potential security risk, depending on what use is made of + these codes subsequently. Examples of such internal use: + + - Some file APIs with 16-bit characters may use the integer value -1 + = U+FFFF to signal an end-of-file (EOF) or error condition. + + - In some UTF-16 receivers, code point U+FFFE might trigger a + byte-swap operation (to convert between UTF-16LE and UTF-16BE). + + With such internal use of noncharacters, it may be desirable and safer + to block those code points in UTF-8 decoders, as they should never + occur legitimately in incoming UTF-8 data, and could trigger unsafe + behaviour in subsequent processing. + + Particularly problematic noncharacters in 16-bit applications: */ + utf8_helper("5.3.1 U+FFFE = ef bf be = \"￾\"", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("5.3.2 U+FFFF = ef bf bf = \"￿\"", MOSQ_ERR_MALFORMED_UTF8); + + /* Other noncharacters: */ + + /* FIXME - these need splitting up into separate tests. */ + utf8_helper("5.3.3 U+FDD0 .. U+FDEF = \"﷐﷑﷒﷓﷔﷕﷖﷗﷘﷙﷚﷛﷜﷝﷞﷟﷠﷡﷢﷣﷤﷥﷦﷧﷨﷩﷪﷫﷬﷭﷮﷯\"", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("﷐", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("﷑", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("﷒", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("﷓", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("﷔", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("﷕", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("﷖", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("﷗", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("﷘", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("﷙", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("﷚", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("﷛", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("﷜", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("﷝", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("﷞", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("﷟", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("﷠", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("﷡", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("﷢", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("﷣", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("﷤", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("﷥", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("﷦", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("﷧", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("﷨", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("﷩", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("﷪", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("﷫", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("﷬", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("﷭", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("﷮", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("﷯", MOSQ_ERR_MALFORMED_UTF8); + + /* 5.3.4 U+nFFFE U+nFFFF (for n = 1..10) */ + + utf8_helper("🿾", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("🿿", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("𯿾", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("𯿿", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("𿿾", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("𿿿", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("񏿾", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("񏿿", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("񟿾", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("񟿿", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("񯿾", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("񯿿", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("񿿾", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("񿿿", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("򏿾", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("򏿿", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("򟿾", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("򟿿", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("򯿾", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("򯿿", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("򿿾", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("򿿿", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("󏿾", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("󏿿", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("󟿾", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("󟿿", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("󯿾", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("󯿿", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("󿿾", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("󿿿", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("􏿾", MOSQ_ERR_MALFORMED_UTF8); + utf8_helper("􏿿", MOSQ_ERR_MALFORMED_UTF8); +} + + +void TEST_utf8_control_characters(void) +{ + char buf[10]; + int i; + + /* U+0001 to U+001F are single byte control characters */ + for(i=0x01; i<0x20; i++){ + buf[0] = i; + buf[1] = '\0'; + utf8_helper(buf, MOSQ_ERR_MALFORMED_UTF8); + } + + /* U+007F is a single byte control character */ + buf[0] = 0x7F; + buf[1] = '\0'; + utf8_helper(buf, MOSQ_ERR_MALFORMED_UTF8); + + /* U+007F to U+009F are two byte control characters */ + for(i=0x80; i<0xA0; i++){ + buf[0] = 0xC2; + buf[1] = i-0x80; + buf[2] = '\0'; + utf8_helper(buf, MOSQ_ERR_MALFORMED_UTF8); + } + +} + + +void TEST_utf8_mqtt_1_5_4_2(void) +{ + char buf[10] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', '\0'}; + + utf8_helper_len(buf, 9, MOSQ_ERR_SUCCESS); + + buf[3] = '\0'; + utf8_helper_len(buf, 9, MOSQ_ERR_MALFORMED_UTF8); +} + + +void TEST_utf8_mqtt_1_5_4_3(void) +{ + char buf[10] = {'a', 'b', 0xEF, 0xBB, 0xBF, 'f', 'g', 'h', 'i', '\0'}; + + utf8_helper_len(buf, 9, MOSQ_ERR_SUCCESS); +} + + +/* ======================================================================== + * TEST SUITE SETUP + * ======================================================================== */ + +int init_utf8_tests(void) +{ + CU_pSuite test_suite = NULL; + + test_suite = CU_add_suite("UTF-8", NULL, NULL); + if(!test_suite){ + printf("Error adding CUnit test suite.\n"); + return 1; + } + + if(0 + || !CU_add_test(test_suite, "UTF-8 empty", TEST_utf8_empty) + || !CU_add_test(test_suite, "UTF-8 valid", TEST_utf8_valid) + || !CU_add_test(test_suite, "UTF-8 truncated", TEST_utf8_truncated) + || !CU_add_test(test_suite, "UTF-8 boundary conditions", TEST_utf8_boundary_conditions) + || !CU_add_test(test_suite, "UTF-8 malformed sequences", TEST_utf8_malformed_sequences) + || !CU_add_test(test_suite, "UTF-8 overlong encoding", TEST_utf8_overlong_encoding) + || !CU_add_test(test_suite, "UTF-8 illegal code positions", TEST_utf8_illegal_code_positions) + || !CU_add_test(test_suite, "UTF-8 control characters", TEST_utf8_control_characters) + || !CU_add_test(test_suite, "UTF-8 MQTT-1.5.4-2", TEST_utf8_mqtt_1_5_4_2) + || !CU_add_test(test_suite, "UTF-8 MQTT-1.5.4-3", TEST_utf8_mqtt_1_5_4_3) + ){ + + printf("Error adding UTF-8 CUnit tests.\n"); + return 1; + } + + return 0; +} + + diff --git a/test/unit/util_topic_test.c b/test/unit/util_topic_test.c new file mode 100644 index 00000000..88f668a2 --- /dev/null +++ b/test/unit/util_topic_test.c @@ -0,0 +1,284 @@ +#include +#include + +#include + +static void match_helper(const char *sub, const char *topic) +{ + int rc; + bool match; + + rc = mosquitto_topic_matches_sub(sub, topic, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, true); +} + +static void no_match_helper(int rc_expected, const char *sub, const char *topic) +{ + int rc; + bool match; + + rc = mosquitto_topic_matches_sub(sub, topic, &match); + CU_ASSERT_EQUAL(rc, rc_expected); + CU_ASSERT_EQUAL(match, false); +} + +/* ======================================================================== + * EMPTY INPUT + * ======================================================================== */ + +static void TEST_empty_input(void) +{ + int rc; + bool match; + + rc = mosquitto_topic_matches_sub("sub", NULL, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); + CU_ASSERT_EQUAL(match, false); + + rc = mosquitto_topic_matches_sub(NULL, "topic", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); + CU_ASSERT_EQUAL(match, false); + + rc = mosquitto_topic_matches_sub(NULL, NULL, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); + CU_ASSERT_EQUAL(match, false); + + rc = mosquitto_topic_matches_sub("sub", "", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); + CU_ASSERT_EQUAL(match, false); + + rc = mosquitto_topic_matches_sub("", "topic", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); + CU_ASSERT_EQUAL(match, false); + + rc = mosquitto_topic_matches_sub("", "", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); + CU_ASSERT_EQUAL(match, false); + + rc = mosquitto_topic_matches_sub2("sub", 3, NULL, 0, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); + CU_ASSERT_EQUAL(match, false); + + rc = mosquitto_topic_matches_sub2(NULL, 0, "topic", 5, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); + CU_ASSERT_EQUAL(match, false); + + rc = mosquitto_topic_matches_sub2(NULL, 0, NULL, 0, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); + CU_ASSERT_EQUAL(match, false); + + rc = mosquitto_topic_matches_sub2("sub", 3, "", 0, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); + CU_ASSERT_EQUAL(match, false); + + rc = mosquitto_topic_matches_sub2("", 0, "topic", 5, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); + CU_ASSERT_EQUAL(match, false); + + rc = mosquitto_topic_matches_sub2("", 0, "", 0, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); + CU_ASSERT_EQUAL(match, false); +} + +/* ======================================================================== + * VALID MATCHING AND NON-MATCHING + * ======================================================================== */ + +static void TEST_valid_matching(void) +{ + match_helper("foo/#", "foo/"); + match_helper("foo/#", "foo"); + match_helper("foo//bar", "foo//bar"); + match_helper("foo//+", "foo//bar"); + match_helper("foo/+/+/baz", "foo///baz"); + match_helper("foo/bar/+", "foo/bar/"); + match_helper("foo/bar", "foo/bar"); + match_helper("foo/+", "foo/bar"); + match_helper("foo/+/baz", "foo/bar/baz"); + match_helper("A/B/+/#", "A/B/B/C"); + match_helper("foo/+/#", "foo/bar/baz"); + match_helper("foo/+/#", "foo/bar"); + match_helper("#", "foo/bar/baz"); + match_helper("#", "foo/bar/baz"); + match_helper("#", "/foo/bar"); + match_helper("/#", "/foo/bar"); +} + + +static void TEST_invalid_but_matching(void) +{ + /* Matching here is "naive treatment of the wildcards would produce a + * match". They shouldn't really match, they should fail. */ + no_match_helper(MOSQ_ERR_INVAL, "+foo", "+foo"); + no_match_helper(MOSQ_ERR_INVAL, "fo+o", "fo+o"); + no_match_helper(MOSQ_ERR_INVAL, "foo+", "foo+"); + no_match_helper(MOSQ_ERR_INVAL, "+foo/bar", "+foo/bar"); + no_match_helper(MOSQ_ERR_INVAL, "foo+/bar", "foo+/bar"); + no_match_helper(MOSQ_ERR_INVAL, "foo/+bar", "foo/+bar"); + no_match_helper(MOSQ_ERR_INVAL, "foo/bar+", "foo/bar+"); + + no_match_helper(MOSQ_ERR_INVAL, "+foo", "afoo"); + no_match_helper(MOSQ_ERR_INVAL, "fo+o", "foao"); + no_match_helper(MOSQ_ERR_INVAL, "foo+", "fooa"); + no_match_helper(MOSQ_ERR_INVAL, "+foo/bar", "afoo/bar"); + no_match_helper(MOSQ_ERR_INVAL, "foo+/bar", "fooa/bar"); + no_match_helper(MOSQ_ERR_INVAL, "foo/+bar", "foo/abar"); + no_match_helper(MOSQ_ERR_INVAL, "foo/bar+", "foo/bara"); + + no_match_helper(MOSQ_ERR_INVAL, "#foo", "#foo"); + no_match_helper(MOSQ_ERR_INVAL, "fo#o", "fo#o"); + no_match_helper(MOSQ_ERR_INVAL, "foo#", "foo#"); + no_match_helper(MOSQ_ERR_INVAL, "#foo/bar", "#foo/bar"); + no_match_helper(MOSQ_ERR_INVAL, "foo#/bar", "foo#/bar"); + no_match_helper(MOSQ_ERR_INVAL, "foo/#bar", "foo/#bar"); + no_match_helper(MOSQ_ERR_INVAL, "foo/bar#", "foo/bar#"); + + no_match_helper(MOSQ_ERR_INVAL, "foo+", "fooa"); +} + + +static void TEST_valid_no_matching(void) +{ + no_match_helper(MOSQ_ERR_SUCCESS, "test/6/#", "test/3"); + + no_match_helper(MOSQ_ERR_SUCCESS, "foo/bar", "foo"); + no_match_helper(MOSQ_ERR_SUCCESS, "foo/+", "foo/bar/baz"); + no_match_helper(MOSQ_ERR_SUCCESS, "foo/+/baz", "foo/bar/bar"); + + no_match_helper(MOSQ_ERR_SUCCESS, "foo/+/#", "fo2/bar/baz"); + + no_match_helper(MOSQ_ERR_SUCCESS, "/#", "foo/bar"); + + no_match_helper(MOSQ_ERR_SUCCESS, "#", "$SYS/bar"); + no_match_helper(MOSQ_ERR_SUCCESS, "$BOB/bar", "$SYS/bar"); +} + + +static void TEST_invalid(void) +{ + no_match_helper(MOSQ_ERR_INVAL, "foo#", "foo"); + no_match_helper(MOSQ_ERR_INVAL, "fo#o/", "foo"); + no_match_helper(MOSQ_ERR_INVAL, "foo#", "fooa"); + no_match_helper(MOSQ_ERR_INVAL, "foo+", "foo"); + no_match_helper(MOSQ_ERR_INVAL, "foo/#a", "foo"); + no_match_helper(MOSQ_ERR_INVAL, "#a", "foo"); + no_match_helper(MOSQ_ERR_INVAL, "foo/#abc", "foo"); + no_match_helper(MOSQ_ERR_INVAL, "#abc", "foo"); + no_match_helper(MOSQ_ERR_INVAL, "/#a", "foo/bar"); +} + +/* ======================================================================== + * PUB TOPIC CHECK + * ======================================================================== */ + +static void pub_topic_helper(const char *topic, int rc_expected) +{ + int rc; + + rc = mosquitto_pub_topic_check(topic); + CU_ASSERT_EQUAL(rc, rc_expected); + + rc = mosquitto_pub_topic_check2(topic, strlen(topic)); + CU_ASSERT_EQUAL(rc, rc_expected); +} + +static void TEST_pub_topic_valid(void) +{ + pub_topic_helper("pub/topic", MOSQ_ERR_SUCCESS); + pub_topic_helper("pub//topic", MOSQ_ERR_SUCCESS); + pub_topic_helper("pub/ /topic", MOSQ_ERR_SUCCESS); +} + +static void TEST_pub_topic_invalid(void) +{ + pub_topic_helper("+pub/topic", MOSQ_ERR_INVAL); + pub_topic_helper("pub+/topic", MOSQ_ERR_INVAL); + pub_topic_helper("pub/+topic", MOSQ_ERR_INVAL); + pub_topic_helper("pub/topic+", MOSQ_ERR_INVAL); + pub_topic_helper("pub/topic/+", MOSQ_ERR_INVAL); + pub_topic_helper("#pub/topic", MOSQ_ERR_INVAL); + pub_topic_helper("pub#/topic", MOSQ_ERR_INVAL); + pub_topic_helper("pub/#topic", MOSQ_ERR_INVAL); + pub_topic_helper("pub/topic#", MOSQ_ERR_INVAL); + pub_topic_helper("pub/topic/#", MOSQ_ERR_INVAL); + pub_topic_helper("+/pub/topic", MOSQ_ERR_INVAL); +} + + +/* ======================================================================== + * SUB TOPIC CHECK + * ======================================================================== */ + +static void sub_topic_helper(const char *topic, int rc_expected) +{ + int rc; + + rc = mosquitto_sub_topic_check(topic); + CU_ASSERT_EQUAL(rc, rc_expected); + + rc = mosquitto_sub_topic_check2(topic, strlen(topic)); + CU_ASSERT_EQUAL(rc, rc_expected); +} + +static void TEST_sub_topic_valid(void) +{ + sub_topic_helper("sub/topic", MOSQ_ERR_SUCCESS); + sub_topic_helper("sub//topic", MOSQ_ERR_SUCCESS); + sub_topic_helper("sub/ /topic", MOSQ_ERR_SUCCESS); + sub_topic_helper("sub/+/topic", MOSQ_ERR_SUCCESS); + sub_topic_helper("+/+/+", MOSQ_ERR_SUCCESS); + sub_topic_helper("+", MOSQ_ERR_SUCCESS); + sub_topic_helper("sub/topic/#", MOSQ_ERR_SUCCESS); + sub_topic_helper("sub//topic/#", MOSQ_ERR_SUCCESS); + sub_topic_helper("sub/ /topic/#", MOSQ_ERR_SUCCESS); + sub_topic_helper("sub/+/topic/#", MOSQ_ERR_SUCCESS); + sub_topic_helper("+/+/+/#", MOSQ_ERR_SUCCESS); + sub_topic_helper("#", MOSQ_ERR_SUCCESS); +} + +static void TEST_sub_topic_invalid(void) +{ + sub_topic_helper("+sub/topic", MOSQ_ERR_INVAL); + sub_topic_helper("sub+/topic", MOSQ_ERR_INVAL); + sub_topic_helper("sub/+topic", MOSQ_ERR_INVAL); + sub_topic_helper("sub/topic+", MOSQ_ERR_INVAL); + sub_topic_helper("#sub/topic", MOSQ_ERR_INVAL); + sub_topic_helper("sub#/topic", MOSQ_ERR_INVAL); + sub_topic_helper("sub/#topic", MOSQ_ERR_INVAL); + sub_topic_helper("sub/topic#", MOSQ_ERR_INVAL); + sub_topic_helper("#/sub/topic", MOSQ_ERR_INVAL); +} + +/* ======================================================================== + * TEST SUITE SETUP + * ======================================================================== */ + +int init_util_topic_tests(void) +{ + CU_pSuite test_suite = NULL; + + test_suite = CU_add_suite("Util topic", NULL, NULL); + if(!test_suite){ + printf("Error adding CUnit util topic test suite.\n"); + return 1; + } + + if(0 + || !CU_add_test(test_suite, "Matching: Empty input", TEST_empty_input) + || !CU_add_test(test_suite, "Matching: Valid matching", TEST_valid_matching) + || !CU_add_test(test_suite, "Matching: Valid no matching", TEST_valid_no_matching) + || !CU_add_test(test_suite, "Matching: Invalid but matching", TEST_invalid_but_matching) + || !CU_add_test(test_suite, "Matching: Invalid", TEST_invalid) + || !CU_add_test(test_suite, "Pub topic: Valid", TEST_pub_topic_valid) + || !CU_add_test(test_suite, "Pub topic: Invalid", TEST_pub_topic_invalid) + || !CU_add_test(test_suite, "Sub topic: Valid", TEST_sub_topic_valid) + || !CU_add_test(test_suite, "Sub topic: Invalid", TEST_sub_topic_invalid) + ){ + + printf("Error adding util topic CUnit tests.\n"); + return 1; + } + + return 0; +} diff --git a/travis-install.sh b/travis-install.sh index ba034832..4c8fbb89 100755 --- a/travis-install.sh +++ b/travis-install.sh @@ -3,7 +3,7 @@ if [ "$TRAVIS_OS_NAME" == "linux" ]; then sudo apt-get update -qq - sudo apt-get install -y debhelper libc-ares-dev libssl-dev libwrap0-dev python-all python3-all uthash-dev uuid-dev libuuid1 xsltproc docbook-xsl + sudo apt-get install -y debhelper libc-ares-dev libssl-dev libwrap0-dev python-all python3-all uthash-dev xsltproc docbook-xsl libcunit1-dev fi if [ "$TRAVIS_OS_NAME" == "osx" ]; then diff --git a/www/README.md b/www/README.md new file mode 100644 index 00000000..923f54bf --- /dev/null +++ b/www/README.md @@ -0,0 +1,3 @@ +This is the mosquitto website, it can be built with `nikola`: + +`nikola build` \ No newline at end of file diff --git a/www/conf.py b/www/conf.py index b4145237..7b5f4ded 100644 --- a/www/conf.py +++ b/www/conf.py @@ -87,6 +87,7 @@ NAVIGATION_LINKS = { #("/sponsoring/", "Sponsoring"), ( ( + ("/roadmap/", "Roadmap"), ("/api/", "API"), ("/man/libmosquitto-3.html", "libmosquitto"), ("/man/mosquitto-8.html", "mosquitto"), diff --git a/www/pages/download.md b/www/pages/download.md index 2a9b54e0..cfe43754 100644 --- a/www/pages/download.md +++ b/www/pages/download.md @@ -11,8 +11,7 @@ # Source -* [mosquitto-1.5.4.tar.gz](https://mosquitto.org/files/source/mosquitto-1.5.4.tar.gz) (319kB) ([GPG signature](https://mosquitto.org/files/source/mosquitto-1.5.4.tar.gz.asc)) -* [mosquitto-1.5.4.tar.gz](https://www.eclipse.org/downloads/download.php?file=/mosquitto/source/mosquitto-1.5.4.tar.gz) (via Eclipse) +* [mosquitto-1.5.5.tar.gz](https://mosquitto.org/files/source/mosquitto-1.5.5.tar.gz) (319kB) ([GPG signature](https://mosquitto.org/files/source/mosquitto-1.5.5.tar.gz.asc)) * [Git source code repository](https://github.com/eclipse/mosquitto) (github.com) Older downloads are available at [https://mosquitto.org/files/](../files/) @@ -25,8 +24,8 @@ distributions. ## Windows -* [mosquitto-1.5.4-install-windows-x64.exe](https://www.eclipse.org/downloads/download.php?file=/mosquitto/binary/win64/mosquitto-1.5.4-install-windows-x64.exe) (~360 kB) (64-bit build, Windows Vista and up, built with Visual Studio Community 2017) -* [mosquitto-1.5.4-install-windows-x32.exe](https://www.eclipse.org/downloads/download.php?file=/mosquitto/binary/win32/mosquitto-1.5.4-install-windows-x86.exe) (~360 kB) (32-bit build, Windows Vista and up, built with Visual Studio Community 2017) +* [mosquitto-1.5.5-install-windows-x64.exe](https://mosquitto.org/files/binary/win64/mosquitto-1.5.5-install-windows-x64.exe) (~360 kB) (64-bit build, Windows Vista and up, built with Visual Studio Community 2017) +* [mosquitto-1.5.5-install-windows-x32.exe](https://mosquitto.org/files/binary/win32/mosquitto-1.5.5-install-windows-x86.exe) (~360 kB) (32-bit build, Windows Vista and up, built with Visual Studio Community 2017) See also readme-windows.txt after installing. diff --git a/www/pages/roadmap.md b/www/pages/roadmap.md new file mode 100644 index 00000000..7e607cf7 --- /dev/null +++ b/www/pages/roadmap.md @@ -0,0 +1,81 @@ + + +# Roadmap + +## Version 1.6 + +The next minor release. The focus of this release is on providing support for +version 5 of the MQTT protocol. + +This release will provide a feature complete implementation, but does not +represent the final interface for all features. In particular, functions are +being added to libmosquitto to provide support for MQTT 5 features, but these +will be consolidated with the API changes planned for version 2.0. + +### Deprecation notices + +#### libmosquittopp + +libmosquittopp, the C++ wrapper around libmosquitto is now deprecated and will +be removed in the next major release (2.0). The wrapper came about by an +external request and at the time it was created there were no other C++ +solutions for MQTT. This has changed in the past years and this wrapper +provides no benefit over true C++ libraries or using the pure C libmosquitto. + +#### libmosquitto API changes + +The Mosquitto project has maintained API and ABI compatibility in libmosquitto +since version 1.0, and has dealt with the introduction of new specification +features by adding new functions which duplicate the behaviour of existing +functions, but with additional arguments to support the new features. +Particularly with regards to adding support for MQTT version 5, this has lead +to a proliferation of functions which offer small variations on a theme. + +The libmosquitto functions listed below (which includes some new functions +included in 1.6) are going to be updated for version 2.0. Functions not listed +here should still be considered at risk of being updated. + +* mosquitto\_will\_set +* mosquitto\_connect\* +* mosquitto\_reconnect\* +* mosquitto\_disconnect +* mosquitto\_publish\* +* mosquitto\_subscribe\* +* mosquitto\_unsubscribe\* +* mosquitto\_loop\* +* mosquitto\_\*\_callback\_set +* All callbacks +* mosquitto\_\*\_topic\_check\* + + +## Version 2.0 + +This is the next major release and includes breaking changes. Other features +planned include: + +## Disk persistence improvements + +A new disk persistence interface will be created to allow persistence to occur +immediately, rather than periodically. This will allow queued messages for +disconnected clients to be removed from memory, and reduce the periodic pause +caused when writing the persistence file. + +## Breaking changes + +### libmosquitto + +The libmosquitto API is being consolidated to better support the new MQTT 5 +features whilst reducing the number of function variants. + +### libmosquittopp + +The C++ wrapper around libmosquitto will be removed in this release. diff --git a/www/pages/security.md b/www/pages/security.md index 7696dca9..b8abf73d 100644 --- a/www/pages/security.md +++ b/www/pages/security.md @@ -19,6 +19,8 @@ follow the steps on [Eclipse Security] page to report it. Listed with most recent first. Further information on security related issues can be found in the [security category]. +* December 2018: [CVE-2018-20145]. Affecting versions **1.5** to **1.5.4** + inclusive, fixed in **1.5.5.**. More details at [version-155-released]. * November 2018: No CVE assigned. Affecting versions **1.4** to **1.5.3** inclusive, fixed in **1.5.4**. More details at [version-154-released]. * September 2018: [CVE-2018-12543] affecting versions **1.5** to **1.5.2** @@ -43,6 +45,7 @@ can be found in the [security category]. inclusive, fixed in **1.4.12**. More details at [security-advisory-cve-2017-7650]. +[version-155-released]: /2018/11/version-155-released/ [version-154-released]: /2018/11/version-154-released/ [security-advisory-cve-2018-12543]: /2018/09/security-advisory-cve-2018-12543/ [security-advisory-cve-2017-7651-cve-2017-7652]: /2018/02/security-advisory-cve-2017-7651-cve-2017-7652/ @@ -52,6 +55,7 @@ can be found in the [security category]. [Eclipse Security]: https://www.eclipse.org/security/ [security category]: /blog/categories/security/ +[CVE-2018-20145]: https://nvd.nist.gov/vuln/detail/CVE-2018-20145 [CVE-2018-12543]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-12543 [CVE-2017-9868]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-9868 [CVE-2017-7655]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-7652 diff --git a/www/posts/2018/11/mqtt5-progress.md b/www/posts/2018/11/mqtt5-progress.md new file mode 100644 index 00000000..2b27e821 --- /dev/null +++ b/www/posts/2018/11/mqtt5-progress.md @@ -0,0 +1,59 @@ + + +Development of support for MQTT 5 is ongoing and making good progress, but has +been substantially delayed due to other non-Mosquitto work having to take +priority. + +It is possible to test the current state of MQTT 5 support by using the `mqtt5` +branch of the [repository]. Please note that this is very much a work in +progress, so parts are incomplete and interfaces may yet change. The client +library in particular has had to have an increase in functions available in +order to provide the features needed whilst providing backwards compatibility. +Part of the plan for the 2.0 release, which will follow after 1.6, is to +consolidate the libmosquitto API with breaking changes. There are more details +on the [roadmap]. + +Current features include: + +* Support for all incoming and outgoing packets, although not everything is + processed. +* Support for sending and receiving all properties, with not all properties + processed. +* Client support for setting properties +* Request/response support (client cannot process incoming correlation data) +* Retain availability +* Message expiry interval support +* Server support for assigned client identifiers +* Payload format indicator support +* Content-type support +* Basic topic alias support from client to broker +* Lots of new tests + +Both `mosquitto_pub` and `mosquitto_sub` support setting properties on the +command line, for example: + +``` +mosquitto_sub -t topic -v -D connect session-expiry-interval 60 -D connect user-property key value -D subscribe user-property sub-key sub-value +``` + +``` +mosquitto_pub -t topic -m '{"key":"value"}' -D publish content-type "application/json" +``` + +``` +./sensor_read.sh | mosquitto_pub -t topic -l -D publish topic-alias 1 +``` + +Further updates will be posted when more features are available. + +[repository]: https://github.com/eclipse/mosquitto/tree/mqtt5 +[roadmap]: https://mosquitto.org/roadmap/ \ No newline at end of file diff --git a/www/posts/2018/12/version-155-released.md b/www/posts/2018/12/version-155-released.md new file mode 100644 index 00000000..b2d5f96e --- /dev/null +++ b/www/posts/2018/12/version-155-released.md @@ -0,0 +1,62 @@ + + +This is a bugfix and security release. + +# Version 1.5.5 changes + +## Security +- If `per_listener_settings` is set to true, then the `acl_file` setting was + ignored for the "default listener" only. This has been fixed. This does not + affect any listeners defined with the `listener` option. Closes [#1073]. + This is now tracked as [CVE-2018-20145]. + +## Broker +- Add `socket_domain` option to allow listeners to disable IPv6 support. + This is required to work around a problem in libwebsockets that means + sockets only listen on IPv6 by default if IPv6 support is compiled in. + Closes [#1004]. +- When using ADNS, don't ask for all network protocols when connecting, + because this can lead to confusing "Protocol not supported" errors if the + network is down. Closes [#1062]. +- Fix outgoing retained messages not being sent by bridges on initial + connection. Closes [#1040]. +- Don't reload `auth_opt_` options on reload, to match the behaviour of the + other plugin options. Closes [#1068]. +- Print message on error when installing/uninstalling as a Windows service. +- All non-error connect/disconnect messages are controlled by the + `connection_messages` option. Closes [#772]. Closes [#613]. Closes [#537]. + +## Library +- Fix reconnect delay backoff behaviour. Closes [#1027]. +- Don't call `on_disconnect()` twice if keepalive tests fail. Closes [#1067]. + +## Client +- Always print leading zeros in `mosquitto_sub` when output format is hex. + Closes [#1066]. + +## Build +- Fix building where TLS-PSK is not available. Closes [#68]. + + +[CVE-2018-20145]: https://nvd.nist.gov/vuln/detail/CVE-2018-20145 +[#68]: https://github.com/eclipse/mosquitto/issues/68 +[#537]: https://github.com/eclipse/mosquitto/issues/537 +[#613]: https://github.com/eclipse/mosquitto/issues/613 +[#772]: https://github.com/eclipse/mosquitto/issues/772 +[#1004]: https://github.com/eclipse/mosquitto/issues/1004 +[#1027]: https://github.com/eclipse/mosquitto/issues/1027 +[#1040]: https://github.com/eclipse/mosquitto/issues/1040 +[#1062]: https://github.com/eclipse/mosquitto/issues/1062 +[#1066]: https://github.com/eclipse/mosquitto/issues/1066 +[#1067]: https://github.com/eclipse/mosquitto/issues/1067 +[#1068]: https://github.com/eclipse/mosquitto/issues/1068 +[#1073]: https://github.com/eclipse/mosquitto/issues/1073