mirror of
https://github.com/eclipse/mosquitto.git
synced 2025-05-09 01:01:11 +08:00
Merge branch 'fixes'
This commit is contained in:
commit
9d9469cbec
@ -4,13 +4,13 @@
|
||||
# To configure the build options either use the CMake gui, or run the command
|
||||
# line utility including the "-i" option.
|
||||
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
cmake_minimum_required(VERSION 3.1)
|
||||
cmake_policy(SET CMP0042 NEW)
|
||||
|
||||
project(mosquitto)
|
||||
set (VERSION 2.0.14)
|
||||
set (VERSION 2.0.15)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/")
|
||||
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/")
|
||||
|
||||
add_definitions (-DCMAKE -DVERSION=\"${VERSION}\")
|
||||
|
||||
@ -67,12 +67,9 @@ option(WITH_THREADING "Include client library threading support?" ON)
|
||||
if (WITH_THREADING)
|
||||
add_definitions("-DWITH_THREADING")
|
||||
if (WIN32)
|
||||
if (CMAKE_CL_64)
|
||||
set (PTHREAD_LIBRARIES C:\\pthreads\\Pre-built.2\\lib\\x64\\pthreadVC2.lib)
|
||||
else (CMAKE_CL_64)
|
||||
set (PTHREAD_LIBRARIES C:\\pthreads\\Pre-built.2\\lib\\x86\\pthreadVC2.lib)
|
||||
endif (CMAKE_CL_64)
|
||||
set (PTHREAD_INCLUDE_DIR C:\\pthreads\\Pre-built.2\\include)
|
||||
find_package(Threads REQUIRED)
|
||||
set (PTHREAD_LIBRARIES Threads::Threads)
|
||||
set (PTHREAD_INCLUDE_DIR "")
|
||||
elseif (ANDROID)
|
||||
set (PTHREAD_LIBRARIES "")
|
||||
set (PTHREAD_INCLUDE_DIR "")
|
||||
|
@ -1,3 +1,69 @@
|
||||
2.0.15 - 2022-08-16
|
||||
===================
|
||||
|
||||
Security:
|
||||
- Deleting the group configured as the anonymous group in the Dynamic Security
|
||||
plugin, would leave a dangling pointer that could lead to a single crash.
|
||||
This is considered a minor issue - only administrative users should have
|
||||
access to dynsec, the impact on availability is one-off, and there is no
|
||||
associated loss of data. It is now forbidden to delete the group configured
|
||||
as the anonymous group.
|
||||
|
||||
Broker:
|
||||
- Fix memory leak when a plugin modifies the topic of a message in
|
||||
MOSQ_EVT_MESSAGE.
|
||||
- Fix bridge `restart_timeout` not being honoured.
|
||||
- Fix potential memory leaks if a plugin modifies the message in the
|
||||
MOSQ_EVT_MESSAGE event.
|
||||
- Fix unused flags in CONNECT command being forced to be 0, which is not
|
||||
required for MQTT v3.1. Closes #2522.
|
||||
- Improve documentation of `persistent_client_expiration` option.
|
||||
Closes #2404.
|
||||
- Add clients to session expiry check list when restarting and reloading from
|
||||
persistence. Closes #2546.
|
||||
- Fix bridges not sending failure notification messages to the local broker if
|
||||
the remote bridge connection fails. Closes #2467. Closes #1488.
|
||||
- Fix some PUBLISH messages not being counted in $SYS stats. Closes #2448.
|
||||
- Fix incorrect return code being sent in DISCONNECT when a client session is
|
||||
taken over. Closes #2607.
|
||||
- Fix confusing "out of memory" error when a client is kicked in the dynamic
|
||||
security plugin. Closes #2525.
|
||||
- Fix confusing error message when dynamic security config file was a
|
||||
directory. Closes #2520.
|
||||
- Fix bridge queued messages not being persisted when local_cleansession is
|
||||
set to false and cleansession is set to true. Closes #2604.
|
||||
- Dynamic security: Fix modifyClient and modifyGroup commands to not modify
|
||||
the client/group if a new group/client being added is not valid.
|
||||
Closes #2598.
|
||||
- Dynamic security: Fix the plugin being able to be loaded twice. Currently
|
||||
only a single plugin can interact with a unique $CONTROL topic. Using
|
||||
multiple instances of the plugin would produce duplicate entries in the
|
||||
config file. Closes #2601. Closes #2470.
|
||||
- Fix case where expired messages were causing queued messages not to be
|
||||
delivered. Closes #2609.
|
||||
|
||||
Client library:
|
||||
- Fix threads library detection on Windows under cmake. Bumps the minimum
|
||||
cmake version to 3.1, which is still ancient.
|
||||
- Fix use of `MOSQ_OPT_TLS_ENGINE` being unable to be used due to the openssl
|
||||
ctx not being initialised until starting to connect. Closes #2537.
|
||||
- Fix incorrect use of SSL_connect. Closes #2594.
|
||||
- Don't set SIGPIPE to ignore, use MSG_NOSIGNAL instead. Closes #2564.
|
||||
- Add documentation of struct mosquitto_message to header. Closes #2561.
|
||||
- Fix documentation omission around mosquitto_reinitialise. Closes #2489.
|
||||
- Fix use of MOSQ_OPT_SSL_CTX when used in conjunction with
|
||||
MOSQ_OPT_SSL_CTX_DEFAULTS. Closes #2463.
|
||||
- Fix failure to close thread in some situations. Closes #2545.
|
||||
|
||||
Clients:
|
||||
- Fix mosquitto_pub incorrectly reusing topic aliases when reconnecting.
|
||||
Closes #2494.
|
||||
|
||||
Apps:
|
||||
- Fix `-o` not working in `mosquitto_ctrl`, and typo in related documentation.
|
||||
Closes #2471.
|
||||
|
||||
|
||||
2.0.14 - 2021-11-17
|
||||
===================
|
||||
|
||||
@ -25,6 +91,9 @@ Broker:
|
||||
- Fix broker sending duplicate CONNACK on failed MQTT v5 reauthentication.
|
||||
Closes #2339.
|
||||
- Fix mosquitto_plugin.h not including mosquitto_broker.h. Closes #2350.
|
||||
- Fix unlimited message quota not being properly checked for incoming
|
||||
messages. Closes #2593.
|
||||
- Fixed build for openssl compiled with OPENSSL_NO_ENGINE. Closes #2589.
|
||||
|
||||
Client library:
|
||||
- Initialise sockpairR/W to invalid in `mosquitto_reinitialise()` to avoid
|
||||
|
@ -139,3 +139,10 @@ void db__msg_add_to_queued_stats(struct mosquitto_msg_data *msg_data, struct mos
|
||||
UNUSED(msg_data);
|
||||
UNUSED(msg);
|
||||
}
|
||||
|
||||
int session_expiry__add_from_persistence(struct mosquitto *context, time_t expiry_time)
|
||||
{
|
||||
UNUSED(context);
|
||||
UNUSED(expiry_time);
|
||||
return 0;
|
||||
}
|
||||
|
@ -22,6 +22,10 @@ Contributors:
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef WIN32
|
||||
# include <strings.h>
|
||||
#endif
|
||||
|
||||
#include "mosquitto_ctrl.h"
|
||||
#include "mosquitto.h"
|
||||
#include "password_mosq.h"
|
||||
|
@ -22,6 +22,10 @@ Contributors:
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef WIN32
|
||||
# include <strings.h>
|
||||
#endif
|
||||
|
||||
#include "mosquitto.h"
|
||||
#include "mosquitto_ctrl.h"
|
||||
#include "password_mosq.h"
|
||||
|
@ -22,6 +22,10 @@ Contributors:
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef WIN32
|
||||
# include <strings.h>
|
||||
#endif
|
||||
|
||||
#include "mosquitto_ctrl.h"
|
||||
|
||||
void ctrl_help(void)
|
||||
|
@ -24,6 +24,10 @@ Contributors:
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef WIN32
|
||||
# include <strings.h>
|
||||
#endif
|
||||
|
||||
#include "lib_load.h"
|
||||
#include "mosquitto.h"
|
||||
#include "mosquitto_ctrl.h"
|
||||
|
@ -89,13 +89,14 @@ int ctrl_config_parse(struct mosq_config *cfg, int *argc, char **argv[])
|
||||
|
||||
init_config(cfg);
|
||||
|
||||
rc = client_config_load(cfg);
|
||||
if(rc) return rc;
|
||||
|
||||
/* Deal with real argc/argv */
|
||||
rc = client_config_line_proc(cfg, argc, argv);
|
||||
if(rc) return rc;
|
||||
|
||||
/* Load options from config file - this must be after `-o` has been processed */
|
||||
rc = client_config_load(cfg);
|
||||
if(rc) return rc;
|
||||
|
||||
#ifdef WITH_TLS
|
||||
if((cfg->certfile && !cfg->keyfile) || (cfg->keyfile && !cfg->certfile)){
|
||||
fprintf(stderr, "Error: Both certfile and keyfile must be provided if one of them is set.\n");
|
||||
@ -531,7 +532,7 @@ int client_config_load(struct mosq_config *cfg)
|
||||
fclose(fptr);
|
||||
return 1;
|
||||
}
|
||||
while(fgets(line, 1024, fptr)){
|
||||
while(fgets(line, sizeof(line), fptr)){
|
||||
if(line[0] == '#') continue; /* Comments */
|
||||
|
||||
while(line[strlen(line)-1] == 10 || line[strlen(line)-1] == 13){
|
||||
|
@ -135,6 +135,7 @@ void my_connect_callback(struct mosquitto *mosq, void *obj, int result, int flag
|
||||
connack_result = result;
|
||||
|
||||
if(!result){
|
||||
first_publish = true;
|
||||
switch(cfg.pub_mode){
|
||||
case MSGMODE_CMD:
|
||||
case MSGMODE_FILE:
|
||||
|
@ -127,7 +127,7 @@ WITH_XTREPORT=no
|
||||
|
||||
# Also bump lib/mosquitto.h, CMakeLists.txt,
|
||||
# installer/mosquitto.nsi, installer/mosquitto64.nsi
|
||||
VERSION=2.0.14
|
||||
VERSION=2.0.15
|
||||
|
||||
# Client library SO version. Bump if incompatible API/ABI changes are made.
|
||||
SOVERSION=1
|
||||
@ -254,10 +254,10 @@ ifeq ($(WITH_TLS),yes)
|
||||
endif
|
||||
|
||||
ifeq ($(WITH_THREADING),yes)
|
||||
LIB_LIBADD:=$(LIB_LIBADD) -lpthread
|
||||
LIB_LDFLAGS:=$(LIB_LDFLAGS) -pthread
|
||||
LIB_CPPFLAGS:=$(LIB_CPPFLAGS) -DWITH_THREADING
|
||||
CLIENT_CPPFLAGS:=$(CLIENT_CPPFLAGS) -DWITH_THREADING
|
||||
STATIC_LIB_DEPS:=$(STATIC_LIB_DEPS) -lpthread
|
||||
STATIC_LIB_DEPS:=$(STATIC_LIB_DEPS) -pthread
|
||||
endif
|
||||
|
||||
ifeq ($(WITH_SOCKS),yes)
|
||||
|
@ -66,7 +66,7 @@ extern "C" {
|
||||
|
||||
#define LIBMOSQUITTO_MAJOR 2
|
||||
#define LIBMOSQUITTO_MINOR 0
|
||||
#define LIBMOSQUITTO_REVISION 14
|
||||
#define LIBMOSQUITTO_REVISION 15
|
||||
/* 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)
|
||||
|
||||
@ -83,7 +83,8 @@ extern "C" {
|
||||
#define MOSQ_LOG_INTERNAL 0x80000000U
|
||||
#define MOSQ_LOG_ALL 0xFFFFFFFFU
|
||||
|
||||
/* Error values */
|
||||
/* Enum: mosq_err_t
|
||||
* Integer values returned from many libmosquitto functions. */
|
||||
enum mosq_err_t {
|
||||
MOSQ_ERR_AUTH_CONTINUE = -4,
|
||||
MOSQ_ERR_NO_SUBSCRIBERS = -3,
|
||||
@ -123,7 +124,12 @@ enum mosq_err_t {
|
||||
MOSQ_ERR_ALREADY_EXISTS = 31,
|
||||
};
|
||||
|
||||
/* Option values */
|
||||
/* Enum: mosq_opt_t
|
||||
*
|
||||
* Client options.
|
||||
*
|
||||
* See <mosquitto_int_option>, <mosquitto_string_option>, and <mosquitto_void_option>.
|
||||
*/
|
||||
enum mosq_opt_t {
|
||||
MOSQ_OPT_PROTOCOL_VERSION = 1,
|
||||
MOSQ_OPT_SSL_CTX = 2,
|
||||
@ -148,6 +154,24 @@ enum mosq_opt_t {
|
||||
#define MQTT_PROTOCOL_V311 4
|
||||
#define MQTT_PROTOCOL_V5 5
|
||||
|
||||
/* Struct: mosquitto_message
|
||||
*
|
||||
* Contains details of a PUBLISH message.
|
||||
*
|
||||
* int mid - the message/packet ID of the PUBLISH message, assuming this is a
|
||||
* QoS 1 or 2 message. Will be set to 0 for QoS 0 messages.
|
||||
*
|
||||
* char *topic - the topic the message was delivered on.
|
||||
*
|
||||
* void *payload - the message payload. This will be payloadlen bytes long, and
|
||||
* may be NULL if a zero length payload was sent.
|
||||
*
|
||||
* int payloadlen - the length of the payload, in bytes.
|
||||
*
|
||||
* int qos - the quality of service of the message, 0, 1, or 2.
|
||||
*
|
||||
* bool retain - set to true for stale retained messages.
|
||||
*/
|
||||
struct mosquitto_message{
|
||||
int mid;
|
||||
char *topic;
|
||||
@ -325,6 +349,7 @@ libmosq_EXPORT void mosquitto_destroy(struct mosquitto *mosq);
|
||||
* 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_MALFORMED_UTF8 - if the client id is not valid UTF-8.
|
||||
*
|
||||
* See Also:
|
||||
* <mosquitto_new>, <mosquitto_destroy>
|
||||
@ -1565,6 +1590,9 @@ libmosq_EXPORT int mosquitto_int_option(struct mosquitto *mosq, enum mosq_opt_t
|
||||
* MOSQ_OPT_TLS_ENGINE - Configure the client for TLS Engine support.
|
||||
* Pass a TLS Engine ID to be used when creating TLS
|
||||
* connections. Must be set before <mosquitto_connect>.
|
||||
* Must be a valid engine, and note that the string will not be used
|
||||
* until a connection attempt is made so this function will return
|
||||
* success even if an invalid engine string is passed.
|
||||
*
|
||||
* MOSQ_OPT_TLS_KEYFORM - Configure the client to treat the keyfile
|
||||
* differently depending on its type. Must be set
|
||||
|
@ -9,7 +9,7 @@
|
||||
!define env_hklm 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"'
|
||||
|
||||
Name "Eclipse Mosquitto"
|
||||
!define VERSION 2.0.14
|
||||
!define VERSION 2.0.15
|
||||
OutFile "mosquitto-${VERSION}-install-windows-x86.exe"
|
||||
|
||||
InstallDir "$PROGRAMFILES\mosquitto"
|
||||
|
@ -9,7 +9,7 @@
|
||||
!define env_hklm 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"'
|
||||
|
||||
Name "Eclipse Mosquitto"
|
||||
!define VERSION 2.0.14
|
||||
!define VERSION 2.0.15
|
||||
OutFile "mosquitto-${VERSION}-install-windows-x64.exe"
|
||||
|
||||
!include "x64.nsh"
|
||||
|
@ -76,6 +76,7 @@ static int mosquitto__connect_init(struct mosquitto *mosq, const char *host, int
|
||||
mosq->msgs_in.inflight_quota = mosq->msgs_in.inflight_maximum;
|
||||
mosq->msgs_out.inflight_quota = mosq->msgs_out.inflight_maximum;
|
||||
mosq->retain_available = 1;
|
||||
mosquitto__set_request_disconnect(mosq, false);
|
||||
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
}
|
||||
@ -255,6 +256,7 @@ int mosquitto_disconnect_v5(struct mosquitto *mosq, int reason_code, const mosqu
|
||||
}
|
||||
|
||||
mosquitto__set_state(mosq, mosq_cs_disconnected);
|
||||
mosquitto__set_request_disconnect(mosq, true);
|
||||
if(mosq->sock == INVALID_SOCKET){
|
||||
return MOSQ_ERR_NO_CONN;
|
||||
}else{
|
||||
|
33
lib/loop.c
33
lib/loop.c
@ -72,12 +72,6 @@ int mosquitto_loop(struct mosquitto *mosq, int timeout, int max_packets)
|
||||
if(mosq->ssl){
|
||||
if(mosq->want_write){
|
||||
FD_SET(mosq->sock, &writefds);
|
||||
}else if(mosq->want_connect){
|
||||
/* Remove possible FD_SET from above, we don't want to check
|
||||
* for writing if we are still connecting, unless want_write is
|
||||
* definitely set. The presence of outgoing packets does not
|
||||
* matter yet. */
|
||||
FD_CLR(mosq->sock, &writefds);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -114,9 +108,11 @@ int mosquitto_loop(struct mosquitto *mosq, int timeout, int max_packets)
|
||||
}
|
||||
|
||||
now = mosquitto_time();
|
||||
pthread_mutex_lock(&mosq->msgtime_mutex);
|
||||
if(mosq->next_msg_out && now + timeout_ms/1000 > mosq->next_msg_out){
|
||||
timeout_ms = (mosq->next_msg_out - now)*1000;
|
||||
}
|
||||
pthread_mutex_unlock(&mosq->msgtime_mutex);
|
||||
|
||||
if(timeout_ms < 0){
|
||||
/* There has been a delay somewhere which means we should have already
|
||||
@ -167,20 +163,12 @@ int mosquitto_loop(struct mosquitto *mosq, int timeout, int max_packets)
|
||||
FD_SET(mosq->sock, &writefds);
|
||||
}
|
||||
if(mosq->sock != INVALID_SOCKET && FD_ISSET(mosq->sock, &writefds)){
|
||||
#ifdef WITH_TLS
|
||||
if(mosq->want_connect){
|
||||
rc = net__socket_connect_tls(mosq);
|
||||
if(rc) return rc;
|
||||
}else
|
||||
#endif
|
||||
{
|
||||
rc = mosquitto_loop_write(mosq, max_packets);
|
||||
if(rc || mosq->sock == INVALID_SOCKET){
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef WITH_SRV
|
||||
if(mosq->achan){
|
||||
ares_process(mosq->achan, &readfds, &writefds);
|
||||
@ -254,7 +242,6 @@ int mosquitto_loop_forever(struct mosquitto *mosq, int timeout, int max_packets)
|
||||
int run = 1;
|
||||
int rc = MOSQ_ERR_SUCCESS;
|
||||
unsigned long reconnect_delay;
|
||||
enum mosquitto_client_state state;
|
||||
|
||||
if(!mosq) return MOSQ_ERR_INVAL;
|
||||
|
||||
@ -293,8 +280,7 @@ int mosquitto_loop_forever(struct mosquitto *mosq, int timeout, int max_packets)
|
||||
pthread_testcancel();
|
||||
#endif
|
||||
rc = MOSQ_ERR_SUCCESS;
|
||||
state = mosquitto__get_state(mosq);
|
||||
if(state == mosq_cs_disconnecting || state == mosq_cs_disconnected){
|
||||
if(mosquitto__get_request_disconnect(mosq)){
|
||||
run = 0;
|
||||
}else{
|
||||
if(mosq->reconnect_delay_max > mosq->reconnect_delay){
|
||||
@ -316,8 +302,7 @@ int mosquitto_loop_forever(struct mosquitto *mosq, int timeout, int max_packets)
|
||||
rc = interruptible_sleep(mosq, (time_t)reconnect_delay);
|
||||
if(rc) return rc;
|
||||
|
||||
state = mosquitto__get_state(mosq);
|
||||
if(state == mosq_cs_disconnecting || state == mosq_cs_disconnected){
|
||||
if(mosquitto__get_request_disconnect(mosq)){
|
||||
run = 0;
|
||||
}else{
|
||||
rc = mosquitto_reconnect(mosq);
|
||||
@ -371,16 +356,6 @@ int mosquitto_loop_read(struct mosquitto *mosq, int max_packets)
|
||||
int i;
|
||||
if(max_packets < 1) return MOSQ_ERR_INVAL;
|
||||
|
||||
#ifdef WITH_TLS
|
||||
if(mosq->want_connect){
|
||||
rc = net__socket_connect_tls(mosq);
|
||||
if (MOSQ_ERR_TLS == rc){
|
||||
rc = mosquitto__loop_rc_handle(mosq, rc);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
pthread_mutex_lock(&mosq->msgs_out.mutex);
|
||||
max_packets = mosq->msgs_out.queue_len;
|
||||
pthread_mutex_unlock(&mosq->msgs_out.mutex);
|
||||
|
@ -109,10 +109,6 @@ struct mosquitto *mosquitto_new(const char *id, bool clean_start, void *userdata
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifndef WIN32
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
#endif
|
||||
|
||||
mosq = (struct mosquitto *)mosquitto__calloc(1, sizeof(struct mosquitto));
|
||||
if(mosq){
|
||||
mosq->sock = INVALID_SOCKET;
|
||||
@ -167,6 +163,9 @@ int mosquitto_reinitialise(struct mosquitto *mosq, const char *id, bool clean_st
|
||||
return MOSQ_ERR_MALFORMED_UTF8;
|
||||
}
|
||||
mosq->id = mosquitto__strdup(id);
|
||||
if(!mosq->id){
|
||||
return MOSQ_ERR_NOMEM;
|
||||
}
|
||||
}
|
||||
mosq->in_packet.payload = NULL;
|
||||
packet__cleanup(&mosq->in_packet);
|
||||
@ -338,8 +337,6 @@ bool mosquitto_want_write(struct mosquitto *mosq)
|
||||
if(mosq->ssl){
|
||||
if (mosq->want_write) {
|
||||
result = true;
|
||||
}else if(mosq->want_connect){
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -272,7 +272,6 @@ struct mosquitto {
|
||||
enum mosquitto__keyform tls_keyform;
|
||||
#endif
|
||||
bool want_write;
|
||||
bool want_connect;
|
||||
#if defined(WITH_THREADING) && !defined(WITH_BROKER)
|
||||
pthread_mutex_t callback_mutex;
|
||||
pthread_mutex_t log_callback_mutex;
|
||||
@ -340,6 +339,7 @@ struct mosquitto {
|
||||
unsigned int reconnect_delay;
|
||||
unsigned int reconnect_delay_max;
|
||||
bool reconnect_exponential_backoff;
|
||||
bool request_disconnect;
|
||||
char threaded;
|
||||
struct mosquitto__packet *out_packet_last;
|
||||
mosquitto_property *connect_properties;
|
||||
|
@ -569,31 +569,7 @@ int net__socket_connect_tls(struct mosquitto *mosq)
|
||||
return MOSQ_ERR_OCSP;
|
||||
}
|
||||
}
|
||||
|
||||
ret = SSL_connect(mosq->ssl);
|
||||
if(ret != 1) {
|
||||
err = SSL_get_error(mosq->ssl, ret);
|
||||
if (err == SSL_ERROR_SYSCALL) {
|
||||
mosq->want_connect = true;
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
}
|
||||
if(err == SSL_ERROR_WANT_READ){
|
||||
mosq->want_connect = true;
|
||||
/* We always try to read anyway */
|
||||
}else if(err == SSL_ERROR_WANT_WRITE){
|
||||
mosq->want_write = true;
|
||||
mosq->want_connect = true;
|
||||
}else{
|
||||
net__print_ssl_error(mosq);
|
||||
|
||||
COMPAT_CLOSE(mosq->sock);
|
||||
mosq->sock = INVALID_SOCKET;
|
||||
net__print_ssl_error(mosq);
|
||||
return MOSQ_ERR_TLS;
|
||||
}
|
||||
}else{
|
||||
mosq->want_connect = false;
|
||||
}
|
||||
SSL_set_connect_state(mosq->ssl);
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
@ -685,8 +661,8 @@ static int net__init_ssl_ctx(struct mosquitto *mosq)
|
||||
* has not been set, or if both of MOSQ_OPT_SSL_CTX and
|
||||
* MOSQ_OPT_SSL_CTX_WITH_DEFAULTS are set. */
|
||||
if(mosq->tls_cafile || mosq->tls_capath || mosq->tls_psk || mosq->tls_use_os_certs){
|
||||
if(!mosq->ssl_ctx){
|
||||
net__init_tls();
|
||||
if(!mosq->ssl_ctx){
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
||||
mosq->ssl_ctx = SSL_CTX_new(SSLv23_client_method());
|
||||
@ -1041,11 +1017,7 @@ ssize_t net__write(struct mosquitto *mosq, const void *buf, size_t count)
|
||||
/* Call normal write/send */
|
||||
#endif
|
||||
|
||||
#ifndef WIN32
|
||||
return write(mosq->sock, buf, count);
|
||||
#else
|
||||
return send(mosq->sock, buf, count, 0);
|
||||
#endif
|
||||
return send(mosq->sock, buf, count, MSG_NOSIGNAL);
|
||||
|
||||
#ifdef WITH_TLS
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ Contributors:
|
||||
#define NET_MOSQ_H
|
||||
|
||||
#ifndef WIN32
|
||||
# include <sys/socket.h>
|
||||
# include <unistd.h>
|
||||
#else
|
||||
# include <winsock2.h>
|
||||
@ -51,6 +52,10 @@ typedef SSIZE_T ssize_t;
|
||||
#define INVALID_SOCKET -1
|
||||
#endif
|
||||
|
||||
#ifndef MSG_NOSIGNAL
|
||||
# define MSG_NOSIGNAL 0
|
||||
#endif
|
||||
|
||||
/* Macros for accessing the MSB and LSB of a uint16_t */
|
||||
#define MOSQ_MSB(A) (uint8_t)((A & 0xFF00) >> 8)
|
||||
#define MOSQ_LSB(A) (uint8_t)(A & 0x00FF)
|
||||
|
@ -284,6 +284,8 @@ int mosquitto_string_option(struct mosquitto *mosq, enum mosq_opt_t option, cons
|
||||
switch(option){
|
||||
case MOSQ_OPT_TLS_ENGINE:
|
||||
#if defined(WITH_TLS) && !defined(OPENSSL_NO_ENGINE)
|
||||
mosquitto__free(mosq->tls_engine);
|
||||
if(value){
|
||||
eng = ENGINE_by_id(value);
|
||||
if(!eng){
|
||||
return MOSQ_ERR_INVAL;
|
||||
@ -293,6 +295,7 @@ int mosquitto_string_option(struct mosquitto *mosq, enum mosq_opt_t option, cons
|
||||
if(!mosq->tls_engine){
|
||||
return MOSQ_ERR_NOMEM;
|
||||
}
|
||||
}
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
#else
|
||||
return MOSQ_ERR_NOT_SUPPORTED;
|
||||
|
@ -236,11 +236,7 @@ int packet__write(struct mosquitto *mosq)
|
||||
#endif
|
||||
|
||||
state = mosquitto__get_state(mosq);
|
||||
#if defined(WITH_TLS) && !defined(WITH_BROKER)
|
||||
if(state == mosq_cs_connect_pending || mosq->want_connect){
|
||||
#else
|
||||
if(state == mosq_cs_connect_pending){
|
||||
#endif
|
||||
pthread_mutex_unlock(&mosq->current_out_packet_mutex);
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
}
|
||||
@ -546,7 +542,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) == CMD_PUBLISH){
|
||||
if(((mosq->in_packet.command)&0xF0) == CMD_PUBLISH){
|
||||
G_PUB_MSGS_RECEIVED_INC(1);
|
||||
}
|
||||
#endif
|
||||
|
@ -1208,6 +1208,7 @@ int mosquitto_property_copy_all(mosquitto_property **dest, const mosquitto_prope
|
||||
}
|
||||
plast = pnew;
|
||||
|
||||
pnew->client_generated = src->client_generated;
|
||||
pnew->identifier = src->identifier;
|
||||
switch(pnew->identifier){
|
||||
case MQTT_PROP_PAYLOAD_FORMAT_INDICATOR:
|
||||
|
@ -21,6 +21,10 @@ Contributors:
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef WIN32
|
||||
# include <strings.h>
|
||||
#endif
|
||||
|
||||
#include "mosquitto.h"
|
||||
#include "mqtt_protocol.h"
|
||||
|
||||
|
@ -104,6 +104,11 @@ int mosquitto__check_keepalive(struct mosquitto *mosq)
|
||||
pthread_mutex_unlock(&mosq->msgtime_mutex);
|
||||
}else{
|
||||
#ifdef WITH_BROKER
|
||||
# ifdef WITH_BRIDGE
|
||||
if(mosq->bridge){
|
||||
context__send_will(mosq);
|
||||
}
|
||||
# endif
|
||||
net__socket_close(mosq);
|
||||
#else
|
||||
net__socket_close(mosq);
|
||||
@ -297,3 +302,23 @@ enum mosquitto_client_state mosquitto__get_state(struct mosquitto *mosq)
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
#ifndef WITH_BROKER
|
||||
void mosquitto__set_request_disconnect(struct mosquitto *mosq, bool request_disconnect)
|
||||
{
|
||||
pthread_mutex_lock(&mosq->state_mutex);
|
||||
mosq->request_disconnect = request_disconnect;
|
||||
pthread_mutex_unlock(&mosq->state_mutex);
|
||||
}
|
||||
|
||||
bool mosquitto__get_request_disconnect(struct mosquitto *mosq)
|
||||
{
|
||||
bool request_disconnect;
|
||||
|
||||
pthread_mutex_lock(&mosq->state_mutex);
|
||||
request_disconnect = mosq->request_disconnect;
|
||||
pthread_mutex_unlock(&mosq->state_mutex);
|
||||
|
||||
return request_disconnect;
|
||||
}
|
||||
#endif
|
||||
|
@ -32,6 +32,10 @@ uint16_t mosquitto__mid_generate(struct mosquitto *mosq);
|
||||
|
||||
int mosquitto__set_state(struct mosquitto *mosq, enum mosquitto_client_state state);
|
||||
enum mosquitto_client_state mosquitto__get_state(struct mosquitto *mosq);
|
||||
#ifndef WITH_BROKER
|
||||
void mosquitto__set_request_disconnect(struct mosquitto *mosq, bool request_disconnect);
|
||||
bool mosquitto__get_request_disconnect(struct mosquitto *mosq);
|
||||
#endif
|
||||
|
||||
#ifdef WITH_TLS
|
||||
int mosquitto__hex2bin_sha1(const char *hex, unsigned char **bin);
|
||||
|
@ -8,10 +8,10 @@ if(NOT WIN32)
|
||||
find_program(XSLTPROC xsltproc OPTIONAL)
|
||||
if(XSLTPROC)
|
||||
function(compile_manpage page)
|
||||
add_custom_command(OUTPUT ${CMAKE_SOURCE_DIR}/man/${page}
|
||||
COMMAND xsltproc ${CMAKE_SOURCE_DIR}/man/${page}.xml -o ${CMAKE_SOURCE_DIR}/man/
|
||||
MAIN_DEPENDENCY ${CMAKE_SOURCE_DIR}/man/${page}.xml)
|
||||
add_custom_target(${page} ALL DEPENDS ${CMAKE_SOURCE_DIR}/man/${page})
|
||||
add_custom_command(OUTPUT ${PROJECT_SOURCE_DIR}/man/${page}
|
||||
COMMAND xsltproc ${PROJECT_SOURCE_DIR}/man/${page}.xml -o ${PROJECT_SOURCE_DIR}/man/
|
||||
MAIN_DEPENDENCY ${PROJECT_SOURCE_DIR}/man/${page}.xml)
|
||||
add_custom_target(${page} ALL DEPENDS ${PROJECT_SOURCE_DIR}/man/${page})
|
||||
endfunction()
|
||||
|
||||
compile_manpage("mosquitto_ctrl.1")
|
||||
|
@ -842,15 +842,21 @@ log_timestamp_format %Y-%m-%dT%H:%M:%S
|
||||
<varlistentry>
|
||||
<term><option>persistent_client_expiration</option> <replaceable>duration</replaceable></term>
|
||||
<listitem>
|
||||
<para>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. This is a
|
||||
non-standard option. As far as the MQTT spec is
|
||||
concerned, persistent clients persist forever.</para>
|
||||
<para>Badly designed clients may set clean session to false
|
||||
whilst using a randomly generated client id. This leads
|
||||
to persistent clients that will never reconnect. This
|
||||
option allows these clients to be removed.</para>
|
||||
<para>
|
||||
This option allows the session of persistent clients (those with clean
|
||||
session set to false) <emphasis>that are not currently connected</emphasis> to be removed if they
|
||||
do not reconnect within a certain time frame. This is a non-standard option
|
||||
in MQTT v3.1. MQTT v3.1.1 and v5.0 allow brokers to remove client sessions.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Badly designed clients may set clean session to false whilst using a randomly
|
||||
generated client id. This leads to persistent clients that connect once and
|
||||
never reconnect. This option allows these clients to be removed. 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.
|
||||
</para>
|
||||
|
||||
<para>The expiration period should be an integer followed
|
||||
by one of h d w m y for hour, day, week, month and year
|
||||
respectively. For example:</para>
|
||||
@ -954,7 +960,9 @@ log_timestamp_format %Y-%m-%dT%H:%M:%S
|
||||
<listitem>
|
||||
<para>Set to <replaceable>true</replaceable> to queue
|
||||
messages with QoS 0 when a persistent client is
|
||||
disconnected. These messages are included in the limit
|
||||
disconnected. When bridges topics are configured with QoS level 1 or 2 incoming
|
||||
QoS 0 messages for these topics are also queued.
|
||||
These messages are included in the limit
|
||||
imposed by max_queued_messages. Defaults to
|
||||
<replaceable>false</replaceable>.</para>
|
||||
<para>Note that the MQTT v3.1.1 spec states that only QoS 1
|
||||
@ -1255,6 +1263,7 @@ log_timestamp_format %Y-%m-%dT%H:%M:%S
|
||||
disconnected as not authorised when this option is
|
||||
set to true. Do not use in conjunction with
|
||||
<option>clientid_prefixes</option>.</para>
|
||||
<para>This does not apply globally, but on a per-listener basis.</para>
|
||||
<para>See also
|
||||
<option>use_identity_as_username</option>.</para>
|
||||
<para>Not reloaded on reload signal.</para>
|
||||
|
@ -141,14 +141,16 @@
|
||||
# accepted. MQTT imposes a maximum payload size of 268435455 bytes.
|
||||
#message_size_limit 0
|
||||
|
||||
# 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.
|
||||
#
|
||||
# This is a non-standard option in MQTT V3.1 but allowed in MQTT v3.1.1.
|
||||
# This option allows the session of persistent clients (those with clean
|
||||
# session set to false) that are not currently connected to be removed if they
|
||||
# do not reconnect within a certain time frame. This is a non-standard option
|
||||
# in MQTT v3.1. MQTT v3.1.1 and v5.0 allow brokers to remove client sessions.
|
||||
#
|
||||
# Badly designed clients may set clean session to false whilst using a randomly
|
||||
# generated client id. This leads to persistent clients that will never
|
||||
# reconnect. This option allows these clients to be removed.
|
||||
# generated client id. This leads to persistent clients that connect once and
|
||||
# never reconnect. This option allows these clients to be removed. 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.
|
||||
#
|
||||
# The expiration period should be an integer followed by one of h d w m y for
|
||||
# hour, day, week, month and year respectively. For example
|
||||
@ -288,6 +290,7 @@
|
||||
# authorised when this option is set to true.
|
||||
# Do not use in conjunction with clientid_prefixes.
|
||||
# See also use_identity_as_username.
|
||||
# This does not apply globally, but on a per-listener basis.
|
||||
#use_username_as_clientid
|
||||
|
||||
# Change the websockets headers size. This is a global option, it is not
|
||||
|
@ -720,10 +720,12 @@ static void client__remove_all_roles(struct dynsec__client *client)
|
||||
int dynsec_clients__process_modify(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data)
|
||||
{
|
||||
char *username;
|
||||
char *clientid;
|
||||
char *password;
|
||||
char *text_name, *text_description;
|
||||
char *clientid = NULL;
|
||||
char *password = NULL;
|
||||
char *text_name = NULL, *text_description = NULL;
|
||||
bool have_clientid = false, have_text_name = false, have_text_description = false, have_rolelist = false, have_password = false;
|
||||
struct dynsec__client *client;
|
||||
struct dynsec__group *group;
|
||||
struct dynsec__rolelist *rolelist = NULL;
|
||||
char *str;
|
||||
int rc;
|
||||
@ -746,81 +748,87 @@ int dynsec_clients__process_modify(cJSON *j_responses, struct mosquitto *context
|
||||
return MOSQ_ERR_INVAL;
|
||||
}
|
||||
|
||||
if(json_get_string(command, "clientid", &clientid, false) == MOSQ_ERR_SUCCESS){
|
||||
if(clientid && strlen(clientid) > 0){
|
||||
str = mosquitto_strdup(clientid);
|
||||
if(str == NULL){
|
||||
if(json_get_string(command, "clientid", &str, false) == MOSQ_ERR_SUCCESS){
|
||||
have_clientid = true;
|
||||
if(str && strlen(str) > 0){
|
||||
clientid = mosquitto_strdup(str);
|
||||
if(clientid == NULL){
|
||||
dynsec__command_reply(j_responses, context, "modifyClient", "Internal error", correlation_data);
|
||||
return MOSQ_ERR_NOMEM;
|
||||
rc = MOSQ_ERR_NOMEM;
|
||||
goto error;
|
||||
}
|
||||
}else{
|
||||
str = NULL;
|
||||
clientid = NULL;
|
||||
}
|
||||
mosquitto_free(client->clientid);
|
||||
client->clientid = str;
|
||||
}
|
||||
|
||||
if(json_get_string(command, "password", &password, false) == MOSQ_ERR_SUCCESS){
|
||||
if(strlen(password) > 0){
|
||||
/* If password == "", we just ignore it */
|
||||
rc = client__set_password(client, password);
|
||||
if(rc != MOSQ_ERR_SUCCESS){
|
||||
dynsec__command_reply(j_responses, context, "modifyClient", "Internal error", correlation_data);
|
||||
mosquitto_kick_client_by_username(username, false);
|
||||
return MOSQ_ERR_NOMEM;
|
||||
}
|
||||
have_password = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(json_get_string(command, "textname", &text_name, false) == MOSQ_ERR_SUCCESS){
|
||||
str = mosquitto_strdup(text_name);
|
||||
if(str == NULL){
|
||||
if(json_get_string(command, "textname", &str, false) == MOSQ_ERR_SUCCESS){
|
||||
have_text_name = true;
|
||||
text_name = mosquitto_strdup(str);
|
||||
if(text_name == NULL){
|
||||
dynsec__command_reply(j_responses, context, "modifyClient", "Internal error", correlation_data);
|
||||
mosquitto_kick_client_by_username(username, false);
|
||||
return MOSQ_ERR_NOMEM;
|
||||
rc = MOSQ_ERR_NOMEM;
|
||||
goto error;
|
||||
}
|
||||
mosquitto_free(client->text_name);
|
||||
client->text_name = str;
|
||||
}
|
||||
|
||||
if(json_get_string(command, "textdescription", &text_description, false) == MOSQ_ERR_SUCCESS){
|
||||
str = mosquitto_strdup(text_description);
|
||||
if(str == NULL){
|
||||
if(json_get_string(command, "textdescription", &str, false) == MOSQ_ERR_SUCCESS){
|
||||
have_text_description = true;
|
||||
text_description = mosquitto_strdup(str);
|
||||
if(text_description == NULL){
|
||||
dynsec__command_reply(j_responses, context, "modifyClient", "Internal error", correlation_data);
|
||||
mosquitto_kick_client_by_username(username, false);
|
||||
return MOSQ_ERR_NOMEM;
|
||||
rc = MOSQ_ERR_NOMEM;
|
||||
goto error;
|
||||
}
|
||||
mosquitto_free(client->text_description);
|
||||
client->text_description = str;
|
||||
}
|
||||
|
||||
rc = dynsec_rolelist__load_from_json(command, &rolelist);
|
||||
if(rc == MOSQ_ERR_SUCCESS){
|
||||
client__remove_all_roles(client);
|
||||
client__add_new_roles(client, rolelist);
|
||||
dynsec_rolelist__cleanup(&rolelist);
|
||||
have_rolelist = true;
|
||||
}else if(rc == ERR_LIST_NOT_FOUND){
|
||||
/* There was no list in the JSON, so no modification */
|
||||
}else if(rc == MOSQ_ERR_NOT_FOUND){
|
||||
dynsec__command_reply(j_responses, context, "modifyClient", "Role not found", correlation_data);
|
||||
dynsec_rolelist__cleanup(&rolelist);
|
||||
mosquitto_kick_client_by_username(username, false);
|
||||
return MOSQ_ERR_INVAL;
|
||||
rc = MOSQ_ERR_INVAL;
|
||||
goto error;
|
||||
}else{
|
||||
if(rc == MOSQ_ERR_INVAL){
|
||||
dynsec__command_reply(j_responses, context, "modifyClient", "'roles' not an array or missing/invalid rolename", correlation_data);
|
||||
}else{
|
||||
dynsec__command_reply(j_responses, context, "modifyClient", "Internal error", correlation_data);
|
||||
}
|
||||
dynsec_rolelist__cleanup(&rolelist);
|
||||
mosquitto_kick_client_by_username(username, false);
|
||||
return MOSQ_ERR_INVAL;
|
||||
rc = MOSQ_ERR_INVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
j_groups = cJSON_GetObjectItem(command, "groups");
|
||||
if(j_groups && cJSON_IsArray(j_groups)){
|
||||
dynsec__remove_client_from_all_groups(username);
|
||||
/* Iterate through list to check all groups are valid */
|
||||
cJSON_ArrayForEach(j_group, j_groups){
|
||||
if(cJSON_IsObject(j_group)){
|
||||
jtmp = cJSON_GetObjectItem(j_group, "groupname");
|
||||
if(jtmp && cJSON_IsString(jtmp)){
|
||||
group = dynsec_groups__find(jtmp->valuestring);
|
||||
if(group == NULL){
|
||||
dynsec__command_reply(j_responses, context, "modifyClient", "'groups' contains an object with a 'groupname' that does not exist", correlation_data);
|
||||
rc = MOSQ_ERR_INVAL;
|
||||
goto error;
|
||||
}
|
||||
}else{
|
||||
dynsec__command_reply(j_responses, context, "modifyClient", "'groups' contains an object with an invalid 'groupname'", correlation_data);
|
||||
rc = MOSQ_ERR_INVAL;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dynsec__remove_client_from_all_groups(username);
|
||||
cJSON_ArrayForEach(j_group, j_groups){
|
||||
if(cJSON_IsObject(j_group)){
|
||||
jtmp = cJSON_GetObjectItem(j_group, "groupname");
|
||||
@ -832,6 +840,44 @@ int dynsec_clients__process_modify(cJSON *j_responses, struct mosquitto *context
|
||||
}
|
||||
}
|
||||
|
||||
if(have_password){
|
||||
/* FIXME - This is the one call that will result in modification on internal error - note that groups have already been modified */
|
||||
rc = client__set_password(client, password);
|
||||
if(rc != MOSQ_ERR_SUCCESS){
|
||||
dynsec__command_reply(j_responses, context, "modifyClient", "Internal error", correlation_data);
|
||||
mosquitto_kick_client_by_username(username, false);
|
||||
/* If this fails we have the situation that the password is set as
|
||||
* invalid, but the config isn't saved, so restarting the broker
|
||||
* *now* will mean the client can log in again. This might be
|
||||
* "good", but is inconsistent, so save the config to be
|
||||
* consistent. */
|
||||
dynsec__config_save();
|
||||
rc = MOSQ_ERR_NOMEM;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if(have_clientid){
|
||||
mosquitto_free(client->clientid);
|
||||
client->clientid = clientid;
|
||||
}
|
||||
|
||||
if(have_text_name){
|
||||
mosquitto_free(client->text_name);
|
||||
client->text_name = text_name;
|
||||
}
|
||||
|
||||
if(have_text_description){
|
||||
mosquitto_free(client->text_description);
|
||||
client->text_description = text_description;
|
||||
}
|
||||
|
||||
if(have_rolelist){
|
||||
client__remove_all_roles(client);
|
||||
client__add_new_roles(client, rolelist);
|
||||
dynsec_rolelist__cleanup(&rolelist);
|
||||
}
|
||||
|
||||
dynsec__config_save();
|
||||
dynsec__command_reply(j_responses, context, "modifyClient", NULL, correlation_data);
|
||||
|
||||
@ -843,6 +889,12 @@ int dynsec_clients__process_modify(cJSON *j_responses, struct mosquitto *context
|
||||
mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | modifyClient | username=%s",
|
||||
admin_clientid, admin_username, username);
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
error:
|
||||
mosquitto_free(clientid);
|
||||
mosquitto_free(text_name);
|
||||
mosquitto_free(text_description);
|
||||
dynsec_rolelist__cleanup(&rolelist);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
@ -466,6 +466,11 @@ int dynsec_groups__process_delete(cJSON *j_responses, struct mosquitto *context,
|
||||
|
||||
group = dynsec_groups__find(groupname);
|
||||
if(group){
|
||||
if(group == dynsec_anonymous_group){
|
||||
dynsec__command_reply(j_responses, context, "deleteGroup", "Deleting the anonymous group is forbidden", correlation_data);
|
||||
return MOSQ_ERR_INVAL;
|
||||
}
|
||||
|
||||
/* Enforce any changes */
|
||||
group__kick_all(group);
|
||||
|
||||
@ -911,10 +916,12 @@ int dynsec_groups__process_remove_role(cJSON *j_responses, struct mosquitto *con
|
||||
|
||||
int dynsec_groups__process_modify(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data)
|
||||
{
|
||||
char *groupname;
|
||||
char *text_name, *text_description;
|
||||
struct dynsec__group *group;
|
||||
char *groupname = NULL;
|
||||
char *text_name = NULL, *text_description = NULL;
|
||||
struct dynsec__client *client = NULL;
|
||||
struct dynsec__group *group = NULL;
|
||||
struct dynsec__rolelist *rolelist = NULL;
|
||||
bool have_text_name = false, have_text_description = false, have_rolelist = false;
|
||||
char *str;
|
||||
int rc;
|
||||
int priority;
|
||||
@ -936,52 +943,73 @@ int dynsec_groups__process_modify(cJSON *j_responses, struct mosquitto *context,
|
||||
return MOSQ_ERR_INVAL;
|
||||
}
|
||||
|
||||
if(json_get_string(command, "textname", &text_name, false) == MOSQ_ERR_SUCCESS){
|
||||
str = mosquitto_strdup(text_name);
|
||||
if(str == NULL){
|
||||
if(json_get_string(command, "textname", &str, false) == MOSQ_ERR_SUCCESS){
|
||||
have_text_name = true;
|
||||
text_name = mosquitto_strdup(str);
|
||||
if(text_name == NULL){
|
||||
dynsec__command_reply(j_responses, context, "modifyGroup", "Internal error", correlation_data);
|
||||
return MOSQ_ERR_NOMEM;
|
||||
rc = MOSQ_ERR_NOMEM;
|
||||
goto error;
|
||||
}
|
||||
mosquitto_free(group->text_name);
|
||||
group->text_name = str;
|
||||
}
|
||||
|
||||
if(json_get_string(command, "textdescription", &text_description, false) == MOSQ_ERR_SUCCESS){
|
||||
str = mosquitto_strdup(text_description);
|
||||
if(str == NULL){
|
||||
if(json_get_string(command, "textdescription", &str, false) == MOSQ_ERR_SUCCESS){
|
||||
have_text_description = true;
|
||||
text_description = mosquitto_strdup(str);
|
||||
if(text_description == NULL){
|
||||
dynsec__command_reply(j_responses, context, "modifyGroup", "Internal error", correlation_data);
|
||||
return MOSQ_ERR_NOMEM;
|
||||
rc = MOSQ_ERR_NOMEM;
|
||||
goto error;
|
||||
}
|
||||
mosquitto_free(group->text_description);
|
||||
group->text_description = str;
|
||||
}
|
||||
|
||||
rc = dynsec_rolelist__load_from_json(command, &rolelist);
|
||||
if(rc == MOSQ_ERR_SUCCESS){
|
||||
dynsec_rolelist__cleanup(&group->rolelist);
|
||||
group->rolelist = rolelist;
|
||||
/* Apply changes below */
|
||||
have_rolelist = true;
|
||||
}else if(rc == ERR_LIST_NOT_FOUND){
|
||||
/* There was no list in the JSON, so no modification */
|
||||
rolelist = NULL;
|
||||
}else if(rc == MOSQ_ERR_NOT_FOUND){
|
||||
dynsec__command_reply(j_responses, context, "modifyGroup", "Role not found", correlation_data);
|
||||
dynsec_rolelist__cleanup(&rolelist);
|
||||
group__kick_all(group);
|
||||
return MOSQ_ERR_INVAL;
|
||||
rc = MOSQ_ERR_INVAL;
|
||||
goto error;
|
||||
}else{
|
||||
if(rc == MOSQ_ERR_INVAL){
|
||||
dynsec__command_reply(j_responses, context, "modifyGroup", "'roles' not an array or missing/invalid rolename", correlation_data);
|
||||
}else{
|
||||
dynsec__command_reply(j_responses, context, "modifyGroup", "Internal error", correlation_data);
|
||||
}
|
||||
dynsec_rolelist__cleanup(&rolelist);
|
||||
group__kick_all(group);
|
||||
return MOSQ_ERR_INVAL;
|
||||
rc = MOSQ_ERR_INVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
j_clients = cJSON_GetObjectItem(command, "clients");
|
||||
if(j_clients && cJSON_IsArray(j_clients)){
|
||||
/* Iterate over array to check clients are valid before proceeding */
|
||||
cJSON_ArrayForEach(j_client, j_clients){
|
||||
if(cJSON_IsObject(j_client)){
|
||||
jtmp = cJSON_GetObjectItem(j_client, "username");
|
||||
if(jtmp && cJSON_IsString(jtmp)){
|
||||
client = dynsec_clients__find(jtmp->valuestring);
|
||||
if(client == NULL){
|
||||
dynsec__command_reply(j_responses, context, "modifyGroup", "'clients' contains an object with a 'username' that does not exist", correlation_data);
|
||||
rc = MOSQ_ERR_INVAL;
|
||||
goto error;
|
||||
}
|
||||
}else{
|
||||
dynsec__command_reply(j_responses, context, "modifyGroup", "'clients' contains an object with an invalid 'username'", correlation_data);
|
||||
rc = MOSQ_ERR_INVAL;
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Kick all clients in the *current* group */
|
||||
group__kick_all(group);
|
||||
dynsec__remove_all_clients_from_group(group);
|
||||
|
||||
/* Now we can add the new clients to the group */
|
||||
cJSON_ArrayForEach(j_client, j_clients){
|
||||
if(cJSON_IsObject(j_client)){
|
||||
jtmp = cJSON_GetObjectItem(j_client, "username");
|
||||
@ -993,11 +1021,28 @@ int dynsec_groups__process_modify(cJSON *j_responses, struct mosquitto *context,
|
||||
}
|
||||
}
|
||||
|
||||
/* Apply remaining changes to group, note that user changes are already applied */
|
||||
if(have_text_name){
|
||||
mosquitto_free(group->text_name);
|
||||
group->text_name = text_name;
|
||||
}
|
||||
|
||||
if(have_text_description){
|
||||
mosquitto_free(group->text_description);
|
||||
group->text_description = text_description;
|
||||
}
|
||||
|
||||
if(have_rolelist){
|
||||
dynsec_rolelist__cleanup(&group->rolelist);
|
||||
group->rolelist = rolelist;
|
||||
}
|
||||
|
||||
/* And save */
|
||||
dynsec__config_save();
|
||||
|
||||
dynsec__command_reply(j_responses, context, "modifyGroup", NULL, correlation_data);
|
||||
|
||||
/* Enforce any changes */
|
||||
/* Enforce any changes - kick any clients in the *new* group */
|
||||
group__kick_all(group);
|
||||
|
||||
admin_clientid = mosquitto_client_id(context);
|
||||
@ -1006,6 +1051,17 @@ int dynsec_groups__process_modify(cJSON *j_responses, struct mosquitto *context,
|
||||
admin_clientid, admin_username, groupname);
|
||||
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
error:
|
||||
mosquitto_free(text_name);
|
||||
mosquitto_free(text_description);
|
||||
dynsec_rolelist__cleanup(&rolelist);
|
||||
|
||||
admin_clientid = mosquitto_client_id(context);
|
||||
admin_username = mosquitto_client_username(context);
|
||||
mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | modifyGroup | groupname=%s",
|
||||
admin_clientid, admin_username, groupname);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
|
@ -25,6 +25,10 @@ Contributors:
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifndef WIN32
|
||||
# include <strings.h>
|
||||
#endif
|
||||
|
||||
#include "json_help.h"
|
||||
#include "mosquitto.h"
|
||||
#include "mosquitto_broker.h"
|
||||
@ -354,18 +358,25 @@ static int dynsec__config_load(void)
|
||||
cJSON *tree;
|
||||
|
||||
/* Load from file */
|
||||
errno = 0;
|
||||
fptr = fopen(config_file, "rb");
|
||||
if(fptr == NULL){
|
||||
mosquitto_log_printf(MOSQ_LOG_ERR, "Error loading Dynamic security plugin config: File is not readable - check permissions.\n");
|
||||
return 1;
|
||||
return MOSQ_ERR_ERRNO;
|
||||
}
|
||||
#ifndef WIN32
|
||||
if(errno == ENOTDIR || errno == EISDIR){
|
||||
mosquitto_log_printf(MOSQ_LOG_ERR, "Error loading Dynamic security plugin config: Config is not a file.\n");
|
||||
return MOSQ_ERR_ERRNO;
|
||||
}
|
||||
#endif
|
||||
|
||||
fseek(fptr, 0, SEEK_END);
|
||||
flen_l = ftell(fptr);
|
||||
if(flen_l < 0){
|
||||
mosquitto_log_printf(MOSQ_LOG_ERR, "Error loading Dynamic security plugin config: %s\n", strerror(errno));
|
||||
fclose(fptr);
|
||||
return 1;
|
||||
return MOSQ_ERR_ERRNO;
|
||||
}else if(flen_l == 0){
|
||||
fclose(fptr);
|
||||
return 0;
|
||||
@ -376,13 +387,13 @@ static int dynsec__config_load(void)
|
||||
if(json_str == NULL){
|
||||
mosquitto_log_printf(MOSQ_LOG_ERR, "Error: Out of memory.");
|
||||
fclose(fptr);
|
||||
return 1;
|
||||
return MOSQ_ERR_NOMEM;
|
||||
}
|
||||
if(fread(json_str, 1, flen, fptr) != flen){
|
||||
mosquitto_log_printf(MOSQ_LOG_WARNING, "Error loading Dynamic security plugin config: Unable to read file contents.\n");
|
||||
mosquitto_free(json_str);
|
||||
fclose(fptr);
|
||||
return 1;
|
||||
return MOSQ_ERR_ERRNO;
|
||||
}
|
||||
fclose(fptr);
|
||||
|
||||
@ -390,7 +401,7 @@ static int dynsec__config_load(void)
|
||||
mosquitto_free(json_str);
|
||||
if(tree == NULL){
|
||||
mosquitto_log_printf(MOSQ_LOG_ERR, "Error loading Dynamic security plugin config: File is not valid JSON.\n");
|
||||
return 1;
|
||||
return MOSQ_ERR_INVAL;
|
||||
}
|
||||
|
||||
if(dynsec__general_config_load(tree)
|
||||
@ -400,7 +411,7 @@ static int dynsec__config_load(void)
|
||||
){
|
||||
|
||||
cJSON_Delete(tree);
|
||||
return 1;
|
||||
return MOSQ_ERR_NOMEM;
|
||||
}
|
||||
|
||||
cJSON_Delete(tree);
|
||||
@ -471,6 +482,7 @@ void dynsec__config_save(void)
|
||||
int mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *options, int option_count)
|
||||
{
|
||||
int i;
|
||||
int rc;
|
||||
|
||||
UNUSED(user_data);
|
||||
|
||||
@ -491,11 +503,46 @@ int mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, s
|
||||
plg_id = identifier;
|
||||
|
||||
dynsec__config_load();
|
||||
mosquitto_callback_register(plg_id, MOSQ_EVT_CONTROL, dynsec_control_callback, "$CONTROL/dynamic-security/v1", NULL);
|
||||
mosquitto_callback_register(plg_id, MOSQ_EVT_BASIC_AUTH, dynsec_auth__basic_auth_callback, NULL, NULL);
|
||||
mosquitto_callback_register(plg_id, MOSQ_EVT_ACL_CHECK, dynsec__acl_check_callback, NULL, NULL);
|
||||
|
||||
rc = mosquitto_callback_register(plg_id, MOSQ_EVT_CONTROL, dynsec_control_callback, "$CONTROL/dynamic-security/v1", NULL);
|
||||
if(rc == MOSQ_ERR_ALREADY_EXISTS){
|
||||
mosquitto_log_printf(MOSQ_LOG_ERR, "Error: Dynamic security plugin can currently only be loaded once.");
|
||||
mosquitto_log_printf(MOSQ_LOG_ERR, "Note that this was previously incorrectly allowed but could cause problems with duplicate entries in the config.");
|
||||
goto error;
|
||||
}else if(rc == MOSQ_ERR_NOMEM){
|
||||
mosquitto_log_printf(MOSQ_LOG_ERR, "Error: Out of memory.");
|
||||
goto error;
|
||||
}else if(rc != MOSQ_ERR_SUCCESS){
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = mosquitto_callback_register(plg_id, MOSQ_EVT_BASIC_AUTH, dynsec_auth__basic_auth_callback, NULL, NULL);
|
||||
if(rc == MOSQ_ERR_ALREADY_EXISTS){
|
||||
mosquitto_log_printf(MOSQ_LOG_ERR, "Error: Dynamic security plugin can only be loaded once.");
|
||||
goto error;
|
||||
}else if(rc == MOSQ_ERR_NOMEM){
|
||||
mosquitto_log_printf(MOSQ_LOG_ERR, "Error: Out of memory.");
|
||||
goto error;
|
||||
}else if(rc != MOSQ_ERR_SUCCESS){
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = mosquitto_callback_register(plg_id, MOSQ_EVT_ACL_CHECK, dynsec__acl_check_callback, NULL, NULL);
|
||||
if(rc == MOSQ_ERR_ALREADY_EXISTS){
|
||||
mosquitto_log_printf(MOSQ_LOG_ERR, "Error: Dynamic security plugin can only be loaded once.");
|
||||
goto error;
|
||||
}else if(rc == MOSQ_ERR_NOMEM){
|
||||
mosquitto_log_printf(MOSQ_LOG_ERR, "Error: Out of memory.");
|
||||
goto error;
|
||||
}else if(rc != MOSQ_ERR_SUCCESS){
|
||||
goto error;
|
||||
}
|
||||
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
error:
|
||||
mosquitto_free(config_file);
|
||||
config_file = NULL;
|
||||
return rc;
|
||||
}
|
||||
|
||||
int mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *options, int option_count)
|
||||
|
@ -24,6 +24,10 @@ Contributors:
|
||||
#include <uthash.h>
|
||||
#include <utlist.h>
|
||||
|
||||
#ifndef WIN32
|
||||
# include <strings.h>
|
||||
#endif
|
||||
|
||||
#include "dynamic_security.h"
|
||||
#include "json_help.h"
|
||||
#include "mosquitto.h"
|
||||
|
@ -11,9 +11,9 @@ ExecStart=/usr/sbin/mosquitto -c /etc/mosquitto/mosquitto.conf
|
||||
ExecReload=/bin/kill -HUP $MAINPID
|
||||
Restart=on-failure
|
||||
ExecStartPre=/bin/mkdir -m 740 -p /var/log/mosquitto
|
||||
ExecStartPre=/bin/chown mosquitto /var/log/mosquitto
|
||||
ExecStartPre=/bin/chown mosquitto:mosquitto /var/log/mosquitto
|
||||
ExecStartPre=/bin/mkdir -m 740 -p /run/mosquitto
|
||||
ExecStartPre=/bin/chown mosquitto /run/mosquitto
|
||||
ExecStartPre=/bin/chown mosquitto:mosquitto /run/mosquitto
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
@ -9,9 +9,9 @@ ExecStart=/usr/sbin/mosquitto -c /etc/mosquitto/mosquitto.conf
|
||||
ExecReload=/bin/kill -HUP $MAINPID
|
||||
Restart=on-failure
|
||||
ExecStartPre=/bin/mkdir -m 740 -p /var/log/mosquitto
|
||||
ExecStartPre=/bin/chown mosquitto /var/log/mosquitto
|
||||
ExecStartPre=/bin/chown mosquitto:mosquitto /var/log/mosquitto
|
||||
ExecStartPre=/bin/mkdir -m 740 -p /run/mosquitto
|
||||
ExecStartPre=/bin/chown mosquitto /run/mosquitto
|
||||
ExecStartPre=/bin/chown mosquitto:mosquitto /run/mosquitto
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
MAJOR=2
|
||||
MINOR=0
|
||||
REVISION=14
|
||||
REVISION=15
|
||||
|
||||
sed -i "s/^VERSION=.*/VERSION=${MAJOR}.${MINOR}.${REVISION}/" config.mk
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
name: mosquitto
|
||||
version: 2.0.14
|
||||
version: 2.0.15
|
||||
summary: Eclipse Mosquitto MQTT broker
|
||||
description: This is a message broker that supports version 5.0, 3.1.1, and 3.1 of the MQTT
|
||||
protocol.
|
||||
|
@ -1897,6 +1897,8 @@ static int config__read_file_core(struct mosquitto__config *config, bool reload,
|
||||
return MOSQ_ERR_INVAL;
|
||||
}
|
||||
cur_bridge->restart_timeout = atoi(token);
|
||||
cur_bridge->backoff_base = 0;
|
||||
cur_bridge->backoff_cap = 0;
|
||||
if(cur_bridge->restart_timeout < 1){
|
||||
log__printf(NULL, MOSQ_LOG_NOTICE, "restart_timeout interval too low, using 1 second.");
|
||||
cur_bridge->restart_timeout = 1;
|
||||
|
@ -65,9 +65,9 @@ int control__process(struct mosquitto *context, struct mosquitto_msg_store *stor
|
||||
}
|
||||
|
||||
if(stored->qos == 1){
|
||||
if(send__puback(context, stored->source_mid, MQTT_RC_SUCCESS, properties)) rc = 1;
|
||||
rc = send__puback(context, stored->source_mid, MQTT_RC_SUCCESS, properties);
|
||||
}else if(stored->qos == 2){
|
||||
if(send__pubrec(context, stored->source_mid, MQTT_RC_SUCCESS, properties)) rc = 1;
|
||||
rc = send__pubrec(context, stored->source_mid, MQTT_RC_SUCCESS, properties);
|
||||
}
|
||||
mosquitto_property_free_all(&properties);
|
||||
|
||||
|
@ -339,7 +339,7 @@ void db__msg_store_compact(void)
|
||||
}
|
||||
|
||||
|
||||
static void db__message_remove(struct mosquitto_msg_data *msg_data, struct mosquitto_client_msg *item)
|
||||
static void db__message_remove_from_inflight(struct mosquitto_msg_data *msg_data, struct mosquitto_client_msg *item)
|
||||
{
|
||||
if(!msg_data || !item){
|
||||
return;
|
||||
@ -356,6 +356,22 @@ static void db__message_remove(struct mosquitto_msg_data *msg_data, struct mosqu
|
||||
}
|
||||
|
||||
|
||||
static void db__message_remove_from_queued(struct mosquitto_msg_data *msg_data, struct mosquitto_client_msg *item)
|
||||
{
|
||||
if(!msg_data || !item){
|
||||
return;
|
||||
}
|
||||
|
||||
DL_DELETE(msg_data->queued, item);
|
||||
if(item->store){
|
||||
db__msg_store_ref_dec(&item->store);
|
||||
}
|
||||
|
||||
mosquitto_property_free_all(&item->properties);
|
||||
mosquitto__free(item);
|
||||
}
|
||||
|
||||
|
||||
void db__message_dequeue_first(struct mosquitto *context, struct mosquitto_msg_data *msg_data)
|
||||
{
|
||||
struct mosquitto_client_msg *msg;
|
||||
@ -390,7 +406,7 @@ int db__message_delete_outgoing(struct mosquitto *context, uint16_t mid, enum mo
|
||||
return MOSQ_ERR_PROTOCOL;
|
||||
}
|
||||
msg_index--;
|
||||
db__message_remove(&context->msgs_out, tail);
|
||||
db__message_remove_from_inflight(&context->msgs_out, tail);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -894,7 +910,7 @@ static int db__message_reconnect_reset_incoming(struct mosquitto *context)
|
||||
if(msg->qos != 2){
|
||||
/* Anything <QoS 2 can be completely retried by the client at
|
||||
* no harm. */
|
||||
db__message_remove(&context->msgs_in, msg);
|
||||
db__message_remove_from_inflight(&context->msgs_in, msg);
|
||||
}else{
|
||||
/* Message state can be preserved here because it should match
|
||||
* whatever the client has got. */
|
||||
@ -950,7 +966,7 @@ int db__message_remove_incoming(struct mosquitto* context, uint16_t mid)
|
||||
if(tail->store->qos != 2){
|
||||
return MOSQ_ERR_PROTOCOL;
|
||||
}
|
||||
db__message_remove(&context->msgs_in, tail);
|
||||
db__message_remove_from_inflight(&context->msgs_in, tail);
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
}
|
||||
}
|
||||
@ -986,12 +1002,12 @@ int db__message_release_incoming(struct mosquitto *context, uint16_t mid)
|
||||
* keep resending it. That means we don't send it to other
|
||||
* clients. */
|
||||
if(topic == NULL){
|
||||
db__message_remove(&context->msgs_in, tail);
|
||||
db__message_remove_from_inflight(&context->msgs_in, tail);
|
||||
deleted = true;
|
||||
}else{
|
||||
rc = sub__messages_queue(source_id, topic, 2, retain, &tail->store);
|
||||
if(rc == MOSQ_ERR_SUCCESS || rc == MOSQ_ERR_NO_SUBSCRIBERS){
|
||||
db__message_remove(&context->msgs_in, tail);
|
||||
db__message_remove_from_inflight(&context->msgs_in, tail);
|
||||
deleted = true;
|
||||
}else{
|
||||
return 1;
|
||||
@ -1021,6 +1037,40 @@ int db__message_release_incoming(struct mosquitto *context, uint16_t mid)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void db__expire_all_messages(struct mosquitto *context)
|
||||
{
|
||||
struct mosquitto_client_msg *msg, *tmp;
|
||||
|
||||
DL_FOREACH_SAFE(context->msgs_out.inflight, msg, tmp){
|
||||
if(msg->store->message_expiry_time && db.now_real_s > msg->store->message_expiry_time){
|
||||
if(msg->qos > 0){
|
||||
util__increment_send_quota(context);
|
||||
}
|
||||
db__message_remove_from_inflight(&context->msgs_out, msg);
|
||||
}
|
||||
}
|
||||
DL_FOREACH_SAFE(context->msgs_out.queued, msg, tmp){
|
||||
if(msg->store->message_expiry_time && db.now_real_s > msg->store->message_expiry_time){
|
||||
db__message_remove_from_queued(&context->msgs_out, msg);
|
||||
}
|
||||
}
|
||||
DL_FOREACH_SAFE(context->msgs_in.inflight, msg, tmp){
|
||||
if(msg->store->message_expiry_time && db.now_real_s > msg->store->message_expiry_time){
|
||||
if(msg->qos > 0){
|
||||
util__increment_receive_quota(context);
|
||||
}
|
||||
db__message_remove_from_inflight(&context->msgs_in, msg);
|
||||
}
|
||||
}
|
||||
DL_FOREACH_SAFE(context->msgs_in.queued, msg, tmp){
|
||||
if(msg->store->message_expiry_time && db.now_real_s > msg->store->message_expiry_time){
|
||||
db__message_remove_from_queued(&context->msgs_in, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int db__message_write_inflight_out_single(struct mosquitto *context, struct mosquitto_client_msg *msg)
|
||||
{
|
||||
mosquitto_property *cmsg_props = NULL, *store_props = NULL;
|
||||
@ -1041,7 +1091,7 @@ static int db__message_write_inflight_out_single(struct mosquitto *context, stru
|
||||
if(msg->direction == mosq_md_out && msg->qos > 0){
|
||||
util__increment_send_quota(context);
|
||||
}
|
||||
db__message_remove(&context->msgs_out, msg);
|
||||
db__message_remove_from_inflight(&context->msgs_out, msg);
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
}else{
|
||||
expiry_interval = (uint32_t)(msg->store->message_expiry_time - db.now_real_s);
|
||||
@ -1061,7 +1111,7 @@ static int db__message_write_inflight_out_single(struct mosquitto *context, stru
|
||||
case mosq_ms_publish_qos0:
|
||||
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(&context->msgs_out, msg);
|
||||
db__message_remove_from_inflight(&context->msgs_out, msg);
|
||||
}else{
|
||||
return rc;
|
||||
}
|
||||
@ -1074,7 +1124,7 @@ static int db__message_write_inflight_out_single(struct mosquitto *context, stru
|
||||
msg->dup = 1; /* Any retry attempts are a duplicate. */
|
||||
msg->state = mosq_ms_wait_for_puback;
|
||||
}else if(rc == MOSQ_ERR_OVERSIZE_PACKET){
|
||||
db__message_remove(&context->msgs_out, msg);
|
||||
db__message_remove_from_inflight(&context->msgs_out, msg);
|
||||
}else{
|
||||
return rc;
|
||||
}
|
||||
@ -1087,7 +1137,7 @@ static int db__message_write_inflight_out_single(struct mosquitto *context, stru
|
||||
msg->dup = 1; /* Any retry attempts are a duplicate. */
|
||||
msg->state = mosq_ms_wait_for_pubrec;
|
||||
}else if(rc == MOSQ_ERR_OVERSIZE_PACKET){
|
||||
db__message_remove(&context->msgs_out, msg);
|
||||
db__message_remove_from_inflight(&context->msgs_out, msg);
|
||||
}else{
|
||||
return rc;
|
||||
}
|
||||
@ -1188,7 +1238,7 @@ int db__message_write_queued_in(struct mosquitto *context)
|
||||
}
|
||||
|
||||
DL_FOREACH_SAFE(context->msgs_in.queued, tail, tmp){
|
||||
if(context->msgs_out.inflight_maximum != 0 && context->msgs_in.inflight_quota == 0){
|
||||
if(context->msgs_in.inflight_maximum != 0 && context->msgs_in.inflight_quota == 0){
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -163,7 +163,7 @@ int handle__connack(struct mosquitto *context)
|
||||
log__printf(NULL, MOSQ_LOG_ERR, "Connection Refused: broker unavailable");
|
||||
return MOSQ_ERR_CONN_LOST;
|
||||
case CONNACK_REFUSED_BAD_USERNAME_PASSWORD:
|
||||
log__printf(NULL, MOSQ_LOG_ERR, "Connection Refused: broker unavailable");
|
||||
log__printf(NULL, MOSQ_LOG_ERR, "Connection Refused: bad user name or password");
|
||||
return MOSQ_ERR_CONN_LOST;
|
||||
case CONNACK_REFUSED_NOT_AUTHORIZED:
|
||||
log__printf(NULL, MOSQ_LOG_ERR, "Connection Refused: not authorised");
|
||||
|
@ -205,6 +205,10 @@ int connect__on_authorised(struct mosquitto *context, void *auth_data_out, uint1
|
||||
found_context->clean_start = true;
|
||||
found_context->session_expiry_interval = 0;
|
||||
mosquitto__set_state(found_context, mosq_cs_duplicate);
|
||||
|
||||
if(found_context->protocol == mosq_p_mqtt5){
|
||||
send__disconnect(found_context, MQTT_RC_SESSION_TAKEN_OVER, NULL);
|
||||
}
|
||||
do_disconnect(found_context, MOSQ_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
@ -314,6 +318,7 @@ int connect__on_authorised(struct mosquitto *context, void *auth_data_out, uint1
|
||||
rc = send__connack(context, connect_ack, CONNACK_ACCEPTED, connack_props);
|
||||
mosquitto_property_free_all(&connack_props);
|
||||
if(rc) return rc;
|
||||
db__expire_all_messages(context);
|
||||
rc = db__message_write_queued_out(context);
|
||||
if(rc) return rc;
|
||||
rc = db__message_write_inflight_out_all(context);
|
||||
@ -458,9 +463,6 @@ int handle__connect(struct mosquitto *context)
|
||||
rc = MOSQ_ERR_PROTOCOL;
|
||||
goto handle_connect_error;
|
||||
}
|
||||
if(context->in_packet.command != CMD_CONNECT){
|
||||
return MOSQ_ERR_MALFORMED_PACKET;
|
||||
}
|
||||
|
||||
/* Read protocol name as length then bytes rather than with read_string
|
||||
* because the length is fixed and we can check that. Removes the need
|
||||
@ -528,6 +530,9 @@ int handle__connect(struct mosquitto *context)
|
||||
rc = MOSQ_ERR_PROTOCOL;
|
||||
goto handle_connect_error;
|
||||
}
|
||||
if((protocol_version&0x7F) != PROTOCOL_VERSION_v31 && context->in_packet.command != CMD_CONNECT){
|
||||
return MOSQ_ERR_MALFORMED_PACKET;
|
||||
}
|
||||
|
||||
if(packet__read_byte(&context->in_packet, &connect_flags)){
|
||||
rc = MOSQ_ERR_PROTOCOL;
|
||||
|
@ -670,6 +670,7 @@ int db__message_write_queued_out(struct mosquitto *context);
|
||||
int db__message_write_queued_in(struct mosquitto *context);
|
||||
void db__msg_add_to_inflight_stats(struct mosquitto_msg_data *msg_data, struct mosquitto_client_msg *msg);
|
||||
void db__msg_add_to_queued_stats(struct mosquitto_msg_data *msg_data, struct mosquitto_client_msg *msg);
|
||||
void db__expire_all_messages(struct mosquitto *context);
|
||||
|
||||
/* ============================================================
|
||||
* Subscription functions
|
||||
@ -818,6 +819,7 @@ void unpwd__free_item(struct mosquitto__unpwd **unpwd, struct mosquitto__unpwd *
|
||||
* Session expiry
|
||||
* ============================================================ */
|
||||
int session_expiry__add(struct mosquitto *context);
|
||||
int session_expiry__add_from_persistence(struct mosquitto *context, time_t expiry_time);
|
||||
void session_expiry__remove(struct mosquitto *context);
|
||||
void session_expiry__remove_all(void);
|
||||
void session_expiry__check(void);
|
||||
|
33
src/net.c
33
src/net.c
@ -19,15 +19,16 @@ Contributors:
|
||||
#include "config.h"
|
||||
|
||||
#ifndef WIN32
|
||||
#include <netdb.h>
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <ifaddrs.h>
|
||||
# include <arpa/inet.h>
|
||||
# include <ifaddrs.h>
|
||||
# include <netdb.h>
|
||||
# include <netinet/tcp.h>
|
||||
# include <strings.h>
|
||||
# include <sys/socket.h>
|
||||
# include <unistd.h>
|
||||
#else
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
# include <winsock2.h>
|
||||
# include <ws2tcpip.h>
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
@ -36,7 +37,7 @@ Contributors:
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#ifdef WITH_WRAP
|
||||
#include <tcpd.h>
|
||||
# include <tcpd.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_NETINET_IN_H
|
||||
@ -49,7 +50,7 @@ Contributors:
|
||||
#endif
|
||||
|
||||
#ifdef __QNX__
|
||||
#include <net/netbyte.h>
|
||||
# include <net/netbyte.h>
|
||||
#endif
|
||||
|
||||
#include "mosquitto_broker_internal.h"
|
||||
@ -59,8 +60,8 @@ Contributors:
|
||||
#include "util_mosq.h"
|
||||
|
||||
#ifdef WITH_TLS
|
||||
#include "tls_mosq.h"
|
||||
#include <openssl/err.h>
|
||||
# include "tls_mosq.h"
|
||||
# include <openssl/err.h>
|
||||
static int tls_ex_index_context = -1;
|
||||
static int tls_ex_index_listener = -1;
|
||||
#endif
|
||||
@ -569,7 +570,7 @@ int net__tls_load_verify(struct mosquitto__listener *listener)
|
||||
#ifdef WITH_TLS
|
||||
int rc;
|
||||
|
||||
#if OPENSSL_VERSION_NUMBER < 0x30000000L
|
||||
# if OPENSSL_VERSION_NUMBER < 0x30000000L
|
||||
if(listener->cafile || listener->capath){
|
||||
rc = SSL_CTX_load_verify_locations(listener->ssl_ctx, listener->cafile, listener->capath);
|
||||
if(rc == 0){
|
||||
@ -582,7 +583,7 @@ int net__tls_load_verify(struct mosquitto__listener *listener)
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
# else
|
||||
if(listener->cafile){
|
||||
rc = SSL_CTX_load_verify_file(listener->ssl_ctx, listener->cafile);
|
||||
if(rc == 0){
|
||||
@ -599,11 +600,13 @@ int net__tls_load_verify(struct mosquitto__listener *listener)
|
||||
return MOSQ_ERR_TLS;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
# endif
|
||||
|
||||
# if !defined(OPENSSL_NO_ENGINE)
|
||||
if(net__load_engine(listener)){
|
||||
return MOSQ_ERR_TLS;
|
||||
}
|
||||
# endif
|
||||
#endif
|
||||
return net__load_certificates(listener);
|
||||
}
|
||||
|
@ -208,7 +208,7 @@ static int persist__client_chunk_restore(FILE *db_fptr)
|
||||
}
|
||||
}
|
||||
}
|
||||
/* FIXME - we should expire clients here if they have exceeded their time */
|
||||
session_expiry__add_from_persistence(context, chunk.F.session_expiry_time);
|
||||
}else{
|
||||
rc = 1;
|
||||
}
|
||||
|
@ -167,8 +167,17 @@ static int persist__client_save(FILE *db_fptr)
|
||||
memset(&chunk, 0, sizeof(struct P_client));
|
||||
|
||||
HASH_ITER(hh_id, db.contexts_by_id, context, ctxt_tmp){
|
||||
if(context && context->clean_start == false){
|
||||
if(context && (context->clean_start == false
|
||||
#ifdef WITH_BRIDGE
|
||||
|| (context->bridge && context->bridge->clean_start_local == false)
|
||||
#endif
|
||||
)){
|
||||
chunk.F.session_expiry_time = context->session_expiry_time;
|
||||
if(context->session_expiry_interval != 0 && context->session_expiry_interval != UINT32_MAX && context->session_expiry_time == 0){
|
||||
chunk.F.session_expiry_time = context->session_expiry_interval + db.now_real_s;
|
||||
}else{
|
||||
chunk.F.session_expiry_time = context->session_expiry_time;
|
||||
}
|
||||
chunk.F.session_expiry_interval = context->session_expiry_interval;
|
||||
chunk.F.last_mid = context->last_mid;
|
||||
chunk.F.id_len = (uint16_t)strlen(context->id);
|
||||
|
30
src/plugin.c
30
src/plugin.c
@ -157,19 +157,29 @@ int plugin__handle_message(struct mosquitto *context, struct mosquitto_msg_store
|
||||
|
||||
DL_FOREACH(opts->plugin_callbacks.message, cb_base){
|
||||
rc = cb_base->cb(MOSQ_EVT_MESSAGE, &event_data, cb_base->userdata);
|
||||
if(rc != MOSQ_ERR_SUCCESS){
|
||||
break;
|
||||
}
|
||||
|
||||
if(stored->topic != event_data.topic){
|
||||
mosquitto__free(stored->topic);
|
||||
stored->topic = event_data.topic;
|
||||
}
|
||||
|
||||
stored->topic = event_data.topic;
|
||||
if(stored->payload != event_data.payload){
|
||||
mosquitto__free(stored->payload);
|
||||
stored->payload = event_data.payload;
|
||||
stored->payloadlen = event_data.payloadlen;
|
||||
}
|
||||
stored->retain = event_data.retain;
|
||||
|
||||
if(stored->properties != event_data.properties){
|
||||
mosquitto_property_free_all(&stored->properties);
|
||||
stored->properties = event_data.properties;
|
||||
}
|
||||
|
||||
if(rc != MOSQ_ERR_SUCCESS){
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
stored->retain = event_data.retain;
|
||||
|
||||
return rc;
|
||||
}
|
||||
@ -180,10 +190,18 @@ void plugin__handle_tick(void)
|
||||
struct mosquitto_evt_tick event_data;
|
||||
struct mosquitto__callback *cb_base;
|
||||
struct mosquitto__security_options *opts;
|
||||
int i;
|
||||
|
||||
/* FIXME - set now_s and now_ns to avoid need for multiple time lookups */
|
||||
if(db.config->per_listener_settings){
|
||||
/* FIXME - iterate over all listeners */
|
||||
for(i=0; i < db.config->listener_count; i++){
|
||||
opts = &db.config->listeners[i].security_options;
|
||||
memset(&event_data, 0, sizeof(event_data));
|
||||
|
||||
DL_FOREACH(opts->plugin_callbacks.tick, cb_base){
|
||||
cb_base->cb(MOSQ_EVT_TICK, &event_data, cb_base->userdata);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
opts = &db.config->security_options;
|
||||
memset(&event_data, 0, sizeof(event_data));
|
||||
|
@ -82,6 +82,31 @@ int session_expiry__add(struct mosquitto *context)
|
||||
}
|
||||
|
||||
|
||||
int session_expiry__add_from_persistence(struct mosquitto *context, time_t expiry_time)
|
||||
{
|
||||
struct session_expiry_list *item;
|
||||
|
||||
if(db.config->persistent_client_expiration == 0){
|
||||
if(context->session_expiry_interval == UINT32_MAX){
|
||||
/* There isn't a global expiry set, and the client has asked to
|
||||
* never expire, so we don't add it to the list. */
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
item = mosquitto__calloc(1, sizeof(struct session_expiry_list));
|
||||
if(!item) return MOSQ_ERR_NOMEM;
|
||||
|
||||
item->context = context;
|
||||
item->context->session_expiry_time = expiry_time;
|
||||
context->expiry_list_item = item;
|
||||
|
||||
DL_INSERT_INORDER(expiry_list, item, session_expiry__cmp);
|
||||
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
void session_expiry__remove(struct mosquitto *context)
|
||||
{
|
||||
if(context->expiry_list_item){
|
||||
|
@ -261,7 +261,7 @@ static int callback_mqtt(
|
||||
|
||||
#ifdef WITH_SYS_TREE
|
||||
g_msgs_sent++;
|
||||
if(((packet->command)&0xF6) == CMD_PUBLISH){
|
||||
if(((packet->command)&0xF0) == CMD_PUBLISH){
|
||||
g_pub_msgs_sent++;
|
||||
}
|
||||
#endif
|
||||
@ -356,7 +356,7 @@ static int callback_mqtt(
|
||||
|
||||
#ifdef WITH_SYS_TREE
|
||||
G_MSGS_RECEIVED_INC(1);
|
||||
if(((mosq->in_packet.command)&0xF5) == CMD_PUBLISH){
|
||||
if(((mosq->in_packet.command)&0xF0) == CMD_PUBLISH){
|
||||
G_PUB_MSGS_RECEIVED_INC(1);
|
||||
}
|
||||
#endif
|
||||
|
34
test/broker/01-connect-take-over.py
Executable file
34
test/broker/01-connect-take-over.py
Executable file
@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# MQTT v5 session takeover test
|
||||
|
||||
from mosq_test_helper import *
|
||||
|
||||
port = mosq_test.get_port()
|
||||
broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port)
|
||||
|
||||
try:
|
||||
rc = 1
|
||||
connect_packet = mosq_test.gen_connect("take-over", proto_ver=5)
|
||||
connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5)
|
||||
disconnect_packet = mosq_test.gen_disconnect(reason_code=mqtt5_rc.MQTT_RC_SESSION_TAKEN_OVER, proto_ver=5)
|
||||
|
||||
sock1 = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)
|
||||
sock2 = mosq_test.do_client_connect(connect_packet, connack_packet, port=port)
|
||||
mosq_test.expect_packet(sock1, "disconnect", disconnect_packet)
|
||||
mosq_test.do_ping(sock2)
|
||||
|
||||
sock2.close()
|
||||
sock1.close()
|
||||
rc = 0
|
||||
except mosq_test.TestError:
|
||||
pass
|
||||
except Exception as e:
|
||||
print(e)
|
||||
finally:
|
||||
broker.terminate()
|
||||
broker.wait()
|
||||
(stdo, stde) = broker.communicate()
|
||||
if rc:
|
||||
print(stde.decode('utf-8'))
|
||||
exit(rc)
|
@ -34,7 +34,9 @@ publish_packet = mosq_test.gen_publish("bridge/ssl/test", qos=0, payload="messag
|
||||
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
ssock = ssl.wrap_socket(sock, ca_certs="../ssl/all-ca.crt", keyfile="../ssl/server.key", certfile="../ssl/server.crt", server_side=True)
|
||||
context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH, cafile="../ssl/all-ca.crt")
|
||||
context.load_cert_chain(certfile="../ssl/server.crt", keyfile="../ssl/server.key")
|
||||
ssock = context.wrap_socket(sock, server_side=True)
|
||||
ssock.settimeout(20)
|
||||
ssock.bind(('', port1))
|
||||
ssock.listen(5)
|
||||
|
@ -31,7 +31,9 @@ broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2,
|
||||
|
||||
try:
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
ssock = ssl.wrap_socket(sock, ca_certs="../ssl/test-root-ca.crt", certfile="../ssl/client.crt", keyfile="../ssl/client.key", cert_reqs=ssl.CERT_REQUIRED)
|
||||
context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile="../ssl/test-root-ca.crt")
|
||||
context.load_cert_chain(certfile="../ssl/client.crt", keyfile="../ssl/client.key")
|
||||
ssock = context.wrap_socket(sock, server_hostname="localhost")
|
||||
ssock.settimeout(20)
|
||||
ssock.connect(("localhost", port1))
|
||||
|
||||
|
@ -31,7 +31,9 @@ broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2,
|
||||
|
||||
try:
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
ssock = ssl.wrap_socket(sock, ca_certs="../ssl/test-root-ca.crt", certfile="../ssl/client-expired.crt", keyfile="../ssl/client-expired.key", cert_reqs=ssl.CERT_REQUIRED)
|
||||
context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile="../ssl/test-root-ca.crt")
|
||||
context.load_cert_chain(certfile="../ssl/client-expired.crt", keyfile="../ssl/client-expired.key")
|
||||
ssock = context.wrap_socket(sock, server_hostname="localhost")
|
||||
ssock.settimeout(20)
|
||||
try:
|
||||
ssock.connect(("localhost", port1))
|
||||
|
@ -30,7 +30,9 @@ broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2,
|
||||
|
||||
try:
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
ssock = ssl.wrap_socket(sock, ca_certs="../ssl/test-root-ca.crt", certfile="../ssl/client-revoked.crt", keyfile="../ssl/client-revoked.key", cert_reqs=ssl.CERT_REQUIRED)
|
||||
context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile="../ssl/test-root-ca.crt")
|
||||
context.load_cert_chain(certfile="../ssl/client-revoked.crt", keyfile="../ssl/client-revoked.key")
|
||||
ssock = context.wrap_socket(sock, server_hostname="localhost")
|
||||
ssock.settimeout(20)
|
||||
try:
|
||||
ssock.connect(("localhost", port1))
|
||||
|
@ -28,7 +28,8 @@ connect_packet = mosq_test.gen_connect("connect-cert-test", keepalive=keepalive)
|
||||
broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True)
|
||||
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
ssock = ssl.wrap_socket(sock, ca_certs="../ssl/test-root-ca.crt", cert_reqs=ssl.CERT_REQUIRED)
|
||||
context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH)
|
||||
ssock = context.wrap_socket(sock, server_hostname="localhost")
|
||||
ssock.settimeout(20)
|
||||
try:
|
||||
ssock.connect(("localhost", port1))
|
||||
|
@ -32,7 +32,9 @@ broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2,
|
||||
|
||||
try:
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
ssock = ssl.wrap_socket(sock, ca_certs="../ssl/test-root-ca.crt", certfile="../ssl/client.crt", keyfile="../ssl/client.key", cert_reqs=ssl.CERT_REQUIRED)
|
||||
context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile="../ssl/test-root-ca.crt")
|
||||
context.load_cert_chain(certfile="../ssl/client.crt", keyfile="../ssl/client.key")
|
||||
ssock = context.wrap_socket(sock, server_hostname="localhost")
|
||||
ssock.settimeout(20)
|
||||
ssock.connect(("localhost", port1))
|
||||
|
||||
|
@ -33,7 +33,9 @@ broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2,
|
||||
|
||||
try:
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
ssock = ssl.wrap_socket(sock, ca_certs="../ssl/test-root-ca.crt", certfile="../ssl/client.crt", keyfile="../ssl/client.key", cert_reqs=ssl.CERT_REQUIRED)
|
||||
context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile="../ssl/test-root-ca.crt")
|
||||
context.load_cert_chain(certfile="../ssl/client.crt", keyfile="../ssl/client.key")
|
||||
ssock = context.wrap_socket(sock, server_hostname="localhost")
|
||||
ssock.settimeout(20)
|
||||
ssock.connect(("localhost", port1))
|
||||
|
||||
|
@ -29,7 +29,8 @@ connack_packet = mosq_test.gen_connack(rc=0)
|
||||
broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True)
|
||||
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
ssock = ssl.wrap_socket(sock, ca_certs="../ssl/test-alt-ca.crt", cert_reqs=ssl.CERT_REQUIRED)
|
||||
context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile="../ssl/test-alt-ca.crt")
|
||||
ssock = context.wrap_socket(sock, server_hostname="localhost")
|
||||
ssock.settimeout(20)
|
||||
try:
|
||||
ssock.connect(("localhost", port1))
|
||||
|
@ -32,7 +32,8 @@ broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2,
|
||||
|
||||
try:
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
ssock = ssl.wrap_socket(sock, ca_certs="../ssl/test-root-ca.crt", cert_reqs=ssl.CERT_REQUIRED)
|
||||
context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile="../ssl/test-root-ca.crt")
|
||||
ssock = context.wrap_socket(sock, server_hostname="localhost")
|
||||
ssock.settimeout(20)
|
||||
ssock.connect(("localhost", port1))
|
||||
|
||||
|
@ -32,7 +32,8 @@ broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2,
|
||||
|
||||
try:
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
ssock = ssl.wrap_socket(sock, ca_certs="../ssl/test-root-ca.crt", cert_reqs=ssl.CERT_REQUIRED)
|
||||
context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile="../ssl/test-root-ca.crt")
|
||||
ssock = context.wrap_socket(sock, server_hostname="localhost")
|
||||
ssock.settimeout(20)
|
||||
ssock.connect(("localhost", port1))
|
||||
|
||||
|
@ -43,7 +43,9 @@ def do_test(option):
|
||||
|
||||
try:
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
ssock = ssl.wrap_socket(sock, ca_certs="../ssl/test-root-ca.crt", certfile="../ssl/client.crt", keyfile="../ssl/client.key", cert_reqs=ssl.CERT_REQUIRED)
|
||||
context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile="../ssl/test-root-ca.crt")
|
||||
context.load_cert_chain(certfile="../ssl/client.crt", keyfile="../ssl/client.key")
|
||||
ssock = context.wrap_socket(sock, server_hostname="localhost")
|
||||
ssock.settimeout(20)
|
||||
ssock.connect(("localhost", port))
|
||||
mosq_test.do_send_receive(ssock, connect_packet, connack_packet, "connack")
|
||||
|
52
test/broker/09-plugin-tick.py
Executable file
52
test/broker/09-plugin-tick.py
Executable file
@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Test whether a plugin can subscribe to the tick event
|
||||
|
||||
from mosq_test_helper import *
|
||||
|
||||
def write_config(filename, port, per_listener_settings="false"):
|
||||
with open(filename, 'w') as f:
|
||||
f.write("per_listener_settings %s\n" % (per_listener_settings))
|
||||
f.write("listener %d\n" % (port))
|
||||
f.write("plugin c/auth_plugin_v5_handle_tick.so\n")
|
||||
f.write("allow_anonymous true\n")
|
||||
|
||||
def do_test(per_listener_settings):
|
||||
proto_ver = 5
|
||||
port = mosq_test.get_port()
|
||||
conf_file = os.path.basename(__file__).replace('.py', '.conf')
|
||||
write_config(conf_file, port, per_listener_settings)
|
||||
|
||||
rc = 1
|
||||
keepalive = 10
|
||||
connect_packet = mosq_test.gen_connect("plugin-tick-test", keepalive=keepalive, username="readwrite", clean_session=False, proto_ver=proto_ver)
|
||||
connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver)
|
||||
|
||||
tick_packet = mosq_test.gen_publish("topic/tick", qos=0, payload="test-message", proto_ver=proto_ver)
|
||||
|
||||
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, timeout=10, port=port)
|
||||
|
||||
mosq_test.expect_packet(sock, "tick message", tick_packet)
|
||||
mosq_test.expect_packet(sock, "tick message", tick_packet)
|
||||
mosq_test.expect_packet(sock, "tick message", tick_packet)
|
||||
|
||||
mosq_test.do_ping(sock)
|
||||
|
||||
rc = 0
|
||||
sock.close()
|
||||
except mosq_test.TestError:
|
||||
pass
|
||||
finally:
|
||||
os.remove(conf_file)
|
||||
broker.terminate()
|
||||
broker.wait()
|
||||
(stdo, stde) = broker.communicate()
|
||||
if rc:
|
||||
print(stde.decode('utf-8'))
|
||||
exit(rc)
|
||||
|
||||
do_test("false")
|
||||
do_test("true")
|
@ -71,6 +71,15 @@ create_role_apply_response = {'responses': [
|
||||
]}
|
||||
|
||||
|
||||
delete_anon_group_command = { "commands": [
|
||||
{ "command": "deleteGroup", "groupname": "anon-clients", "correlationData": "40" }
|
||||
]
|
||||
}
|
||||
delete_anon_group_response = {'responses': [
|
||||
{'command': 'deleteGroup', "error":'Deleting the anonymous group is forbidden', 'correlationData': '40'}
|
||||
]}
|
||||
|
||||
|
||||
|
||||
rc = 1
|
||||
keepalive = 10
|
||||
@ -136,6 +145,9 @@ try:
|
||||
csock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port)
|
||||
mosq_test.do_send_receive(csock, subscribe_packet, suback_packet_success, "suback 3")
|
||||
|
||||
# Try to delete anon group, this should fail
|
||||
command_check(sock, delete_anon_group_command, delete_anon_group_response)
|
||||
|
||||
rc = 0
|
||||
|
||||
sock.close()
|
||||
|
@ -28,6 +28,7 @@ msg_sequence_test:
|
||||
./01-connect-disconnect-v5.py
|
||||
./01-connect-max-connections.py
|
||||
./01-connect-max-keepalive.py
|
||||
./01-connect-take-over.py
|
||||
./01-connect-uname-no-password-denied.py
|
||||
./01-connect-uname-or-anon.py
|
||||
./01-connect-uname-password-denied-no-will.py
|
||||
@ -183,6 +184,7 @@ endif
|
||||
./09-plugin-auth-v2-unpwd-fail.py
|
||||
./09-plugin-auth-v2-unpwd-success.py
|
||||
./09-plugin-publish.py
|
||||
./09-plugin-tick.py
|
||||
./09-pwfile-parse-invalid.py
|
||||
|
||||
10 :
|
||||
|
@ -18,6 +18,7 @@ PLUGIN_SRC = \
|
||||
auth_plugin_v4.c \
|
||||
auth_plugin_v5.c \
|
||||
auth_plugin_v5_handle_message.c \
|
||||
auth_plugin_v5_handle_tick.c \
|
||||
plugin_control.c
|
||||
|
||||
PLUGINS = ${PLUGIN_SRC:.c=.so}
|
||||
|
38
test/broker/c/auth_plugin_v5_handle_tick.c
Normal file
38
test/broker/c/auth_plugin_v5_handle_tick.c
Normal file
@ -0,0 +1,38 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <mosquitto.h>
|
||||
#include <mosquitto_broker.h>
|
||||
#include <mosquitto_plugin.h>
|
||||
|
||||
static int handle_tick(int event, void *event_data, void *user_data);
|
||||
|
||||
static mosquitto_plugin_id_t *plg_id;
|
||||
|
||||
|
||||
int mosquitto_plugin_version(int supported_version_count, const int *supported_versions)
|
||||
{
|
||||
return 5;
|
||||
}
|
||||
|
||||
int mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)
|
||||
{
|
||||
plg_id = identifier;
|
||||
|
||||
mosquitto_callback_register(plg_id, MOSQ_EVT_TICK, handle_tick, NULL, NULL);
|
||||
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
int mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count)
|
||||
{
|
||||
mosquitto_callback_unregister(plg_id, MOSQ_EVT_TICK, handle_tick, NULL);
|
||||
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
}
|
||||
|
||||
int handle_tick(int event, void *event_data, void *user_data)
|
||||
{
|
||||
mosquitto_broker_publish_copy("plugin-tick-test", "topic/tick", strlen("test-message"), "test-message", 0, false, NULL);
|
||||
return MOSQ_ERR_SUCCESS;
|
||||
}
|
@ -3,6 +3,14 @@
|
||||
"comment": "CONNECT TESTS ARE INCOMPLETE",
|
||||
"group": "v3.1 CONNECT",
|
||||
"tests": [
|
||||
{ "name": "10 ok ", "connect":false, "expect_disconnect":false, "msgs":[
|
||||
{"type":"send", "payload":"10 0F 0006 4D5149736470 03 01 000A 0001 70", "comment":"minimal valid CONNECT"},
|
||||
{"type":"recv", "payload":"20 02 00 00", "comment": "CONNACK"}
|
||||
]},
|
||||
{ "name": "14 ok ", "connect":false, "expect_disconnect":false, "msgs":[
|
||||
{"type":"send", "payload":"14 0F 0006 4D5149736470 03 01 000A 0001 70", "comment":"CONNECT with QoS=1"},
|
||||
{"type":"recv", "payload":"20 02 00 00", "comment": "CONNACK"}
|
||||
]},
|
||||
{ "name": "10 proto ver 2", "connect":false, "msgs":[
|
||||
{"type":"send", "payload":"10 0F 0006 4D5149736470 02 00 000A 0001 70", "comment":"CONNECT"},
|
||||
{"type":"recv", "payload":"20 02 00 01", "comment": "CONNACK identifier rejected"}
|
||||
|
@ -10,6 +10,7 @@ tests = [
|
||||
(1, './01-connect-disconnect-v5.py'),
|
||||
(1, './01-connect-max-connections.py'),
|
||||
(1, './01-connect-max-keepalive.py'),
|
||||
(1, './01-connect-take-over.py'),
|
||||
(1, './01-connect-uname-no-password-denied.py'),
|
||||
(1, './01-connect-uname-or-anon.py'),
|
||||
(1, './01-connect-uname-password-denied-no-will.py'),
|
||||
@ -153,6 +154,7 @@ tests = [
|
||||
(1, './09-plugin-auth-v2-unpwd-fail.py'),
|
||||
(1, './09-plugin-auth-v2-unpwd-success.py'),
|
||||
(1, './09-plugin-publish.py'),
|
||||
(1, './09-plugin-tick.py'),
|
||||
(1, './09-pwfile-parse-invalid.py'),
|
||||
|
||||
(2, './10-listener-mount-point.py'),
|
||||
|
@ -26,9 +26,10 @@ disconnect_packet = mosq_test.gen_disconnect()
|
||||
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
ssock = ssl.wrap_socket(sock, ca_certs="../ssl/all-ca.crt",
|
||||
keyfile="../ssl/server.key", certfile="../ssl/server.crt",
|
||||
server_side=True, cert_reqs=ssl.CERT_REQUIRED)
|
||||
context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH, cafile="../ssl/all-ca.crt")
|
||||
context.load_cert_chain(certfile="../ssl/server.crt", keyfile="../ssl/server.key")
|
||||
context.verify_mode = ssl.CERT_REQUIRED
|
||||
ssock = context.wrap_socket(sock, server_side=True)
|
||||
ssock.settimeout(10)
|
||||
ssock.bind(('', port))
|
||||
ssock.listen(5)
|
||||
|
@ -26,9 +26,10 @@ disconnect_packet = mosq_test.gen_disconnect()
|
||||
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
ssock = ssl.wrap_socket(sock, ca_certs="../ssl/all-ca.crt",
|
||||
keyfile="../ssl/server.key", certfile="../ssl/server.crt",
|
||||
server_side=True, cert_reqs=ssl.CERT_REQUIRED)
|
||||
context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH, cafile="../ssl/all-ca.crt")
|
||||
context.load_cert_chain(certfile="../ssl/server.crt", keyfile="../ssl/server.key")
|
||||
context.verify_mode = ssl.CERT_REQUIRED
|
||||
ssock = context.wrap_socket(sock, server_side=True)
|
||||
ssock.settimeout(10)
|
||||
ssock.bind(('', port))
|
||||
ssock.listen(5)
|
||||
|
@ -25,7 +25,9 @@ disconnect_packet = mosq_test.gen_disconnect()
|
||||
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
ssock = ssl.wrap_socket(sock, ca_certs="../ssl/all-ca.crt", keyfile="../ssl/server.key", certfile="../ssl/server.crt", server_side=True)
|
||||
context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH, cafile="../ssl/all-ca.crt")
|
||||
context.load_cert_chain(certfile="../ssl/server.crt", keyfile="../ssl/server.key")
|
||||
ssock = context.wrap_socket(sock, server_side=True)
|
||||
ssock.settimeout(10)
|
||||
ssock.bind(('', port))
|
||||
ssock.listen(5)
|
||||
|
@ -10,9 +10,10 @@ if sys.version < '2.7':
|
||||
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
ssock = ssl.wrap_socket(sock, ca_certs="../ssl/all-ca.crt",
|
||||
keyfile="../ssl/server.key", certfile="../ssl/server.crt",
|
||||
server_side=True, cert_reqs=ssl.CERT_REQUIRED)
|
||||
context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH, cafile="../ssl/all-ca.crt")
|
||||
context.load_cert_chain(certfile="../ssl/server.crt", keyfile="../ssl/server.key")
|
||||
context.verify_mode = ssl.CERT_REQUIRED
|
||||
ssock = context.wrap_socket(sock, server_side=True)
|
||||
ssock.settimeout(10)
|
||||
ssock.bind(('', port))
|
||||
ssock.listen(5)
|
||||
|
@ -65,6 +65,8 @@ ifeq ($(WITH_TLS),yes)
|
||||
./08-ssl-bad-cacert.py $@/08-ssl-bad-cacert.test
|
||||
./08-ssl-connect-cert-auth-enc.py $@/08-ssl-connect-cert-auth-enc.test
|
||||
./08-ssl-connect-cert-auth.py $@/08-ssl-connect-cert-auth.test
|
||||
./08-ssl-connect-cert-auth.py $@/08-ssl-connect-cert-auth-custom-ssl-ctx.test
|
||||
./08-ssl-connect-cert-auth.py $@/08-ssl-connect-cert-auth-custom-ssl-ctx-default.test
|
||||
./08-ssl-connect-no-auth.py $@/08-ssl-connect-no-auth.test
|
||||
endif
|
||||
./09-util-topic-tokenise.py $@/09-util-topic-tokenise.test
|
||||
|
67
test/lib/c/08-ssl-connect-cert-auth-custom-ssl-ctx-default.c
Normal file
67
test/lib/c/08-ssl-connect-cert-auth-custom-ssl-ctx-default.c
Normal file
@ -0,0 +1,67 @@
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <mosquitto.h>
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
static int run = -1;
|
||||
|
||||
void handle_sigint(int signal)
|
||||
{
|
||||
run = 0;
|
||||
}
|
||||
|
||||
void on_connect(struct mosquitto *mosq, void *obj, int rc)
|
||||
{
|
||||
if(rc){
|
||||
exit(1);
|
||||
}else{
|
||||
mosquitto_disconnect(mosq);
|
||||
}
|
||||
}
|
||||
|
||||
void on_disconnect(struct mosquitto *mosq, void *obj, int rc)
|
||||
{
|
||||
run = rc;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int rc;
|
||||
struct mosquitto *mosq;
|
||||
SSL_CTX *ssl_ctx;
|
||||
int port = atoi(argv[1]);
|
||||
|
||||
mosquitto_lib_init();
|
||||
|
||||
OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS \
|
||||
| OPENSSL_INIT_ADD_ALL_DIGESTS \
|
||||
| OPENSSL_INIT_LOAD_CONFIG, NULL);
|
||||
ssl_ctx = SSL_CTX_new(TLS_client_method());
|
||||
|
||||
mosq = mosquitto_new("08-ssl-connect-crt-auth", true, NULL);
|
||||
if(mosq == NULL){
|
||||
return 1;
|
||||
}
|
||||
|
||||
mosquitto_int_option(mosq, MOSQ_OPT_SSL_CTX_WITH_DEFAULTS, 1);
|
||||
mosquitto_void_option(mosq, MOSQ_OPT_SSL_CTX, ssl_ctx);
|
||||
|
||||
mosquitto_tls_set(mosq, "../ssl/test-root-ca.crt", "../ssl/certs", "../ssl/client.crt", "../ssl/client.key", NULL);
|
||||
mosquitto_connect_callback_set(mosq, on_connect);
|
||||
mosquitto_disconnect_callback_set(mosq, on_disconnect);
|
||||
|
||||
rc = mosquitto_connect(mosq, "localhost", port, 60);
|
||||
|
||||
signal(SIGINT, handle_sigint);
|
||||
while(run == -1){
|
||||
mosquitto_loop(mosq, -1, 1);
|
||||
}
|
||||
SSL_CTX_free(ssl_ctx);
|
||||
mosquitto_destroy(mosq);
|
||||
|
||||
mosquitto_lib_cleanup();
|
||||
return run;
|
||||
}
|
71
test/lib/c/08-ssl-connect-cert-auth-custom-ssl-ctx.c
Normal file
71
test/lib/c/08-ssl-connect-cert-auth-custom-ssl-ctx.c
Normal file
@ -0,0 +1,71 @@
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <mosquitto.h>
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
static int run = -1;
|
||||
|
||||
void handle_sigint(int signal)
|
||||
{
|
||||
run = 0;
|
||||
}
|
||||
|
||||
void on_connect(struct mosquitto *mosq, void *obj, int rc)
|
||||
{
|
||||
if(rc){
|
||||
exit(1);
|
||||
}else{
|
||||
mosquitto_disconnect(mosq);
|
||||
}
|
||||
}
|
||||
|
||||
void on_disconnect(struct mosquitto *mosq, void *obj, int rc)
|
||||
{
|
||||
run = rc;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int rc;
|
||||
struct mosquitto *mosq;
|
||||
SSL_CTX *ssl_ctx;
|
||||
int port = atoi(argv[1]);
|
||||
|
||||
mosquitto_lib_init();
|
||||
|
||||
OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS \
|
||||
| OPENSSL_INIT_ADD_ALL_DIGESTS \
|
||||
| OPENSSL_INIT_LOAD_CONFIG, NULL);
|
||||
ssl_ctx = SSL_CTX_new(TLS_client_method());
|
||||
|
||||
SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
|
||||
SSL_CTX_use_certificate_chain_file(ssl_ctx, "../ssl/client.crt");
|
||||
SSL_CTX_use_PrivateKey_file(ssl_ctx, "../ssl/client.key", SSL_FILETYPE_PEM);
|
||||
SSL_CTX_load_verify_locations(ssl_ctx, "../ssl/test-root-ca.crt", "../ssl/certs");
|
||||
|
||||
mosq = mosquitto_new("08-ssl-connect-crt-auth", true, NULL);
|
||||
if(mosq == NULL){
|
||||
return 1;
|
||||
}
|
||||
mosquitto_tls_set(mosq, "../ssl/test-root-ca.crt", "../ssl/certs", "../ssl/client.crt", "../ssl/client.key", NULL);
|
||||
mosquitto_connect_callback_set(mosq, on_connect);
|
||||
mosquitto_disconnect_callback_set(mosq, on_disconnect);
|
||||
|
||||
mosquitto_int_option(mosq, MOSQ_OPT_SSL_CTX_WITH_DEFAULTS, 0);
|
||||
mosquitto_void_option(mosq, MOSQ_OPT_SSL_CTX, ssl_ctx);
|
||||
|
||||
rc = mosquitto_connect(mosq, "localhost", port, 60);
|
||||
|
||||
signal(SIGINT, handle_sigint);
|
||||
while(run == -1){
|
||||
mosquitto_loop(mosq, -1, 1);
|
||||
}
|
||||
SSL_CTX_free(ssl_ctx);
|
||||
mosquitto_destroy(mosq);
|
||||
|
||||
mosquitto_lib_cleanup();
|
||||
return run;
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
include ../../../config.mk
|
||||
|
||||
.PHONY: all clean reallyclean
|
||||
|
||||
CFLAGS=-I../../../include -Werror
|
||||
@ -55,6 +57,13 @@ SRC = \
|
||||
11-prop-send-payload-format.c \
|
||||
11-prop-send-content-type.c
|
||||
|
||||
ifeq ($(WITH_TLS),yes)
|
||||
SRC += \
|
||||
08-ssl-connect-cert-auth-custom-ssl-ctx.c \
|
||||
08-ssl-connect-cert-auth-custom-ssl-ctx-default.c
|
||||
LIBS += -lssl -lcrypto
|
||||
endif
|
||||
|
||||
TESTS = ${SRC:.c=.test}
|
||||
|
||||
all : ${TESTS}
|
||||
|
@ -48,6 +48,8 @@ tests = [
|
||||
(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-cert-auth.py', 'c/08-ssl-connect-cert-auth-custom-ssl-ctx.test']),
|
||||
(1, ['./08-ssl-connect-cert-auth.py', 'c/08-ssl-connect-cert-auth-custom-ssl-ctx-default.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']),
|
||||
|
@ -205,3 +205,10 @@ void context__add_to_by_id(struct mosquitto *context)
|
||||
HASH_ADD_KEYPTR(hh_id, db.contexts_by_id, context->id, strlen(context->id), context);
|
||||
}
|
||||
}
|
||||
|
||||
int session_expiry__add_from_persistence(struct mosquitto *context, time_t expiry_time)
|
||||
{
|
||||
UNUSED(context);
|
||||
UNUSED(expiry_time);
|
||||
return 0;
|
||||
}
|
||||
|
@ -121,3 +121,10 @@ void context__add_to_by_id(struct mosquitto *context)
|
||||
HASH_ADD_KEYPTR(hh_id, db.contexts_by_id, context->id, strlen(context->id), context);
|
||||
}
|
||||
}
|
||||
|
||||
int session_expiry__add_from_persistence(struct mosquitto *context, time_t expiry_time)
|
||||
{
|
||||
UNUSED(context);
|
||||
UNUSED(expiry_time);
|
||||
return 0;
|
||||
}
|
||||
|
@ -219,3 +219,10 @@ void util__increment_send_quota(struct mosquitto *mosq)
|
||||
{
|
||||
mosq->msgs_out.inflight_quota++;
|
||||
}
|
||||
|
||||
int session_expiry__add_from_persistence(struct mosquitto *context, time_t expiry_time)
|
||||
{
|
||||
UNUSED(context);
|
||||
UNUSED(expiry_time);
|
||||
return 0;
|
||||
}
|
||||
|
@ -389,9 +389,9 @@ admin username and any other options once and not have to add them to the
|
||||
command line every time.
|
||||
|
||||
mosquitto_ctrl will try to load a configuration file from a default location.
|
||||
For Windows this is at `%USER_PROFILE%\mosquitto_ctrl.conf`. For other systems,
|
||||
it will try `$XDG_CONFIG_HOME/mosquitto_ctrl.conf` or
|
||||
`$HOME/.config/mosquitto_ctrl.conf`.
|
||||
For Windows this is at `%USER_PROFILE%\mosquitto_ctrl`. For other systems,
|
||||
it will try `$XDG_CONFIG_HOME/mosquitto_ctrl` or
|
||||
`$HOME/.config/mosquitto_ctrl`.
|
||||
|
||||
You may override this behaviour by manually specifying an options file with
|
||||
`-o <path to options file>`.
|
||||
|
100
www/posts/2022/08/version-2-0-15-released.md
Normal file
100
www/posts/2022/08/version-2-0-15-released.md
Normal file
@ -0,0 +1,100 @@
|
||||
<!--
|
||||
.. title: Version 2.0.15 released.
|
||||
.. slug: version-2-0-15-released
|
||||
.. date: 2022-08-16 12:57:38 UTC+1
|
||||
.. tags: Releases
|
||||
.. category:
|
||||
.. link:
|
||||
.. description:
|
||||
.. type: text
|
||||
-->
|
||||
|
||||
Versions 2.0.15 of Mosquitto has been released. This is a security
|
||||
and bugfix release.
|
||||
|
||||
# Security
|
||||
- Deleting the group configured as the anonymous group in the Dynamic Security
|
||||
plugin, would leave a dangling pointer that could lead to a single crash.
|
||||
This is considered a minor issue - only administrative users should have
|
||||
access to dynsec, the impact on availability is one-off, and there is no
|
||||
associated loss of data. It is now forbidden to delete the group configured
|
||||
as the anonymous group.
|
||||
|
||||
# Broker
|
||||
- Fix memory leak when a plugin modifies the topic of a message in
|
||||
`MOSQ_EVT_MESSAGE`.
|
||||
- Fix bridge `restart_timeout` not being honoured.
|
||||
- Fix potential memory leaks if a plugin modifies the message in the
|
||||
`MOSQ_EVT_MESSAGE` event.
|
||||
- Fix unused flags in CONNECT command being forced to be 0, which is not
|
||||
required for MQTT v3.1. Closes [#2522].
|
||||
- Improve documentation of `persistent_client_expiration` option.
|
||||
Closes [#2404].
|
||||
- Add clients to session expiry check list when restarting and reloading from
|
||||
persistence. Closes [#2546].
|
||||
- Fix bridges not sending failure notification messages to the local broker if
|
||||
the remote bridge connection fails. Closes [#2467]. Closes [#1488].
|
||||
- Fix some PUBLISH messages not being counted in $SYS stats. Closes [#2448].
|
||||
- Fix incorrect return code being sent in DISCONNECT when a client session is
|
||||
taken over. Closes [#2607].
|
||||
- Fix confusing "out of memory" error when a client is kicked in the dynamic
|
||||
security plugin. Closes [#2525].
|
||||
- Fix confusing error message when dynamic security config file was a
|
||||
directory. Closes [#2520].
|
||||
- Fix bridge queued messages not being persisted when local_cleansession is
|
||||
set to false and cleansession is set to true. Closes [#2604].
|
||||
- Dynamic security: Fix modifyClient and modifyGroup commands to not modify
|
||||
the client/group if a new group/client being added is not valid.
|
||||
Closes [#2598].
|
||||
- Dynamic security: Fix the plugin being able to be loaded twice. Currently
|
||||
only a single plugin can interact with a unique $CONTROL topic. Using
|
||||
multiple instances of the plugin would produce duplicate entries in the
|
||||
config file. Closes [#2601]. Closes [#2470].
|
||||
- Fix case where expired messages were causing queued messages not to be
|
||||
delivered. Closes [#2609].
|
||||
|
||||
# Client library
|
||||
- Fix threads library detection on Windows under cmake. Bumps the minimum
|
||||
cmake version to 3.1, which is still ancient.
|
||||
- Fix use of `MOSQ_OPT_TLS_ENGINE` being unable to be used due to the openssl
|
||||
ctx not being initialised until starting to connect. Closes [#2537].
|
||||
- Fix incorrect use of SSL_connect. Closes [#2594].
|
||||
- Don't set SIGPIPE to ignore, use MSG_NOSIGNAL instead. Closes [#2564].
|
||||
- Add documentation of struct mosquitto_message to header. Closes [#2561].
|
||||
- Fix documentation omission around mosquitto_reinitialise. Closes [#2489].
|
||||
- Fix use of MOSQ_OPT_SSL_CTX when used in conjunction with
|
||||
MOSQ_OPT_SSL_CTX_DEFAULTS. Closes [#2463].
|
||||
- Fix failure to close thread in some situations. Closes [#2545].
|
||||
|
||||
# Clients
|
||||
- Fix mosquitto_pub incorrectly reusing topic aliases when reconnecting.
|
||||
Closes [#2494].
|
||||
|
||||
# Apps
|
||||
- Fix `-o` not working in `mosquitto_ctrl`, and typo in related documentation.
|
||||
Closes [#2471].
|
||||
|
||||
|
||||
[#1488]: https://github.com/eclipse/mosquitto/issues/1488
|
||||
[#2404]: https://github.com/eclipse/mosquitto/issues/2404
|
||||
[#2448]: https://github.com/eclipse/mosquitto/issues/2448
|
||||
[#2463]: https://github.com/eclipse/mosquitto/issues/2463
|
||||
[#2467]: https://github.com/eclipse/mosquitto/issues/2467
|
||||
[#2470]: https://github.com/eclipse/mosquitto/issues/2470
|
||||
[#2471]: https://github.com/eclipse/mosquitto/issues/2471
|
||||
[#2489]: https://github.com/eclipse/mosquitto/issues/2489
|
||||
[#2494]: https://github.com/eclipse/mosquitto/issues/2494
|
||||
[#2520]: https://github.com/eclipse/mosquitto/issues/2520
|
||||
[#2522]: https://github.com/eclipse/mosquitto/issues/2522
|
||||
[#2525]: https://github.com/eclipse/mosquitto/issues/2525
|
||||
[#2537]: https://github.com/eclipse/mosquitto/issues/2537
|
||||
[#2545]: https://github.com/eclipse/mosquitto/issues/2545
|
||||
[#2546]: https://github.com/eclipse/mosquitto/issues/2546
|
||||
[#2561]: https://github.com/eclipse/mosquitto/issues/2561
|
||||
[#2564]: https://github.com/eclipse/mosquitto/issues/2564
|
||||
[#2594]: https://github.com/eclipse/mosquitto/issues/2594
|
||||
[#2598]: https://github.com/eclipse/mosquitto/issues/2598
|
||||
[#2601]: https://github.com/eclipse/mosquitto/issues/2601
|
||||
[#2604]: https://github.com/eclipse/mosquitto/issues/2604
|
||||
[#2607]: https://github.com/eclipse/mosquitto/issues/2607
|
||||
[#2609]: https://github.com/eclipse/mosquitto/issues/2609
|
Loading…
x
Reference in New Issue
Block a user