1
0
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:
Roger A. Light 2018-02-28 11:15:43 +00:00
commit 80f567975c
82 changed files with 806 additions and 211 deletions

View File

@ -11,7 +11,7 @@ project(mosquitto)
cmake_minimum_required(VERSION 2.8)
# Only for version 3 and up. cmake_policy(SET CMP0042 NEW)
set (VERSION 1.4.14)
set (VERSION 1.4.15)
if (WIN32)
execute_process(COMMAND cmd /c echo %DATE% %TIME% OUTPUT_VARIABLE TIMESTAMP

View File

@ -1,9 +1,46 @@
1.4.15 - 20180228
=================
Security:
- Fix CVE-2017-7652. If a SIGHUP is sent to the broker when there are no more
file descriptors, then opening the configuration file will fail and security
settings will be set back to their default values.
- Fix CVE-2017-7651. Unauthenticated clients can cause excessive memory use by
setting "remaining length" to be a large value. This is now mitigated by
limiting the size of remaining length to valid values. A "memory_limit"
configuration option has also been added to allow the overall memory used by
the broker to be limited.
Broker:
- Use constant time memcmp for password comparisons.
- Fix incorrect PSK key being used if it had leading zeroes.
- Fix memory leak if a client provided a username/password for a listener with
use_identity_as_username configured.
- Fix use_identity_as_username not working on websockets clients.
- Don't crash if an auth plugin returns MOSQ_ERR_AUTH for a username check on
a websockets client. Closes #490.
- Fix 08-ssl-bridge.py test when using async dns lookups. Closes #507.
- Lines in the config file are no longer limited to 1024 characters long.
Closes #652.
- Fix $SYS counters of messages and bytes sent when message is sent over
a Websockets. Closes #250.
- Fix upgrade_outgoing_qos for retained message. Closes #534.
- Fix CONNACK message not being sent for unauthorised connect on websockets.
Closes #8.
Client library:
- Fix incorrect PSK key being used if it had leading zeroes.
- Initialise "result" variable as soon as possible in
mosquitto_topic_matches_sub. Closes #654.
- No need to close socket again if setting non-blocking failed. Closes #649.
- Fix mosquitto_topic_matches_sub() not correctly matching foo/bar against
foo/+/#. Closes #670.
Clients:
- Correctly handle empty files with "mosquitto_pub -l". Closes #676.
Build:
- Don't run TLS-PSK tests if TLS-PSK disabled at compile time. Closes #636.
1.4.14 - 20170710

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2014 Roger Light <roger@atchoo.org>
Copyright (c) 2014-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2014 Roger Light <roger@atchoo.org>
Copyright (c) 2014-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2009-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2009-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
@ -34,6 +34,7 @@ Contributors:
#define STATUS_CONNECTING 0
#define STATUS_CONNACK_RECVD 1
#define STATUS_WAITING 2
#define STATUS_DISCONNECTING 3
/* Global variables for use in callbacks. See sub_client.c for an example of
* using a struct to hold variables for use in callbacks. */
@ -410,9 +411,16 @@ int main(int argc, char *argv[])
}
}
if(feof(stdin)){
if(last_mid == -1){
/* Empty file */
mosquitto_disconnect(mosq);
disconnect_sent = true;
status = STATUS_DISCONNECTING;
}else{
last_mid = mid_sent;
status = STATUS_WAITING;
}
}
}else if(status == STATUS_WAITING){
if(last_mid_sent == last_mid && disconnect_sent == false){
mosquitto_disconnect(mosq);

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2009-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2009-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0

View File

@ -86,7 +86,7 @@ WITH_SOCKS:=yes
# Also bump lib/mosquitto.h, CMakeLists.txt,
# installer/mosquitto.nsi, installer/mosquitto-cygwin.nsi
VERSION=1.4.14
VERSION=1.4.15
TIMESTAMP:=$(shell date "+%F %T%z")
# Client library SO version. Bump if incompatible API/ABI changes are made.

View File

@ -43,8 +43,13 @@ void message_callback(struct mosquitto *mosq, void *obj, const struct mosquitto_
bind[0].buffer_type = MYSQL_TYPE_STRING;
bind[0].buffer = message->topic;
bind[0].buffer_length = strlen(message->topic);
// Note: payload is normally a binary blob and could contains
// NULL byte. This sample does not handle it and assume payload is a
// string.
bind[1].buffer_type = MYSQL_TYPE_STRING;
bind[1].buffer = message->payload;
bind[1].buffer_length = message->payloadlen;
mysql_stmt_bind_param(stmt, bind);
mysql_stmt_execute(stmt);

View File

@ -7,7 +7,7 @@
!define env_hklm 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"'
Name "mosquitto"
!define VERSION 1.4.14
!define VERSION 1.4.15
OutFile "mosquitto-${VERSION}-install-cygwin.exe"
InstallDir "$PROGRAMFILES\mosquitto"

View File

@ -9,7 +9,7 @@
!define env_hklm 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"'
Name "mosquitto"
!define VERSION 1.4.14
!define VERSION 1.4.15
OutFile "mosquitto-${VERSION}-install-win32.exe"
InstallDir "$PROGRAMFILES\mosquitto"

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2010-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2010-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2010-2013 Roger Light <roger@atchoo.org>
Copyright (c) 2010-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2009-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2009-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2009-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2009-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2009-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2009-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
@ -37,8 +37,32 @@ static unsigned long memcount = 0;
static unsigned long max_memcount = 0;
#endif
#ifdef WITH_BROKER
static size_t mem_limit = 0;
void memory__set_limit(size_t lim)
{
#ifdef LINUX
struct rlimit r;
r.rlim_cur = lim;
r.rlim_max = lim;
setrlimit(RLIMIT_CPU, &r);
mem_limit = 0;
#else
mem_limit = lim;
#endif
}
#endif
void *_mosquitto_calloc(size_t nmemb, size_t size)
{
#ifdef REAL_WITH_MEMORY_TRACKING
if(mem_limit && memcount + size > mem_limit){
return NULL;
}
#endif
void *mem = calloc(nmemb, size);
#ifdef REAL_WITH_MEMORY_TRACKING
@ -64,6 +88,11 @@ void _mosquitto_free(void *mem)
void *_mosquitto_malloc(size_t size)
{
#ifdef REAL_WITH_MEMORY_TRACKING
if(mem_limit && memcount + size > mem_limit){
return NULL;
}
#endif
void *mem = malloc(size);
#ifdef REAL_WITH_MEMORY_TRACKING
@ -90,6 +119,11 @@ unsigned long _mosquitto_max_memory_used(void)
void *_mosquitto_realloc(void *ptr, size_t size)
{
#ifdef REAL_WITH_MEMORY_TRACKING
if(mem_limit && memcount + size > mem_limit){
return NULL;
}
#endif
void *mem;
#ifdef REAL_WITH_MEMORY_TRACKING
if(ptr){
@ -110,6 +144,11 @@ void *_mosquitto_realloc(void *ptr, size_t size)
char *_mosquitto_strdup(const char *s)
{
#ifdef REAL_WITH_MEMORY_TRACKING
if(mem_limit && memcount + strlen(s) > mem_limit){
return NULL;
}
#endif
char *str = strdup(s);
#ifdef REAL_WITH_MEMORY_TRACKING

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2010-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2010-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
@ -34,4 +34,8 @@ unsigned long _mosquitto_max_memory_used(void);
void *_mosquitto_realloc(void *ptr, size_t size);
char *_mosquitto_strdup(const char *s);
#ifdef WITH_BROKER
void memory__set_limit(size_t lim);
#endif
#endif

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2010-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2010-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2010-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2010-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2010-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2010-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
@ -1202,15 +1202,20 @@ int mosquitto_loop_write(struct mosquitto *mosq, int max_packets)
bool mosquitto_want_write(struct mosquitto *mosq)
{
bool result = false;
if(mosq->out_packet || mosq->current_out_packet){
return true;
#ifdef WITH_TLS
}else if(mosq->ssl && mosq->want_write){
return true;
#endif
}else{
return false;
result = true;
}
#ifdef WITH_TLS
if(mosq->ssl){
if (mosq->want_write) {
result = true;
}else if(mosq->want_connect){
result = false;
}
}
#endif
return result;
}
int mosquitto_opts_set(struct mosquitto *mosq, enum mosq_opt_t option, void *value)

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2010-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2010-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
@ -45,7 +45,7 @@ extern "C" {
#define LIBMOSQUITTO_MAJOR 1
#define LIBMOSQUITTO_MINOR 4
#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)

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2010-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2010-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2009-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2009-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2009-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2009-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
@ -214,6 +214,10 @@ int _mosquitto_socket_close(struct mosquitto *mosq)
assert(mosq);
#ifdef WITH_TLS
#ifdef WITH_WEBSOCKETS
if(!mosq->wsi)
#endif
{
if(mosq->ssl){
SSL_shutdown(mosq->ssl);
SSL_free(mosq->ssl);
@ -223,24 +227,26 @@ int _mosquitto_socket_close(struct mosquitto *mosq)
SSL_CTX_free(mosq->ssl_ctx);
mosq->ssl_ctx = NULL;
}
}
#endif
#ifdef WITH_WEBSOCKETS
if(mosq->wsi)
{
if(mosq->state != mosq_cs_disconnecting){
mosq->state = mosq_cs_disconnect_ws;
}
libwebsocket_callback_on_writable(mosq->ws_context, mosq->wsi);
}else
#endif
{
if((int)mosq->sock >= 0){
#ifdef WITH_BROKER
HASH_DELETE(hh_sock, db->contexts_by_sock, mosq);
#endif
rc = COMPAT_CLOSE(mosq->sock);
mosq->sock = INVALID_SOCKET;
#ifdef WITH_WEBSOCKETS
}else if(mosq->sock == WEBSOCKET_CLIENT){
if(mosq->state != mosq_cs_disconnecting){
mosq->state = mosq_cs_disconnect_ws;
}
if(mosq->wsi){
libwebsocket_callback_on_writable(mosq->ws_context, mosq->wsi);
}
mosq->sock = INVALID_SOCKET;
#endif
}
#ifdef WITH_BROKER
@ -323,7 +329,6 @@ int _mosquitto_try_connect_step2(struct mosquitto *mosq, uint16_t port, mosq_soc
/* Set non-blocking */
if(_mosquitto_socket_nonblock(*sock)){
COMPAT_CLOSE(*sock);
continue;
}
@ -338,7 +343,6 @@ int _mosquitto_try_connect_step2(struct mosquitto *mosq, uint16_t port, mosq_soc
/* Set non-blocking */
if(_mosquitto_socket_nonblock(*sock)){
COMPAT_CLOSE(*sock);
continue;
}
break;
@ -423,7 +427,6 @@ int _mosquitto_try_connect(struct mosquitto *mosq, const char *host, uint16_t po
if(!blocking){
/* Set non-blocking */
if(_mosquitto_socket_nonblock(*sock)){
COMPAT_CLOSE(*sock);
continue;
}
}
@ -440,7 +443,6 @@ int _mosquitto_try_connect(struct mosquitto *mosq, const char *host, uint16_t po
if(blocking){
/* Set non-blocking */
if(_mosquitto_socket_nonblock(*sock)){
COMPAT_CLOSE(*sock);
continue;
}
}
@ -869,7 +871,11 @@ int _mosquitto_packet_write(struct mosquitto *mosq)
}
pthread_mutex_unlock(&mosq->out_packet_mutex);
#if defined(WITH_TLS) && !defined(WITH_BROKER)
if((mosq->state == mosq_cs_connect_pending)||mosq->want_connect){
#else
if(mosq->state == mosq_cs_connect_pending){
#endif
pthread_mutex_unlock(&mosq->current_out_packet_mutex);
return MOSQ_ERR_SUCCESS;
}
@ -1084,6 +1090,36 @@ int _mosquitto_packet_read(struct mosquitto *mosq)
* positive. */
mosq->in_packet.remaining_count *= -1;
#ifdef WITH_BROKER
/* Check packet sizes before allocating memory.
* Will need modifying for MQTT v5. */
switch(mosq->in_packet.command & 0xF0){
case CONNECT:
if(mosq->in_packet.remaining_length > 327699){
return MOSQ_ERR_PROTOCOL;
}
break;
case PUBACK:
case PUBREC:
case PUBREL:
case PUBCOMP:
case UNSUBACK:
if(mosq->in_packet.remaining_length != 2){
return MOSQ_ERR_PROTOCOL;
}
break;
case PINGREQ:
case PINGRESP:
case DISCONNECT:
if(mosq->in_packet.remaining_length != 0){
return MOSQ_ERR_PROTOCOL;
}
break;
}
#endif
if(mosq->in_packet.remaining_length > 0){
mosq->in_packet.payload = _mosquitto_malloc(mosq->in_packet.remaining_length*sizeof(uint8_t));
if(!mosq->in_packet.payload) return MOSQ_ERR_NOMEM;
@ -1244,7 +1280,6 @@ int _mosquitto_socketpair(mosq_sock_t *pairR, mosq_sock_t *pairW)
continue;
}
if(_mosquitto_socket_nonblock(spR)){
COMPAT_CLOSE(spR);
COMPAT_CLOSE(listensock);
continue;
}
@ -1272,7 +1307,6 @@ int _mosquitto_socketpair(mosq_sock_t *pairR, mosq_sock_t *pairW)
if(_mosquitto_socket_nonblock(spW)){
COMPAT_CLOSE(spR);
COMPAT_CLOSE(spW);
COMPAT_CLOSE(listensock);
continue;
}
@ -1290,13 +1324,11 @@ int _mosquitto_socketpair(mosq_sock_t *pairR, mosq_sock_t *pairW)
return MOSQ_ERR_ERRNO;
}
if(_mosquitto_socket_nonblock(sv[0])){
COMPAT_CLOSE(sv[0]);
COMPAT_CLOSE(sv[1]);
return MOSQ_ERR_ERRNO;
}
if(_mosquitto_socket_nonblock(sv[1])){
COMPAT_CLOSE(sv[0]);
COMPAT_CLOSE(sv[1]);
return MOSQ_ERR_ERRNO;
}
*pairR = sv[0];

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2010-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2010-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2009-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2009-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2010-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2010-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2009-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2009-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2009-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2009-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2009-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2009-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2009-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2009-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2010-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2010-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2014 Roger Light <roger@atchoo.org>
Copyright (c) 2014-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2014 Roger Light <roger@atchoo.org>
Copyright (c) 2014-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2013,2014 Roger Light <roger@atchoo.org>
Copyright (c) 2013-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2011-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2011-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2013,2014 Roger Light <roger@atchoo.org>
Copyright (c) 2013-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2013,2014 Roger Light <roger@atchoo.org>
Copyright (c) 2013-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2013,2014 Roger Light <roger@atchoo.org>
Copyright (c) 2013-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2013,2014 Roger Light <roger@atchoo.org>
Copyright (c) 2013-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2009-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2009-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
@ -228,13 +228,17 @@ int mosquitto_topic_matches_sub(const char *sub, const char *topic, bool *result
int spos, tpos;
bool multilevel_wildcard = false;
if(!sub || !topic || !result) return MOSQ_ERR_INVAL;
if(!result) return MOSQ_ERR_INVAL;
*result = false;
if(!sub || !topic){
return MOSQ_ERR_INVAL;
}
slen = strlen(sub);
tlen = strlen(topic);
if(!slen || !tlen){
*result = false;
return MOSQ_ERR_INVAL;
}
@ -242,7 +246,6 @@ int mosquitto_topic_matches_sub(const char *sub, const char *topic, bool *result
if((sub[0] == '$' && topic[0] != '$')
|| (topic[0] == '$' && sub[0] != '$')){
*result = false;
return MOSQ_ERR_SUCCESS;
}
}
@ -269,7 +272,6 @@ int mosquitto_topic_matches_sub(const char *sub, const char *topic, bool *result
return MOSQ_ERR_SUCCESS;
}else if(tpos == tlen && spos == slen-1 && sub[spos] == '+'){
if(spos > 0 && sub[spos-1] != '/'){
*result = false;
return MOSQ_ERR_INVAL;
}
spos++;
@ -280,12 +282,10 @@ int mosquitto_topic_matches_sub(const char *sub, const char *topic, bool *result
if(sub[spos] == '+'){
/* Check for bad "+foo" or "a/+foo" subscription */
if(spos > 0 && sub[spos-1] != '/'){
*result = false;
return MOSQ_ERR_INVAL;
}
/* Check for bad "foo+" or "foo+/a" subscription */
if(spos < slen-1 && sub[spos+1] != '/'){
*result = false;
return MOSQ_ERR_INVAL;
}
spos++;
@ -298,19 +298,28 @@ int mosquitto_topic_matches_sub(const char *sub, const char *topic, bool *result
}
}else if(sub[spos] == '#'){
if(spos > 0 && sub[spos-1] != '/'){
*result = false;
return MOSQ_ERR_INVAL;
}
multilevel_wildcard = true;
if(spos+1 != slen){
*result = false;
return MOSQ_ERR_INVAL;
}else{
*result = true;
return MOSQ_ERR_SUCCESS;
}
}else{
*result = false;
/* Check for e.g. foo/bar matching foo/+/# */
if(spos > 0
&& spos+2 == slen
&& tpos == tlen
&& sub[spos-1] == '+'
&& sub[spos] == '/'
&& sub[spos+1] == '#')
{
*result = true;
multilevel_wildcard = true;
return MOSQ_ERR_SUCCESS;
}
return MOSQ_ERR_SUCCESS;
}
}

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2009-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2009-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2010-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2010-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2010-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2010-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0

View File

@ -50,6 +50,7 @@
<itemizedlist mark="circle">
<listitem><para>openssl req -out server.csr -key server.key -new</para></listitem>
</itemizedlist>
<note><para>When prompted for the CN (Common Name), please enter either your server (or broker) hostname or domain name.</para></note>
<para>Send the CSR to the CA, or sign it with your CA key:</para>
<itemizedlist mark="circle">

View File

@ -116,7 +116,7 @@
<para><code>user &lt;username&gt;</code></para>
<para>The username referred to here is the same as in
<option>password_fil</option>e. It is not the
<option>password_file</option>. It is not the
clientid.</para>
<para>It is also possible to define ACLs based on pattern
@ -1031,9 +1031,9 @@
<listitem>
<para>Set the clientid to use on the local broker. If not
defined, this defaults to
<option>local.&lt;clientid&gt;</option>. If you are
<option>local.&lt;remote_clientid&gt;</option>. If you are
bridging a broker to itself, it is important that
local_clientid and clientid do not match.</para>
local_clientid and remote_clientid do not match.</para>
</listitem>
</varlistentry>
<varlistentry>
@ -1059,7 +1059,7 @@
notification messages to the local and remote brokers
giving information about the state of the bridge
connection. Retained messages are published to the
topic $SYS/broker/connection/&lt;clientid&gt;/state
topic $SYS/broker/connection/&lt;remote_clientid&gt;/state
unless otherwise set with
<option>notification_topic</option>s. If the message
is 1 then the connection is active, or 0 if the
@ -1073,7 +1073,7 @@
<para>Choose the topic on which notifications will be
published for this bridge. If not set the messages will
be sent on the topic
$SYS/broker/connection/&lt;clientid&gt;/state.</para>
$SYS/broker/connection/&lt;remote_clientid&gt;/state.</para>
</listitem>
</varlistentry>
<varlistentry>
@ -1295,21 +1295,6 @@ topic clients/total in 0 test/mosquitto/org $SYS/broker/
<para>The following options are available for all bridges to
configure SSL/TLS support.</para>
<variablelist>
<varlistentry>
<term><option>bridge_attempt_unsubscribe</option> [ true | false ]</term>
<listitem>
<para>If a bridge has topics that have "out" direction,
the default behaviour is to send an unsubscribe
request to the remote broker on that topic. This
means that changing a topic direction from "in" to
"out" will not keep receiving incoming messages.
Sending these unsubscribe requests is not always
desirable, setting
<option>bridge_attempt_unsubscribe</option> to
<replaceable>false</replaceable> will disable
sending the unsubscribe request.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>bridge_cafile</option> <replaceable>file path</replaceable></term>
<listitem>

View File

@ -41,7 +41,7 @@
<refsect1>
<title>Description</title>
<para><command>mosquitto_passwd</command> is a tool for managing
password files the mosquitto MQTT broker.</para>
password files for the mosquitto MQTT broker.</para>
<para>Usernames must not contain ":". Passwords are stored in a similar
format to
<citerefentry><refentrytitle>crypt</refentrytitle><manvolnum>3</manvolnum></citerefentry>.</para>

View File

@ -220,7 +220,7 @@
#crlfile
# If you wish to control which encryption ciphers are used, use the ciphers
# option. The list of available ciphers can be optained using the "openssl
# option. The list of available ciphers can be obtained using the "openssl
# ciphers" command and should be provided in the same format as the output of
# that command.
# If unset defaults to DEFAULT:!aNULL:!eNULL:!LOW:!EXPORT:!SSLv2:@STRENGTH
@ -253,7 +253,7 @@
# When using PSK, the encryption ciphers used will be chosen from the list of
# available PSK ciphers. If you want to control which ciphers are available,
# use the "ciphers" option. The list of available ciphers can be optained
# use the "ciphers" option. The list of available ciphers can be obtained
# using the "openssl ciphers" command and should be provided in the same format
# as the output of that command.
#ciphers

View File

@ -69,6 +69,7 @@ already be built. Use `make binary` to skip building the man pages, or install
* libuuid (uuid-dev) - disable with `make WITH_UUID=no`
* libwebsockets (libwebsockets-dev) - enable with `make WITH_WEBSOCKETS=yes`
* openssl (libssl-dev on Debian based systems) - disable with `make WITH_TLS=no`
* xsltproc (xsltproc and docbook-xsl on Debian based systems) - only needed when building from git sources - disable with `make WITH_DOCS=no`
## Credits

View File

@ -2,7 +2,7 @@
MAJOR=1
MINOR=4
REVISION=14
REVISION=15
sed -i "s/^VERSION=.*/VERSION=${MAJOR}.${MINOR}.${REVISION}/" config.mk

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2009-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2009-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2009-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2009-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
@ -64,6 +64,35 @@ static int _conf_parse_int(char **token, const char *name, int *value, char *sav
static int _conf_parse_string(char **token, const char *name, char **value, char *saveptr);
static int _config_read_file(struct mqtt3_config *config, bool reload, const char *file, struct config_recurse *config_tmp, int level, int *lineno);
static char *fgets_extending(char **buf, int *buflen, FILE *stream)
{
char *rc;
char endchar;
int offset = 0;
char *newbuf;
do{
rc = fgets(&((*buf)[offset]), *buflen-offset, stream);
if(feof(stream)){
return rc;
}
endchar = (*buf)[strlen(*buf)-1];
if(endchar == '\n'){
return rc;
}
/* No EOL char found, so extend buffer */
offset = *buflen-1;
*buflen += 1000;
newbuf = realloc(*buf, *buflen);
if(!newbuf){
return NULL;
}
*buf = newbuf;
}while(1);
}
static int _conf_attempt_resolve(const char *host, const char *text, int log, const char *msg)
{
struct addrinfo gai_hints;
@ -100,7 +129,7 @@ static int _conf_attempt_resolve(const char *host, const char *text, int log, co
return MOSQ_ERR_SUCCESS;
}
static void _config_init_reload(struct mqtt3_config *config)
static void _config_init_reload(struct mosquitto_db *db, struct mqtt3_config *config)
{
int i;
/* Set defaults */
@ -136,7 +165,7 @@ static void _config_init_reload(struct mqtt3_config *config)
#else
config->log_facility = LOG_DAEMON;
config->log_dest = MQTT3_LOG_STDERR;
if(config->verbose){
if(db->verbose){
config->log_type = INT_MAX;
}else{
config->log_type = MOSQ_LOG_ERR | MOSQ_LOG_WARNING | MOSQ_LOG_NOTICE | MOSQ_LOG_INFO;
@ -168,11 +197,10 @@ static void _config_init_reload(struct mqtt3_config *config)
}
}
void mqtt3_config_init(struct mqtt3_config *config)
void mqtt3_config_init(struct mosquitto_db *db, struct mqtt3_config *config)
{
memset(config, 0, sizeof(struct mqtt3_config));
_config_init_reload(config);
config->config_file = NULL;
_config_init_reload(db, config);
config->daemon = false;
config->default_listener.host = NULL;
config->default_listener.port = 0;
@ -205,7 +233,6 @@ void mqtt3_config_init(struct mqtt3_config *config)
#endif
config->auth_plugin = NULL;
config->auth_plugin_deny_special_chars = true;
config->verbose = false;
config->message_size_limit = 0;
}
@ -219,7 +246,6 @@ void mqtt3_config_cleanup(struct mqtt3_config *config)
if(config->acl_file) _mosquitto_free(config->acl_file);
if(config->auto_id_prefix) _mosquitto_free(config->auto_id_prefix);
if(config->clientid_prefixes) _mosquitto_free(config->clientid_prefixes);
if(config->config_file) _mosquitto_free(config->config_file);
if(config->password_file) _mosquitto_free(config->password_file);
if(config->persistence_location) _mosquitto_free(config->persistence_location);
if(config->persistence_file) _mosquitto_free(config->persistence_file);
@ -240,10 +266,13 @@ void mqtt3_config_cleanup(struct mqtt3_config *config)
if(config->listeners[i].psk_hint) _mosquitto_free(config->listeners[i].psk_hint);
if(config->listeners[i].crlfile) _mosquitto_free(config->listeners[i].crlfile);
if(config->listeners[i].tls_version) _mosquitto_free(config->listeners[i].tls_version);
if(config->listeners[i].ssl_ctx) SSL_CTX_free(config->listeners[i].ssl_ctx);
#endif
#ifdef WITH_WEBSOCKETS
if(config->listeners[i].http_dir) _mosquitto_free(config->listeners[i].http_dir);
if(!config->listeners[i].ws_context) /* libwebsockets frees its own SSL_CTX */
#endif
{
SSL_CTX_free(config->listeners[i].ssl_ctx);
}
#endif
}
_mosquitto_free(config->listeners);
@ -322,7 +351,7 @@ static void print_usage(void)
printf("\nSee http://mosquitto.org/ for more information.\n\n");
}
int mqtt3_config_parse_args(struct mqtt3_config *config, int argc, char *argv[])
int mqtt3_config_parse_args(struct mosquitto_db *db, struct mqtt3_config *config, int argc, char *argv[])
{
int i;
int port_tmp;
@ -330,13 +359,9 @@ int mqtt3_config_parse_args(struct mqtt3_config *config, int argc, char *argv[])
for(i=1; i<argc; i++){
if(!strcmp(argv[i], "-c") || !strcmp(argv[i], "--config-file")){
if(i<argc-1){
config->config_file = _mosquitto_strdup(argv[i+1]);
if(!config->config_file){
_mosquitto_log_printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory.");
return MOSQ_ERR_NOMEM;
}
db->config_file = argv[i+1];
if(mqtt3_config_read(config, false)){
if(mqtt3_config_read(db, config, false)){
_mosquitto_log_printf(NULL, MOSQ_LOG_ERR, "Error: Unable to open configuration file.");
return MOSQ_ERR_INVAL;
}
@ -368,7 +393,7 @@ int mqtt3_config_parse_args(struct mqtt3_config *config, int argc, char *argv[])
}
i++;
}else if(!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose")){
config->verbose = true;
db->verbose = true;
}else{
fprintf(stderr, "Error: Unknown option '%s'.\n",argv[i]);
print_usage();
@ -443,22 +468,87 @@ int mqtt3_config_parse_args(struct mqtt3_config *config, int argc, char *argv[])
if(!config->user){
config->user = "mosquitto";
}
if(config->verbose){
if(db->verbose){
config->log_type = INT_MAX;
}
return MOSQ_ERR_SUCCESS;
}
int mqtt3_config_read(struct mqtt3_config *config, bool reload)
/* Copy reloaded config into existing config struct */
void config__copy(struct mqtt3_config *src, struct mqtt3_config *dest)
{
_mosquitto_free(dest->acl_file);
dest->acl_file = src->acl_file;
dest->allow_anonymous = src->allow_anonymous;
dest->allow_duplicate_messages = src->allow_duplicate_messages;
dest->allow_zero_length_clientid = src->allow_zero_length_clientid;
_mosquitto_free(dest->auto_id_prefix);
dest->auto_id_prefix = src->auto_id_prefix;
dest->auto_id_prefix_len = src->auto_id_prefix_len;
dest->autosave_interval = src->autosave_interval;
dest->autosave_on_changes = src->autosave_on_changes;
_mosquitto_free(dest->clientid_prefixes);
dest->clientid_prefixes = src->clientid_prefixes;
dest->connection_messages = src->connection_messages;
dest->log_dest = src->log_dest;
dest->log_facility = src->log_facility;
dest->log_type = src->log_type;
dest->log_timestamp = src->log_timestamp;
_mosquitto_free(dest->log_file);
dest->log_file = src->log_file;
dest->message_size_limit = src->message_size_limit;
_mosquitto_free(dest->password_file);
dest->password_file = src->password_file;
dest->persistence = src->persistence;
_mosquitto_free(dest->persistence_location);
dest->persistence_location = src->persistence_location;
_mosquitto_free(dest->persistence_file);
dest->persistence_file = src->persistence_file;
_mosquitto_free(dest->persistence_filepath);
dest->persistence_filepath = src->persistence_filepath;
dest->persistent_client_expiration = src->persistent_client_expiration;
_mosquitto_free(dest->psk_file);
dest->psk_file = src->psk_file;
dest->queue_qos0_messages = src->queue_qos0_messages;
dest->retry_interval = src->retry_interval;
dest->sys_interval = src->sys_interval;
dest->upgrade_outgoing_qos = src->upgrade_outgoing_qos;
#ifdef WITH_WEBSOCKETS
dest->websockets_log_level = src->websockets_log_level;
#endif
}
int mqtt3_config_read(struct mosquitto_db *db, struct mqtt3_config *config, bool reload)
{
int rc = MOSQ_ERR_SUCCESS;
struct config_recurse cr;
int lineno;
int len;
struct mqtt3_config config_reload;
#ifdef WITH_BRIDGE
int i;
#endif
if(reload){
memset(&config_reload, 0, sizeof(struct mqtt3_config));
}
cr.log_dest = MQTT3_LOG_NONE;
cr.log_dest_set = 0;
cr.log_type = MOSQ_LOG_NONE;
@ -466,18 +556,24 @@ int mqtt3_config_read(struct mqtt3_config *config, bool reload)
cr.max_inflight_messages = 20;
cr.max_queued_messages = 100;
if(!config->config_file) return 0;
if(!db->config_file) return 0;
if(reload){
/* Re-initialise appropriate config vars to default for reload. */
_config_init_reload(config);
_config_init_reload(db, &config_reload);
rc = _config_read_file(&config_reload, reload, db->config_file, &cr, 0, &lineno);
}else{
rc = _config_read_file(config, reload, db->config_file, &cr, 0, &lineno);
}
rc = _config_read_file(config, reload, config->config_file, &cr, 0, &lineno);
if(rc){
_mosquitto_log_printf(NULL, MOSQ_LOG_ERR, "Error found at %s:%d.", config->config_file, lineno);
_mosquitto_log_printf(NULL, MOSQ_LOG_ERR, "Error found at %s:%d.", db->config_file, lineno);
return rc;
}
if(reload){
config__copy(&config_reload, config);
}
#ifdef WITH_PERSISTENCE
if(config->persistence){
if(!config->persistence_file){
@ -529,7 +625,7 @@ int mqtt3_config_read(struct mqtt3_config *config, bool reload)
if(cr.log_dest_set){
config->log_dest = cr.log_dest;
}
if(config->verbose){
if(db->verbose){
config->log_type = INT_MAX;
}else if(cr.log_type_set){
config->log_type = cr.log_type;
@ -537,10 +633,9 @@ int mqtt3_config_read(struct mqtt3_config *config, bool reload)
return MOSQ_ERR_SUCCESS;
}
int _config_read_file_core(struct mqtt3_config *config, bool reload, const char *file, struct config_recurse *cr, int level, int *lineno, FILE *fptr)
int _config_read_file_core(struct mqtt3_config *config, bool reload, const char *file, struct config_recurse *cr, int level, int *lineno, FILE *fptr, char **buf, int *buflen)
{
int rc;
char buf[1024];
char *token;
int tmp_int;
char *saveptr = NULL;
@ -569,13 +664,13 @@ int _config_read_file_core(struct mqtt3_config *config, bool reload, const char
*lineno = 0;
while(fgets(buf, 1024, fptr)){
while(fgets_extending(buf, buflen, fptr)){
(*lineno)++;
if(buf[0] != '#' && buf[0] != 10 && buf[0] != 13){
while(buf[strlen(buf)-1] == 10 || buf[strlen(buf)-1] == 13){
buf[strlen(buf)-1] = 0;
if((*buf)[0] != '#' && (*buf)[0] != 10 && (*buf)[0] != 13){
while((*buf)[strlen((*buf))-1] == 10 || (*buf)[strlen((*buf))-1] == 13){
(*buf)[strlen((*buf))-1] = 0;
}
token = strtok_r(buf, " ", &saveptr);
token = strtok_r((*buf), " ", &saveptr);
if(token){
if(!strcmp(token, "acl_file")){
if(reload){
@ -1013,7 +1108,7 @@ int _config_read_file_core(struct mqtt3_config *config, bool reload, const char
snprintf(conf_file, len, "%s\\%s", token, find_data.cFileName);
conf_file[len] = '\0';
rc = _config_read_file(config, reload, conf_file, cr, level+1, &lineno_ext);
rc = _config_read_file(config, reload, conf_file, cr, level+1, &lineno_ext, buf, buflen);
if(rc){
FindClose(fh);
_mosquitto_log_printf(NULL, MOSQ_LOG_ERR, "Error found at %s:%d.", conf_file, lineno_ext);
@ -1286,6 +1381,14 @@ int _config_read_file_core(struct mqtt3_config *config, bool reload, const char
}else{
_mosquitto_log_printf(NULL, MOSQ_LOG_ERR, "Error: Empty max_queued_messages value in configuration.");
}
}else if(!strcmp(token, "memory_limit")){
size_t lim;
if(_conf_parse_int(&token, "memory_limit", (int *)&lim, saveptr)) return MOSQ_ERR_INVAL;
if(lim < 0){
_mosquitto_log_printf(NULL, MOSQ_LOG_ERR, "Error: Invalid memory_limit value (%lu).", lim);
return MOSQ_ERR_INVAL;
}
memory__set_limit(lim);
}else if(!strcmp(token, "message_size_limit")){
if(_conf_parse_int(&token, "message_size_limit", (int *)&config->message_size_limit, saveptr)) return MOSQ_ERR_INVAL;
if(config->message_size_limit > MQTT_MAX_PAYLOAD){
@ -1765,6 +1868,8 @@ int _config_read_file(struct mqtt3_config *config, bool reload, const char *file
{
int rc;
FILE *fptr = NULL;
char *buf;
int buflen;
fptr = _mosquitto_fopen(file, "rt", false);
if(!fptr){
@ -1772,7 +1877,15 @@ int _config_read_file(struct mqtt3_config *config, bool reload, const char *file
return 1;
}
rc = _config_read_file_core(config, reload, file, cr, level, lineno, fptr);
buflen = 1000;
buf = _mosquitto_malloc(buflen);
if(!buf){
_mosquitto_log_printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory.");
return MOSQ_ERR_NOMEM;
}
rc = _config_read_file_core(config, reload, file, cr, level, lineno, fptr, &buf, &buflen);
_mosquitto_free(buf);
fclose(fptr);
return rc;

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2009-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2009-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2009-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2009-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2012-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2012-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2009-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2009-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2009-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2009-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
@ -390,7 +390,7 @@ int mosquitto_main_loop(struct mosquitto_db *db, mosq_sock_t *listensock, int li
#endif
if(flag_reload){
_mosquitto_log_printf(NULL, MOSQ_LOG_INFO, "Reloading config.");
mqtt3_config_read(db->config, true);
mqtt3_config_read(db, db->config, true);
mosquitto_security_cleanup(db, true);
mosquitto_security_init(db, true);
mosquitto_security_apply(db);

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2009-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2009-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
@ -259,8 +259,8 @@ int main(int argc, char *argv[])
_mosquitto_net_init();
mqtt3_config_init(&config);
rc = mqtt3_config_parse_args(&config, argc, argv);
mqtt3_config_init(&int_db, &config);
rc = mqtt3_config_parse_args(&int_db, &config, argc, argv);
if(rc != MOSQ_ERR_SUCCESS) return rc;
int_db.config = &config;
@ -292,8 +292,8 @@ int main(int argc, char *argv[])
return rc;
}
_mosquitto_log_printf(NULL, MOSQ_LOG_INFO, "mosquitto version %s (build date %s) starting", VERSION, TIMESTAMP);
if(config.config_file){
_mosquitto_log_printf(NULL, MOSQ_LOG_INFO, "Config loaded from %s.", config.config_file);
if(int_db.config_file){
_mosquitto_log_printf(NULL, MOSQ_LOG_INFO, "Config loaded from %s.", int_db.config_file);
}else{
_mosquitto_log_printf(NULL, MOSQ_LOG_INFO, "Using default config.");
}

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2009-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2009-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
@ -101,7 +101,6 @@ struct _mqtt3_listener {
};
struct mqtt3_config {
char *config_file;
char *acl_file;
bool allow_anonymous;
bool allow_duplicate_messages;
@ -137,7 +136,6 @@ struct mqtt3_config {
int sys_interval;
bool upgrade_outgoing_qos;
char *user;
bool verbose;
#ifdef WITH_WEBSOCKETS
int websockets_log_level;
bool have_websockets_listener;
@ -260,9 +258,11 @@ struct mosquitto_db{
int bridge_count;
#endif
int msg_store_count;
char *config_file;
struct mqtt3_config *config;
int persistence_changes;
struct _mosquitto_auth_plugin auth_plugin;
bool verbose;
#ifdef WITH_SYS_TREE
int subscription_count;
int retained_count;
@ -365,14 +365,14 @@ struct mosquitto_db *_mosquitto_get_db(void);
* Config functions
* ============================================================ */
/* Initialise config struct to default values. */
void mqtt3_config_init(struct mqtt3_config *config);
void mqtt3_config_init(struct mosquitto_db *db, struct mqtt3_config *config);
/* Parse command line options into config. */
int mqtt3_config_parse_args(struct mqtt3_config *config, int argc, char *argv[]);
int mqtt3_config_parse_args(struct mosquitto_db *db, struct mqtt3_config *config, int argc, char *argv[]);
/* Read configuration data from config->config_file into config.
* If reload is true, don't process config options that shouldn't be reloaded (listeners etc)
* Returns 0 on success, 1 if there is a configuration error or if a file cannot be opened.
*/
int mqtt3_config_read(struct mqtt3_config *config, bool reload);
int mqtt3_config_read(struct mosquitto_db *db, struct mqtt3_config *config, bool reload);
/* Free all config data. */
void mqtt3_config_cleanup(struct mqtt3_config *config);

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2012-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2012-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2012-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2012-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2009-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2009-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2010-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2010-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2010-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2010-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2009-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2009-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2009-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2009-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2009-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2009-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
@ -336,6 +336,12 @@ int mqtt3_handle_connect(struct mosquitto_db *db, struct mosquitto *context)
#ifdef WITH_TLS
if(context->listener && context->listener->ssl_ctx && context->listener->use_identity_as_username){
/* Don't need the username or password if provided */
_mosquitto_free(username);
username = NULL;
_mosquitto_free(password);
password = NULL;
if(!context->ssl){
_mosquitto_send_connack(context, 0, CONNACK_REFUSED_BAD_USERNAME_PASSWORD);
rc = 1;

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2011-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2011-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2011-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2011-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2009-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2009-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2011-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2011-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2010-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2010-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
@ -681,9 +681,12 @@ static int _retain_process(struct mosquitto_db *db, struct mosquitto_msg_store *
return rc;
}
if (db->config->upgrade_outgoing_qos){
qos = sub_qos;
} else {
qos = retained->qos;
if(qos > sub_qos) qos = sub_qos;
}
if(qos > 0){
mid = _mosquitto_mid_generate(context);
}else{

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2009-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2009-2018 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2014 Roger Light <roger@atchoo.org>
Copyright (c) 2014-2018 Roger Light <roger@atchoo.org>
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -201,6 +201,12 @@ static int callback_mqtt(struct libwebsocket_context *context,
mosq->ws_context = context;
#endif
mosq->wsi = wsi;
if(in){
mosq->ssl = (SSL *)in;
if(!mosq->listener->ssl_ctx){
mosq->listener->ssl_ctx = SSL_get_SSL_CTX(mosq->ssl);
}
}
u->mosq = mosq;
}else{
return -1;
@ -234,6 +240,7 @@ static int callback_mqtt(struct libwebsocket_context *context,
mosq->pollfd_index = -1;
}
mosq->wsi = NULL;
mosq->ssl = NULL;
do_disconnect(db, mosq);
}
break;
@ -243,7 +250,7 @@ static int callback_mqtt(struct libwebsocket_context *context,
return -1;
}
mosq = u->mosq;
if(!mosq || mosq->state == mosq_cs_disconnect_ws || mosq->state == mosq_cs_disconnecting){
if(!mosq){
return -1;
}
@ -276,14 +283,30 @@ static int callback_mqtt(struct libwebsocket_context *context,
count = packet->to_process;
#endif
if(count < 0){
if (mosq->state == mosq_cs_disconnect_ws || mosq->state == mosq_cs_disconnecting){
return -1;
}
return 0;
}
#ifdef WITH_SYS_TREE
g_bytes_sent += count;
#endif
packet->to_process -= count;
packet->pos += count;
if(packet->to_process > 0){
if (mosq->state == mosq_cs_disconnect_ws || mosq->state == mosq_cs_disconnecting){
return -1;
}
break;
}
#ifdef WITH_SYS_TREE
g_msgs_sent++;
if(((packet->command)&0xF6) == PUBLISH){
g_pub_msgs_sent++;
}
#endif
/* Free data and reset values */
mosq->current_out_packet = mosq->out_packet;
if(mosq->out_packet){
@ -298,6 +321,9 @@ static int callback_mqtt(struct libwebsocket_context *context,
mosq->next_msg_out = mosquitto_time() + mosq->keepalive;
}
if (mosq->state == mosq_cs_disconnect_ws || mosq->state == mosq_cs_disconnecting){
return -1;
}
if(mosq->current_out_packet){
libwebsocket_callback_on_writable(mosq->ws_context, mosq->wsi);
}
@ -379,7 +405,12 @@ static int callback_mqtt(struct libwebsocket_context *context,
mosq->last_msg_in = mosquitto_time();
if(rc){
if(rc && (mosq->out_packet || mosq->current_out_packet)) {
if(mosq->state != mosq_cs_disconnecting){
mosq->state = mosq_cs_disconnect_ws;
}
libwebsocket_callback_on_writable(mosq->ws_context, mosq->wsi);
} else if (rc) {
do_disconnect(db, mosq);
return -1;
}

View File

@ -0,0 +1,2 @@
port 1888
upgrade_outgoing_qos true

View File

@ -0,0 +1,48 @@
#!/usr/bin/env python
# Test whether a retained PUBLISH to a topic with QoS 0 is sent with subscriber QoS
# when upgrade_outgoing_qos is true
import inspect, os, sys
# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder
cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],"..")))
if cmd_subfolder not in sys.path:
sys.path.insert(0, cmd_subfolder)
import mosq_test
rc = 1
keepalive = 60
mid = 16
connect_packet = mosq_test.gen_connect("retain-qos0-test", keepalive=keepalive)
connack_packet = mosq_test.gen_connack(rc=0)
publish_packet = mosq_test.gen_publish("retain/qos0/test", qos=0, payload="retained message", retain=True)
subscribe_packet = mosq_test.gen_subscribe(mid, "retain/qos0/test", 1)
suback_packet = mosq_test.gen_suback(mid, 1)
publish_packet2 = mosq_test.gen_publish("retain/qos0/test", mid=1, qos=1, payload="retained message", retain=True)
broker = mosq_test.start_broker(filename=os.path.basename(__file__))
try:
sock = mosq_test.do_client_connect(connect_packet, connack_packet)
sock.send(publish_packet)
#sock.close()
#sock = mosq_test.do_client_connect(connect_packet, connack_packet)
sock.send(subscribe_packet)
if mosq_test.expect_packet(sock, "suback", suback_packet):
if mosq_test.expect_packet(sock, "publish", publish_packet2):
rc = 0
sock.close()
finally:
broker.terminate()
broker.wait()
if rc:
(stdo, stde) = broker.communicate()
print(stde)
exit(rc)

View File

@ -0,0 +1,14 @@
port 1889
retry_interval 10
connection bridge_sample
address 127.0.0.1:1888
bridge_attempt_unsubscribe false
topic # in 0 local/topic/ remote/topic/
topic prefix/# in 0 local2/topic/ remote2/topic/
topic +/value in 0 local3/topic/ remote3/topic/
topic ic/+ in 0 local4/top remote4/tip
topic clients/total in 0 test/mosquitto/org $SYS/broker/
notifications false
restart_timeout 5

View File

@ -0,0 +1,117 @@
#!/usr/bin/env python
# Test remapping of topic name for incoming message
import socket
import inspect, os, sys
# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder
cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],"..")))
if cmd_subfolder not in sys.path:
sys.path.insert(0, cmd_subfolder)
import mosq_test
rc = 1
keepalive = 60
client_id = socket.gethostname()+".bridge_sample"
connect_packet = mosq_test.gen_connect(client_id, keepalive=keepalive, clean_session=False, proto_ver=128+3)
connack_packet = mosq_test.gen_connack(rc=0)
client_connect_packet = mosq_test.gen_connect("pub-test", keepalive=keepalive)
client_connack_packet = mosq_test.gen_connack(rc=0)
ssock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ssock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
ssock.settimeout(4)
ssock.bind(('', 1888))
ssock.listen(5)
broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=1889)
def test(bridge, sock):
if not mosq_test.expect_packet(bridge, "connect", connect_packet):
return 1
bridge.send(connack_packet)
mid = 0
patterns = [
"remote/topic/#",
"remote2/topic/prefix/#",
"remote3/topic/+/value",
"remote4/tipic/+",
"$SYS/broker/clients/total",
]
for pattern in ("remote/topic/#", "remote2/topic/prefix/#", "remote3/topic/+/value"):
mid += 1
subscribe_packet = mosq_test.gen_subscribe(mid, pattern, 0)
suback_packet = mosq_test.gen_suback(mid, 0)
if not mosq_test.expect_packet(bridge, "subscribe", subscribe_packet):
return 1
bridge.send(suback_packet)
mid += 1
subscribe_packet = mosq_test.gen_subscribe(mid, "#", 0)
suback_packet = mosq_test.gen_suback(mid, 0)
sock.send(subscribe_packet)
if not mosq_test.expect_packet(sock, "suback", suback_packet):
return 1
cases = [
('local/topic/something', 'remote/topic/something'),
('local/topic/some/t/h/i/n/g', 'remote/topic/some/t/h/i/n/g'),
('local/topic/value', 'remote/topic/value'),
# Don't work, #40 must be fixed before
# ('local/topic', 'remote/topic'),
('local2/topic/prefix/something', 'remote2/topic/prefix/something'),
('local3/topic/something/value', 'remote3/topic/something/value'),
('local4/topic/something', 'remote4/tipic/something'),
('test/mosquitto/orgclients/total', '$SYS/broker/clients/total'),
]
for (local_topic, remote_topic) in cases:
mid += 1
remote_publish_packet = mosq_test.gen_publish(
remote_topic, qos=0, mid=mid, payload=''
)
local_publish_packet = mosq_test.gen_publish(
local_topic, qos=0, mid=mid, payload=''
)
bridge.send(remote_publish_packet)
match = mosq_test.expect_packet(sock, "publish", local_publish_packet)
if not match:
print("Fail on cases local_topic=%r, remote_topic=%r" % (
local_topic, remote_topic,
))
return 1
return 0
try:
(bridge, address) = ssock.accept()
bridge.settimeout(2)
sock = mosq_test.do_client_connect(
client_connect_packet, client_connack_packet,
port=1889,
)
rc = test(bridge, sock)
sock.close()
bridge.close()
finally:
try:
bridge.close()
except NameError:
pass
broker.terminate()
broker.wait()
if rc:
(stdo, stde) = broker.communicate()
print(stde)
ssock.close()
exit(rc)

View File

@ -0,0 +1,15 @@
port 1889
retry_interval 10
connection bridge_sample
address 127.0.0.1:1888
bridge_attempt_unsubscribe false
topic # out 0 local/topic/ remote/topic/
topic prefix/# out 0 local2/topic/ remote2/topic/
topic +/value out 0 local3/topic/ remote3/topic/
topic ic/+ out 0 local4/top remote4/tip
# this one is invalid
topic +/value out 0 local5/top remote5/tip
notifications false
restart_timeout 5

View File

@ -0,0 +1,109 @@
#!/usr/bin/env python
# Test remapping of topic name for outgoing message
import socket
import inspect, os, sys
# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder
cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],"..")))
if cmd_subfolder not in sys.path:
sys.path.insert(0, cmd_subfolder)
import mosq_test
rc = 1
keepalive = 60
client_id = socket.gethostname()+".bridge_sample"
connect_packet = mosq_test.gen_connect(client_id, keepalive=keepalive, clean_session=False, proto_ver=128+3)
connack_packet = mosq_test.gen_connack(rc=0)
client_connect_packet = mosq_test.gen_connect("pub-test", keepalive=keepalive)
client_connack_packet = mosq_test.gen_connack(rc=0)
ssock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ssock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
ssock.settimeout(4)
ssock.bind(('', 1888))
ssock.listen(5)
broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=1889)
def test(bridge, sock):
if not mosq_test.expect_packet(bridge, "connect", connect_packet):
return 1
bridge.send(connack_packet)
cases = [
('local/topic/something', 'remote/topic/something'),
('local/topic/some/t/h/i/n/g', 'remote/topic/some/t/h/i/n/g'),
('local/topic/value', 'remote/topic/value'),
# Don't work, #40 must be fixed before
# ('local/topic', 'remote/topic'),
('local2/topic/something', None), # don't match topic pattern
('local2/topic/prefix/something', 'remote2/topic/prefix/something'),
('local3/topic/something/value', 'remote3/topic/something/value'),
('local4/topic/something', 'remote4/tipic/something'),
('local5/topic/something', None),
]
mid = 3
for (local_topic, remote_topic) in cases:
mid += 1
local_publish_packet = mosq_test.gen_publish(
local_topic, qos=0, mid=mid, payload=''
)
sock.send(local_publish_packet)
if remote_topic:
remote_publish_packet = mosq_test.gen_publish(
remote_topic, qos=0, mid=mid, payload=''
)
match = mosq_test.expect_packet(bridge, "publish", remote_publish_packet)
if not match:
print("Fail on cases local_topic=%r, remote_topic=%r" % (
local_topic, remote_topic,
))
return 1
else:
bridge.settimeout(3)
try:
bridge.recv(1)
print("FAIL: Received data when nothing is expected")
print("Fail on cases local_topic=%r, remote_topic=%r" % (
local_topic, remote_topic,
))
return 1
except socket.timeout:
pass
bridge.settimeout(20)
return 0
try:
(bridge, address) = ssock.accept()
bridge.settimeout(2)
sock = mosq_test.do_client_connect(
client_connect_packet, client_connack_packet,
port=1889,
)
rc = test(bridge, sock)
sock.close()
bridge.close()
finally:
try:
bridge.close()
except NameError:
pass
broker.terminate()
broker.wait()
if rc:
(stdo, stde) = broker.communicate()
print(stde)
ssock.close()
exit(rc)

View File

@ -1,9 +1,10 @@
port 1889
connection bridge_test
address localhost:1888
address 127.0.0.1:1888
topic bridge/# both 0
notifications false
restart_timeout 2
#bridge_cafile ../ssl/test-root-ca.crt
bridge_cafile ../ssl/all-ca.crt

View File

@ -59,6 +59,7 @@ endif
./04-retain-qos0-repeated.py
./04-retain-qos1-qos0.py
./04-retain-qos0-clear.py
./04-retain-upgrade-outgoing-qos.py
05 :
./05-clean-session-qos1.py
@ -71,6 +72,8 @@ endif
./06-bridge-b2br-disconnect-qos2.py
./06-bridge-fail-persist-resend-qos1.py
./06-bridge-fail-persist-resend-qos2.py
./06-bridge-b2br-remapping.py
./06-bridge-br2b-remapping.py
07 :
./07-will-qos0.py
@ -89,9 +92,11 @@ ifeq ($(WITH_TLS),yes)
./08-ssl-connect-identity.py
./08-ssl-connect-no-identity.py
./08-ssl-bridge.py
ifeq ($(WITH_TLS_PSK),yes)
./08-tls-psk-pub.py
./08-tls-psk-bridge.py
endif
endif
09 :
./09-plugin-auth-unpwd-success.py

View File

@ -2,6 +2,9 @@
#include <stdlib.h>
#include <mosquitto.h>
#define EXPECT_MATCH(A, B) do_check((A), (B), false)
#define EXPECT_NOMATCH(A, B) do_check((A), (B), true)
void do_check(const char *sub, const char *topic, bool bad_res)
{
bool match;
@ -16,42 +19,44 @@ void do_check(const char *sub, const char *topic, bool bad_res)
int main(int argc, char *argv[])
{
do_check("foo/#", "foo/", false);
do_check("foo#", "foo", true);
do_check("fo#o/", "foo", true);
do_check("foo#", "fooa", true);
do_check("foo+", "foo", true);
do_check("foo+", "fooa", true);
EXPECT_MATCH("foo/#", "foo/");
EXPECT_NOMATCH("foo#", "foo");
EXPECT_NOMATCH("fo#o/", "foo");
EXPECT_NOMATCH("foo#", "fooa");
EXPECT_NOMATCH("foo+", "foo");
EXPECT_NOMATCH("foo+", "fooa");
do_check("test/6/#", "test/3", true);
do_check("foo/bar", "foo/bar", false);
do_check("foo/+", "foo/bar", false);
do_check("foo/+/baz", "foo/bar/baz", false);
EXPECT_NOMATCH("test/6/#", "test/3");
EXPECT_MATCH("foo/bar", "foo/bar");
EXPECT_MATCH("foo/+", "foo/bar");
EXPECT_MATCH("foo/+/baz", "foo/bar/baz");
do_check("A/B/+/#", "A/B/B/C", false);
EXPECT_MATCH("A/B/+/#", "A/B/B/C");
do_check("foo/+/#", "foo/bar/baz", false);
do_check("#", "foo/bar/baz", false);
EXPECT_MATCH("foo/+/#", "foo/bar/baz");
EXPECT_MATCH("foo/+/#", "foo/bar");
EXPECT_MATCH("#", "foo/bar/baz");
EXPECT_MATCH("#", "foo/bar/baz");
do_check("foo/bar", "foo", true);
do_check("foo/+", "foo/bar/baz", true);
do_check("foo/+/baz", "foo/bar/bar", true);
EXPECT_NOMATCH("foo/bar", "foo");
EXPECT_NOMATCH("foo/+", "foo/bar/baz");
EXPECT_NOMATCH("foo/+/baz", "foo/bar/bar");
do_check("foo/+/#", "fo2/bar/baz", true);
EXPECT_NOMATCH("foo/+/#", "fo2/bar/baz");
do_check("#", "/foo/bar", false);
do_check("/#", "/foo/bar", false);
do_check("/#", "foo/bar", true);
EXPECT_MATCH("#", "/foo/bar");
EXPECT_MATCH("/#", "/foo/bar");
EXPECT_NOMATCH("/#", "foo/bar");
do_check("foo//bar", "foo//bar", false);
do_check("foo//+", "foo//bar", false);
do_check("foo/+/+/baz", "foo///baz", false);
do_check("foo/bar/+", "foo/bar/", false);
EXPECT_MATCH("foo//bar", "foo//bar");
EXPECT_MATCH("foo//+", "foo//bar");
EXPECT_MATCH("foo/+/+/baz", "foo///baz");
EXPECT_MATCH("foo/bar/+", "foo/bar/");
do_check("$SYS/bar", "$SYS/bar", false);
do_check("#", "$SYS/bar", true);
do_check("$BOB/bar", "$SYS/bar", true);
EXPECT_MATCH("$SYS/bar", "$SYS/bar");
EXPECT_NOMATCH("#", "$SYS/bar");
EXPECT_NOMATCH("$BOB/bar", "$SYS/bar");
return 0;
}