diff --git a/ChangeLog.txt b/ChangeLog.txt index 092880e6..788485f8 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -72,6 +72,55 @@ Build: build and install static versions of the client libraries. +1.4.11 - 20170220 +================= + +Broker: +- Fix crash when "lazy" type bridge attempts to reconnect. Closes #259. +- maximum_connections now applies to websockets listeners. Closes #271. +- Allow bridges to use TLS with IPv6. +- Don't error on zero length persistence files. Closes #316. +- For http only websockets clients, close files served over http in all cases + when the client disconnects. Closes #354. +- Fix error message when websockets http_dir directory does not exist. +- Improve password utility error message. Closes #379. + +Clients: +- Use of --ciphers no longer requires you to also pass --tls-version. + Closes #380. + +Client library: +- Clients can now use TLS with IPv6. +- Fix potential socket leakage when reconnecting. Closes #304. +- Fix potential negative timeout being passed to pselect. Closes #329. + + +1.4.10 - 20160816 +================= + +Broker: +- Fix TLS operation with websockets listeners and libwebsockts 2.x. Closes + #186. +- Don't disconnect client on HUP before reading the pending data. Closes #7. +- Fix some $SYS messages being incorrectly persisted. Closes #191. +- Support OpenSSL 1.1.0. +- Call fsync after persisting data to ensure it is correctly written. Closes + #189. +- Fix persistence saving of subscription QoS on big-endian machines. +- Fix will retained flag handling on Windows. Closes #222. +- Broker now displays an error if it is unable to open the log file. Closes + #234. + +Client library: +- Support OpenSSL 1.1.0. +- Fixed the C++ library not allowing SOCKS support to be used. Closes #198. +- Fix memory leak when verifying a server certificate with a subjectAltName + section. Closes #237. + +Build: +- Don't attempt to install docs when WITH_DOCS=no. Closes #184. + + 1.4.9 - 20160603 ================ diff --git a/Makefile b/Makefile index 967ba266..1fc0805d 100644 --- a/Makefile +++ b/Makefile @@ -36,7 +36,9 @@ test : mosquitto install : mosquitto set -e; for d in ${DIRS}; do $(MAKE) -C $${d} install; done +ifeq ($(WITH_DOCS),yes) set -e; for d in ${DOCDIRS}; do $(MAKE) -C $${d} install; done +endif $(INSTALL) -d ${DESTDIR}/etc/mosquitto $(INSTALL) -m 644 mosquitto.conf ${DESTDIR}/etc/mosquitto/mosquitto.conf.example $(INSTALL) -m 644 aclfile.example ${DESTDIR}/etc/mosquitto/aclfile.example diff --git a/THANKS.txt b/THANKS.txt index e1b1072e..28431898 100644 --- a/THANKS.txt +++ b/THANKS.txt @@ -56,6 +56,7 @@ Michael Hekel Michael Laing Michael Rushton Mike Bush +Milan Tucic Neil Bothwick Nicholas Humfrey Nicholas O'Leary diff --git a/client/client_shared.c b/client/client_shared.c index 2749eb5b..6928afe9 100644 --- a/client/client_shared.c +++ b/client/client_shared.c @@ -902,7 +902,7 @@ int client_opts_set(struct mosquitto *mosq, struct mosq_config *cfg) return 1; } # endif - if(cfg->tls_version && mosquitto_tls_opts_set(mosq, 1, cfg->tls_version, cfg->ciphers)){ + if((cfg->tls_version || cfg->ciphers) && mosquitto_tls_opts_set(mosq, 1, cfg->tls_version, cfg->ciphers)){ if(!cfg->quiet) fprintf(stderr, "Error: Problem setting TLS options.\n"); mosquitto_lib_cleanup(); return 1; @@ -972,10 +972,11 @@ int client_connect(struct mosquitto *mosq, struct mosq_config *cfg) }else{ port = 1883; } - }else{ + }else +#endif + { port = cfg->port; } -#endif #ifdef WITH_SRV if(cfg->use_srv){ diff --git a/config.h b/config.h index 6bdcb40d..6d80bbd1 100644 --- a/config.h +++ b/config.h @@ -13,6 +13,7 @@ * ============================================================ */ #if defined(_MSC_VER) && _MSC_VER < 1900 # define snprintf sprintf_s +# define EPROTO ECONNABORTED #endif #ifdef WIN32 @@ -27,6 +28,3 @@ #define uthash_malloc(sz) mosquitto__malloc(sz) #define uthash_free(ptr,sz) mosquitto__free(ptr) -#ifndef EPROTO -# define EPROTO ECONNABORTED -#endif diff --git a/config.mk b/config.mk index 680c8b81..347b7118 100644 --- a/config.mk +++ b/config.mk @@ -86,6 +86,9 @@ WITH_STRIP:=no # Build static libraries WITH_STATIC_LIBRARIES:=no +# Build with async dns lookup support for bridges (temporary). Requires glibc. +#WITH_ADNS:=yes + # ============================================================================= # End of user configuration # ============================================================================= @@ -166,6 +169,10 @@ ifeq ($(UNAME),QNX) LIB_LIBS:=$(LIB_LIBS) -lsocket endif +ifeq ($(UNAME),Linux) + BROKER_LIBS:=$(BROKER_LIBS) -lanl +endif + ifeq ($(WITH_WRAP),yes) BROKER_LIBS:=$(BROKER_LIBS) -lwrap BROKER_CFLAGS:=$(BROKER_CFLAGS) -DWITH_WRAP @@ -250,6 +257,11 @@ ifeq ($(WITH_EC),yes) BROKER_CFLAGS:=$(BROKER_CFLAGS) -DWITH_EC endif +ifeq ($(WITH_ADNS),yes) + BROKER_LIBS:=$(BROKER_LIBS) -lanl + BROKER_CFLAGS:=$(BROKER_CFLAGS) -DWITH_ADNS +endif + MAKE_ALL:=mosquitto ifeq ($(WITH_DOCS),yes) MAKE_ALL:=$(MAKE_ALL) docs diff --git a/docker/1.4.10/Dockerfile b/docker/1.4.10/Dockerfile new file mode 100644 index 00000000..3101650b --- /dev/null +++ b/docker/1.4.10/Dockerfile @@ -0,0 +1,13 @@ +FROM alpine:3.5 +MAINTAINER David Audet + +LABEL Description="Eclipse Mosquitto MQTT Broker" + +RUN apk --no-cache add mosquitto=1.4.10-r2 && \ + mkdir -p /mosquitto/config /mosquitto/data /mosquitto/log && \ + cp /etc/mosquitto/mosquitto.conf /mosquitto/config && \ + chown -R mosquitto:mosquitto /mosquitto + +COPY docker-entrypoint.sh / +ENTRYPOINT ["/docker-entrypoint.sh"] +CMD ["/usr/sbin/mosquitto", "-c", "/mosquitto/config/mosquitto.conf"] diff --git a/docker/1.4.10/README.md b/docker/1.4.10/README.md new file mode 100644 index 00000000..bc8dfb72 --- /dev/null +++ b/docker/1.4.10/README.md @@ -0,0 +1,49 @@ +#Eclipse Mosquitto v1.4.10 Docker Image + +##Mount Points + +Three mount points have been created in the image to be used for configuration, persistent storage and logs. +``` +/mosquitto/config +/mosquitto/data +/mosquitto/log +``` + + +##Configuration + +When running the image, the default configuration values are used. +To use a custom configuration file, mount a **local** configuration file to `/mosquitto/config/mosquitto.conf` +``` +docker run -it -p 1883:1883 -p 9001:9001 -v :/mosquitto/config/mosquitto.conf mosquitto:1.4.10 +``` + +Configuration can be changed to: + +* persist data to `/mosquitto/data` +* log to `/mosquitto/log/mosquitto.log` + +i.e. add the following to `mosquitto.conf`: +``` +persistence true +persistence_location /mosquitto/data/ + +log_dest file /mosquitto/log/mosquitto.log +``` + +**Note**: If a volume is used, the data will persist between containers. + +##Build +Build the image: +``` +docker build -t mosquitto:1.4.10 . +``` + +##Run +Run a container using the new image: +``` +docker run -it -p 1883:1883 -p 9001:9001 -v :/mosquitto/config/mosquitto.conf -v /mosquitto/data -v /mosquitto/log mosquitto:1.4.10 +``` +:boom: if the mosquitto configuration (mosquitto.conf) was modified +to use non-default ports, the docker run command will need to be updated +to expose the ports that have been configured. diff --git a/docker/1.4.10/docker-entrypoint.sh b/docker/1.4.10/docker-entrypoint.sh new file mode 100755 index 00000000..1a9fc8d0 --- /dev/null +++ b/docker/1.4.10/docker-entrypoint.sh @@ -0,0 +1,5 @@ +#!/bin/ash + +set -e +exec "$@" + diff --git a/docker/1.4.4/Dockerfile b/docker/1.4.4/Dockerfile new file mode 100644 index 00000000..bd757e3b --- /dev/null +++ b/docker/1.4.4/Dockerfile @@ -0,0 +1,13 @@ +FROM alpine:3.3 +MAINTAINER David Audet + +LABEL Description="Eclipse Mosquitto MQTT Broker" + +RUN apk --no-cache add mosquitto=1.4.4-r0 && \ + mkdir -p /mosquitto/config /mosquitto/data /mosquitto/log && \ + cp /etc/mosquitto/mosquitto.conf /mosquitto/config && \ + chown -R mosquitto:mosquitto /mosquitto + +COPY docker-entrypoint.sh / +ENTRYPOINT ["/docker-entrypoint.sh"] +CMD ["/usr/sbin/mosquitto", "-c", "/mosquitto/config/mosquitto.conf"] diff --git a/docker/1.4.4/README.md b/docker/1.4.4/README.md new file mode 100644 index 00000000..dd4fbbb8 --- /dev/null +++ b/docker/1.4.4/README.md @@ -0,0 +1,49 @@ +#Eclipse Mosquitto v1.4.4 Docker Image + +##Mount Points + +Three mount points have been created in the image to be used for configuration, persistent storage and logs. +``` +/mosquitto/config +/mosquitto/data +/mosquitto/log +``` + + +##Configuration + +When running the image, the default configuration values are used. +To use a custom configuration file, mount a **local** configuration file to `/mosquitto/config/mosquitto.conf` +``` +docker run -it -p 1883:1883 -p 9001:9001 -v :/mosquitto/config/mosquitto.conf mosquitto:1.4.4 +``` + +Configuration can be changed to: + +* persist data to `/mosquitto/data` +* log to `/mosquitto/log/mosquitto.log` + +i.e. add the following to `mosquitto.conf`: +``` +persistence true +persistence_location /mosquitto/data/ + +log_dest file /mosquitto/log/mosquitto.log +``` + +**Note**: If a volume is used, the data will persist between containers. + +##Build +Build the image: +``` +docker build -t mosquitto:1.4.4 . +``` + +##Run +Run a container using the new image: +``` +docker run -it -p 1883:1883 -p 9001:9001 -v :/mosquitto/config/mosquitto.conf -v /mosquitto/data -v /mosquitto/log mosquitto:1.4.4 +``` +:boom: if the mosquitto configuration (mosquitto.conf) was modified +to use non-default ports, the docker run command will need to be updated +to expose the ports that have been configured. \ No newline at end of file diff --git a/docker/1.4.4/docker-entrypoint.sh b/docker/1.4.4/docker-entrypoint.sh new file mode 100755 index 00000000..1a9fc8d0 --- /dev/null +++ b/docker/1.4.4/docker-entrypoint.sh @@ -0,0 +1,5 @@ +#!/bin/ash + +set -e +exec "$@" + diff --git a/docker/1.4.8/Dockerfile b/docker/1.4.8/Dockerfile new file mode 100644 index 00000000..a9027a78 --- /dev/null +++ b/docker/1.4.8/Dockerfile @@ -0,0 +1,13 @@ +FROM alpine:3.4 +MAINTAINER David Audet + +LABEL Description="Eclipse Mosquitto MQTT Broker" + +RUN apk --no-cache add mosquitto=1.4.8-r2 && \ + mkdir -p /mosquitto/config /mosquitto/data /mosquitto/log && \ + cp /etc/mosquitto/mosquitto.conf /mosquitto/config && \ + chown -R mosquitto:mosquitto /mosquitto + +COPY docker-entrypoint.sh / +ENTRYPOINT ["/docker-entrypoint.sh"] +CMD ["/usr/sbin/mosquitto", "-c", "/mosquitto/config/mosquitto.conf"] diff --git a/docker/1.4.8/README.md b/docker/1.4.8/README.md new file mode 100644 index 00000000..811d01f7 --- /dev/null +++ b/docker/1.4.8/README.md @@ -0,0 +1,49 @@ +#Eclipse Mosquitto v1.4.8 Docker Image + +##Mount Points + +Three mount points have been created in the image to be used for configuration, persistent storage and logs. +``` +/mosquitto/config +/mosquitto/data +/mosquitto/log +``` + + +##Configuration + +When running the image, the default configuration values are used. +To use a custom configuration file, mount a **local** configuration file to `/mosquitto/config/mosquitto.conf` +``` +docker run -it -p 1883:1883 -p 9001:9001 -v :/mosquitto/config/mosquitto.conf mosquitto:1.4.8 +``` + +Configuration can be changed to: + +* persist data to `/mosquitto/data` +* log to `/mosquitto/log/mosquitto.log` + +i.e. add the following to `mosquitto.conf`: +``` +persistence true +persistence_location /mosquitto/data/ + +log_dest file /mosquitto/log/mosquitto.log +``` + +**Note**: If a volume is used, the data will persist between containers. + +##Build +Build the image: +``` +docker build -t mosquitto:1.4.8 . +``` + +##Run +Run a container using the new image: +``` +docker run -it -p 1883:1883 -p 9001:9001 -v :/mosquitto/config/mosquitto.conf -v /mosquitto/data -v /mosquitto/log mosquitto:1.4.8 +``` +:boom: if the mosquitto configuration (mosquitto.conf) was modified +to use non-default ports, the docker run command will need to be updated +to expose the ports that have been configured. diff --git a/docker/1.4.8/docker-entrypoint.sh b/docker/1.4.8/docker-entrypoint.sh new file mode 100755 index 00000000..1a9fc8d0 --- /dev/null +++ b/docker/1.4.8/docker-entrypoint.sh @@ -0,0 +1,5 @@ +#!/bin/ash + +set -e +exec "$@" + diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 00000000..6fe5506c --- /dev/null +++ b/docker/README.md @@ -0,0 +1,4 @@ +# Docker Images + +This directory contains the required files to build Mosquitto Docker images. + diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index b9c8676a..abb1fcc5 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -13,7 +13,12 @@ if (${WITH_THREADING} STREQUAL ON) endif (CMAKE_CL_64) set (PTHREAD_INCLUDE_DIR C:\\pthreads\\Pre-built.2\\include) else (WIN32) - set (PTHREAD_LIBRARIES pthread) + find_library(LIBPTHREAD pthread) + if (LIBPTHREAD) + set (PTHREAD_LIBRARIES pthread) + else (LIBPTHREAD) + set (PTHREAD_LIBRARIES "") + endif() set (PTHREAD_INCLUDE_DIR "") endif (WIN32) else (${WITH_THREADING} STREQUAL ON) @@ -64,7 +69,10 @@ set(C_SRC set (LIBRARIES ${OPENSSL_LIBRARIES} ${PTHREAD_LIBRARIES}) if (UNIX AND NOT APPLE) - set (LIBRARIES ${LIBRARIES} rt) + find_library(LIBRT rt) + if (LIBRT) + set (LIBRARIES ${LIBRARIES} rt) + endif (LIBRT) endif (UNIX AND NOT APPLE) if (WIN32) diff --git a/lib/cpp/mosquittopp.cpp b/lib/cpp/mosquittopp.cpp index fd194d0b..d3b0c31f 100644 --- a/lib/cpp/mosquittopp.cpp +++ b/lib/cpp/mosquittopp.cpp @@ -334,11 +334,7 @@ void mosquittopp::user_data_set(void *userdata) int mosquittopp::socks5_set(const char *host, int port, const char *username, const char *password) { -#ifdef WITH_SOCKS return mosquitto_socks5_set(m_mosq, host, port, username, password); -#else - return MOSQ_ERR_NOT_SUPPORTED; -#endif } diff --git a/lib/cpp/mosquittopp.h b/lib/cpp/mosquittopp.h index 8fff67f1..0d0413ae 100644 --- a/lib/cpp/mosquittopp.h +++ b/lib/cpp/mosquittopp.h @@ -4,18 +4,18 @@ Copyright (c) 2010-2016 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License v1.0 and Eclipse Distribution License v1.0 which accompany this distribution. - + The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. - + Contributors: Roger Light - initial implementation and documentation. */ -#ifndef _MOSQUITTOPP_H_ -#define _MOSQUITTOPP_H_ +#ifndef MOSQUITTOPP_H +#define MOSQUITTOPP_H #ifdef _WIN32 # ifdef mosquittopp_EXPORTS @@ -28,8 +28,8 @@ Contributors: #endif #include -#include #include +#include namespace mosqpp { @@ -122,15 +122,16 @@ class mosqpp_EXPORT mosquittopp { bool want_write(); int threaded_set(bool threaded=true); int socks5_set(const char *host, int port=1080, const char *username=NULL, const char *password=NULL); - - virtual void on_connect(int rc) {return;}; - virtual void on_disconnect(int rc) {return;}; - virtual void on_publish(int mid) {return;}; - virtual void on_message(const struct mosquitto_message *message) {return;}; - virtual void on_subscribe(int mid, int qos_count, const int *granted_qos) {return;}; - virtual void on_unsubscribe(int mid) {return;}; - virtual void on_log(int level, const char *str) {return;}; - virtual void on_error() {return;}; + + // names in the functions commented to prevent unused parameter warning + virtual void on_connect(int /*rc*/) {return;} + virtual void on_disconnect(int /*rc*/) {return;} + virtual void on_publish(int /*mid*/) {return;} + virtual void on_message(const struct mosquitto_message * /*message*/) {return;} + virtual void on_subscribe(int /*mid*/, int /*qos_count*/, const int * /*granted_qos*/) {return;} + virtual void on_unsubscribe(int /*mid*/) {return;} + virtual void on_log(int /*level*/, const char * /*str*/) {return;} + virtual void on_error() {return;} }; } diff --git a/lib/handle_publish.c b/lib/handle_publish.c index dc42c79d..bd4953e8 100644 --- a/lib/handle_publish.c +++ b/lib/handle_publish.c @@ -15,6 +15,7 @@ Contributors: */ #include +#include #include "mosquitto.h" #include "mosquitto_internal.h" diff --git a/lib/mosquitto.c b/lib/mosquitto.c index 66450687..dc328d08 100644 --- a/lib/mosquitto.c +++ b/lib/mosquitto.c @@ -507,6 +507,10 @@ static int mosquitto__reconnect(struct mosquitto *mosq, bool blocking) message__reconnect_reset(mosq); + if(mosq->sock != INVALID_SOCKET){ + net__socket_close(mosq); //close socket + } + #ifdef WITH_SOCKS if(mosq->socks5_host){ rc = net__socket_connect(mosq, mosq->socks5_host, mosq->socks5_port, mosq->bind_address, blocking); @@ -891,6 +895,12 @@ int mosquitto_loop(struct mosquitto *mosq, int timeout, int max_packets) timeout = (mosq->next_msg_out - now)*1000; } + if(timeout < 0){ + /* There has been a delay somewhere which means we should have already + * sent a message. */ + timeout = 0; + } + local_timeout.tv_sec = timeout/1000; #ifdef HAVE_PSELECT local_timeout.tv_nsec = (timeout-local_timeout.tv_sec*1000)*1e6; diff --git a/lib/mosquitto_internal.h b/lib/mosquitto_internal.h index 04e7cc47..c45b6951 100644 --- a/lib/mosquitto_internal.h +++ b/lib/mosquitto_internal.h @@ -56,6 +56,9 @@ Contributors: #include "mosquitto.h" #include "time_mosq.h" #ifdef WITH_BROKER +# ifdef __linux__ +# include +# endif # include "uthash.h" struct mosquitto_client_msg; #endif @@ -151,6 +154,9 @@ struct mosquitto { mosq_sock_t sock; #ifndef WITH_BROKER mosq_sock_t sockpairR, sockpairW; +#endif +#if defined(__GLIBC__) && defined(WITH_ADNS) + struct gaicb *adns; /* For getaddrinfo_a */ #endif enum mosquitto__protocol protocol; char *address; diff --git a/lib/net_mosq.c b/lib/net_mosq.c index 8a1cbae0..068bfaab 100644 --- a/lib/net_mosq.c +++ b/lib/net_mosq.c @@ -14,12 +14,15 @@ Contributors: Roger Light - initial implementation and documentation. */ +#define _GNU_SOURCE + #include #include #include #include #include #ifndef WIN32 +#define _GNU_SOURCE #include #include #include @@ -198,6 +201,95 @@ static unsigned int psk_client_callback(SSL *ssl, const char *hint, } #endif +#if defined(WITH_BROKER) && defined(__GLIBC__) && defined(WITH_ADNS) +/* Async connect, part 1 (dns lookup) */ +int net__try_connect_step1(struct mosquitto *mosq, const char *host) +{ + int s; + void *sevp = NULL; + + if(mosq->adns){ + mosquitto__free(mosq->adns); + } + mosq->adns = mosquitto__calloc(1, sizeof(struct gaicb)); + if(!mosq->adns){ + return MOSQ_ERR_NOMEM; + } + mosq->adns->ar_name = host; + + s = getaddrinfo_a(GAI_NOWAIT, &mosq->adns, 1, sevp); + if(s){ + errno = s; + mosquitto__free(mosq->adns); + mosq->adns = NULL; + return MOSQ_ERR_EAI; + } + + return MOSQ_ERR_SUCCESS; +} + +/* Async connect part 2, the connection. */ +int net__try_connect_step2(struct mosquitto *mosq, uint16_t port, mosq_sock_t *sock) +{ + struct addrinfo *ainfo, *rp; + int rc; + + ainfo = mosq->adns->ar_result; + + for(rp = ainfo; rp != NULL; rp = rp->ai_next){ + *sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if(*sock == INVALID_SOCKET) continue; + + if(rp->ai_family == PF_INET){ + ((struct sockaddr_in *)rp->ai_addr)->sin_port = htons(port); + }else if(rp->ai_family == PF_INET6){ + ((struct sockaddr_in6 *)rp->ai_addr)->sin6_port = htons(port); + }else{ + COMPAT_CLOSE(*sock); + continue; + } + + /* Set non-blocking */ + if(net__socket_nonblock(*sock)){ + COMPAT_CLOSE(*sock); + continue; + } + + rc = connect(*sock, rp->ai_addr, rp->ai_addrlen); +#ifdef WIN32 + errno = WSAGetLastError(); +#endif + if(rc == 0 || errno == EINPROGRESS || errno == COMPAT_EWOULDBLOCK){ + if(rc < 0 && (errno == EINPROGRESS || errno == COMPAT_EWOULDBLOCK)){ + rc = MOSQ_ERR_CONN_PENDING; + } + + /* Set non-blocking */ + if(net__socket_nonblock(*sock)){ + COMPAT_CLOSE(*sock); + continue; + } + break; + } + + COMPAT_CLOSE(*sock); + *sock = INVALID_SOCKET; + } + freeaddrinfo(mosq->adns->ar_result); + mosq->adns->ar_result = NULL; + + mosquitto__free(mosq->adns); + mosq->adns = NULL; + + if(!rp){ + return MOSQ_ERR_ERRNO; + } + + return rc; +} + +#endif + int net__try_connect(struct mosquitto *mosq, const char *host, uint16_t port, mosq_sock_t *sock, const char *bind_address, bool blocking) { @@ -212,14 +304,7 @@ int net__try_connect(struct mosquitto *mosq, const char *host, uint16_t port, mo *sock = INVALID_SOCKET; memset(&hints, 0, sizeof(struct addrinfo)); -#ifdef WITH_TLS - if(mosq->tls_cafile || mosq->tls_capath || mosq->tls_psk){ - hints.ai_family = PF_INET; - }else -#endif - { - hints.ai_family = PF_UNSPEC; - } + hints.ai_family = PF_UNSPEC; hints.ai_flags = AI_ADDRCONFIG; hints.ai_socktype = SOCK_STREAM; @@ -241,7 +326,7 @@ int net__try_connect(struct mosquitto *mosq, const char *host, uint16_t port, mo for(rp = ainfo; rp != NULL; rp = rp->ai_next){ *sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if(*sock == INVALID_SOCKET) continue; - + if(rp->ai_family == PF_INET){ ((struct sockaddr_in *)rp->ai_addr)->sin_port = htons(port); }else if(rp->ai_family == PF_INET6){ @@ -321,8 +406,6 @@ void net__print_ssl_error(struct mosquitto *mosq) int net__socket_connect_tls(struct mosquitto *mosq) { int ret, err; - unsigned long e; - char ebuf[256]; ret = SSL_connect(mosq->ssl); if(ret != 1) { @@ -354,25 +437,13 @@ int net__socket_connect_tls(struct mosquitto *mosq) } #endif - -/* Create a socket and connect it to 'ip' on port 'port'. - * Returns -1 on failure (ip is NULL, socket creation/connection error) - * Returns sock number on success. - */ -int net__socket_connect(struct mosquitto *mosq, const char *host, uint16_t port, const char *bind_address, bool blocking) +int net__socket_connect_step3(struct mosquitto *mosq, const char *host, uint16_t port, const char *bind_address, bool blocking) { - mosq_sock_t sock = INVALID_SOCKET; - int rc; #ifdef WITH_TLS int ret; BIO *bio; #endif - if(!mosq || !host || !port) return MOSQ_ERR_INVAL; - - rc = net__try_connect(mosq, host, port, &sock, bind_address, blocking); - if(rc > 0) return rc; - #ifdef WITH_TLS if(mosq->tls_cafile || mosq->tls_capath || mosq->tls_psk){ #if OPENSSL_VERSION_NUMBER >= 0x10001000L @@ -386,7 +457,7 @@ int net__socket_connect(struct mosquitto *mosq, const char *host, uint16_t port, mosq->ssl_ctx = SSL_CTX_new(TLSv1_client_method()); }else{ log__printf(mosq, MOSQ_LOG_ERR, "Error: Protocol %s not supported.", mosq->tls_version); - COMPAT_CLOSE(sock); + COMPAT_CLOSE(mosq->sock); return MOSQ_ERR_INVAL; } #else @@ -394,13 +465,13 @@ int net__socket_connect(struct mosquitto *mosq, const char *host, uint16_t port, mosq->ssl_ctx = SSL_CTX_new(TLSv1_client_method()); }else{ log__printf(mosq, MOSQ_LOG_ERR, "Error: Protocol %s not supported.", mosq->tls_version); - COMPAT_CLOSE(sock); + COMPAT_CLOSE(mosq->sock); return MOSQ_ERR_INVAL; } #endif if(!mosq->ssl_ctx){ log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to create TLS context."); - COMPAT_CLOSE(sock); + COMPAT_CLOSE(mosq->sock); net__print_ssl_error(mosq); return MOSQ_ERR_TLS; } @@ -418,7 +489,7 @@ int net__socket_connect(struct mosquitto *mosq, const char *host, uint16_t port, ret = SSL_CTX_set_cipher_list(mosq->ssl_ctx, mosq->tls_ciphers); if(ret == 0){ log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to set TLS ciphers. Check cipher list \"%s\".", mosq->tls_ciphers); - COMPAT_CLOSE(sock); + COMPAT_CLOSE(mosq->sock); net__print_ssl_error(mosq); return MOSQ_ERR_TLS; } @@ -443,7 +514,7 @@ int net__socket_connect(struct mosquitto *mosq, const char *host, uint16_t port, log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load CA certificates, check capath \"%s\".", mosq->tls_capath); } #endif - COMPAT_CLOSE(sock); + COMPAT_CLOSE(mosq->sock); net__print_ssl_error(mosq); return MOSQ_ERR_TLS; } @@ -466,7 +537,7 @@ int net__socket_connect(struct mosquitto *mosq, const char *host, uint16_t port, #else log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load client certificate \"%s\".", mosq->tls_certfile); #endif - COMPAT_CLOSE(sock); + COMPAT_CLOSE(mosq->sock); net__print_ssl_error(mosq); return MOSQ_ERR_TLS; } @@ -479,14 +550,14 @@ int net__socket_connect(struct mosquitto *mosq, const char *host, uint16_t port, #else log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load client key file \"%s\".", mosq->tls_keyfile); #endif - COMPAT_CLOSE(sock); + COMPAT_CLOSE(mosq->sock); net__print_ssl_error(mosq); return MOSQ_ERR_TLS; } ret = SSL_CTX_check_private_key(mosq->ssl_ctx); if(ret != 1){ log__printf(mosq, MOSQ_LOG_ERR, "Error: Client certificate/key are inconsistent."); - COMPAT_CLOSE(sock); + COMPAT_CLOSE(mosq->sock); net__print_ssl_error(mosq); return MOSQ_ERR_TLS; } @@ -499,28 +570,45 @@ int net__socket_connect(struct mosquitto *mosq, const char *host, uint16_t port, mosq->ssl = SSL_new(mosq->ssl_ctx); if(!mosq->ssl){ - COMPAT_CLOSE(sock); + COMPAT_CLOSE(mosq->sock); net__print_ssl_error(mosq); return MOSQ_ERR_TLS; } + SSL_set_ex_data(mosq->ssl, tls_ex_index_mosq, mosq); - bio = BIO_new_socket(sock, BIO_NOCLOSE); + bio = BIO_new_socket(mosq->sock, BIO_NOCLOSE); if(!bio){ - COMPAT_CLOSE(sock); + COMPAT_CLOSE(mosq->sock); net__print_ssl_error(mosq); return MOSQ_ERR_TLS; } SSL_set_bio(mosq->ssl, bio, bio); - mosq->sock = sock; if(net__socket_connect_tls(mosq)){ return MOSQ_ERR_TLS; } } #endif + return MOSQ_ERR_SUCCESS; +} + +/* Create a socket and connect it to 'ip' on port 'port'. + * Returns -1 on failure (ip is NULL, socket creation/connection error) + * Returns sock number on success. + */ +int net__socket_connect(struct mosquitto *mosq, const char *host, uint16_t port, const char *bind_address, bool blocking) +{ + mosq_sock_t sock = INVALID_SOCKET; + int rc; + + if(!mosq || !host || !port) return MOSQ_ERR_INVAL; + + rc = net__try_connect(mosq, host, port, &sock, bind_address, blocking); + if(rc > 0) return rc; mosq->sock = sock; + rc = net__socket_connect_step3(mosq, host, port, bind_address, blocking); return rc; } @@ -612,7 +700,7 @@ ssize_t net__write(struct mosquitto *mosq, void *buf, size_t count) } -int net__socket_nonblock(int sock) +int net__socket_nonblock(mosq_sock_t sock) { #ifndef WIN32 int opt; diff --git a/lib/net_mosq.h b/lib/net_mosq.h index a0ab8756..353f6a23 100644 --- a/lib/net_mosq.h +++ b/lib/net_mosq.h @@ -59,6 +59,9 @@ int net__socket_close(struct mosquitto_db *db, struct mosquitto *mosq); int net__socket_close(struct mosquitto *mosq); #endif int net__try_connect(struct mosquitto *mosq, const char *host, uint16_t port, mosq_sock_t *sock, const char *bind_address, bool blocking); +int net__try_connect_step1(struct mosquitto *mosq, const char *host); +int net__try_connect_step2(struct mosquitto *mosq, uint16_t port, mosq_sock_t *sock); +int net__socket_connect_step3(struct mosquitto *mosq, const char *host, uint16_t port, const char *bind_address, bool blocking); int net__socket_nonblock(mosq_sock_t sock); int net__socketpair(mosq_sock_t *sp1, mosq_sock_t *sp2); diff --git a/lib/packet_mosq.c b/lib/packet_mosq.c index 730097ae..87a5ab88 100644 --- a/lib/packet_mosq.c +++ b/lib/packet_mosq.c @@ -16,6 +16,7 @@ Contributors: #include #include +#include #ifdef WITH_BROKER # include "mosquitto_broker_internal.h" diff --git a/lib/send_connect.c b/lib/send_connect.c index 93d41cdb..2ef01e5f 100644 --- a/lib/send_connect.c +++ b/lib/send_connect.c @@ -15,6 +15,7 @@ Contributors: */ #include +#include #include "logging_mosq.h" #include "memory_mosq.h" diff --git a/lib/tls_mosq.c b/lib/tls_mosq.c index 3be31430..2a78ab18 100644 --- a/lib/tls_mosq.c +++ b/lib/tls_mosq.c @@ -134,6 +134,7 @@ int mosquitto__verify_certificate_hostname(X509 *cert, const char *hostname) if(nval->type == GEN_DNS){ data = ASN1_STRING_data(nval->d.dNSName); if(data && !mosquitto__cmp_hostname_wildcard((char *)data, hostname)){ + sk_GENERAL_NAME_pop_free(san, GENERAL_NAME_free); return 1; } have_san_dns = true; @@ -141,20 +142,24 @@ int mosquitto__verify_certificate_hostname(X509 *cert, const char *hostname) data = ASN1_STRING_data(nval->d.iPAddress); if(nval->d.iPAddress->length == 4 && ipv4_ok){ if(!memcmp(ipv4_addr, data, 4)){ + sk_GENERAL_NAME_pop_free(san, GENERAL_NAME_free); return 1; } }else if(nval->d.iPAddress->length == 16 && ipv6_ok){ if(!memcmp(ipv6_addr, data, 16)){ + sk_GENERAL_NAME_pop_free(san, GENERAL_NAME_free); return 1; } } } } + sk_GENERAL_NAME_pop_free(san, GENERAL_NAME_free); if(have_san_dns){ /* Only check CN if subjectAltName DNS entry does not exist. */ return 0; } } + subj = X509_get_subject_name(cert); if(X509_NAME_get_text_by_NID(subj, NID_commonName, name, sizeof(name)) > 0){ name[sizeof(name) - 1] = '\0'; diff --git a/logo/mosquitto-logo-min.svg b/logo/mosquitto-logo-min.svg new file mode 100644 index 00000000..5438312b --- /dev/null +++ b/logo/mosquitto-logo-min.svg @@ -0,0 +1,65 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/logo/mosquitto-logo-only.svg b/logo/mosquitto-logo-only.svg index 90304382..e43167d7 100644 --- a/logo/mosquitto-logo-only.svg +++ b/logo/mosquitto-logo-only.svg @@ -14,8 +14,8 @@ inkscape:version="0.91 r13725" xml:space="preserve" width="278.23288" - height="212.13112" - viewBox="0 0 278.23288 212.13112" + height="278.23288" + viewBox="0 0 278.23288 278.23288" sodipodi:docname="mosquitto-logo-only.svg">image/svg+xml \ No newline at end of file + transform="matrix(1.25,0,0,-1.25,-387.06488,575.71439)"> \ No newline at end of file diff --git a/man/mosquitto_passwd.1.xml b/man/mosquitto_passwd.1.xml index 6a75b8d4..087ed166 100644 --- a/man/mosquitto_passwd.1.xml +++ b/man/mosquitto_passwd.1.xml @@ -41,7 +41,7 @@ Description mosquitto_passwd is a tool for managing - password files the the mosquitto MQTT broker. + password files the mosquitto MQTT broker. Usernames must not contain ":". Passwords are stored in a similar format to crypt3. diff --git a/readme-windows.txt b/readme-windows.txt index 7917a90a..d8cd5696 100644 --- a/readme-windows.txt +++ b/readme-windows.txt @@ -11,8 +11,9 @@ separately in the case that they are not already available. Capabilities ------------ -The network support in Windows is severely limited. The broker is limited to approximately -1024 MQTT connections. +Some versions of Windows have limitations on the number of concurrent +connections. Non-server versions have been reported to be limited to +approximately 1024 connections. Websockets @@ -20,7 +21,7 @@ Websockets The broker executables provided in the installers do not have Websockets support enabled. If you wish to have a version of the broker with Websockets support, you will need to compile -libwebsockets version v1.3-chrome37-firefox30 yourself and mosquitto version 1.4 yourself. +libwebsockets version v1.7 onwards because no Windows binaries are provided. Please note that on Windows, libwebsockets limits connections to a maximum of 64 clients. diff --git a/readme.md b/readme.md index 34874fe9..9515380d 100644 --- a/readme.md +++ b/readme.md @@ -2,7 +2,11 @@ Eclipse Mosquitto ================= Mosquitto is an open source implementation of a server for version 3.1 and -3.1.1 of the MQTT protocol. +3.1.1 of the MQTT protocol. It also includes a C and C++ client library, and +the `mosquitto_pub` and `mosquitto_sub` utilities for publishing and +subscribing. + +## Links See the following links for more information on MQTT: @@ -17,6 +21,57 @@ Mosquitto project information is available at the following locations: There is also a public test server available at +## Installing + +See for details on installing binaries for +various platforms. + +## Quick start + +If you have installed a binary package the broker should have been started +automatically. If not, it can be started with a basic configuration: + + mosquitto + +Then use `mosquitto_sub` to subscribe to a topic: + + mosquitto_sub -t 'test/topic' -v + +And to publish a message: + + mosquitto_pub -t 'test/topic' -m 'hello world' + +## Documentation + +Documentation for the broker, clients and client library API can be found in +the man pages, which are available online at . There +are also pages with an introduction to the features of MQTT, the +`mosquitto_passwd` utility for dealing with username/passwords, and a +description of the configuration file options available for the broker. + +Detailed client library API documentation can be found at + +## Building from source + +To build from source the recommended route for end users is to download the +archive from . + +On Windows and Mac, use `cmake` to build. On other platforms, just run `make` +to build. For Windows, see also `readme-windows.md`. + +If you are building from the git repository then the documentation will not +already be built. Use `make binary` to skip building the man pages, or install +`docbook-xsl` on Debian/Ubuntu systems. + +### Build Dependencies + +* c-ares (libc-ares-dev on Debian based systems) - disable with `make WITH_DNS_SRV=no` +* libuuid (uuid-dev) - disable with `make WITH_UUID=no` +* libwebsockets (libwebsockets-dev) - enable with `make WITH_LIBWEBSOCKETS=yes` +* openssl (libssl-dev on Debian based systems) - disable with `make WITH_TLS=no` + +## Credits + Mosquitto was written by Roger Light Master: [![Travis Build Status (master)](https://travis-ci.org/eclipse/mosquitto.svg?branch=master)](https://travis-ci.org/eclipse/mosquitto) diff --git a/set-version.sh b/set-version.sh index 71631010..75c0b529 100755 --- a/set-version.sh +++ b/set-version.sh @@ -2,7 +2,7 @@ MAJOR=1 MINOR=4 -REVISION=9 +REVISION=11 sed -i "s/^VERSION=.*/VERSION=${MAJOR}.${MINOR}.${REVISION}/" config.mk diff --git a/snap/mosquitto.conf b/snap/mosquitto.conf new file mode 100644 index 00000000..43396eb7 --- /dev/null +++ b/snap/mosquitto.conf @@ -0,0 +1,3 @@ +port 1883 +persistence true +user root diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml new file mode 100644 index 00000000..b5925130 --- /dev/null +++ b/snap/snapcraft.yaml @@ -0,0 +1,49 @@ +name: mosquitto-simple +version: 1.4.11 +summary: Eclipse Mosquitto MQTT broker +description: This is a message broker that supports version 3.1 and 3.1.1 of the MQTT + protocol. + MQTT provides a method of carrying out messaging using a publish/subscribe + model. It is lightweight, both in terms of bandwidth usage and ease of + implementation. This makes it particularly useful at the edge of the network + where a sensor or other simple device may be implemented using an arduino for + example. +confinement: strict + +apps: + mosquitto: + command: usr/local/sbin/mosquitto -c $SNAP/mosquitto.conf + daemon: simple + restart-condition: always + plugs: [network, network-bind] + + +parts: + script: + plugin: dump + source: snap/ + prime: + - mosquitto.conf + + + mosquitto: + plugin: make + source: https://github.com/eclipse/mosquitto + source-type: git + + build-packages: + - libssl-dev + - uuid-dev + - libc-ares-dev + - xsltproc + - docbook-xsl + stage-packages: + - libssl1.0.0 + - libuuid1 + - libc-ares2 + prime: + - usr/local/sbin/mosquitto + - lib/*-linux-gnu/libcrypto.so* + - lib/*-linux-gnu/libssl.so* + - lib/*-linux-gnu/libuuid.so* + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8cf18712..64316cda 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -114,11 +114,25 @@ add_executable(mosquitto ${MOSQ_SRCS}) set (MOSQ_LIBS ${MOSQ_LIBS} ${OPENSSL_LIBRARIES}) +# Check for getaddrinfo_a +include(CheckLibraryExists) +check_library_exists(anl getaddrinfo_a "" HAVE_GETADDRINFO_A) +if (HAVE_GETADDRINFO_A) + add_definitions(-DHAVE_GETADDRINFO_A) + set (MOSQ_LIBS ${MOSQ_LIBS} anl) +endif (HAVE_GETADDRINFO_A) + + + if (UNIX) if (APPLE) set (MOSQ_LIBS ${MOSQ_LIBS} dl m) else (APPLE) - set (MOSQ_LIBS ${MOSQ_LIBS} rt dl m) + set (MOSQ_LIBS ${MOSQ_LIBS} dl m) + find_library(LIBRT rt) + if (LIBRT) + set (MOSQ_LIBS ${MOSQ_LIBS} rt) + endif (LIBRT) endif (APPLE) endif (UNIX) diff --git a/src/bridge.c b/src/bridge.c index 6037e9dd..6cea9995 100644 --- a/src/bridge.c +++ b/src/bridge.c @@ -100,8 +100,165 @@ int bridge__new(struct mosquitto_db *db, struct mosquitto__bridge *bridge) return MOSQ_ERR_NOMEM; } +#if defined(__GLIBC__) && defined(WITH_ADNS) + new_context->bridge->restart_t = 1; /* force quick restart of bridge */ + return bridge__connect_step1(db, new_context); +#else return bridge__connect(db, new_context); +#endif +} + +#if defined(__GLIBC__) && defined(WITH_ADNS) +int bridge__connect_step1(struct mosquitto_db *db, struct mosquitto *context) +{ + int rc; + int i; + char *notification_topic; + int notification_topic_len; + uint8_t notification_payload; + + if(!context || !context->bridge) return MOSQ_ERR_INVAL; + + context->state = mosq_cs_new; + context->sock = INVALID_SOCKET; + context->last_msg_in = mosquitto_time(); + context->next_msg_out = mosquitto_time() + context->bridge->keepalive; + context->keepalive = context->bridge->keepalive; + context->clean_session = context->bridge->clean_session; + context->in_packet.payload = NULL; + context->ping_t = 0; + context->bridge->lazy_reconnect = false; + mqtt3_bridge_packet_cleanup(context); + mqtt3_db_message_reconnect_reset(db, context); + + if(context->clean_session){ + mqtt3_db_messages_delete(db, context); + } + + /* Delete all local subscriptions even for clean_session==false. We don't + * remove any messages and the next loop carries out the resubscription + * anyway. This means any unwanted subs will be removed. + */ + mqtt3_subs_clean_session(db, context); + + for(i=0; ibridge->topic_count; i++){ + if(context->bridge->topics[i].direction == bd_out || context->bridge->topics[i].direction == bd_both){ + _mosquitto_log_printf(NULL, MOSQ_LOG_DEBUG, "Bridge %s doing local SUBSCRIBE on topic %s", context->id, context->bridge->topics[i].local_topic); + if(mqtt3_sub_add(db, context, context->bridge->topics[i].local_topic, context->bridge->topics[i].qos, &db->subs)) return 1; + } + } + + if(context->bridge->notifications){ + if(context->bridge->notification_topic){ + if(!context->bridge->initial_notification_done){ + notification_payload = '0'; + mqtt3_db_messages_easy_queue(db, context, context->bridge->notification_topic, 1, 1, ¬ification_payload, 1); + context->bridge->initial_notification_done = true; + } + notification_payload = '0'; + rc = _mosquitto_will_set(context, context->bridge->notification_topic, 1, ¬ification_payload, 1, true); + if(rc != MOSQ_ERR_SUCCESS){ + return rc; + } + }else{ + notification_topic_len = strlen(context->bridge->remote_clientid)+strlen("$SYS/broker/connection//state"); + notification_topic = _mosquitto_malloc(sizeof(char)*(notification_topic_len+1)); + if(!notification_topic) return MOSQ_ERR_NOMEM; + + snprintf(notification_topic, notification_topic_len+1, "$SYS/broker/connection/%s/state", context->bridge->remote_clientid); + + if(!context->bridge->initial_notification_done){ + notification_payload = '0'; + mqtt3_db_messages_easy_queue(db, context, notification_topic, 1, 1, ¬ification_payload, 1); + context->bridge->initial_notification_done = true; + } + + notification_payload = '0'; + rc = _mosquitto_will_set(context, notification_topic, 1, ¬ification_payload, 1, true); + _mosquitto_free(notification_topic); + if(rc != MOSQ_ERR_SUCCESS){ + return rc; + } + } + } + + _mosquitto_log_printf(NULL, MOSQ_LOG_NOTICE, "Connecting bridge %s (%s:%d)", context->bridge->name, context->bridge->addresses[context->bridge->cur_address].address, context->bridge->addresses[context->bridge->cur_address].port); + rc = _mosquitto_try_connect_step1(context, context->bridge->addresses[context->bridge->cur_address].address); + if(rc > 0 ){ + if(rc == MOSQ_ERR_TLS){ + _mosquitto_socket_close(db, context); + return rc; /* Error already printed */ + }else if(rc == MOSQ_ERR_ERRNO){ + _mosquitto_log_printf(NULL, MOSQ_LOG_ERR, "Error creating bridge: %s.", strerror(errno)); + }else if(rc == MOSQ_ERR_EAI){ + _mosquitto_log_printf(NULL, MOSQ_LOG_ERR, "Error creating bridge: %s.", gai_strerror(errno)); + } + + return rc; + } + + return MOSQ_ERR_SUCCESS; +} + + +int bridge__connect_step2(struct mosquitto_db *db, struct mosquitto *context) +{ + int rc; + + if(!context || !context->bridge) return MOSQ_ERR_INVAL; + + _mosquitto_log_printf(NULL, MOSQ_LOG_NOTICE, "Connecting bridge %s (%s:%d)", context->bridge->name, context->bridge->addresses[context->bridge->cur_address].address, context->bridge->addresses[context->bridge->cur_address].port); + rc = _mosquitto_try_connect_step2(context, context->bridge->addresses[context->bridge->cur_address].port, &context->sock); + if(rc > 0 ){ + if(rc == MOSQ_ERR_TLS){ + _mosquitto_socket_close(db, context); + return rc; /* Error already printed */ + }else if(rc == MOSQ_ERR_ERRNO){ + _mosquitto_log_printf(NULL, MOSQ_LOG_ERR, "Error creating bridge: %s.", strerror(errno)); + }else if(rc == MOSQ_ERR_EAI){ + _mosquitto_log_printf(NULL, MOSQ_LOG_ERR, "Error creating bridge: %s.", gai_strerror(errno)); + } + + return rc; + } + + rc = _mosquitto_socket_connect_step3(context, context->bridge->addresses[context->bridge->cur_address].address, context->bridge->addresses[context->bridge->cur_address].port, NULL, false); + if(rc > 0 ){ + if(rc == MOSQ_ERR_TLS){ + _mosquitto_socket_close(db, context); + return rc; /* Error already printed */ + }else if(rc == MOSQ_ERR_ERRNO){ + _mosquitto_log_printf(NULL, MOSQ_LOG_ERR, "Error creating bridge: %s.", strerror(errno)); + }else if(rc == MOSQ_ERR_EAI){ + _mosquitto_log_printf(NULL, MOSQ_LOG_ERR, "Error creating bridge: %s.", gai_strerror(errno)); + } + + return rc; + } + + HASH_ADD(hh_sock, db->contexts_by_sock, sock, sizeof(context->sock), context); + + if(rc == MOSQ_ERR_CONN_PENDING){ + context->state = mosq_cs_connect_pending; + } + rc = _mosquitto_send_connect(context, context->keepalive, context->clean_session); + if(rc == MOSQ_ERR_SUCCESS){ + return MOSQ_ERR_SUCCESS; + }else if(rc == MOSQ_ERR_ERRNO && errno == ENOTCONN){ + return MOSQ_ERR_SUCCESS; + }else{ + if(rc == MOSQ_ERR_TLS){ + return rc; /* Error already printed */ + }else if(rc == MOSQ_ERR_ERRNO){ + _mosquitto_log_printf(NULL, MOSQ_LOG_ERR, "Error creating bridge: %s.", strerror(errno)); + }else if(rc == MOSQ_ERR_EAI){ + _mosquitto_log_printf(NULL, MOSQ_LOG_ERR, "Error creating bridge: %s.", gai_strerror(errno)); + } + _mosquitto_socket_close(db, context); + return rc; + } } +#else int bridge__connect(struct mosquitto_db *db, struct mosquitto *context) { @@ -218,6 +375,8 @@ int bridge__connect(struct mosquitto_db *db, struct mosquitto *context) return rc; } } +#endif + void bridge__packet_cleanup(struct mosquitto *context) { diff --git a/src/conf.c b/src/conf.c index e7a1d8a7..318b3288 100644 --- a/src/conf.c +++ b/src/conf.c @@ -36,7 +36,7 @@ Contributors: #endif #if !defined(WIN32) && !defined(__CYGWIN__) -# include +# include #endif #include "mosquitto_broker_internal.h" diff --git a/src/handle_connect.c b/src/handle_connect.c index f6b7e006..e492a538 100644 --- a/src/handle_connect.c +++ b/src/handle_connect.c @@ -205,7 +205,7 @@ int handle__connect(struct mosquitto_db *db, struct mosquitto *context) rc = MOSQ_ERR_PROTOCOL; goto handle_connect_error; } - will_retain = connect_flags & 0x20; + will_retain = ((connect_flags & 0x20) == 0x20); // Temporary hack because MSVC<1800 doesn't have stdbool.h. password_flag = connect_flags & 0x40; username_flag = connect_flags & 0x80; diff --git a/src/logging.c b/src/logging.c index bd6c8b51..8eb70bda 100644 --- a/src/logging.c +++ b/src/logging.c @@ -71,6 +71,8 @@ int log__init(struct mosquitto__config *config) } config->log_fptr = mosquitto__fopen(config->log_file, "at"); if(!config->log_fptr){ + log_destinations = MQTT3_LOG_STDERR; + log_priorities = MOSQ_LOG_ERR; log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to open log file %s for writing.", config->log_file); return MOSQ_ERR_INVAL; } diff --git a/src/loop.c b/src/loop.c index 01603885..4e300f68 100644 --- a/src/loop.c +++ b/src/loop.c @@ -245,36 +245,69 @@ int mosquitto_main_loop(struct mosquitto_db *db, mosq_sock_t *listensock, int li context->bridge->primary_retry = now + 5; } }else{ - if(context->bridge->start_type == bst_lazy && context->bridge->lazy_reconnect){ - rc = bridge__connect(db, context); - if(rc){ - context->bridge->cur_address++; - if(context->bridge->cur_address == context->bridge->address_count){ - context->bridge->cur_address = 0; + if((context->bridge->start_type == bst_lazy && context->bridge->lazy_reconnect) + || (context->bridge->start_type == bst_automatic && now > context->bridge->restart_t)){ + +#if defined(__GLIBC__) && defined(WITH_ADNS) + if(context->adns){ + /* Waiting on DNS lookup */ + rc = gai_error(context->adns); + if(rc == EAI_INPROGRESS){ + /* Just keep on waiting */ + }else if(rc == 0){ + rc = bridge__connect_step2(db, context); + if(rc == MOSQ_ERR_SUCCESS){ + pollfds[pollfd_index].fd = context->sock; + pollfds[pollfd_index].events = POLLIN; + pollfds[pollfd_index].revents = 0; + if(context->current_out_packet){ + pollfds[pollfd_index].events |= POLLOUT; + } + context->pollfd_index = pollfd_index; + pollfd_index++; + }else{ + context->bridge->cur_address++; + if(context->bridge->cur_address == context->bridge->address_count){ + context->bridge->cur_address = 0; + } + } + }else{ + /* Need to retry */ + if(context->adns->ar_result){ + freeaddrinfo(context->adns->ar_result); + } + _mosquitto_free(context->adns); + context->adns = NULL; } - } - } - if(context->bridge->start_type == bst_automatic && now > context->bridge->restart_t){ - context->bridge->restart_t = 0; - rc = bridge__connect(db, context); - if(rc == MOSQ_ERR_SUCCESS){ - pollfds[pollfd_index].fd = context->sock; - pollfds[pollfd_index].events = POLLIN; - pollfds[pollfd_index].revents = 0; - if(context->current_out_packet){ - pollfds[pollfd_index].events |= POLLOUT; - } - context->pollfd_index = pollfd_index; - pollfd_index++; }else{ - /* Retry later. */ - context->bridge->restart_t = now+context->bridge->restart_timeout; - - context->bridge->cur_address++; - if(context->bridge->cur_address == context->bridge->address_count){ - context->bridge->cur_address = 0; + rc = bridge__connect_step1(db, context); + if(rc){ + context->bridge->cur_address++; + if(context->bridge->cur_address == context->bridge->address_count){ + context->bridge->cur_address = 0; + } } } +#else + { + rc = bridge__connect(db, context); + if(rc == MOSQ_ERR_SUCCESS){ + pollfds[pollfd_index].fd = context->sock; + pollfds[pollfd_index].events = POLLIN; + pollfds[pollfd_index].revents = 0; + if(context->current_out_packet){ + pollfds[pollfd_index].events |= POLLOUT; + } + context->pollfd_index = pollfd_index; + pollfd_index++; + }else{ + context->bridge->cur_address++; + if(context->bridge->cur_address == context->bridge->address_count){ + context->bridge->cur_address = 0; + } + } + } +#endif } } } @@ -442,10 +475,6 @@ static void loop_handle_reads_writes(struct mosquitto_db *db, struct pollfd *pol } assert(pollfds[context->pollfd_index].fd == context->sock); - if(pollfds[context->pollfd_index].revents & (POLLERR | POLLNVAL | POLLHUP)){ - do_disconnect(db, context); - continue; - } #ifdef WITH_TLS if(pollfds[context->pollfd_index].revents & POLLOUT || context->want_write || @@ -489,6 +518,10 @@ static void loop_handle_reads_writes(struct mosquitto_db *db, struct pollfd *pol } }while(SSL_DATA_PENDING(context)); } + if(pollfds[context->pollfd_index].revents & (POLLERR | POLLNVAL | POLLHUP)){ + do_disconnect(db, context); + continue; + } } } diff --git a/src/mosquitto.c b/src/mosquitto.c index 7601ed73..f6e966db 100644 --- a/src/mosquitto.c +++ b/src/mosquitto.c @@ -264,7 +264,10 @@ int main(int argc, char *argv[]) /* Initialise logging only after initialising the database in case we're * logging to topics */ - log__init(&config); + if(log__init(&config)){ + rc = 1; + return rc; + } log__printf(NULL, MOSQ_LOG_INFO, "mosquitto version %s (build date %s) starting", VERSION, TIMESTAMP); if(config.config_file){ log__printf(NULL, MOSQ_LOG_INFO, "Config loaded from %s.", config.config_file); diff --git a/src/mosquitto_broker_internal.h b/src/mosquitto_broker_internal.h index 3256e89e..f1badbe8 100644 --- a/src/mosquitto_broker_internal.h +++ b/src/mosquitto_broker_internal.h @@ -14,8 +14,8 @@ Contributors: Roger Light - initial implementation and documentation. */ -#ifndef MQTT3_H -#define MQTT3_H +#ifndef MOSQUITTO_BROKER_INTERNAL_H +#define MOSQUITTO_BROKER_INTERNAL_H #include "config.h" #include @@ -31,6 +31,7 @@ Contributors: # define libwebsocket_write(A, B, C, D) lws_write((A), (B), (C), (D)) # define libwebsocket_get_socket_fd(A) lws_get_socket_fd((A)) # define libwebsockets_return_http_status(A, B, C, D) lws_return_http_status((B), (C), (D)) +# define libwebsockets_get_protocol(A) lws_get_protocol((A)) # define libwebsocket_context lws_context # define libwebsocket_protocols lws_protocols @@ -570,6 +571,8 @@ int log__printf(struct mosquitto *mosq, int level, const char *fmt, ...) __attri #ifdef WITH_BRIDGE int bridge__new(struct mosquitto_db *db, struct mosquitto__bridge *bridge); int bridge__connect(struct mosquitto_db *db, struct mosquitto *context); +int bridge__connect_step1(struct mosquitto_db *db, struct mosquitto *context); +int bridge__connect_step2(struct mosquitto_db *db, struct mosquitto *context); void bridge__packet_cleanup(struct mosquitto *context); #endif diff --git a/src/mosquitto_passwd.c b/src/mosquitto_passwd.c index 4becd6f1..9689dd89 100644 --- a/src/mosquitto_passwd.c +++ b/src/mosquitto_passwd.c @@ -90,7 +90,11 @@ int output_new_password(FILE *fptr, const char *username, const char *password) unsigned char hash[EVP_MAX_MD_SIZE]; unsigned int hash_len; const EVP_MD *digest; +#if OPENSSL_VERSION_NUMBER < 0x10100000L EVP_MD_CTX context; +#else + EVP_MD_CTX *context; +#endif rc = RAND_bytes(salt, SALT_LEN); if(!rc){ @@ -113,12 +117,21 @@ int output_new_password(FILE *fptr, const char *username, const char *password) return 1; } +#if OPENSSL_VERSION_NUMBER < 0x10100000L EVP_MD_CTX_init(&context); EVP_DigestInit_ex(&context, digest, NULL); EVP_DigestUpdate(&context, password, strlen(password)); EVP_DigestUpdate(&context, salt, SALT_LEN); EVP_DigestFinal_ex(&context, hash, &hash_len); EVP_MD_CTX_cleanup(&context); +#else + context = EVP_MD_CTX_new(); + EVP_DigestInit_ex(context, digest, NULL); + EVP_DigestUpdate(context, password, strlen(password)); + EVP_DigestUpdate(context, salt, SALT_LEN); + EVP_DigestFinal_ex(context, hash, &hash_len); + EVP_MD_CTX_free(context); +#endif rc = base64_encode(hash, hash_len, &hash64); if(rc){ @@ -364,35 +377,50 @@ int main(int argc, char *argv[]) OpenSSL_add_all_digests(); - if(argc == 5){ - if(!strcmp(argv[1], "-b")){ - batch_mode = true; - }else{ - fprintf(stderr, "Error: Unknown option '%s'\n", argv[1]); + if(argc == 1){ + print_usage(); + return 1; + } + + if(!strcmp(argv[1], "-c")){ + create_new = true; + if(argc != 4){ + fprintf(stderr, "Error: -c argument given but password file or username missing.\n"); return 1; + }else{ + password_file_tmp = argv[2]; + username = argv[3]; } - password_file_tmp = argv[2]; - username = argv[3]; - password_cmd = argv[4]; - }else if(argc == 4){ - if(!strcmp(argv[1], "-c")){ - create_new = true; - }else if(!strcmp(argv[1], "-D")){ - delete_user = true; + }else if(!strcmp(argv[1], "-D")){ + delete_user = true; + if(argc != 4){ + fprintf(stderr, "Error: -D argument given but password file or username missing.\n"); + return 1; }else{ - fprintf(stderr, "Error: Unknown option '%s'\n", argv[1]); + password_file_tmp = argv[2]; + username = argv[3]; + } + }else if(!strcmp(argv[1], "-b")){ + batch_mode = true; + if(argc != 5){ + fprintf(stderr, "Error: -b argument given but password file, username or password missing.\n"); return 1; + }else{ + password_file_tmp = argv[2]; + username = argv[3]; + password_cmd = argv[4]; } - password_file_tmp = argv[2]; - username = argv[3]; - }else if(argc == 3){ - if(!strcmp(argv[1], "-U")){ + }else if(!strcmp(argv[1], "-U")){ + if(argc != 3){ + fprintf(stderr, "Error: -U argument given but password file missing.\n"); + return 1; + }else{ do_update_file = true; password_file_tmp = argv[2]; - }else{ - password_file_tmp = argv[1]; - username = argv[2]; } + }else if(argc == 3){ + password_file_tmp = argv[1]; + username = argv[2]; }else{ print_usage(); return 1; diff --git a/src/mosquitto_plugin.h b/src/mosquitto_plugin.h index 8c0c0ffb..f14a55a2 100644 --- a/src/mosquitto_plugin.h +++ b/src/mosquitto_plugin.h @@ -53,6 +53,10 @@ struct mosquitto_acl_msg { * * gcc -I -fPIC -shared plugin.c -o plugin.so * + * On Mac OS X: + * + * gcc -I -fPIC -shared plugin.c -undefined dynamic_lookup -o plugin.so + * * Authentication plugins can implement one or both of authentication and * access control. If your plugin does not wish to handle either of * authentication or access control it should return MOSQ_ERR_PLUGIN_DEFER. In diff --git a/src/net.c b/src/net.c index 6d03fc02..9a4ce971 100644 --- a/src/net.c +++ b/src/net.c @@ -298,7 +298,7 @@ static int mosquitto__tls_server_ctx(struct mosquitto__listener *listener) #endif #ifdef WITH_EC -#if OPENSSL_VERSION_NUMBER >= 0x10002000L +#if OPENSSL_VERSION_NUMBER >= 0x10002000L && OPENSSL_VERSION_NUMBER < 0x10100000L SSL_CTX_set_ecdh_auto(listener->ssl_ctx, 1); #elif OPENSSL_VERSION_NUMBER >= 0x10000000L && OPENSSL_VERSION_NUMBER < 0x10002000L ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); diff --git a/src/persist.c b/src/persist.c index 9dc643e5..91def9a7 100644 --- a/src/persist.c +++ b/src/persist.c @@ -140,7 +140,7 @@ static int persist__message_store_write(struct mosquitto_db *db, FILE *db_fptr) stored = db->msg_store; while(stored){ if(stored->topic && !strncmp(stored->topic, "$SYS", 4)){ - if(stored->ref_count == 1 && stored->dest_id_count == 0){ + if(stored->ref_count <= 1 && stored->dest_id_count == 0){ /* $SYS messages that are only retained shouldn't be persisted. */ stored = stored->next; continue; @@ -262,6 +262,7 @@ static int persist__subs_retain_write(struct mosquitto_db *db, FILE *db_fptr, st char *thistopic; uint32_t length; uint16_t i16temp; + uint8_t i8temp; dbid_t i64temp; size_t slen; @@ -293,7 +294,8 @@ static int persist__subs_retain_write(struct mosquitto_db *db, FILE *db_fptr, st write_e(db_fptr, &i16temp, sizeof(uint16_t)); write_e(db_fptr, thistopic, slen); - write_e(db_fptr, &sub->qos, sizeof(uint8_t)); + i8temp = (uint8_t )sub->qos; + write_e(db_fptr, &i8temp, sizeof(uint8_t)); } sub = sub->next; } @@ -359,6 +361,36 @@ int persist__backup(struct mosquitto_db *db, bool shutdown) } snprintf(outfile, len, "%s.new", db->config->persistence_filepath); outfile[len] = '\0'; + +#ifndef WIN32 + /** + * + * If a system lost power during the rename operation at the + * end of this file the filesystem could potentially be left + * with a directory that looks like this after powerup: + * + * 24094 -rw-r--r-- 2 root root 4099 May 30 16:27 mosquitto.db + * 24094 -rw-r--r-- 2 root root 4099 May 30 16:27 mosquitto.db.new + * + * The 24094 shows that mosquitto.db.new is hard-linked to the + * same file as mosquitto.db. If fopen(outfile, "wb") is naively + * called then mosquitto.db will be truncated and the database + * potentially corrupted. + * + * Any existing mosquitto.db.new file must be removed prior to + * opening to guarantee that it is not hard-linked to + * mosquitto.db. + * + */ + rc = unlink(outfile); + if (rc != 0) { + if (errno != ENOENT) { + log__printf(NULL, MOSQ_LOG_INFO, "Error saving in-memory database, unable to remove %s.", outfile); + goto error; + } + } +#endif + db_fptr = mosquitto__fopen(outfile, "wb"); if(db_fptr == NULL){ log__printf(NULL, MOSQ_LOG_INFO, "Error saving in-memory database, unable to open %s for writing.", outfile); @@ -392,6 +424,32 @@ int persist__backup(struct mosquitto_db *db, bool shutdown) persist__client_write(db, db_fptr); persist__subs_retain_write_all(db, db_fptr); +#ifndef WIN32 + /** + * + * Closing a file does not guarantee that the contents are + * written to disk. Need to flush to send data from app to OS + * buffers, then fsync to deliver data from OS buffers to disk + * (as well as disk hardware permits). + * + * man close (http://linux.die.net/man/2/close, 2016-06-20): + * + * "successful close does not guarantee that the data has + * been successfully saved to disk, as the kernel defers + * writes. It is not common for a filesystem to flush + * the buffers when the stream is closed. If you need + * to be sure that the data is physically stored, use + * fsync(2). (It will depend on the disk hardware at this + * point." + * + * This guarantees that the new state file will not overwrite + * the old state file before its contents are valid. + * + */ + + fflush(db_fptr); + fsync(fileno(db_fptr)); +#endif fclose(db_fptr); #ifdef WIN32 @@ -767,7 +825,14 @@ int persist__restore(struct mosquitto_db *db) fptr = mosquitto__fopen(db->config->persistence_filepath, "rb"); if(fptr == NULL) return MOSQ_ERR_SUCCESS; - read_e(fptr, &header, 15); + rlen = fread(&header, 1, 15, fptr); + if(rlen == 0){ + fclose(fptr); + log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Persistence file is empty."); + return 0; + }else if(rlen != 15){ + goto error; + } if(!memcmp(header, magic, 15)){ // Restore DB as normal read_e(fptr, &crc, sizeof(uint32_t)); diff --git a/src/security_default.c b/src/security_default.c index 4986da7f..1b502806 100644 --- a/src/security_default.c +++ b/src/security_default.c @@ -768,6 +768,7 @@ int mosquitto_psk_key_get_default(struct mosquitto_db *db, const char *hint, con int pw__digest(const char *password, const unsigned char *salt, unsigned int salt_len, unsigned char *hash, unsigned int *hash_len) { const EVP_MD *digest; +#if OPENSSL_VERSION_NUMBER < 0x10100000L EVP_MD_CTX context; digest = EVP_get_digestbyname("sha512"); @@ -783,6 +784,23 @@ int pw__digest(const char *password, const unsigned char *salt, unsigned int sal /* hash is assumed to be EVP_MAX_MD_SIZE bytes long. */ EVP_DigestFinal_ex(&context, hash, hash_len); EVP_MD_CTX_cleanup(&context); +#else + EVP_MD_CTX *context; + + digest = EVP_get_digestbyname("sha512"); + if(!digest){ + // FIXME fprintf(stderr, "Error: Unable to create openssl digest.\n"); + return 1; + } + + context = EVP_MD_CTX_new(); + EVP_DigestInit_ex(context, digest, NULL); + EVP_DigestUpdate(context, password, strlen(password)); + EVP_DigestUpdate(context, salt, salt_len); + /* hash is assumed to be EVP_MAX_MD_SIZE bytes long. */ + EVP_DigestFinal_ex(context, hash, hash_len); + EVP_MD_CTX_free(context); +#endif return MOSQ_ERR_SUCCESS; } diff --git a/src/websockets.c b/src/websockets.c index bb1c0584..a7d09725 100644 --- a/src/websockets.c +++ b/src/websockets.c @@ -150,7 +150,8 @@ static int callback_mqtt(struct libwebsocket_context *context, struct mosquitto_db *db; struct mosquitto *mosq = NULL; struct mosquitto__packet *packet; - int count; + int count, i, j; + const struct libwebsocket_protocols *p; struct libws_mqtt_data *u = (struct libws_mqtt_data *)user; size_t pos; uint8_t *buf; @@ -163,6 +164,21 @@ static int callback_mqtt(struct libwebsocket_context *context, case LWS_CALLBACK_ESTABLISHED: mosq = context__init(db, WEBSOCKET_CLIENT); if(mosq){ + p = libwebsockets_get_protocol(wsi); + for (i=0; iconfig->listener_count; i++){ + if (db->config->listeners[i].protocol == mp_websockets) { + for (j=0; db->config->listeners[i].ws_protocol[j].name; j++){ + if (p == &db->config->listeners[i].ws_protocol[j]){ + mosq->listener = &db->config->listeners[i]; + mosq->listener->client_count++; + } + } + } + } + if(!mosq->listener){ + mosquitto__free(mosq); + return -1; + } #if !defined(LWS_LIBRARY_VERSION_NUMBER) mosq->ws_context = context; #endif @@ -178,6 +194,12 @@ static int callback_mqtt(struct libwebsocket_context *context, u->mosq = NULL; return -1; } + if(mosq->listener->max_connections > 0 && mosq->listener->client_count > mosq->listener->max_connections){ + log__printf(NULL, MOSQ_LOG_NOTICE, "Client connection from %s denied: max_connections exceeded.", mosq->address); + mosquitto__free(mosq); + u->mosq = NULL; + return -1; + } break; case LWS_CALLBACK_CLOSED: @@ -455,6 +477,7 @@ static int callback_http(struct libwebsocket_context *context, if(fstat(fileno(u->fptr), &filestat) < 0){ libwebsockets_return_http_status(context, wsi, HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL); fclose(u->fptr); + u->fptr = NULL; return -1; } #ifdef WIN32 @@ -463,6 +486,8 @@ static int callback_http(struct libwebsocket_context *context, if(!S_ISREG(filestat.st_mode)){ #endif libwebsockets_return_http_status(context, wsi, HTTP_STATUS_FORBIDDEN, NULL); + fclose(u->fptr); + u->fptr = NULL; return -1; } @@ -472,6 +497,7 @@ static int callback_http(struct libwebsocket_context *context, (unsigned int)filestat.st_size); if(libwebsocket_write(wsi, buf, buflen, LWS_WRITE_HTTP) < 0){ fclose(u->fptr); + u->fptr = NULL; return -1; } libwebsocket_callback_on_writable(context, wsi); @@ -496,6 +522,7 @@ static int callback_http(struct libwebsocket_context *context, buflen = fread(buf, 1, sizeof(buf), u->fptr); if(buflen < 1){ fclose(u->fptr); + u->fptr = NULL; return -1; } wlen = libwebsocket_write(wsi, buf, buflen, LWS_WRITE_HTTP); @@ -516,6 +543,16 @@ static int callback_http(struct libwebsocket_context *context, }else{ return -1; } + break; + + case LWS_CALLBACK_CLOSED: + case LWS_CALLBACK_CLOSED_HTTP: + case LWS_CALLBACK_HTTP_FILE_COMPLETION: + if(u && u->fptr){ + fclose(u->fptr); + u->fptr = NULL; + } + break; default: return 0; @@ -570,6 +607,9 @@ struct libwebsocket_context *mosq_websockets_init(struct mosquitto__listener *li } #endif info.options |= LWS_SERVER_OPTION_DISABLE_IPV6; +#if LWS_LIBRARY_VERSION_MAJOR>1 + info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; +#endif user = mosquitto__calloc(1, sizeof(struct libws_mqtt_hack)); if(!user){ @@ -587,7 +627,7 @@ struct libwebsocket_context *mosq_websockets_init(struct mosquitto__listener *li if(!user->http_dir){ mosquitto__free(user); mosquitto__free(p); - log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to open http dir \"%s\".", user->http_dir); + log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to open http dir \"%s\".", listener->http_dir); return NULL; } }