mosquitto-2.0.18/SECURITY.md 0000664 0000000 0000000 00000000330 14502137606 0015437 0 ustar 00root root 0000000 0000000 # Security Policy
## Reporting a Vulnerability
If you think you have found a security vulnerability in Mosquitto, please follow the steps on [Eclipse Security](https://www.eclipse.org/security/) page to report it.
mosquitto-2.0.18/THANKS.txt 0000664 0000000 0000000 00000002725 14502137606 0015411 0 ustar 00root root 0000000 0000000 These people have reported bugs / provided patches / done something else to aid
the mosquitto project. Thanks to you all!
If you think I've missed you off the list, please rest assured that it wasn't
intentional and get in touch and I'll fix it.
Adam Rudd
Alexandre Zia
Andrew Elwell
Andy Piper
Andy Stanford-Clark
Anton Prokofiev
Axel Busch
Bart Van Der Meerssche
BD Walker
Ben Tobin
Bob Blackrock
Brad Stancel
Brett Francis
Chris Willing
Christian Sampaio
Christoph Krey
Craig Hollabaugh
Cristian Manuel Vertiz Fernandez
Dan Anderson
Daniel Deadwyler
Daniel O'Conner
Dariusz Suchojad
Darren Oliver
David Huang
David Monro
Dirk O. Kaar
Dominik Obermaier
Dominik Zajac
Ed Morris
Fabian Ruff
Fang Chong
Frank Hansen
Gary Koh
Gianfranco Costamagna
Guido Hinderberger
Hiram van Paassen
Jan-Piet Mens
Joan Zapata
Joe McIlvain
Karl Palsson
Larry Lendo
Martin Assarsson
Martin Rauscher
Martin van Wingerden
Marty Lee
Matt Daubney
Michael C
Michael Frisch
Michael Hekel
Michael Laing
Michael Rushton
Mike Bush
Milan Tucic
Neil Bothwick
Nicholas Humfrey
Nicholas O'Leary
Nithin Kumar
Patrick Geary
Paul Diston
Peter Magnusson
Pryce Jones
Peter George
Richard Eagland
Rob Pridham
Robin Gingras
Roland de Boo
Sebastian Kroll
Sergio Rubio
Sharon Ben-Asher
sskaje
Stefan Hudelmaier
Stefano Costa
Stephen Owens
Stephen Woods
Steven Lougheed
Surendra Reddy
Szymon Kochanski
Tammo Besemann
Thomas Hilbig
Tobias Assarsson
Toby Jaffey
Tomas Novotny
Vicente Ruiz
Wayne Ingram
Yun Wu
Yuvraaj Kelkar
賴 冠廷
mosquitto-2.0.18/about.html 0000664 0000000 0000000 00000004700 14502137606 0015653 0 ustar 00root root 0000000 0000000
About
About This Content
May 8, 2014
License
The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise
indicated below, the Content is provided to you under the terms and conditions of the
Eclipse Public License Version 2.0 ("EPL") and Eclipse Distribution License Version 1.0 ("EDL").
A copy of the EPL is available at https://www.eclipse.org/legal/epl-2.0/
and a copy of the EDL is available at
http://www.eclipse.org/org/documents/edl-v10.php.
For purposes of the EPL, "Program" will mean the Content.
If you did not receive this Content directly from the Eclipse Foundation, the Content is
being redistributed by another party ("Redistributor") and different terms and conditions may
apply to your use of any object code in the Content. Check the Redistributor's license that was
provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise
indicated below, the terms and conditions of the EPL still apply to any source code in the Content
and such source code may be obtained at http://www.eclipse.org.
Third Party Content
The Content includes items that have been sourced from third parties as set out below. If you
did not receive this Content directly from the Eclipse Foundation, the following is provided
for informational purposes only, and you should look to the Redistributor's license for
terms and conditions of use.
libwebsockets 2.4.2
This project makes use of the libwebsockets library.
The use of libwebsockets is based on the terms and conditions of the
LGPL 2.1 with some specific exceptions.
https://github.com/warmcat/libwebsockets/blob/v2.4.2/LICENSE
When libwebsockets is distributed with the project, it is being used
subject to the Static Linking Exception (Section 2) of the License. As
a result, the content is not subject to the LGPL 2.1.
mosquitto-2.0.18/aclfile.example 0000664 0000000 0000000 00000000346 14502137606 0016631 0 ustar 00root root 0000000 0000000 # This affects access control for clients with no username.
topic read $SYS/#
# This only affects clients with username "roger".
user roger
topic foo/bar
# This affects all clients.
pattern write $SYS/broker/connection/%c/state
mosquitto-2.0.18/apps/ 0000775 0000000 0000000 00000000000 14502137606 0014615 5 ustar 00root root 0000000 0000000 mosquitto-2.0.18/apps/CMakeLists.txt 0000664 0000000 0000000 00000000104 14502137606 0017350 0 ustar 00root root 0000000 0000000 add_subdirectory(mosquitto_ctrl)
add_subdirectory(mosquitto_passwd)
mosquitto-2.0.18/apps/Makefile 0000664 0000000 0000000 00000001131 14502137606 0016251 0 ustar 00root root 0000000 0000000 DIRS= \
db_dump \
mosquitto_ctrl \
mosquitto_passwd
.PHONY : all binary check clean reallyclean test install uninstall
all :
set -e; for d in ${DIRS}; do $(MAKE) -C $${d}; done
binary :
set -e; for d in ${DIRS}; do $(MAKE) -C $${d} $@; done
clean :
set -e; for d in ${DIRS}; do $(MAKE) -C $${d} $@; done
reallyclean :
set -e; for d in ${DIRS}; do $(MAKE) -C $${d} $@; done
check : test
test :
set -e; for d in ${DIRS}; do $(MAKE) -C $${d} $@; done
install :
set -e; for d in ${DIRS}; do $(MAKE) -C $${d} $@; done
uninstall :
set -e; for d in ${DIRS}; do $(MAKE) -C $${d} $@; done
mosquitto-2.0.18/apps/db_dump/ 0000775 0000000 0000000 00000000000 14502137606 0016227 5 ustar 00root root 0000000 0000000 mosquitto-2.0.18/apps/db_dump/Makefile 0000664 0000000 0000000 00000005001 14502137606 0017663 0 ustar 00root root 0000000 0000000 include ../../config.mk
CFLAGS_FINAL=${CFLAGS} -I../../include -I../../ -I../../lib -I../../src -I../../deps -DWITH_BROKER -DWITH_PERSISTENCE
OBJS = \
db_dump.o \
print.o \
\
memory_mosq.o \
memory_public.o \
packet_datatypes.o \
packet_mosq.o \
persist_read.o \
persist_read_v234.o \
persist_read_v5.o \
property_mosq.o \
send_disconnect.o \
stubs.o \
time_mosq.o \
topic_tok.o \
utf8_mosq.o
.PHONY: all clean reallyclean
all : mosquitto_db_dump
mosquitto_db_dump : ${OBJS}
${CROSS_COMPILE}${CC} $^ -o $@ ${LDFLAGS} ${LIBS}
db_dump.o : db_dump.c db_dump.h ../../src/persist.h
${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@
print.o : print.c db_dump.h ../../src/persist.h
${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@
memory_mosq.o : ../../lib/memory_mosq.c ../../lib/memory_mosq.h
${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@
memory_public.o : ../../src/memory_public.c
${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@
net_mosq.o : ../../lib/net_mosq.c ../../lib/net_mosq.h
${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@
packet_datatypes.o : ../../lib/packet_datatypes.c ../../lib/packet_mosq.h
${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@
packet_mosq.o : ../../lib/packet_mosq.c ../../lib/packet_mosq.h
${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@
persist_read.o : ../../src/persist_read.c ../../src/persist.h ../../src/mosquitto_broker_internal.h
${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@
persist_read_v234.o : ../../src/persist_read_v234.c ../../src/persist.h ../../src/mosquitto_broker_internal.h
${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@
persist_read_v5.o : ../../src/persist_read_v5.c ../../src/persist.h ../../src/mosquitto_broker_internal.h
${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@
property_mosq.o : ../../lib/property_mosq.c ../../lib/property_mosq.h
${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@
read_handle.o : ../../src/read_handle.c
${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@
stubs.o : stubs.c
${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@
send_disconnect.o : ../../lib/send_disconnect.c
${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@
time_mosq.o : ../../lib/time_mosq.c
${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@
topic_tok.o : ../../src/topic_tok.c
${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@
utf8_mosq.o : ../../lib/utf8_mosq.c
${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@
reallyclean: clean
clean :
-rm -f *.o mosquitto_db_dump
install:
uninstall:
mosquitto-2.0.18/apps/db_dump/db_dump.c 0000664 0000000 0000000 00000026756 14502137606 0020025 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 2010-2019 Roger Light
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License 2.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
https://www.eclipse.org/legal/epl-2.0/
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
Contributors:
Roger Light - initial implementation and documentation.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "db_dump.h"
#include
#include
#include
#define mosquitto__malloc(A) malloc((A))
#define mosquitto__free(A) free((A))
#define _mosquitto_malloc(A) malloc((A))
#define _mosquitto_free(A) free((A))
#include
#include "db_dump.h"
struct client_data
{
UT_hash_handle hh_id;
char *id;
uint32_t subscriptions;
uint32_t subscription_size;
int messages;
long message_size;
};
struct msg_store_chunk
{
UT_hash_handle hh;
dbid_t store_id;
uint32_t length;
};
struct mosquitto_db db;
extern uint32_t db_version;
static int stats = 0;
static int client_stats = 0;
static int do_print = 1;
/* Counts */
static long cfg_count = 0;
static long client_count = 0;
static long client_msg_count = 0;
static long msg_store_count = 0;
static long retain_count = 0;
static long sub_count = 0;
/* ====== */
struct client_data *clients_by_id = NULL;
struct msg_store_chunk *msgs_by_id = NULL;
static void free__sub(struct P_sub *chunk)
{
free(chunk->client_id);
free(chunk->topic);
}
static void free__client(struct P_client *chunk)
{
free(chunk->client_id);
}
static void free__client_msg(struct P_client_msg *chunk)
{
free(chunk->client_id);
mosquitto_property_free_all(&chunk->properties);
}
static void free__msg_store(struct P_msg_store *chunk)
{
free(chunk->topic);
free(chunk->payload);
mosquitto_property_free_all(&chunk->properties);
}
static int dump__cfg_chunk_process(FILE *db_fd, uint32_t length)
{
struct PF_cfg chunk;
int rc;
cfg_count++;
memset(&chunk, 0, sizeof(struct PF_cfg));
if(db_version == 6 || db_version == 5){
rc = persist__chunk_cfg_read_v56(db_fd, &chunk);
}else{
rc = persist__chunk_cfg_read_v234(db_fd, &chunk);
}
if(rc){
fprintf(stderr, "Error: Corrupt persistent database.");
fclose(db_fd);
return rc;
}
if(do_print) printf("DB_CHUNK_CFG:\n");
if(do_print) printf("\tLength: %d\n", length);
if(do_print) printf("\tShutdown: %d\n", chunk.shutdown);
if(do_print) printf("\tDB ID size: %d\n", chunk.dbid_size);
if(chunk.dbid_size != sizeof(dbid_t)){
fprintf(stderr, "Error: Incompatible database configuration (dbid size is %d bytes, expected %zu)",
chunk.dbid_size, sizeof(dbid_t));
fclose(db_fd);
return 1;
}
if(do_print) printf("\tLast DB ID: %" PRIu64 "\n", chunk.last_db_id);
return 0;
}
static int dump__client_chunk_process(FILE *db_fd, uint32_t length)
{
struct P_client chunk;
int rc = 0;
struct client_data *cc;
client_count++;
memset(&chunk, 0, sizeof(struct P_client));
if(db_version == 6 || db_version == 5){
rc = persist__chunk_client_read_v56(db_fd, &chunk, db_version);
}else{
rc = persist__chunk_client_read_v234(db_fd, &chunk, db_version);
}
if(rc){
fprintf(stderr, "Error: Corrupt persistent database.");
return rc;
}
if(client_stats){
cc = calloc(1, sizeof(struct client_data));
if(!cc){
fprintf(stderr, "Error: Out of memory.\n");
fclose(db_fd);
free(chunk.client_id);
return 1;
}
cc->id = strdup(chunk.client_id);
HASH_ADD_KEYPTR(hh_id, clients_by_id, cc->id, strlen(cc->id), cc);
}
if(do_print) {
print__client(&chunk, length);
}
free__client(&chunk);
return 0;
}
static int dump__client_msg_chunk_process(FILE *db_fd, uint32_t length)
{
struct P_client_msg chunk;
struct client_data *cc;
struct msg_store_chunk *msc;
int rc;
client_msg_count++;
memset(&chunk, 0, sizeof(struct P_client_msg));
if(db_version == 6 || db_version == 5){
rc = persist__chunk_client_msg_read_v56(db_fd, &chunk, length);
}else{
rc = persist__chunk_client_msg_read_v234(db_fd, &chunk);
}
if(rc){
fprintf(stderr, "Error: Corrupt persistent database.");
fclose(db_fd);
return rc;
}
if(client_stats){
HASH_FIND(hh_id, clients_by_id, chunk.client_id, strlen(chunk.client_id), cc);
if(cc){
cc->messages++;
cc->message_size += length;
HASH_FIND(hh, msgs_by_id, &chunk.F.store_id, sizeof(dbid_t), msc);
if(msc){
cc->message_size += msc->length;
}
}
}
if(do_print) {
print__client_msg(&chunk, length);
}
free__client_msg(&chunk);
return 0;
}
static int dump__msg_store_chunk_process(FILE *db_fptr, uint32_t length)
{
struct P_msg_store chunk;
struct mosquitto_msg_store *stored = NULL;
struct mosquitto_msg_store_load *load;
int64_t message_expiry_interval64;
uint32_t message_expiry_interval;
int rc = 0;
struct msg_store_chunk *mcs;
msg_store_count++;
memset(&chunk, 0, sizeof(struct P_msg_store));
if(db_version == 6 || db_version == 5){
rc = persist__chunk_msg_store_read_v56(db_fptr, &chunk, length);
}else{
rc = persist__chunk_msg_store_read_v234(db_fptr, &chunk, db_version);
}
if(rc){
fprintf(stderr, "Error: Corrupt persistent database.");
fclose(db_fptr);
return rc;
}
load = mosquitto__calloc(1, sizeof(struct mosquitto_msg_store_load));
if(!load){
fclose(db_fptr);
mosquitto__free(chunk.source.id);
mosquitto__free(chunk.source.username);
mosquitto__free(chunk.topic);
mosquitto__free(chunk.payload);
log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory.");
return MOSQ_ERR_NOMEM;
}
if(chunk.F.expiry_time > 0){
message_expiry_interval64 = chunk.F.expiry_time - time(NULL);
if(message_expiry_interval64 < 0 || message_expiry_interval64 > UINT32_MAX){
message_expiry_interval = 0;
}else{
message_expiry_interval = (uint32_t)message_expiry_interval64;
}
}else{
message_expiry_interval = 0;
}
stored = mosquitto__calloc(1, sizeof(struct mosquitto_msg_store));
if(stored == NULL){
mosquitto__free(load);
fclose(db_fptr);
mosquitto__free(chunk.source.id);
mosquitto__free(chunk.source.username);
mosquitto__free(chunk.topic);
mosquitto__free(chunk.payload);
return MOSQ_ERR_NOMEM;
}
stored->source_mid = chunk.F.source_mid;
stored->topic = chunk.topic;
stored->qos = chunk.F.qos;
stored->retain = chunk.F.retain;
stored->payloadlen = chunk.F.payloadlen;
stored->payload = chunk.payload;
stored->properties = chunk.properties;
rc = db__message_store(&chunk.source, stored, message_expiry_interval,
chunk.F.store_id, mosq_mo_client);
mosquitto__free(chunk.source.id);
mosquitto__free(chunk.source.username);
chunk.source.id = NULL;
chunk.source.username = NULL;
if(rc == MOSQ_ERR_SUCCESS){
stored->source_listener = chunk.source.listener;
load->db_id = stored->db_id;
load->store = stored;
HASH_ADD(hh, db.msg_store_load, db_id, sizeof(dbid_t), load);
}else{
mosquitto__free(load);
fclose(db_fptr);
return rc;
}
if(client_stats){
mcs = calloc(1, sizeof(struct msg_store_chunk));
if(!mcs){
errno = ENOMEM;
return 1;
}
mcs->store_id = chunk.F.store_id;
mcs->length = length;
HASH_ADD(hh, msgs_by_id, store_id, sizeof(dbid_t), mcs);
}
if(do_print){
print__msg_store(&chunk, length);
}
free__msg_store(&chunk);
return 0;
}
static int dump__retain_chunk_process(FILE *db_fd, uint32_t length)
{
struct P_retain chunk;
int rc;
retain_count++;
if(do_print) printf("DB_CHUNK_RETAIN:\n");
if(do_print) printf("\tLength: %d\n", length);
if(db_version == 6 || db_version == 5){
rc = persist__chunk_retain_read_v56(db_fd, &chunk);
}else{
rc = persist__chunk_retain_read_v234(db_fd, &chunk);
}
if(rc){
fclose(db_fd);
return rc;
}
if(do_print) printf("\tStore ID: %" PRIu64 "\n", chunk.F.store_id);
return 0;
}
static int dump__sub_chunk_process(FILE *db_fd, uint32_t length)
{
int rc = 0;
struct P_sub chunk;
struct client_data *cc;
sub_count++;
memset(&chunk, 0, sizeof(struct P_sub));
if(db_version == 6 || db_version == 5){
rc = persist__chunk_sub_read_v56(db_fd, &chunk);
}else{
rc = persist__chunk_sub_read_v234(db_fd, &chunk);
}
if(rc){
fprintf(stderr, "Error: Corrupt persistent database.");
fclose(db_fd);
return rc;
}
if(client_stats){
HASH_FIND(hh_id, clients_by_id, chunk.client_id, strlen(chunk.client_id), cc);
if(cc){
cc->subscriptions++;
cc->subscription_size += length;
}
}
if(do_print) {
print__sub(&chunk, length);
}
free__sub(&chunk);
return 0;
}
int main(int argc, char *argv[])
{
FILE *fd;
char header[15];
int rc = 0;
uint32_t crc;
uint32_t i32temp;
uint32_t length;
uint32_t chunk;
char *filename;
struct client_data *cc, *cc_tmp;
if(argc == 2){
filename = argv[1];
}else if(argc == 3 && !strcmp(argv[1], "--stats")){
stats = 1;
do_print = 0;
filename = argv[2];
}else if(argc == 3 && !strcmp(argv[1], "--client-stats")){
client_stats = 1;
do_print = 0;
filename = argv[2];
}else{
fprintf(stderr, "Usage: db_dump [--stats | --client-stats] \n");
return 1;
}
memset(&db, 0, sizeof(struct mosquitto_db));
fd = fopen(filename, "rb");
if(!fd){
fprintf(stderr, "Error: Unable to open %s\n", filename);
return 0;
}
read_e(fd, &header, 15);
if(!memcmp(header, magic, 15)){
if(do_print) printf("Mosquitto DB dump\n");
/* Restore DB as normal */
read_e(fd, &crc, sizeof(uint32_t));
if(do_print) printf("CRC: %d\n", crc);
read_e(fd, &i32temp, sizeof(uint32_t));
db_version = ntohl(i32temp);
if(do_print) printf("DB version: %d\n", db_version);
if(db_version > MOSQ_DB_VERSION){
if(do_print) printf("Warning: mosquitto_db_dump does not support this DB version, continuing but expecting errors.\n");
}
while(persist__chunk_header_read(fd, &chunk, &length) == MOSQ_ERR_SUCCESS){
switch(chunk){
case DB_CHUNK_CFG:
if(dump__cfg_chunk_process(fd, length)) return 1;
break;
case DB_CHUNK_MSG_STORE:
if(dump__msg_store_chunk_process(fd, length)) return 1;
break;
case DB_CHUNK_CLIENT_MSG:
if(dump__client_msg_chunk_process(fd, length)) return 1;
break;
case DB_CHUNK_RETAIN:
if(dump__retain_chunk_process(fd, length)) return 1;
break;
case DB_CHUNK_SUB:
if(dump__sub_chunk_process(fd, length)) return 1;
break;
case DB_CHUNK_CLIENT:
if(dump__client_chunk_process(fd, length)) return 1;
break;
default:
fprintf(stderr, "Warning: Unsupported chunk \"%d\" in persistent database file. Ignoring.\n", chunk);
if(fseek(fd, length, SEEK_CUR) < 0){
fprintf(stderr, "Error seeking in file.\n");
return 1;
}
break;
}
}
}else{
fprintf(stderr, "Error: Unrecognised file format.");
rc = 1;
}
fclose(fd);
if(stats){
printf("DB_CHUNK_CFG: %ld\n", cfg_count);
printf("DB_CHUNK_MSG_STORE: %ld\n", msg_store_count);
printf("DB_CHUNK_CLIENT_MSG: %ld\n", client_msg_count);
printf("DB_CHUNK_RETAIN: %ld\n", retain_count);
printf("DB_CHUNK_SUB: %ld\n", sub_count);
printf("DB_CHUNK_CLIENT: %ld\n", client_count);
}
if(client_stats){
HASH_ITER(hh_id, clients_by_id, cc, cc_tmp){
printf("SC: %d SS: %d MC: %d MS: %ld ", cc->subscriptions, cc->subscription_size, cc->messages, cc->message_size);
printf("%s\n", cc->id);
free(cc->id);
}
}
return rc;
error:
fprintf(stderr, "Error: %s.", strerror(errno));
if(fd) fclose(fd);
return 1;
}
mosquitto-2.0.18/apps/db_dump/db_dump.h 0000664 0000000 0000000 00000001607 14502137606 0020016 0 ustar 00root root 0000000 0000000 #ifndef DB_DUMP_H
#define DB_DUMP_H
/*
Copyright (c) 2010-2019 Roger Light
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License 2.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
https://www.eclipse.org/legal/epl-2.0/
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
Contributors:
Roger Light - initial implementation and documentation.
*/
#include
void print__client(struct P_client *chunk, uint32_t length);
void print__client_msg(struct P_client_msg *chunk, uint32_t length);
void print__msg_store(struct P_msg_store *chunk, uint32_t length);
void print__sub(struct P_sub *chunk, uint32_t length);
#endif
mosquitto-2.0.18/apps/db_dump/print.c 0000664 0000000 0000000 00000015533 14502137606 0017536 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 2010-2019 Roger Light
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License 2.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
https://www.eclipse.org/legal/epl-2.0/
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
Contributors:
Roger Light - initial implementation and documentation.
*/
#include
#include
#include "db_dump.h"
#include
#include
#include
#include
#include
static void print__properties(mosquitto_property *properties)
{
int i;
if(properties == NULL) return;
printf("\tProperties:\n");
while(properties){
switch(properties->identifier){
case MQTT_PROP_PAYLOAD_FORMAT_INDICATOR:
printf("\t\tPayload format indicator: %d\n", properties->value.i8);
break;
case MQTT_PROP_REQUEST_PROBLEM_INFORMATION:
printf("\t\tRequest problem information: %d\n", properties->value.i8);
break;
case MQTT_PROP_REQUEST_RESPONSE_INFORMATION:
printf("\t\tRequest response information: %d\n", properties->value.i8);
break;
case MQTT_PROP_MAXIMUM_QOS:
printf("\t\tMaximum QoS: %d\n", properties->value.i8);
break;
case MQTT_PROP_RETAIN_AVAILABLE:
printf("\t\tRetain available: %d\n", properties->value.i8);
break;
case MQTT_PROP_WILDCARD_SUB_AVAILABLE:
printf("\t\tWildcard sub available: %d\n", properties->value.i8);
break;
case MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE:
printf("\t\tSubscription ID available: %d\n", properties->value.i8);
break;
case MQTT_PROP_SHARED_SUB_AVAILABLE:
printf("\t\tShared subscription available: %d\n", properties->value.i8);
break;
case MQTT_PROP_SERVER_KEEP_ALIVE:
printf("\t\tServer keep alive: %d\n", properties->value.i16);
break;
case MQTT_PROP_RECEIVE_MAXIMUM:
printf("\t\tReceive maximum: %d\n", properties->value.i16);
break;
case MQTT_PROP_TOPIC_ALIAS_MAXIMUM:
printf("\t\tTopic alias maximum: %d\n", properties->value.i16);
break;
case MQTT_PROP_TOPIC_ALIAS:
printf("\t\tTopic alias: %d\n", properties->value.i16);
break;
case MQTT_PROP_MESSAGE_EXPIRY_INTERVAL:
printf("\t\tMessage expiry interval: %d\n", properties->value.i32);
break;
case MQTT_PROP_SESSION_EXPIRY_INTERVAL:
printf("\t\tSession expiry interval: %d\n", properties->value.i32);
break;
case MQTT_PROP_WILL_DELAY_INTERVAL:
printf("\t\tWill delay interval: %d\n", properties->value.i32);
break;
case MQTT_PROP_MAXIMUM_PACKET_SIZE:
printf("\t\tMaximum packet size: %d\n", properties->value.i32);
break;
case MQTT_PROP_SUBSCRIPTION_IDENTIFIER:
printf("\t\tSubscription identifier: %d\n", properties->value.varint);
break;
case MQTT_PROP_CONTENT_TYPE:
printf("\t\tContent type: %s\n", properties->value.s.v);
break;
case MQTT_PROP_RESPONSE_TOPIC:
printf("\t\tResponse topic: %s\n", properties->value.s.v);
break;
case MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER:
printf("\t\tAssigned client identifier: %s\n", properties->value.s.v);
break;
case MQTT_PROP_AUTHENTICATION_METHOD:
printf("\t\tAuthentication method: %s\n", properties->value.s.v);
break;
case MQTT_PROP_RESPONSE_INFORMATION:
printf("\t\tResponse information: %s\n", properties->value.s.v);
break;
case MQTT_PROP_SERVER_REFERENCE:
printf("\t\tServer reference: %s\n", properties->value.s.v);
break;
case MQTT_PROP_REASON_STRING:
printf("\t\tReason string: %s\n", properties->value.s.v);
break;
case MQTT_PROP_AUTHENTICATION_DATA:
printf("\t\tAuthentication data: ");
for(i=0; ivalue.bin.len; i++){
printf("%02X", properties->value.bin.v[i]);
}
printf("\n");
break;
case MQTT_PROP_CORRELATION_DATA:
printf("\t\tCorrelation data: ");
for(i=0; ivalue.bin.len; i++){
printf("%02X", properties->value.bin.v[i]);
}
printf("\n");
break;
case MQTT_PROP_USER_PROPERTY:
printf("\t\tUser property: %s , %s\n", properties->name.v, properties->value.s.v);
break;
default:
printf("\t\tInvalid property type: %d\n", properties->identifier);
break;
}
properties = properties->next;
}
}
void print__client(struct P_client *chunk, uint32_t length)
{
printf("DB_CHUNK_CLIENT:\n");
printf("\tLength: %d\n", length);
printf("\tClient ID: %s\n", chunk->client_id);
if(chunk->username){
printf("\tUsername: %s\n", chunk->username);
}
if(chunk->F.listener_port > 0){
printf("\tListener port: %u\n", chunk->F.listener_port);
}
printf("\tLast MID: %d\n", chunk->F.last_mid);
printf("\tSession expiry time: %" PRIu64 "\n", chunk->F.session_expiry_time);
printf("\tSession expiry interval: %u\n", chunk->F.session_expiry_interval);
}
void print__client_msg(struct P_client_msg *chunk, uint32_t length)
{
printf("DB_CHUNK_CLIENT_MSG:\n");
printf("\tLength: %d\n", length);
printf("\tClient ID: %s\n", chunk->client_id);
printf("\tStore ID: %" PRIu64 "\n", chunk->F.store_id);
printf("\tMID: %d\n", chunk->F.mid);
printf("\tQoS: %d\n", chunk->F.qos);
printf("\tRetain: %d\n", (chunk->F.retain_dup&0xF0)>>4);
printf("\tDirection: %d\n", chunk->F.direction);
printf("\tState: %d\n", chunk->F.state);
printf("\tDup: %d\n", chunk->F.retain_dup&0x0F);
print__properties(chunk->properties);
}
void print__msg_store(struct P_msg_store *chunk, uint32_t length)
{
uint8_t *payload;
printf("DB_CHUNK_MSG_STORE:\n");
printf("\tLength: %d\n", length);
printf("\tStore ID: %" PRIu64 "\n", chunk->F.store_id);
/* printf("\tSource ID: %s\n", chunk->source_id); */
/* printf("\tSource Username: %s\n", chunk->source_username); */
printf("\tSource Port: %d\n", chunk->F.source_port);
printf("\tSource MID: %d\n", chunk->F.source_mid);
printf("\tTopic: %s\n", chunk->topic);
printf("\tQoS: %d\n", chunk->F.qos);
printf("\tRetain: %d\n", chunk->F.retain);
printf("\tPayload Length: %d\n", chunk->F.payloadlen);
printf("\tExpiry Time: %" PRIu64 "\n", chunk->F.expiry_time);
payload = chunk->payload;
if(chunk->F.payloadlen < 256){
/* Print payloads with UTF-8 data below an arbitrary limit of 256 bytes */
if(mosquitto_validate_utf8((char *)payload, (uint16_t)chunk->F.payloadlen) == MOSQ_ERR_SUCCESS){
printf("\tPayload: %s\n", payload);
}
}
print__properties(chunk->properties);
}
void print__sub(struct P_sub *chunk, uint32_t length)
{
printf("DB_CHUNK_SUB:\n");
printf("\tLength: %u\n", length);
printf("\tClient ID: %s\n", chunk->client_id);
printf("\tTopic: %s\n", chunk->topic);
printf("\tQoS: %d\n", chunk->F.qos);
printf("\tSubscription ID: %d\n", chunk->F.identifier);
printf("\tOptions: 0x%02X\n", chunk->F.options);
}
mosquitto-2.0.18/apps/db_dump/stubs.c 0000664 0000000 0000000 00000005376 14502137606 0017546 0 ustar 00root root 0000000 0000000 #include
#include
#include "misc_mosq.h"
#include "mosquitto_broker_internal.h"
#include "mosquitto_internal.h"
#include "util_mosq.h"
#ifndef UNUSED
# define UNUSED(A) (void)(A)
#endif
struct mosquitto *context__init(mosq_sock_t sock)
{
UNUSED(sock);
return NULL;
}
void context__add_to_by_id(struct mosquitto *context)
{
UNUSED(context);
}
int db__message_store(const struct mosquitto *source, struct mosquitto_msg_store *stored, uint32_t message_expiry_interval, dbid_t store_id, enum mosquitto_msg_origin origin)
{
UNUSED(source);
UNUSED(stored);
UNUSED(message_expiry_interval);
UNUSED(store_id);
UNUSED(origin);
return 0;
}
void db__msg_store_ref_inc(struct mosquitto_msg_store *store)
{
UNUSED(store);
}
int handle__packet(struct mosquitto *context)
{
UNUSED(context);
return 0;
}
int log__printf(struct mosquitto *mosq, unsigned int level, const char *fmt, ...)
{
UNUSED(mosq);
UNUSED(level);
UNUSED(fmt);
return 0;
}
FILE *mosquitto__fopen(const char *path, const char *mode, bool restrict_read)
{
UNUSED(path);
UNUSED(mode);
UNUSED(restrict_read);
return NULL;
}
enum mosquitto_client_state mosquitto__get_state(struct mosquitto *mosq)
{
UNUSED(mosq);
return mosq_cs_new;
}
int mux__add_out(struct mosquitto *mosq)
{
UNUSED(mosq);
return 0;
}
int mux__remove_out(struct mosquitto *mosq)
{
UNUSED(mosq);
return 0;
}
ssize_t net__read(struct mosquitto *mosq, void *buf, size_t count)
{
UNUSED(mosq);
UNUSED(buf);
UNUSED(count);
return 0;
}
ssize_t net__write(struct mosquitto *mosq, const void *buf, size_t count)
{
UNUSED(mosq);
UNUSED(buf);
UNUSED(count);
return 0;
}
int retain__store(const char *topic, struct mosquitto_msg_store *stored, char **split_topics)
{
UNUSED(topic);
UNUSED(stored);
UNUSED(split_topics);
return 0;
}
int sub__add(struct mosquitto *context, const char *sub, uint8_t qos, uint32_t identifier, int options, struct mosquitto__subhier **root)
{
UNUSED(context);
UNUSED(sub);
UNUSED(qos);
UNUSED(identifier);
UNUSED(options);
UNUSED(root);
return 0;
}
int sub__messages_queue(const char *source_id, const char *topic, uint8_t qos, int retain, struct mosquitto_msg_store **stored)
{
UNUSED(source_id);
UNUSED(topic);
UNUSED(qos);
UNUSED(retain);
UNUSED(stored);
return 0;
}
int keepalive__update(struct mosquitto *context)
{
UNUSED(context);
return 0;
}
void db__msg_add_to_inflight_stats(struct mosquitto_msg_data *msg_data, struct mosquitto_client_msg *msg)
{
UNUSED(msg_data);
UNUSED(msg);
}
void db__msg_add_to_queued_stats(struct mosquitto_msg_data *msg_data, struct mosquitto_client_msg *msg)
{
UNUSED(msg_data);
UNUSED(msg);
}
int session_expiry__add_from_persistence(struct mosquitto *context, time_t expiry_time)
{
UNUSED(context);
UNUSED(expiry_time);
return 0;
}
mosquitto-2.0.18/apps/mosquitto_ctrl/ 0000775 0000000 0000000 00000000000 14502137606 0017705 5 ustar 00root root 0000000 0000000 mosquitto-2.0.18/apps/mosquitto_ctrl/CMakeLists.txt 0000664 0000000 0000000 00000002602 14502137606 0022445 0 ustar 00root root 0000000 0000000 if (WITH_TLS AND CJSON_FOUND)
add_definitions("-DWITH_CJSON")
include_directories(${mosquitto_SOURCE_DIR} ${mosquitto_SOURCE_DIR}/include
${mosquitto_SOURCE_DIR}/lib ${mosquitto_SOURCE_DIR}/src
${OPENSSL_INCLUDE_DIR} ${STDBOOL_H_PATH} ${STDINT_H_PATH}
${CJSON_INCLUDE_DIRS} ${mosquitto_SOURCE_DIR}/apps/mosquitto_passwd)
link_directories(${CJSON_DIR})
add_executable(mosquitto_ctrl
mosquitto_ctrl.c mosquitto_ctrl.h
client.c
dynsec.c
dynsec_client.c
dynsec_group.c
dynsec_role.c
../mosquitto_passwd/get_password.c ../mosquitto_passwd/get_password.h
../../lib/memory_mosq.c ../../lib/memory_mosq.h
../../lib/misc_mosq.c ../../lib/misc_mosq.h
../../src/memory_public.c
options.c
../../src/password_mosq.c ../../src/password_mosq.h
)
if (WITH_STATIC_LIBRARIES)
target_link_libraries(mosquitto_ctrl libmosquitto_static)
else()
target_link_libraries(mosquitto_ctrl libmosquitto)
endif()
if (UNIX)
if (APPLE)
target_link_libraries(mosquitto_ctrl dl)
elseif (${CMAKE_SYSTEM_NAME} MATCHES "OpenBSD")
#
elseif (${CMAKE_SYSTEM_NAME} MATCHES "NetBSD")
#
elseif(QNX)
#
else(APPLE)
target_link_libraries(mosquitto_ctrl dl)
endif (APPLE)
endif (UNIX)
target_link_libraries(mosquitto_ctrl ${OPENSSL_LIBRARIES} ${CJSON_LIBRARIES})
install(TARGETS mosquitto_ctrl RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
endif (WITH_TLS AND CJSON_FOUND)
mosquitto-2.0.18/apps/mosquitto_ctrl/Makefile 0000664 0000000 0000000 00000006170 14502137606 0021351 0 ustar 00root root 0000000 0000000 include ../../config.mk
.PHONY: all install uninstall clean reallyclean
ifeq ($(WITH_SHARED_LIBRARIES),yes)
LIBMOSQ:=../../lib/libmosquitto.so.${SOVERSION}
else
ifeq ($(WITH_THREADING),yes)
LIBMOSQ:=../../lib/libmosquitto.a -lpthread -lssl -lcrypto
else
LIBMOSQ:=../../lib/libmosquitto.a
endif
endif
LOCAL_CPPFLAGS:=-I../mosquitto_passwd -DWITH_CJSON
OBJS= mosquitto_ctrl.o \
client.o \
dynsec.o \
dynsec_client.o \
dynsec_group.o \
dynsec_role.o \
get_password.o \
memory_mosq.o \
memory_public.o \
misc_mosq.o \
options.o \
password_mosq.o
EXAMPLE_OBJS= example.o
ifeq ($(WITH_TLS),yes)
ifeq ($(WITH_CJSON),yes)
TARGET:=mosquitto_ctrl mosquitto_ctrl_example.so
else
TARGET:=
endif
else
TARGET:=
endif
all : ${TARGET}
mosquitto_ctrl : ${OBJS} ${LIBMOSQ}
${CROSS_COMPILE}${CC} ${APP_LDFLAGS} $^ -o $@ $(PASSWD_LDADD) $(LOCAL_LDFLAGS) $(LIBMOSQ) -lcjson -ldl
mosquitto_ctrl_example.so : ${EXAMPLE_OBJS}
$(CROSS_COMPILE)$(CC) $(PLUGIN_CPPFLAGS) $(PLUGIN_CFLAGS) $(PLUGIN_LDFLAGS) -shared $< -o $@
mosquitto_ctrl.o : mosquitto_ctrl.c mosquitto_ctrl.h
${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@
client.o : client.c mosquitto_ctrl.h
${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@
dynsec.o : dynsec.c mosquitto_ctrl.h
${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@
dynsec_client.o : dynsec_client.c mosquitto_ctrl.h
${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@
dynsec_group.o : dynsec_group.c mosquitto_ctrl.h
${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@
dynsec_role.o : dynsec_role.c mosquitto_ctrl.h
${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@
example.o : example.c mosquitto_ctrl.h
${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(PLUGIN_CPPFLAGS) $(PLUGIN_CFLAGS) -c $< -o $@
get_password.o : ../mosquitto_passwd/get_password.c ../mosquitto_passwd/get_password.h
${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@
memory_mosq.o : ../../lib/memory_mosq.c
${CROSS_COMPILE}${CC} $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@
memory_public.o : ../../src/memory_public.c
${CROSS_COMPILE}${CC} $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@
options.o : options.c mosquitto_ctrl.h
${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@
misc_mosq.o : ../../lib/misc_mosq.c ../../lib/misc_mosq.h
${CROSS_COMPILE}${CC} $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@
password_mosq.o : ../../src/password_mosq.c ../../src/password_mosq.h
${CROSS_COMPILE}${CC} $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@
../../lib/libmosquitto.so.${SOVERSION} :
$(MAKE) -C ../../lib
../../lib/libmosquitto.a :
$(MAKE) -C ../../lib libmosquitto.a
install : all
ifeq ($(WITH_TLS),yes)
ifeq ($(WITH_CJSON),yes)
$(INSTALL) -d "${DESTDIR}$(prefix)/bin"
$(INSTALL) ${STRIP_OPTS} mosquitto_ctrl "${DESTDIR}${prefix}/bin/mosquitto_ctrl"
endif
endif
uninstall :
-rm -f "${DESTDIR}${prefix}/bin/mosquitto_ctrl"
clean :
-rm -f *.o mosquitto_ctrl *.gcda *.gcno *.so
reallyclean : clean
-rm -rf *.orig *.db
mosquitto-2.0.18/apps/mosquitto_ctrl/client.c 0000664 0000000 0000000 00000010226 14502137606 0021330 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 2020 Roger Light
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License 2.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
https://www.eclipse.org/legal/epl-2.0/
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "mosquitto_ctrl.h"
static int run = 1;
static void on_message(struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg, const mosquitto_property *properties)
{
struct mosq_ctrl *ctrl = obj;
UNUSED(properties);
if(ctrl->payload_callback){
ctrl->payload_callback(ctrl, msg->payloadlen, msg->payload);
}
mosquitto_disconnect_v5(mosq, 0, NULL);
run = 0;
}
static void on_publish(struct mosquitto *mosq, void *obj, int mid, int reason_code, const mosquitto_property *properties)
{
UNUSED(obj);
UNUSED(mid);
UNUSED(properties);
if(reason_code > 127){
fprintf(stderr, "Publish error: %s\n", mosquitto_reason_string(reason_code));
run = 0;
mosquitto_disconnect_v5(mosq, 0, NULL);
}
}
static void on_subscribe(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos, const mosquitto_property *properties)
{
struct mosq_ctrl *ctrl = obj;
UNUSED(mid);
UNUSED(properties);
if(qos_count == 1){
if(granted_qos[0] < 128){
/* Success */
mosquitto_publish(mosq, NULL, ctrl->request_topic, (int)strlen(ctrl->payload), ctrl->payload, ctrl->cfg.qos, 0);
free(ctrl->request_topic);
ctrl->request_topic = NULL;
free(ctrl->payload);
ctrl->payload = NULL;
}else{
if(ctrl->cfg.protocol_version == MQTT_PROTOCOL_V5){
fprintf(stderr, "Subscribe error: %s\n", mosquitto_reason_string(granted_qos[0]));
}else{
fprintf(stderr, "Subscribe error: Subscription refused.\n");
}
run = 0;
mosquitto_disconnect_v5(mosq, 0, NULL);
}
}else{
run = 0;
mosquitto_disconnect_v5(mosq, 0, NULL);
}
}
static void on_connect(struct mosquitto *mosq, void *obj, int reason_code, int flags, const mosquitto_property *properties)
{
struct mosq_ctrl *ctrl = obj;
UNUSED(flags);
UNUSED(properties);
if(reason_code == 0){
if(ctrl->response_topic){
mosquitto_subscribe(mosq, NULL, ctrl->response_topic, ctrl->cfg.qos);
free(ctrl->response_topic);
ctrl->response_topic = NULL;
}
}else{
if(ctrl->cfg.protocol_version == MQTT_PROTOCOL_V5){
if(reason_code == MQTT_RC_UNSUPPORTED_PROTOCOL_VERSION){
fprintf(stderr, "Connection error: %s. Try connecting to an MQTT v5 broker, or use MQTT v3.x mode.\n", mosquitto_reason_string(reason_code));
}else{
fprintf(stderr, "Connection error: %s\n", mosquitto_reason_string(reason_code));
}
}else{
fprintf(stderr, "Connection error: %s\n", mosquitto_connack_string(reason_code));
}
run = 0;
mosquitto_disconnect_v5(mosq, 0, NULL);
}
}
int client_request_response(struct mosq_ctrl *ctrl)
{
struct mosquitto *mosq;
int rc;
time_t start;
if(ctrl->cfg.cafile == NULL && ctrl->cfg.capath == NULL){
fprintf(stderr, "Warning: You are running mosquitto_ctrl without encryption.\nThis means all of the configuration changes you are making are visible on the network, including passwords.\n\n");
}
mosquitto_lib_init();
mosq = mosquitto_new(ctrl->cfg.id, true, ctrl);
rc = client_opts_set(mosq, &ctrl->cfg);
if(rc) goto cleanup;
mosquitto_connect_v5_callback_set(mosq, on_connect);
mosquitto_subscribe_v5_callback_set(mosq, on_subscribe);
mosquitto_publish_v5_callback_set(mosq, on_publish);
mosquitto_message_v5_callback_set(mosq, on_message);
rc = client_connect(mosq, &ctrl->cfg);
if(rc) goto cleanup;
start = time(NULL);
while(run && start+10 > time(NULL)){
mosquitto_loop(mosq, -1, 1);
}
cleanup:
mosquitto_destroy(mosq);
mosquitto_lib_cleanup();
return rc;
}
mosquitto-2.0.18/apps/mosquitto_ctrl/dynsec.c 0000664 0000000 0000000 00000066677 14502137606 0021364 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 2020 Roger Light
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License 2.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
https://www.eclipse.org/legal/epl-2.0/
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include
#include
#include
#include
#ifndef WIN32
# include
# include
# include
#endif
#include "mosquitto_ctrl.h"
#include "mosquitto.h"
#include "password_mosq.h"
#include "get_password.h"
#include "misc_mosq.h"
void dynsec__print_usage(void)
{
printf("\nDynamic Security module\n");
printf("=======================\n");
printf("\nInitialisation\n--------------\n");
printf("Create a new configuration file with an admin user:\n");
printf(" mosquitto_ctrl dynsec init [admin-password]\n");
printf("\nGeneral\n-------\n");
printf("Get ACL default access: getDefaultACLAccess\n");
printf("Set ACL default access: setDefaultACLAccess allow|deny\n");
printf("Get group for anonymous clients: getAnonymousGroup\n");
printf("Set group for anonymous clients: setAnonymousGroup \n");
printf("\nClients\n-------\n");
printf("Create a new client: createClient [-c clientid] [-p password]\n");
printf("Delete a client: deleteClient \n");
printf("Set a client password: setClientPassword [password]\n");
printf("Set a client id: setClientId [clientid]\n");
printf("Add a role to a client: addClientRole [priority]\n");
printf(" Higher priority (larger numerical value) roles are evaluated first.\n");
printf("Remove role from a client: removeClientRole \n");
printf("Get client information: getClient \n");
printf("List all clients: listClients [count [offset]]\n");
printf("Enable client: enableClient \n");
printf("Disable client: disableClient \n");
printf("\nGroups\n------\n");
printf("Create a new group: createGroup \n");
printf("Delete a group: deleteGroup \n");
printf("Add a role to a group: addGroupRole [priority]\n");
printf(" Higher priority (larger numerical value) roles are evaluated first.\n");
printf("Remove role from a group: removeGroupRole \n");
printf("Add client to a group: addGroupClient [priority]\n");
printf(" Priority sets the group priority for the given client only.\n");
printf(" Higher priority (larger numerical value) groups are evaluated first.\n");
printf("Remove client from a group: removeGroupClient \n");
printf("Get group information: getGroup \n");
printf("List all groups: listGroups [count [offset]]\n");
printf("\nRoles\n------\n");
printf("Create a new role: createRole \n");
printf("Delete a role: deleteRole \n");
printf("Add an ACL to a role: addRoleACL [priority]\n");
printf(" Higher priority (larger numerical value) ACLs are evaluated first.\n");
printf("Remove ACL from a role: removeRoleACL \n");
printf("Get role information: getRole \n");
printf("List all roles: listRoles [count [offset]]\n");
printf("\naclspec: allow|deny\n");
printf("acltype: publishClientSend|publishClientReceive\n");
printf(" |subscribeLiteral|subscribePattern\n");
printf(" |unsubscribeLiteral|unsubscribePattern\n");
printf("\nFor more information see:\n");
printf(" https://mosquitto.org/documentation/dynamic-security/\n\n");
}
cJSON *cJSON_AddIntToObject(cJSON * const object, const char * const name, int number)
{
char buf[30];
snprintf(buf, sizeof(buf), "%d", number);
return cJSON_AddRawToObject(object, name, buf);
}
/* ################################################################
* #
* # Payload callback
* #
* ################################################################ */
static void print_list(cJSON *j_response, const char *arrayname, const char *keyname)
{
cJSON *j_data, *j_array, *j_elem, *j_name;
j_data = cJSON_GetObjectItem(j_response, "data");
if(j_data == NULL){
fprintf(stderr, "Error: Invalid response from server.\n");
return;
}
j_array = cJSON_GetObjectItem(j_data, arrayname);
if(j_array == NULL || !cJSON_IsArray(j_array)){
fprintf(stderr, "Error: Invalid response from server.\n");
return;
}
cJSON_ArrayForEach(j_elem, j_array){
if(cJSON_IsObject(j_elem)){
j_name = cJSON_GetObjectItem(j_elem, keyname);
if(j_name && cJSON_IsString(j_name)){
printf("%s\n", j_name->valuestring);
}
}else if(cJSON_IsString(j_elem)){
printf("%s\n", j_elem->valuestring);
}
}
}
static void print_roles(cJSON *j_roles, size_t slen)
{
bool first;
cJSON *j_elem, *jtmp;
if(j_roles && cJSON_IsArray(j_roles)){
first = true;
cJSON_ArrayForEach(j_elem, j_roles){
jtmp = cJSON_GetObjectItem(j_elem, "rolename");
if(jtmp && cJSON_IsString(jtmp)){
if(first){
first = false;
printf("%-*s %s", (int)slen, "Roles:", jtmp->valuestring);
}else{
printf("%-*s %s", (int)slen, "", jtmp->valuestring);
}
jtmp = cJSON_GetObjectItem(j_elem, "priority");
if(jtmp && cJSON_IsNumber(jtmp)){
printf(" (priority: %d)", (int)jtmp->valuedouble);
}else{
printf(" (priority: -1)");
}
printf("\n");
}
}
}else{
printf("Roles:\n");
}
}
static void print_client(cJSON *j_response)
{
cJSON *j_data, *j_client, *j_array, *j_elem, *jtmp;
bool first;
j_data = cJSON_GetObjectItem(j_response, "data");
if(j_data == NULL || !cJSON_IsObject(j_data)){
fprintf(stderr, "Error: Invalid response from server.\n");
return;
}
j_client = cJSON_GetObjectItem(j_data, "client");
if(j_client == NULL || !cJSON_IsObject(j_client)){
fprintf(stderr, "Error: Invalid response from server.\n");
return;
}
jtmp = cJSON_GetObjectItem(j_client, "username");
if(jtmp == NULL || !cJSON_IsString(jtmp)){
fprintf(stderr, "Error: Invalid response from server.\n");
return;
}
printf("Username: %s\n", jtmp->valuestring);
jtmp = cJSON_GetObjectItem(j_client, "clientid");
if(jtmp && cJSON_IsString(jtmp)){
printf("Clientid: %s\n", jtmp->valuestring);
}else{
printf("Clientid:\n");
}
jtmp = cJSON_GetObjectItem(j_client, "disabled");
if(jtmp && cJSON_IsBool(jtmp)){
printf("Disabled: %s\n", cJSON_IsTrue(jtmp)?"true":"false");
}
j_array = cJSON_GetObjectItem(j_client, "roles");
print_roles(j_array, strlen("Username:"));
j_array = cJSON_GetObjectItem(j_client, "groups");
if(j_array && cJSON_IsArray(j_array)){
first = true;
cJSON_ArrayForEach(j_elem, j_array){
jtmp = cJSON_GetObjectItem(j_elem, "groupname");
if(jtmp && cJSON_IsString(jtmp)){
if(first){
printf("Groups: %s", jtmp->valuestring);
first = false;
}else{
printf(" %s", jtmp->valuestring);
}
jtmp = cJSON_GetObjectItem(j_elem, "priority");
if(jtmp && cJSON_IsNumber(jtmp)){
printf(" (priority: %d)", (int)jtmp->valuedouble);
}else{
printf(" (priority: -1)");
}
printf("\n");
}
}
}else{
printf("Groups:\n");
}
}
static void print_group(cJSON *j_response)
{
cJSON *j_data, *j_group, *j_array, *j_elem, *jtmp;
bool first;
j_data = cJSON_GetObjectItem(j_response, "data");
if(j_data == NULL || !cJSON_IsObject(j_data)){
fprintf(stderr, "Error: Invalid response from server.\n");
return;
}
j_group = cJSON_GetObjectItem(j_data, "group");
if(j_group == NULL || !cJSON_IsObject(j_group)){
fprintf(stderr, "Error: Invalid response from server.\n");
return;
}
jtmp = cJSON_GetObjectItem(j_group, "groupname");
if(jtmp == NULL || !cJSON_IsString(jtmp)){
fprintf(stderr, "Error: Invalid response from server.\n");
return;
}
printf("Groupname: %s\n", jtmp->valuestring);
j_array = cJSON_GetObjectItem(j_group, "roles");
print_roles(j_array, strlen("Groupname:"));
j_array = cJSON_GetObjectItem(j_group, "clients");
if(j_array && cJSON_IsArray(j_array)){
first = true;
cJSON_ArrayForEach(j_elem, j_array){
jtmp = cJSON_GetObjectItem(j_elem, "username");
if(jtmp && cJSON_IsString(jtmp)){
if(first){
first = false;
printf("Clients: %s\n", jtmp->valuestring);
}else{
printf(" %s\n", jtmp->valuestring);
}
}
}
}
}
static void print_role(cJSON *j_response)
{
cJSON *j_data, *j_role, *j_array, *j_elem, *jtmp;
bool first;
j_data = cJSON_GetObjectItem(j_response, "data");
if(j_data == NULL || !cJSON_IsObject(j_data)){
fprintf(stderr, "Error: Invalid response from server.\n");
return;
}
j_role = cJSON_GetObjectItem(j_data, "role");
if(j_role == NULL || !cJSON_IsObject(j_role)){
fprintf(stderr, "Error: Invalid response from server.\n");
return;
}
jtmp = cJSON_GetObjectItem(j_role, "rolename");
if(jtmp == NULL || !cJSON_IsString(jtmp)){
fprintf(stderr, "Error: Invalid response from server.\n");
return;
}
printf("Rolename: %s\n", jtmp->valuestring);
j_array = cJSON_GetObjectItem(j_role, "acls");
if(j_array && cJSON_IsArray(j_array)){
first = true;
cJSON_ArrayForEach(j_elem, j_array){
jtmp = cJSON_GetObjectItem(j_elem, "acltype");
if(jtmp && cJSON_IsString(jtmp)){
if(first){
first = false;
printf("ACLs: %-20s", jtmp->valuestring);
}else{
printf(" %-20s", jtmp->valuestring);
}
jtmp = cJSON_GetObjectItem(j_elem, "allow");
if(jtmp && cJSON_IsBool(jtmp)){
printf(" : %s", cJSON_IsTrue(jtmp)?"allow":"deny ");
}
jtmp = cJSON_GetObjectItem(j_elem, "topic");
if(jtmp && cJSON_IsString(jtmp)){
printf(" : %s", jtmp->valuestring);
}
jtmp = cJSON_GetObjectItem(j_elem, "priority");
if(jtmp && cJSON_IsNumber(jtmp)){
printf(" (priority: %d)", (int)jtmp->valuedouble);
}else{
printf(" (priority: -1)");
}
printf("\n");
}
}
}
}
static void print_anonymous_group(cJSON *j_response)
{
cJSON *j_data, *j_group, *j_groupname;
j_data = cJSON_GetObjectItem(j_response, "data");
if(j_data == NULL || !cJSON_IsObject(j_data)){
fprintf(stderr, "Error: Invalid response from server.\n");
return;
}
j_group = cJSON_GetObjectItem(j_data, "group");
if(j_group == NULL || !cJSON_IsObject(j_group)){
fprintf(stderr, "Error: Invalid response from server.\n");
return;
}
j_groupname = cJSON_GetObjectItem(j_group, "groupname");
if(j_groupname == NULL || !cJSON_IsString(j_groupname)){
fprintf(stderr, "Error: Invalid response from server.\n");
return;
}
printf("%s\n", j_groupname->valuestring);
}
static void print_default_acl_access(cJSON *j_response)
{
cJSON *j_data, *j_acls, *j_acl, *j_acltype, *j_allow;
j_data = cJSON_GetObjectItem(j_response, "data");
if(j_data == NULL || !cJSON_IsObject(j_data)){
fprintf(stderr, "Error: Invalid response from server.\n");
return;
}
j_acls = cJSON_GetObjectItem(j_data, "acls");
if(j_acls == NULL || !cJSON_IsArray(j_acls)){
fprintf(stderr, "Error: Invalid response from server.\n");
return;
}
cJSON_ArrayForEach(j_acl, j_acls){
j_acltype = cJSON_GetObjectItem(j_acl, "acltype");
j_allow = cJSON_GetObjectItem(j_acl, "allow");
if(j_acltype == NULL || !cJSON_IsString(j_acltype)
|| j_allow == NULL || !cJSON_IsBool(j_allow)
){
fprintf(stderr, "Error: Invalid response from server.\n");
return;
}
printf("%-20s : %s\n", j_acltype->valuestring, cJSON_IsTrue(j_allow)?"allow":"deny");
}
}
static void dynsec__payload_callback(struct mosq_ctrl *ctrl, long payloadlen, const void *payload)
{
cJSON *tree, *j_responses, *j_response, *j_command, *j_error;
UNUSED(ctrl);
#if CJSON_VERSION_FULL < 1007013
UNUSED(payloadlen);
tree = cJSON_Parse(payload);
#else
tree = cJSON_ParseWithLength(payload, (size_t)payloadlen);
#endif
if(tree == NULL){
fprintf(stderr, "Error: Payload not JSON.\n");
return;
}
j_responses = cJSON_GetObjectItem(tree, "responses");
if(j_responses == NULL || !cJSON_IsArray(j_responses)){
fprintf(stderr, "Error: Payload missing data.\n");
cJSON_Delete(tree);
return;
}
j_response = cJSON_GetArrayItem(j_responses, 0);
if(j_response == NULL){
fprintf(stderr, "Error: Payload missing data.\n");
cJSON_Delete(tree);
return;
}
j_command = cJSON_GetObjectItem(j_response, "command");
if(j_command == NULL){
fprintf(stderr, "Error: Payload missing data.\n");
cJSON_Delete(tree);
return;
}
j_error = cJSON_GetObjectItem(j_response, "error");
if(j_error){
fprintf(stderr, "%s: Error: %s\n", j_command->valuestring, j_error->valuestring);
}else{
if(!strcasecmp(j_command->valuestring, "listClients")){
print_list(j_response, "clients", "username");
}else if(!strcasecmp(j_command->valuestring, "listGroups")){
print_list(j_response, "groups", "groupname");
}else if(!strcasecmp(j_command->valuestring, "listRoles")){
print_list(j_response, "roles", "rolename");
}else if(!strcasecmp(j_command->valuestring, "getClient")){
print_client(j_response);
}else if(!strcasecmp(j_command->valuestring, "getGroup")){
print_group(j_response);
}else if(!strcasecmp(j_command->valuestring, "getRole")){
print_role(j_response);
}else if(!strcasecmp(j_command->valuestring, "getDefaultACLAccess")){
print_default_acl_access(j_response);
}else if(!strcasecmp(j_command->valuestring, "getAnonymousGroup")){
print_anonymous_group(j_response);
}else{
/* fprintf(stderr, "%s: Success\n", j_command->valuestring); */
}
}
cJSON_Delete(tree);
}
/* ################################################################
* #
* # Default ACL access
* #
* ################################################################ */
static int dynsec__set_default_acl_access(int argc, char *argv[], cJSON *j_command)
{
char *acltype, *access;
bool b_access;
cJSON *j_acls, *j_acl;
if(argc == 2){
acltype = argv[0];
access = argv[1];
}else{
return MOSQ_ERR_INVAL;
}
if(strcasecmp(acltype, "publishClientSend")
&& strcasecmp(acltype, "publishClientReceive")
&& strcasecmp(acltype, "subscribe")
&& strcasecmp(acltype, "unsubscribe")){
return MOSQ_ERR_INVAL;
}
if(!strcasecmp(access, "allow")){
b_access = true;
}else if(!strcasecmp(access, "deny")){
b_access = false;
}else{
fprintf(stderr, "Error: access must be \"allow\" or \"deny\".\n");
return MOSQ_ERR_INVAL;
}
if(cJSON_AddStringToObject(j_command, "command", "setDefaultACLAccess") == NULL
|| (j_acls = cJSON_AddArrayToObject(j_command, "acls")) == NULL
){
return MOSQ_ERR_NOMEM;
}
j_acl = cJSON_CreateObject();
if(j_acl == NULL){
return MOSQ_ERR_NOMEM;
}
cJSON_AddItemToArray(j_acls, j_acl);
if(cJSON_AddStringToObject(j_acl, "acltype", acltype) == NULL
|| cJSON_AddBoolToObject(j_acl, "allow", b_access) == NULL
){
return MOSQ_ERR_NOMEM;
}
return MOSQ_ERR_SUCCESS;
}
static int dynsec__get_default_acl_access(int argc, char *argv[], cJSON *j_command)
{
UNUSED(argc);
UNUSED(argv);
if(cJSON_AddStringToObject(j_command, "command", "getDefaultACLAccess") == NULL
){
return MOSQ_ERR_NOMEM;
}
return MOSQ_ERR_SUCCESS;
}
/* ################################################################
* #
* # Init
* #
* ################################################################ */
static cJSON *init_add_acl_to_role(cJSON *j_acls, const char *type, const char *topic)
{
cJSON *j_acl;
j_acl = cJSON_CreateObject();
if(j_acl == NULL) return NULL;
if(cJSON_AddStringToObject(j_acl, "acltype", type) == NULL
|| cJSON_AddStringToObject(j_acl, "topic", topic) == NULL
|| cJSON_AddBoolToObject(j_acl, "allow", true) == NULL
){
cJSON_Delete(j_acl);
return NULL;
}
cJSON_AddItemToArray(j_acls, j_acl);
return j_acl;
}
static cJSON *init_add_role(const char *rolename)
{
cJSON *j_role, *j_acls;
j_role = cJSON_CreateObject();
if(j_role == NULL){
return NULL;
}
if(cJSON_AddStringToObject(j_role, "rolename", rolename) == NULL){
cJSON_Delete(j_role);
return NULL;
}
j_acls = cJSON_CreateArray();
if(j_acls == NULL){
cJSON_Delete(j_role);
return NULL;
}
cJSON_AddItemToObject(j_role, "acls", j_acls);
if(init_add_acl_to_role(j_acls, "publishClientSend", "$CONTROL/dynamic-security/#") == NULL
|| init_add_acl_to_role(j_acls, "publishClientReceive", "$CONTROL/dynamic-security/#") == NULL
|| init_add_acl_to_role(j_acls, "subscribePattern", "$CONTROL/dynamic-security/#") == NULL
|| init_add_acl_to_role(j_acls, "publishClientReceive", "$SYS/#") == NULL
|| init_add_acl_to_role(j_acls, "subscribePattern", "$SYS/#") == NULL
|| init_add_acl_to_role(j_acls, "publishClientReceive", "#") == NULL
|| init_add_acl_to_role(j_acls, "subscribePattern", "#") == NULL
|| init_add_acl_to_role(j_acls, "unsubscribePattern", "#") == NULL
){
cJSON_Delete(j_role);
return NULL;
}
return j_role;
}
static cJSON *init_add_client(const char *username, const char *password, const char *rolename)
{
cJSON *j_client, *j_roles, *j_role;
struct mosquitto_pw pw;
char *salt64 = NULL, *hash64 = NULL;
char buf[10];
memset(&pw, 0, sizeof(pw));
pw.hashtype = pw_sha512_pbkdf2;
if(pw__hash(password, &pw, true, PW_DEFAULT_ITERATIONS) != 0){
return NULL;
}
if(base64__encode(pw.salt, sizeof(pw.salt), &salt64)
|| base64__encode(pw.password_hash, sizeof(pw.password_hash), &hash64)
){
fprintf(stderr, "dynsec init: Internal error while encoding password.\n");
free(salt64);
free(hash64);
return NULL;
}
j_client = cJSON_CreateObject();
if(j_client == NULL){
free(salt64);
free(hash64);
return NULL;
}
snprintf(buf, sizeof(buf), "%d", PW_DEFAULT_ITERATIONS);
if(cJSON_AddStringToObject(j_client, "username", username) == NULL
|| cJSON_AddStringToObject(j_client, "textName", "Dynsec admin user") == NULL
|| cJSON_AddStringToObject(j_client, "password", hash64) == NULL
|| cJSON_AddStringToObject(j_client, "salt", salt64) == NULL
|| cJSON_AddRawToObject(j_client, "iterations", buf) == NULL
){
free(salt64);
free(hash64);
cJSON_Delete(j_client);
return NULL;
}
free(salt64);
free(hash64);
j_roles = cJSON_CreateArray();
if(j_roles == NULL){
cJSON_Delete(j_client);
return NULL;
}
cJSON_AddItemToObject(j_client, "roles", j_roles);
j_role = cJSON_CreateObject();
if(j_role == NULL){
cJSON_Delete(j_client);
return NULL;
}
cJSON_AddItemToArray(j_roles, j_role);
if(cJSON_AddStringToObject(j_role, "rolename", rolename) == NULL){
cJSON_Delete(j_client);
return NULL;
}
return j_client;
}
static cJSON *init_create(const char *username, const char *password, const char *rolename)
{
cJSON *tree, *j_clients, *j_client, *j_roles, *j_role;
cJSON *j_default_access;
tree = cJSON_CreateObject();
if(tree == NULL) return NULL;
if((j_clients = cJSON_AddArrayToObject(tree, "clients")) == NULL
|| (j_roles = cJSON_AddArrayToObject(tree, "roles")) == NULL
|| (j_default_access = cJSON_AddObjectToObject(tree, "defaultACLAccess")) == NULL
){
cJSON_Delete(tree);
return NULL;
}
/* Set default behaviour:
* * Client can not publish to the broker by default.
* * Broker *CAN* publish to the client by default.
* * Client con not subscribe to topics by default.
* * Client *CAN* unsubscribe from topics by default.
*/
if(cJSON_AddBoolToObject(j_default_access, "publishClientSend", false) == NULL
|| cJSON_AddBoolToObject(j_default_access, "publishClientReceive", true) == NULL
|| cJSON_AddBoolToObject(j_default_access, "subscribe", false) == NULL
|| cJSON_AddBoolToObject(j_default_access, "unsubscribe", true) == NULL
){
cJSON_Delete(tree);
return NULL;
}
j_client = init_add_client(username, password, rolename);
if(j_client == NULL){
cJSON_Delete(tree);
return NULL;
}
cJSON_AddItemToArray(j_clients, j_client);
j_role = init_add_role(rolename);
if(j_role == NULL){
cJSON_Delete(tree);
return NULL;
}
cJSON_AddItemToArray(j_roles, j_role);
return tree;
}
/* mosquitto_ctrl dynsec init [role-name] */
static int dynsec_init(int argc, char *argv[])
{
char *filename;
char *admin_user;
char *admin_password;
char *json_str;
cJSON *tree;
FILE *fptr;
char prompt[200], verify_prompt[200];
char password[200];
int rc;
if(argc < 2){
fprintf(stderr, "dynsec init: Not enough arguments - filename, or admin-user missing.\n");
return MOSQ_ERR_INVAL;
}
if(argc > 3){
fprintf(stderr, "dynsec init: Too many arguments.\n");
return MOSQ_ERR_INVAL;
}
filename = argv[0];
admin_user = argv[1];
if(argc == 3){
admin_password = argv[2];
}else{
snprintf(prompt, sizeof(prompt), "New password for %s: ", admin_user);
snprintf(verify_prompt, sizeof(verify_prompt), "Reenter password for %s: ", admin_user);
rc = get_password(prompt, verify_prompt, false, password, sizeof(password));
if(rc){
mosquitto_lib_cleanup();
return -1;
}
admin_password = password;
}
tree = init_create(admin_user, admin_password, "admin");
if(tree == NULL){
fprintf(stderr, "dynsec init: Out of memory.\n");
return MOSQ_ERR_NOMEM;
}
json_str = cJSON_Print(tree);
cJSON_Delete(tree);
#ifdef WIN32
fptr = mosquitto__fopen(filename, "wb", true);
#else
int fd = open(filename, O_CREAT | O_EXCL | O_WRONLY, 0640);
if(fd < 0){
free(json_str);
fprintf(stderr, "dynsec init: Unable to open '%s' for writing (%s).\n", filename, strerror(errno));
return -1;
}
fptr = fdopen(fd, "wb");
#endif
if(fptr){
fprintf(fptr, "%s", json_str);
free(json_str);
fclose(fptr);
}else{
free(json_str);
fprintf(stderr, "dynsec init: Unable to open '%s' for writing.\n", filename);
return -1;
}
printf("The client '%s' has been created in the file '%s'.\n", admin_user, filename);
printf("This client is configured to allow you to administer the dynamic security plugin only.\n");
printf("It does not have access to publish messages to normal topics.\n");
printf("You should create your application clients to do that, for example:\n");
printf(" mosquitto_ctrl dynsec createClient \n");
printf(" mosquitto_ctrl dynsec createRole \n");
printf(" mosquitto_ctrl dynsec addRoleACL publishClientSend my/topic [priority]\n");
printf(" mosquitto_ctrl dynsec addClientRole [priority]\n");
printf("See https://mosquitto.org/documentation/dynamic-security/ for details of all commands.\n");
return -1; /* Suppress client connection */
}
/* ################################################################
* #
* # Main
* #
* ################################################################ */
int dynsec__main(int argc, char *argv[], struct mosq_ctrl *ctrl)
{
int rc = -1;
cJSON *j_tree;
cJSON *j_commands, *j_command;
if(!strcasecmp(argv[0], "help")){
dynsec__print_usage();
return -1;
}else if(!strcasecmp(argv[0], "init")){
return dynsec_init(argc-1, &argv[1]);
}
/* The remaining commands need a network connection and JSON command. */
ctrl->payload_callback = dynsec__payload_callback;
ctrl->request_topic = strdup("$CONTROL/dynamic-security/v1");
ctrl->response_topic = strdup("$CONTROL/dynamic-security/v1/response");
if(ctrl->request_topic == NULL || ctrl->response_topic == NULL){
return MOSQ_ERR_NOMEM;
}
j_tree = cJSON_CreateObject();
if(j_tree == NULL) return MOSQ_ERR_NOMEM;
j_commands = cJSON_AddArrayToObject(j_tree, "commands");
if(j_commands == NULL){
cJSON_Delete(j_tree);
j_tree = NULL;
return MOSQ_ERR_NOMEM;
}
j_command = cJSON_CreateObject();
if(j_command == NULL){
cJSON_Delete(j_tree);
j_tree = NULL;
return MOSQ_ERR_NOMEM;
}
cJSON_AddItemToArray(j_commands, j_command);
if(!strcasecmp(argv[0], "setDefaultACLAccess")){
rc = dynsec__set_default_acl_access(argc-1, &argv[1], j_command);
}else if(!strcasecmp(argv[0], "getDefaultACLAccess")){
rc = dynsec__get_default_acl_access(argc-1, &argv[1], j_command);
}else if(!strcasecmp(argv[0], "createClient")){
rc = dynsec_client__create(argc-1, &argv[1], j_command);
}else if(!strcasecmp(argv[0], "deleteClient")){
rc = dynsec_client__delete(argc-1, &argv[1], j_command);
}else if(!strcasecmp(argv[0], "getClient")){
rc = dynsec_client__get(argc-1, &argv[1], j_command);
}else if(!strcasecmp(argv[0], "listClients")){
rc = dynsec_client__list_all(argc-1, &argv[1], j_command);
}else if(!strcasecmp(argv[0], "setClientId")){
rc = dynsec_client__set_id(argc-1, &argv[1], j_command);
}else if(!strcasecmp(argv[0], "setClientPassword")){
rc = dynsec_client__set_password(argc-1, &argv[1], j_command);
}else if(!strcasecmp(argv[0], "addClientRole")){
rc = dynsec_client__add_remove_role(argc-1, &argv[1], j_command, argv[0]);
}else if(!strcasecmp(argv[0], "removeClientRole")){
rc = dynsec_client__add_remove_role(argc-1, &argv[1], j_command, argv[0]);
}else if(!strcasecmp(argv[0], "enableClient")){
rc = dynsec_client__enable_disable(argc-1, &argv[1], j_command, argv[0]);
}else if(!strcasecmp(argv[0], "disableClient")){
rc = dynsec_client__enable_disable(argc-1, &argv[1], j_command, argv[0]);
}else if(!strcasecmp(argv[0], "createGroup")){
rc = dynsec_group__create(argc-1, &argv[1], j_command);
}else if(!strcasecmp(argv[0], "deleteGroup")){
rc = dynsec_group__delete(argc-1, &argv[1], j_command);
}else if(!strcasecmp(argv[0], "getGroup")){
rc = dynsec_group__get(argc-1, &argv[1], j_command);
}else if(!strcasecmp(argv[0], "listGroups")){
rc = dynsec_group__list_all(argc-1, &argv[1], j_command);
}else if(!strcasecmp(argv[0], "addGroupRole")){
rc = dynsec_group__add_remove_role(argc-1, &argv[1], j_command, argv[0]);
}else if(!strcasecmp(argv[0], "removeGroupRole")){
rc = dynsec_group__add_remove_role(argc-1, &argv[1], j_command, argv[0]);
}else if(!strcasecmp(argv[0], "addGroupClient")){
rc = dynsec_group__add_remove_client(argc-1, &argv[1], j_command, argv[0]);
}else if(!strcasecmp(argv[0], "removeGroupClient")){
rc = dynsec_group__add_remove_client(argc-1, &argv[1], j_command, argv[0]);
}else if(!strcasecmp(argv[0], "setAnonymousGroup")){
rc = dynsec_group__set_anonymous(argc-1, &argv[1], j_command);
}else if(!strcasecmp(argv[0], "getAnonymousGroup")){
rc = dynsec_group__get_anonymous(argc-1, &argv[1], j_command);
}else if(!strcasecmp(argv[0], "createRole")){
rc = dynsec_role__create(argc-1, &argv[1], j_command);
}else if(!strcasecmp(argv[0], "deleteRole")){
rc = dynsec_role__delete(argc-1, &argv[1], j_command);
}else if(!strcasecmp(argv[0], "getRole")){
rc = dynsec_role__get(argc-1, &argv[1], j_command);
}else if(!strcasecmp(argv[0], "listRoles")){
rc = dynsec_role__list_all(argc-1, &argv[1], j_command);
}else if(!strcasecmp(argv[0], "addRoleACL")){
rc = dynsec_role__add_acl(argc-1, &argv[1], j_command);
}else if(!strcasecmp(argv[0], "removeRoleACL")){
rc = dynsec_role__remove_acl(argc-1, &argv[1], j_command);
}else{
fprintf(stderr, "Command '%s' not recognised.\n", argv[0]);
return MOSQ_ERR_UNKNOWN;
}
if(rc == MOSQ_ERR_SUCCESS){
ctrl->payload = cJSON_PrintUnformatted(j_tree);
cJSON_Delete(j_tree);
if(ctrl->payload == NULL){
fprintf(stderr, "Error: Out of memory.\n");
return MOSQ_ERR_NOMEM;
}
}
return rc;
}
mosquitto-2.0.18/apps/mosquitto_ctrl/dynsec_client.c 0000664 0000000 0000000 00000014514 14502137606 0022701 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 2020 Roger Light
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License 2.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
https://www.eclipse.org/legal/epl-2.0/
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
Contributors:
Roger Light - initial implementation and documentation.
*/
#include
#include
#include
#include
#include "mosquitto.h"
#include "mosquitto_ctrl.h"
#include "get_password.h"
#include "password_mosq.h"
int dynsec_client__create(int argc, char *argv[], cJSON *j_command)
{
char *username = NULL, *password = NULL, *clientid = NULL;
char prompt[200], verify_prompt[200];
char password_buf[200];
int rc;
int i;
bool request_password = true;
if(argc == 0){
return MOSQ_ERR_INVAL;
}
username = argv[0];
for(i=1; i 0 && cJSON_AddIntToObject(j_command, "count", count) == NULL)
|| (offset > 0 && cJSON_AddIntToObject(j_command, "offset", offset) == NULL)
){
return MOSQ_ERR_NOMEM;
}else{
return MOSQ_ERR_SUCCESS;
}
}
mosquitto-2.0.18/apps/mosquitto_ctrl/dynsec_group.c 0000664 0000000 0000000 00000011025 14502137606 0022551 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 2020 Roger Light
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License 2.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
https://www.eclipse.org/legal/epl-2.0/
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include
#include
#include
#include
#include "mosquitto.h"
#include "mosquitto_ctrl.h"
#include "password_mosq.h"
int dynsec_group__create(int argc, char *argv[], cJSON *j_command)
{
char *groupname = NULL;
if(argc == 1){
groupname = argv[0];
}else{
return MOSQ_ERR_INVAL;
}
if(cJSON_AddStringToObject(j_command, "command", "createGroup") == NULL
|| cJSON_AddStringToObject(j_command, "groupname", groupname) == NULL
){
return MOSQ_ERR_NOMEM;
}else{
return MOSQ_ERR_SUCCESS;
}
}
int dynsec_group__delete(int argc, char *argv[], cJSON *j_command)
{
char *groupname = NULL;
if(argc == 1){
groupname = argv[0];
}else{
return MOSQ_ERR_INVAL;
}
if(cJSON_AddStringToObject(j_command, "command", "deleteGroup") == NULL
|| cJSON_AddStringToObject(j_command, "groupname", groupname) == NULL
){
return MOSQ_ERR_NOMEM;
}else{
return MOSQ_ERR_SUCCESS;
}
}
int dynsec_group__get_anonymous(int argc, char *argv[], cJSON *j_command)
{
UNUSED(argc);
UNUSED(argv);
if(cJSON_AddStringToObject(j_command, "command", "getAnonymousGroup") == NULL
){
return MOSQ_ERR_NOMEM;
}else{
return MOSQ_ERR_SUCCESS;
}
}
int dynsec_group__set_anonymous(int argc, char *argv[], cJSON *j_command)
{
char *groupname = NULL;
if(argc == 1){
groupname = argv[0];
}else{
return MOSQ_ERR_INVAL;
}
if(cJSON_AddStringToObject(j_command, "command", "setAnonymousGroup") == NULL
|| cJSON_AddStringToObject(j_command, "groupname", groupname) == NULL
){
return MOSQ_ERR_NOMEM;
}else{
return MOSQ_ERR_SUCCESS;
}
}
int dynsec_group__get(int argc, char *argv[], cJSON *j_command)
{
char *groupname = NULL;
if(argc == 1){
groupname = argv[0];
}else{
return MOSQ_ERR_INVAL;
}
if(cJSON_AddStringToObject(j_command, "command", "getGroup") == NULL
|| cJSON_AddStringToObject(j_command, "groupname", groupname) == NULL
){
return MOSQ_ERR_NOMEM;
}else{
return MOSQ_ERR_SUCCESS;
}
}
int dynsec_group__add_remove_role(int argc, char *argv[], cJSON *j_command, const char *command)
{
char *groupname = NULL, *rolename = NULL;
int priority = -1;
if(argc == 2){
groupname = argv[0];
rolename = argv[1];
}else if(argc == 3){
groupname = argv[0];
rolename = argv[1];
priority = atoi(argv[2]);
}else{
return MOSQ_ERR_INVAL;
}
if(cJSON_AddStringToObject(j_command, "command", command) == NULL
|| cJSON_AddStringToObject(j_command, "groupname", groupname) == NULL
|| cJSON_AddStringToObject(j_command, "rolename", rolename) == NULL
|| (priority != -1 && cJSON_AddIntToObject(j_command, "priority", priority) == NULL)
){
return MOSQ_ERR_NOMEM;
}else{
return MOSQ_ERR_SUCCESS;
}
}
int dynsec_group__list_all(int argc, char *argv[], cJSON *j_command)
{
int count = -1, offset = -1;
if(argc == 0){
/* All groups */
}else if(argc == 1){
count = atoi(argv[0]);
}else if(argc == 2){
count = atoi(argv[0]);
offset = atoi(argv[1]);
}else{
return MOSQ_ERR_INVAL;
}
if(cJSON_AddStringToObject(j_command, "command", "listGroups") == NULL
|| (count > 0 && cJSON_AddIntToObject(j_command, "count", count) == NULL)
|| (offset > 0 && cJSON_AddIntToObject(j_command, "offset", offset) == NULL)
){
return MOSQ_ERR_NOMEM;
}else{
return MOSQ_ERR_SUCCESS;
}
}
int dynsec_group__add_remove_client(int argc, char *argv[], cJSON *j_command, const char *command)
{
char *username, *groupname;
int priority = -1;
if(argc == 2){
groupname = argv[0];
username = argv[1];
}else if(argc == 3){
groupname = argv[0];
username = argv[1];
priority = atoi(argv[2]);
}else{
return MOSQ_ERR_INVAL;
}
if(cJSON_AddStringToObject(j_command, "command", command) == NULL
|| cJSON_AddStringToObject(j_command, "username", username) == NULL
|| cJSON_AddStringToObject(j_command, "groupname", groupname) == NULL
|| (priority != -1 && cJSON_AddIntToObject(j_command, "priority", priority) == NULL)
){
return MOSQ_ERR_NOMEM;
}else{
return MOSQ_ERR_SUCCESS;
}
}
mosquitto-2.0.18/apps/mosquitto_ctrl/dynsec_role.c 0000664 0000000 0000000 00000011314 14502137606 0022357 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 2020 Roger Light
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License 2.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
https://www.eclipse.org/legal/epl-2.0/
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include
#include
#include
#include
#ifndef WIN32
# include
#endif
#include "mosquitto.h"
#include "mosquitto_ctrl.h"
#include "password_mosq.h"
int dynsec_role__create(int argc, char *argv[], cJSON *j_command)
{
char *rolename = NULL;
if(argc == 1){
rolename = argv[0];
}else{
return MOSQ_ERR_INVAL;
}
if(cJSON_AddStringToObject(j_command, "command", "createRole") == NULL
|| cJSON_AddStringToObject(j_command, "rolename", rolename) == NULL
){
return MOSQ_ERR_NOMEM;
}else{
return MOSQ_ERR_SUCCESS;
}
}
int dynsec_role__delete(int argc, char *argv[], cJSON *j_command)
{
char *rolename = NULL;
if(argc == 1){
rolename = argv[0];
}else{
return MOSQ_ERR_INVAL;
}
if(cJSON_AddStringToObject(j_command, "command", "deleteRole") == NULL
|| cJSON_AddStringToObject(j_command, "rolename", rolename) == NULL
){
return MOSQ_ERR_NOMEM;
}else{
return MOSQ_ERR_SUCCESS;
}
}
int dynsec_role__get(int argc, char *argv[], cJSON *j_command)
{
char *rolename = NULL;
if(argc == 1){
rolename = argv[0];
}else{
return MOSQ_ERR_INVAL;
}
if(cJSON_AddStringToObject(j_command, "command", "getRole") == NULL
|| cJSON_AddStringToObject(j_command, "rolename", rolename) == NULL
){
return MOSQ_ERR_NOMEM;
}else{
return MOSQ_ERR_SUCCESS;
}
}
int dynsec_role__list_all(int argc, char *argv[], cJSON *j_command)
{
int count = -1, offset = -1;
if(argc == 0){
/* All roles */
}else if(argc == 1){
count = atoi(argv[0]);
}else if(argc == 2){
count = atoi(argv[0]);
offset = atoi(argv[1]);
}else{
return MOSQ_ERR_INVAL;
}
if(cJSON_AddStringToObject(j_command, "command", "listRoles") == NULL
|| (count > 0 && cJSON_AddIntToObject(j_command, "count", count) == NULL)
|| (offset > 0 && cJSON_AddIntToObject(j_command, "offset", offset) == NULL)
){
return MOSQ_ERR_NOMEM;
}else{
return MOSQ_ERR_SUCCESS;
}
}
int dynsec_role__add_acl(int argc, char *argv[], cJSON *j_command)
{
char *rolename, *acltype, *topic, *action;
bool allow;
int priority = -1;
if(argc == 5){
rolename = argv[0];
acltype = argv[1];
topic = argv[2];
action = argv[3];
priority = atoi(argv[4]);
}else if(argc == 4){
rolename = argv[0];
acltype = argv[1];
topic = argv[2];
action = argv[3];
}else{
return MOSQ_ERR_INVAL;
}
if(strcasecmp(acltype, "publishClientSend")
&& strcasecmp(acltype, "publishClientReceive")
&& strcasecmp(acltype, "subscribeLiteral")
&& strcasecmp(acltype, "subscribePattern")
&& strcasecmp(acltype, "unsubscribeLiteral")
&& strcasecmp(acltype, "unsubscribePattern")){
return MOSQ_ERR_INVAL;
}
if(!strcasecmp(action, "allow")){
allow = true;
}else if(!strcasecmp(action, "deny")){
allow = false;
}else{
return MOSQ_ERR_INVAL;
}
if(cJSON_AddStringToObject(j_command, "command", "addRoleACL") == NULL
|| cJSON_AddStringToObject(j_command, "rolename", rolename) == NULL
|| cJSON_AddStringToObject(j_command, "acltype", acltype) == NULL
|| cJSON_AddStringToObject(j_command, "topic", topic) == NULL
|| cJSON_AddBoolToObject(j_command, "allow", allow) == NULL
|| (priority != -1 && cJSON_AddIntToObject(j_command, "priority", priority) == NULL)
){
return MOSQ_ERR_NOMEM;
}else{
return MOSQ_ERR_SUCCESS;
}
}
int dynsec_role__remove_acl(int argc, char *argv[], cJSON *j_command)
{
char *rolename, *acltype, *topic;
if(argc == 3){
rolename = argv[0];
acltype = argv[1];
topic = argv[2];
}else{
return MOSQ_ERR_INVAL;
}
if(strcasecmp(acltype, "publishClientSend")
&& strcasecmp(acltype, "publishClientReceive")
&& strcasecmp(acltype, "subscribeLiteral")
&& strcasecmp(acltype, "subscribePattern")
&& strcasecmp(acltype, "unsubscribeLiteral")
&& strcasecmp(acltype, "unsubscribePattern")){
return MOSQ_ERR_INVAL;
}
if(cJSON_AddStringToObject(j_command, "command", "removeRoleACL") == NULL
|| cJSON_AddStringToObject(j_command, "rolename", rolename) == NULL
|| cJSON_AddStringToObject(j_command, "acltype", acltype) == NULL
|| cJSON_AddStringToObject(j_command, "topic", topic) == NULL
){
return MOSQ_ERR_NOMEM;
}else{
return MOSQ_ERR_SUCCESS;
}
}
mosquitto-2.0.18/apps/mosquitto_ctrl/example.c 0000664 0000000 0000000 00000002077 14502137606 0021512 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 2020 Roger Light
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License 2.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
https://www.eclipse.org/legal/epl-2.0/
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include
#include
#include
#include
#ifndef WIN32
# include
#endif
#include "mosquitto_ctrl.h"
void ctrl_help(void)
{
printf("\nExample module\n");
printf("==============\n");
printf(" mosquitto_ctrl example help\n");
}
int ctrl_main(int argc, char *argv[], struct mosq_ctrl *ctrl)
{
UNUSED(argc);
UNUSED(ctrl);
if(!strcasecmp(argv[0], "help")){
ctrl_help();
return -1;
}else{
return MOSQ_ERR_INVAL;
}
}
mosquitto-2.0.18/apps/mosquitto_ctrl/mosquitto_ctrl.c 0000664 0000000 0000000 00000005262 14502137606 0023146 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 2020 Roger Light
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License 2.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
https://www.eclipse.org/legal/epl-2.0/
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include
#include
#include
#include
#include
#ifndef WIN32
# include
#endif
#include "lib_load.h"
#include "mosquitto.h"
#include "mosquitto_ctrl.h"
static void print_version(void)
{
int major, minor, revision;
mosquitto_lib_version(&major, &minor, &revision);
printf("mosquitto_ctrl version %s running on libmosquitto %d.%d.%d.\n", VERSION, major, minor, revision);
}
static void print_usage(void)
{
printf("mosquitto_ctrl is a tool for administering certain Mosquitto features.\n");
print_version();
printf("\nGeneral usage: mosquitto_ctrl \n");
printf("For module specific help use: mosquitto_ctrl help\n");
printf("\nModules available: dynsec\n");
printf("\nFor more information see:\n");
printf(" https://mosquitto.org/man/mosquitto_ctrl-1.html\n\n");
}
int main(int argc, char *argv[])
{
struct mosq_ctrl ctrl;
int rc = MOSQ_ERR_SUCCESS;
FUNC_ctrl_main l_ctrl_main = NULL;
void *lib = NULL;
char lib_name[200];
if(argc == 1){
print_usage();
return 1;
}
memset(&ctrl, 0, sizeof(ctrl));
init_config(&ctrl.cfg);
/* Shift program name out of args */
argc--;
argv++;
ctrl_config_parse(&ctrl.cfg, &argc, &argv);
if(argc < 2){
print_usage();
return 1;
}
/* In built modules */
if(!strcasecmp(argv[0], "dynsec")){
l_ctrl_main = dynsec__main;
}else{
/* Attempt external module */
snprintf(lib_name, sizeof(lib_name), "mosquitto_ctrl_%s.so", argv[0]);
lib = LIB_LOAD(lib_name);
if(lib){
l_ctrl_main = (FUNC_ctrl_main)LIB_SYM(lib, "ctrl_main");
}
}
if(l_ctrl_main == NULL){
fprintf(stderr, "Error: Module '%s' not supported.\n", argv[0]);
rc = MOSQ_ERR_NOT_SUPPORTED;
}
if(l_ctrl_main){
rc = l_ctrl_main(argc-1, &argv[1], &ctrl);
if(rc < 0){
/* Usage print */
rc = 0;
}else if(rc == MOSQ_ERR_SUCCESS){
rc = client_request_response(&ctrl);
}else if(rc == MOSQ_ERR_UNKNOWN){
/* Message printed already */
}else{
fprintf(stderr, "Error: %s\n", mosquitto_strerror(rc));
}
}
client_config_cleanup(&ctrl.cfg);
return rc;
}
mosquitto-2.0.18/apps/mosquitto_ctrl/mosquitto_ctrl.h 0000664 0000000 0000000 00000007762 14502137606 0023162 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 2020 Roger Light
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License 2.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
https://www.eclipse.org/legal/epl-2.0/
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
Contributors:
Roger Light - initial implementation and documentation.
*/
#ifndef MOSQUITTO_CTRL_H
#define MOSQUITTO_CTRL_H
#include
#include
#include "mosquitto.h"
#define PORT_UNDEFINED -1
#define PORT_UNIX 0
struct mosq_config {
char *id;
int protocol_version;
int keepalive;
char *host;
int port;
int qos;
char *bind_address;
bool debug;
bool quiet;
char *username;
char *password;
char *options_file;
#ifdef WITH_TLS
char *cafile;
char *capath;
char *certfile;
char *keyfile;
char *ciphers;
bool insecure;
char *tls_alpn;
char *tls_version;
char *tls_engine;
char *tls_engine_kpass_sha1;
char *keyform;
# ifdef FINAL_WITH_TLS_PSK
char *psk;
char *psk_identity;
# endif
#endif
bool verbose; /* sub */
unsigned int timeout; /* sub */
#ifdef WITH_SOCKS
char *socks5_host;
int socks5_port;
char *socks5_username;
char *socks5_password;
#endif
};
struct mosq_ctrl {
struct mosq_config cfg;
char *request_topic;
char *response_topic;
char *payload;
void (*payload_callback)(struct mosq_ctrl *, long , const void *);
void *userdata;
};
typedef int (*FUNC_ctrl_main)(int argc, char *argv[], struct mosq_ctrl *ctrl);
void init_config(struct mosq_config *cfg);
int ctrl_config_parse(struct mosq_config *cfg, int *argc, char **argv[]);
int client_config_load(struct mosq_config *cfg);
void client_config_cleanup(struct mosq_config *cfg);
int client_request_response(struct mosq_ctrl *ctrl);
int client_opts_set(struct mosquitto *mosq, struct mosq_config *cfg);
int client_connect(struct mosquitto *mosq, struct mosq_config *cfg);
cJSON *cJSON_AddIntToObject(cJSON * const object, const char * const name, int number);
void dynsec__print_usage(void);
int dynsec__main(int argc, char *argv[], struct mosq_ctrl *ctrl);
int dynsec_client__add_remove_role(int argc, char *argv[], cJSON *j_command, const char *command);
int dynsec_client__create(int argc, char *argv[], cJSON *j_command);
int dynsec_client__delete(int argc, char *argv[], cJSON *j_command);
int dynsec_client__enable_disable(int argc, char *argv[], cJSON *j_command, const char *command);
int dynsec_client__get(int argc, char *argv[], cJSON *j_command);
int dynsec_client__list_all(int argc, char *argv[], cJSON *j_command);
int dynsec_client__set_id(int argc, char *argv[], cJSON *j_command);
int dynsec_client__set_password(int argc, char *argv[], cJSON *j_command);
int dynsec_group__add_remove_client(int argc, char *argv[], cJSON *j_command, const char *command);
int dynsec_group__add_remove_role(int argc, char *argv[], cJSON *j_command, const char *command);
int dynsec_group__create(int argc, char *argv[], cJSON *j_command);
int dynsec_group__delete(int argc, char *argv[], cJSON *j_command);
int dynsec_group__get(int argc, char *argv[], cJSON *j_command);
int dynsec_group__list_all(int argc, char *argv[], cJSON *j_command);
int dynsec_group__set_anonymous(int argc, char *argv[], cJSON *j_command);
int dynsec_group__get_anonymous(int argc, char *argv[], cJSON *j_command);
int dynsec_role__create(int argc, char *argv[], cJSON *j_command);
int dynsec_role__delete(int argc, char *argv[], cJSON *j_command);
int dynsec_role__get(int argc, char *argv[], cJSON *j_command);
int dynsec_role__list_all(int argc, char *argv[], cJSON *j_command);
int dynsec_role__add_acl(int argc, char *argv[], cJSON *j_command);
int dynsec_role__remove_acl(int argc, char *argv[], cJSON *j_command);
/* Functions to implement as an external module: */
void ctrl_help(void);
int ctrl_main(int argc, char *argv[], struct mosq_ctrl *ctrl);
#endif
mosquitto-2.0.18/apps/mosquitto_ctrl/options.c 0000664 0000000 0000000 00000054443 14502137606 0021556 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 2014-2020 Roger Light
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License 2.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
https://www.eclipse.org/legal/epl-2.0/
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include
#include
#include
#include
#include
#include
#ifndef WIN32
#include
#include
#else
#include
#include
#define snprintf sprintf_s
#define strncasecmp _strnicmp
#endif
#include
#include
#include "mosquitto_ctrl.h"
#include "get_password.h"
#ifdef WITH_SOCKS
static int mosquitto__parse_socks_url(struct mosq_config *cfg, char *url);
#endif
static int client_config_line_proc(struct mosq_config *cfg, int *argc, char **argvp[]);
void init_config(struct mosq_config *cfg)
{
cfg->qos = 1;
cfg->port = PORT_UNDEFINED;
cfg->protocol_version = MQTT_PROTOCOL_V5;
}
void client_config_cleanup(struct mosq_config *cfg)
{
free(cfg->id);
free(cfg->host);
free(cfg->bind_address);
free(cfg->username);
free(cfg->password);
free(cfg->options_file);
#ifdef WITH_TLS
free(cfg->cafile);
free(cfg->capath);
free(cfg->certfile);
free(cfg->keyfile);
free(cfg->ciphers);
free(cfg->tls_alpn);
free(cfg->tls_version);
free(cfg->tls_engine);
free(cfg->tls_engine_kpass_sha1);
free(cfg->keyform);
# ifdef FINAL_WITH_TLS_PSK
free(cfg->psk);
free(cfg->psk_identity);
# endif
#endif
#ifdef WITH_SOCKS
free(cfg->socks5_host);
free(cfg->socks5_username);
free(cfg->socks5_password);
#endif
}
int ctrl_config_parse(struct mosq_config *cfg, int *argc, char **argv[])
{
int rc;
init_config(cfg);
/* Deal with real argc/argv */
rc = client_config_line_proc(cfg, argc, argv);
if(rc) return rc;
/* Load options from config file - this must be after `-o` has been processed */
rc = client_config_load(cfg);
if(rc) return rc;
#ifdef WITH_TLS
if((cfg->certfile && !cfg->keyfile) || (cfg->keyfile && !cfg->certfile)){
fprintf(stderr, "Error: Both certfile and keyfile must be provided if one of them is set.\n");
return 1;
}
if((cfg->keyform && !cfg->keyfile)){
fprintf(stderr, "Error: If keyform is set, keyfile must be also specified.\n");
return 1;
}
if((cfg->tls_engine_kpass_sha1 && (!cfg->keyform || !cfg->tls_engine))){
fprintf(stderr, "Error: when using tls-engine-kpass-sha1, both tls-engine and keyform must also be provided.\n");
return 1;
}
#endif
#ifdef FINAL_WITH_TLS_PSK
if((cfg->cafile || cfg->capath) && cfg->psk){
fprintf(stderr, "Error: Only one of --psk or --cafile/--capath may be used at once.\n");
return 1;
}
if(cfg->psk && !cfg->psk_identity){
fprintf(stderr, "Error: --psk-identity required if --psk used.\n");
return 1;
}
#endif
if(!cfg->host){
cfg->host = strdup("localhost");
if(!cfg->host){
fprintf(stderr, "Error: Out of memory.\n");
return 1;
}
}
return MOSQ_ERR_SUCCESS;
}
/* Process a tokenised single line from a file or set of real argc/argv */
static int client_config_line_proc(struct mosq_config *cfg, int *argc, char **argvp[])
{
char **argv = *argvp;
while((*argc) && argv[0][0] == '-'){
if(!strcmp(argv[0], "-A")){
if((*argc) == 1){
fprintf(stderr, "Error: -A argument given but no address specified.\n\n");
return 1;
}else{
cfg->bind_address = strdup(argv[1]);
}
argv++;
(*argc)--;
#ifdef WITH_TLS
}else if(!strcmp(argv[0], "--cafile")){
if((*argc) == 1){
fprintf(stderr, "Error: --cafile argument given but no file specified.\n\n");
return 1;
}else{
cfg->cafile = strdup(argv[1]);
}
argv++;
(*argc)--;
}else if(!strcmp(argv[0], "--capath")){
if((*argc) == 1){
fprintf(stderr, "Error: --capath argument given but no directory specified.\n\n");
return 1;
}else{
cfg->capath = strdup(argv[1]);
}
argv++;
(*argc)--;
}else if(!strcmp(argv[0], "--cert")){
if((*argc) == 1){
fprintf(stderr, "Error: --cert argument given but no file specified.\n\n");
return 1;
}else{
cfg->certfile = strdup(argv[1]);
}
argv++;
(*argc)--;
}else if(!strcmp(argv[0], "--ciphers")){
if((*argc) == 1){
fprintf(stderr, "Error: --ciphers argument given but no ciphers specified.\n\n");
return 1;
}else{
cfg->ciphers = strdup(argv[1]);
}
argv++;
(*argc)--;
#endif
}else if(!strcmp(argv[0], "-d") || !strcmp(argv[0], "--debug")){
cfg->debug = true;
}else if(!strcmp(argv[0], "--help")){
return 2;
}else if(!strcmp(argv[0], "-h") || !strcmp(argv[0], "--host")){
if((*argc) == 1){
fprintf(stderr, "Error: -h argument given but no host specified.\n\n");
return 1;
}else{
cfg->host = strdup(argv[1]);
}
argv++;
(*argc)--;
#ifdef WITH_TLS
}else if(!strcmp(argv[0], "--insecure")){
cfg->insecure = true;
#endif
}else if(!strcmp(argv[0], "-i") || !strcmp(argv[0], "--id")){
if((*argc) == 1){
fprintf(stderr, "Error: -i argument given but no id specified.\n\n");
return 1;
}else{
cfg->id = strdup(argv[1]);
}
argv++;
(*argc)--;
#ifdef WITH_TLS
}else if(!strcmp(argv[0], "--key")){
if((*argc) == 1){
fprintf(stderr, "Error: --key argument given but no file specified.\n\n");
return 1;
}else{
cfg->keyfile = strdup(argv[1]);
}
argv++;
(*argc)--;
}else if(!strcmp(argv[0], "--keyform")){
if((*argc) == 1){
fprintf(stderr, "Error: --keyform argument given but no keyform specified.\n\n");
return 1;
}else{
cfg->keyform = strdup(argv[1]);
}
argv++;
(*argc)--;
#endif
}else if(!strcmp(argv[0], "-L") || !strcmp(argv[0], "--url")){
if((*argc) == 1){
fprintf(stderr, "Error: -L argument given but no URL specified.\n\n");
return 1;
} else {
char *url = argv[1];
char *topic;
char *tmp;
if(!strncasecmp(url, "mqtt://", 7)) {
url += 7;
cfg->port = 1883;
} else if(!strncasecmp(url, "mqtts://", 8)) {
url += 8;
cfg->port = 8883;
} else {
fprintf(stderr, "Error: unsupported URL scheme.\n\n");
return 1;
}
topic = strchr(url, '/');
if(!topic){
fprintf(stderr, "Error: Invalid URL for -L argument specified - topic missing.\n");
return 1;
}
*topic++ = 0;
tmp = strchr(url, '@');
if(tmp) {
*tmp++ = 0;
char *colon = strchr(url, ':');
if(colon) {
*colon = 0;
cfg->password = strdup(colon + 1);
}
cfg->username = strdup(url);
url = tmp;
}
cfg->host = url;
tmp = strchr(url, ':');
if(tmp) {
*tmp++ = 0;
cfg->port = atoi(tmp);
}
/* Now we've removed the port, time to get the host on the heap */
cfg->host = strdup(cfg->host);
}
argv++;
(*argc)--;
}else if(!strcmp(argv[0], "-o")){
if((*argc) == 1){
fprintf(stderr, "Error: -o argument given but no options file specified.\n\n");
return 1;
}else{
cfg->options_file = strdup(argv[1]);
}
argv++;
(*argc)--;
}else if(!strcmp(argv[0], "-p") || !strcmp(argv[0], "--port")){
if((*argc) == 1){
fprintf(stderr, "Error: -p argument given but no port specified.\n\n");
return 1;
}else{
cfg->port = atoi(argv[1]);
if(cfg->port<0 || cfg->port>65535){
fprintf(stderr, "Error: Invalid port given: %d\n", cfg->port);
return 1;
}
}
argv++;
(*argc)--;
}else if(!strcmp(argv[0], "-P") || !strcmp(argv[0], "--pw")){
if((*argc) == 1){
fprintf(stderr, "Error: -P argument given but no password specified.\n\n");
return 1;
}else{
cfg->password = strdup(argv[1]);
}
argv++;
(*argc)--;
#ifdef WITH_SOCKS
}else if(!strcmp(argv[0], "--proxy")){
if((*argc) == 1){
fprintf(stderr, "Error: --proxy argument given but no proxy url specified.\n\n");
return 1;
}else{
if(mosquitto__parse_socks_url(cfg, argv[1])){
return 1;
}
}
argv++;
(*argc)--;
#endif
#ifdef FINAL_WITH_TLS_PSK
}else if(!strcmp(argv[0], "--psk")){
if((*argc) == 1){
fprintf(stderr, "Error: --psk argument given but no key specified.\n\n");
return 1;
}else{
cfg->psk = strdup(argv[1]);
}
argv++;
(*argc)--;
}else if(!strcmp(argv[0], "--psk-identity")){
if((*argc) == 1){
fprintf(stderr, "Error: --psk-identity argument given but no identity specified.\n\n");
return 1;
}else{
cfg->psk_identity = strdup(argv[1]);
}
argv++;
(*argc)--;
#endif
}else if(!strcmp(argv[0], "-q") || !strcmp(argv[0], "--qos")){
if((*argc) == 1){
fprintf(stderr, "Error: -q argument given but no QoS specified.\n\n");
return 1;
}else{
cfg->qos = atoi(argv[1]);
if(cfg->qos<0 || cfg->qos>2){
fprintf(stderr, "Error: Invalid QoS given: %d\n", cfg->qos);
return 1;
}
}
argv++;
(*argc)--;
}else if(!strcmp(argv[0], "--quiet")){
cfg->quiet = true;
#ifdef WITH_TLS
}else if(!strcmp(argv[0], "--tls-alpn")){
if((*argc) == 1){
fprintf(stderr, "Error: --tls-alpn argument given but no protocol specified.\n\n");
return 1;
}else{
cfg->tls_alpn = strdup(argv[1]);
}
argv++;
(*argc)--;
}else if(!strcmp(argv[0], "--tls-engine")){
if((*argc) == 1){
fprintf(stderr, "Error: --tls-engine argument given but no engine_id specified.\n\n");
return 1;
}else{
cfg->tls_engine = strdup(argv[1]);
}
argv++;
(*argc)--;
}else if(!strcmp(argv[0], "--tls-engine-kpass-sha1")){
if((*argc) == 1){
fprintf(stderr, "Error: --tls-engine-kpass-sha1 argument given but no kpass sha1 specified.\n\n");
return 1;
}else{
cfg->tls_engine_kpass_sha1 = strdup(argv[1]);
}
argv++;
(*argc)--;
}else if(!strcmp(argv[0], "--tls-version")){
if((*argc) == 1){
fprintf(stderr, "Error: --tls-version argument given but no version specified.\n\n");
return 1;
}else{
cfg->tls_version = strdup(argv[1]);
}
argv++;
(*argc)--;
#endif
}else if(!strcmp(argv[0], "-u") || !strcmp(argv[0], "--username")){
if((*argc) == 1){
fprintf(stderr, "Error: -u argument given but no username specified.\n\n");
return 1;
}else{
cfg->username = strdup(argv[1]);
}
argv++;
(*argc)--;
}else if(!strcmp(argv[0], "--unix")){
if((*argc) == 1){
fprintf(stderr, "Error: --unix argument given but no socket path specified.\n\n");
return 1;
}else{
cfg->host = strdup(argv[1]);
cfg->port = 0;
}
argv++;
(*argc)--;
}else if(!strcmp(argv[0], "-V") || !strcmp(argv[0], "--protocol-version")){
if((*argc) == 1){
fprintf(stderr, "Error: --protocol-version argument given but no version specified.\n\n");
return 1;
}else{
if(!strcmp(argv[1], "mqttv31") || !strcmp(argv[1], "31")){
cfg->protocol_version = MQTT_PROTOCOL_V31;
}else if(!strcmp(argv[1], "mqttv311") || !strcmp(argv[1], "311")){
cfg->protocol_version = MQTT_PROTOCOL_V311;
}else if(!strcmp(argv[1], "mqttv5") || !strcmp(argv[1], "5")){
cfg->protocol_version = MQTT_PROTOCOL_V5;
}else{
fprintf(stderr, "Error: Invalid protocol version argument given.\n\n");
return 1;
}
}
argv++;
(*argc)--;
}else if(!strcmp(argv[0], "-v") || !strcmp(argv[0], "--verbose")){
cfg->verbose = 1;
}else if(!strcmp(argv[0], "--version")){
return 3;
}else{
goto unknown_option;
}
argv++;
(*argc)--;
}
*argvp = argv;
return MOSQ_ERR_SUCCESS;
unknown_option:
fprintf(stderr, "Error: Unknown option '%s'.\n",argv[0]);
return 1;
}
static char *get_default_cfg_location(void)
{
char *loc = NULL;
size_t len;
#ifndef WIN32
char *env;
#else
char env[1024];
int rc;
#endif
#ifndef WIN32
env = getenv("XDG_CONFIG_HOME");
if(env){
len = strlen(env) + strlen("/mosquitto_ctrl") + 1;
loc = malloc(len);
if(!loc){
fprintf(stderr, "Error: Out of memory.\n");
return NULL;
}
snprintf(loc, len, "%s/mosquitto_ctrl", env);
loc[len-1] = '\0';
}else{
env = getenv("HOME");
if(env){
len = strlen(env) + strlen("/.config/mosquitto_ctrl") + 1;
loc = malloc(len);
if(!loc){
fprintf(stderr, "Error: Out of memory.\n");
return NULL;
}
snprintf(loc, len, "%s/.config/mosquitto_ctrl", env);
loc[len-1] = '\0';
}
}
#else
rc = GetEnvironmentVariable("USERPROFILE", env, 1024);
if(rc > 0 && rc < 1024){
len = strlen(env) + strlen("\\mosquitto_ctrl.conf") + 1;
loc = malloc(len);
if(!loc){
fprintf(stderr, "Error: Out of memory.\n");
return NULL;
}
snprintf(loc, len, "%s\\mosquitto_ctrl.conf", env);
loc[len-1] = '\0';
}
#endif
return loc;
}
int client_config_load(struct mosq_config *cfg)
{
int rc;
FILE *fptr = NULL;
char line[1024];
int count;
char **local_args, **args;
char *default_cfg;
if(cfg->options_file){
fptr = fopen(cfg->options_file, "rt");
}else{
default_cfg = get_default_cfg_location();
if(default_cfg){
fptr = fopen(default_cfg, "rt");
free(default_cfg);
}
}
if(fptr){
local_args = malloc(3*sizeof(char *));
if(local_args == NULL){
fprintf(stderr, "Error: Out of memory.\n");
fclose(fptr);
return 1;
}
while(fgets(line, sizeof(line), fptr)){
if(line[0] == '#') continue; /* Comments */
while(line[strlen(line)-1] == 10 || line[strlen(line)-1] == 13){
line[strlen(line)-1] = 0;
}
local_args[0] = strtok(line, " ");
if(local_args[0]){
local_args[1] = strtok(NULL, " ");
if(local_args[1]){
count = 2;
}else{
count = 1;
}
args = local_args;
rc = client_config_line_proc(cfg, &count, &args);
if(rc){
fclose(fptr);
free(local_args);
return rc;
}
}
}
fclose(fptr);
free(local_args);
}
return 0;
}
int client_opts_set(struct mosquitto *mosq, struct mosq_config *cfg)
{
int rc;
char prompt[1000];
char password[1000];
mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, cfg->protocol_version);
if(cfg->username && cfg->password == NULL){
/* Ask for password */
snprintf(prompt, sizeof(prompt), "Password for %s: ", cfg->username);
rc = get_password(prompt, NULL, false, password, sizeof(password));
if(rc){
fprintf(stderr, "Error getting password.\n");
mosquitto_lib_cleanup();
return 1;
}
cfg->password = strdup(password);
if(cfg->password == NULL){
fprintf(stderr, "Error: Out of memory.\n");
mosquitto_lib_cleanup();
return 1;
}
}
if((cfg->username || cfg->password) && mosquitto_username_pw_set(mosq, cfg->username, cfg->password)){
fprintf(stderr, "Error: Problem setting username and/or password.\n");
mosquitto_lib_cleanup();
return 1;
}
#ifdef WITH_TLS
if(cfg->keyform && mosquitto_string_option(mosq, MOSQ_OPT_TLS_KEYFORM, cfg->keyform)){
fprintf(stderr, "Error: Problem setting key form, it must be one of 'pem' or 'engine'.\n");
mosquitto_lib_cleanup();
return 1;
}
if(cfg->cafile || cfg->capath){
rc = mosquitto_tls_set(mosq, cfg->cafile, cfg->capath, cfg->certfile, cfg->keyfile, NULL);
if(rc){
if(rc == MOSQ_ERR_INVAL){
fprintf(stderr, "Error: Problem setting TLS options: File not found.\n");
}else{
fprintf(stderr, "Error: Problem setting TLS options: %s.\n", mosquitto_strerror(rc));
}
mosquitto_lib_cleanup();
return 1;
}
}
if(cfg->insecure && mosquitto_tls_insecure_set(mosq, true)){
fprintf(stderr, "Error: Problem setting TLS insecure option.\n");
mosquitto_lib_cleanup();
return 1;
}
if(cfg->tls_engine && mosquitto_string_option(mosq, MOSQ_OPT_TLS_ENGINE, cfg->tls_engine)){
fprintf(stderr, "Error: Problem setting TLS engine, is %s a valid engine?\n", cfg->tls_engine);
mosquitto_lib_cleanup();
return 1;
}
if(cfg->tls_engine_kpass_sha1 && mosquitto_string_option(mosq, MOSQ_OPT_TLS_ENGINE_KPASS_SHA1, cfg->tls_engine_kpass_sha1)){
fprintf(stderr, "Error: Problem setting TLS engine key pass sha, is it a 40 character hex string?\n");
mosquitto_lib_cleanup();
return 1;
}
if(cfg->tls_alpn && mosquitto_string_option(mosq, MOSQ_OPT_TLS_ALPN, cfg->tls_alpn)){
fprintf(stderr, "Error: Problem setting TLS ALPN protocol.\n");
mosquitto_lib_cleanup();
return 1;
}
# ifdef FINAL_WITH_TLS_PSK
if(cfg->psk && mosquitto_tls_psk_set(mosq, cfg->psk, cfg->psk_identity, NULL)){
fprintf(stderr, "Error: Problem setting TLS-PSK options.\n");
mosquitto_lib_cleanup();
return 1;
}
# endif
if((cfg->tls_version || cfg->ciphers) && mosquitto_tls_opts_set(mosq, 1, cfg->tls_version, cfg->ciphers)){
fprintf(stderr, "Error: Problem setting TLS options, check the options are valid.\n");
mosquitto_lib_cleanup();
return 1;
}
#endif
#ifdef WITH_SOCKS
if(cfg->socks5_host){
rc = mosquitto_socks5_set(mosq, cfg->socks5_host, cfg->socks5_port, cfg->socks5_username, cfg->socks5_password);
if(rc){
mosquitto_lib_cleanup();
return rc;
}
}
#endif
return MOSQ_ERR_SUCCESS;
}
int client_connect(struct mosquitto *mosq, struct mosq_config *cfg)
{
#ifndef WIN32
char *err;
#else
char err[1024];
#endif
int rc;
int port;
if(cfg->port == PORT_UNDEFINED){
#ifdef WITH_TLS
if(cfg->cafile || cfg->capath
# ifdef FINAL_WITH_TLS_PSK
|| cfg->psk
# endif
){
port = 8883;
}else
#endif
{
port = 1883;
}
}else{
port = cfg->port;
}
rc = mosquitto_connect_bind_v5(mosq, cfg->host, port, 60, cfg->bind_address, NULL);
if(rc>0){
if(rc == MOSQ_ERR_ERRNO){
#ifndef WIN32
err = strerror(errno);
#else
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, errno, 0, (LPTSTR)&err, 1024, NULL);
#endif
fprintf(stderr, "Error: %s\n", err);
}else{
fprintf(stderr, "Unable to connect (%s).\n", mosquitto_strerror(rc));
}
mosquitto_lib_cleanup();
return rc;
}
return MOSQ_ERR_SUCCESS;
}
#ifdef WITH_SOCKS
/* Convert %25 -> %, %3a, %3A -> :, %40 -> @ */
static int mosquitto__urldecode(char *str)
{
int i, j;
size_t len;
if(!str) return 0;
if(!strchr(str, '%')) return 0;
len = strlen(str);
for(i=0; i= len){
return 1;
}
if(str[i+1] == '2' && str[i+2] == '5'){
str[i] = '%';
len -= 2;
for(j=i+1; j start){
len = i-start;
if(host){
/* Have already seen a @ , so this must be of form
* socks5h://username[:password]@host:port */
port = malloc(len + 1);
if(!port){
fprintf(stderr, "Error: Out of memory.\n");
goto cleanup;
}
memcpy(port, &(str[start]), len);
port[len] = '\0';
}else if(username_or_host){
/* Haven't seen a @ before, so must be of form
* socks5h://host:port */
host = username_or_host;
username_or_host = NULL;
port = malloc(len + 1);
if(!port){
fprintf(stderr, "Error: Out of memory.\n");
goto cleanup;
}
memcpy(port, &(str[start]), len);
port[len] = '\0';
}else{
host = malloc(len + 1);
if(!host){
fprintf(stderr, "Error: Out of memory.\n");
goto cleanup;
}
memcpy(host, &(str[start]), len);
host[len] = '\0';
}
}
if(!host){
fprintf(stderr, "Error: Invalid proxy.\n");
goto cleanup;
}
if(mosquitto__urldecode(username)){
goto cleanup;
}
if(mosquitto__urldecode(password)){
goto cleanup;
}
if(port){
port_int = atoi(port);
if(port_int < 1 || port_int > 65535){
fprintf(stderr, "Error: Invalid proxy port %d\n", port_int);
goto cleanup;
}
free(port);
}else{
port_int = 1080;
}
cfg->socks5_username = username;
cfg->socks5_password = password;
cfg->socks5_host = host;
cfg->socks5_port = port_int;
return 0;
cleanup:
free(username_or_host);
free(username);
free(password);
free(host);
free(port);
return 1;
}
#endif
mosquitto-2.0.18/apps/mosquitto_passwd/ 0000775 0000000 0000000 00000000000 14502137606 0020242 5 ustar 00root root 0000000 0000000 mosquitto-2.0.18/apps/mosquitto_passwd/CMakeLists.txt 0000664 0000000 0000000 00000001152 14502137606 0023001 0 ustar 00root root 0000000 0000000 include_directories(${mosquitto_SOURCE_DIR} ${mosquitto_SOURCE_DIR}/include
${mosquitto_SOURCE_DIR}/lib ${mosquitto_SOURCE_DIR}/src
${OPENSSL_INCLUDE_DIR} ${STDBOOL_H_PATH} ${STDINT_H_PATH})
if (WITH_TLS)
add_executable(mosquitto_passwd
mosquitto_passwd.c
get_password.c get_password.h
../../lib/memory_mosq.c ../../lib/memory_mosq.h
../../src/memory_public.c
../../lib/misc_mosq.c
../../src/password_mosq.c ../../src/password_mosq.h
)
target_link_libraries(mosquitto_passwd ${OPENSSL_LIBRARIES})
install(TARGETS mosquitto_passwd RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
endif (WITH_TLS)
mosquitto-2.0.18/apps/mosquitto_passwd/Makefile 0000664 0000000 0000000 00000002512 14502137606 0021702 0 ustar 00root root 0000000 0000000 include ../../config.mk
.PHONY: all install uninstall clean reallyclean
OBJS= mosquitto_passwd.o \
get_password.o \
memory_mosq.o \
memory_public.o \
misc_mosq.o \
password_mosq.o
ifeq ($(WITH_TLS),yes)
all: mosquitto_passwd
else
all:
endif
mosquitto_passwd : ${OBJS}
${CROSS_COMPILE}${CC} ${APP_LDFLAGS} $^ -o $@ $(PASSWD_LDADD)
mosquitto_passwd.o : mosquitto_passwd.c
${CROSS_COMPILE}${CC} $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@
get_password.o : get_password.c
${CROSS_COMPILE}${CC} $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@
memory_mosq.o : ../../lib/memory_mosq.c
${CROSS_COMPILE}${CC} $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@
memory_public.o : ../../src/memory_public.c
${CROSS_COMPILE}${CC} $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@
misc_mosq.o : ../../lib/misc_mosq.c ../../lib/misc_mosq.h
${CROSS_COMPILE}${CC} $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@
password_mosq.o : ../../src/password_mosq.c ../../src/password_mosq.h
${CROSS_COMPILE}${CC} $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@
install : all
ifeq ($(WITH_TLS),yes)
$(INSTALL) -d "${DESTDIR}$(prefix)/bin"
$(INSTALL) ${STRIP_OPTS} mosquitto_passwd "${DESTDIR}${prefix}/bin/mosquitto_passwd"
endif
uninstall :
-rm -f "${DESTDIR}${prefix}/bin/mosquitto_passwd"
clean :
-rm -f *.o mosquitto_passwd *.gcda *.gcno
reallyclean : clean
-rm -rf *.orig *.db
mosquitto-2.0.18/apps/mosquitto_passwd/get_password.c 0000664 0000000 0000000 00000005604 14502137606 0023114 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 2012-2020 Roger Light
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License 2.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
https://www.eclipse.org/legal/epl-2.0/
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include
#include
#include
#include
#ifdef WIN32
# include
# include
# define snprintf sprintf_s
# include
# include
#else
# include
# include
# include
#endif
#include "get_password.h"
#define MAX_BUFFER_LEN 65500
#define SALT_LEN 12
void get_password__reset_term(void)
{
#ifndef WIN32
struct termios ts;
tcgetattr(0, &ts);
ts.c_lflag |= ECHO | ICANON;
tcsetattr(0, TCSANOW, &ts);
#endif
}
static int gets_quiet(char *s, int len)
{
#ifdef WIN32
HANDLE h;
DWORD con_orig, con_quiet = 0;
DWORD read_len = 0;
memset(s, 0, len);
h = GetStdHandle(STD_INPUT_HANDLE);
GetConsoleMode(h, &con_orig);
con_quiet = con_orig;
con_quiet &= ~ENABLE_ECHO_INPUT;
con_quiet |= ENABLE_LINE_INPUT;
SetConsoleMode(h, con_quiet);
if(!ReadConsole(h, s, len, &read_len, NULL)){
SetConsoleMode(h, con_orig);
return 1;
}
while(s[strlen(s)-1] == 10 || s[strlen(s)-1] == 13){
s[strlen(s)-1] = 0;
}
if(strlen(s) == 0){
return 1;
}
SetConsoleMode(h, con_orig);
return 0;
#else
struct termios ts_quiet, ts_orig;
char *rs;
memset(s, 0, (size_t)len);
tcgetattr(0, &ts_orig);
ts_quiet = ts_orig;
ts_quiet.c_lflag &= (unsigned int)(~(ECHO | ICANON));
tcsetattr(0, TCSANOW, &ts_quiet);
rs = fgets(s, len, stdin);
tcsetattr(0, TCSANOW, &ts_orig);
if(!rs){
return 1;
}else{
while(s[strlen(s)-1] == 10 || s[strlen(s)-1] == 13){
s[strlen(s)-1] = 0;
}
if(strlen(s) == 0){
return 1;
}
}
return 0;
#endif
}
int get_password(const char *prompt, const char *verify_prompt, bool quiet, char *password, size_t len)
{
char pw1[MAX_BUFFER_LEN], pw2[MAX_BUFFER_LEN];
size_t minLen;
minLen = len < MAX_BUFFER_LEN ? len : MAX_BUFFER_LEN;
printf("%s", prompt);
fflush(stdout);
if(gets_quiet(pw1, (int)minLen)){
if(!quiet){
fprintf(stderr, "Error: Empty password.\n");
}
return 1;
}
printf("\n");
if(verify_prompt){
printf("%s", verify_prompt);
fflush(stdout);
if(gets_quiet(pw2, (int)minLen)){
if(!quiet){
fprintf(stderr, "Error: Empty password.\n");
}
return 1;
}
printf("\n");
if(strcmp(pw1, pw2)){
if(!quiet){
fprintf(stderr, "Error: Passwords do not match.\n");
}
return 2;
}
}
strncpy(password, pw1, minLen);
return 0;
}
mosquitto-2.0.18/apps/mosquitto_passwd/get_password.h 0000664 0000000 0000000 00000001443 14502137606 0023116 0 ustar 00root root 0000000 0000000 #ifndef GET_PASSWORD_H
#define GET_PASSWORD_H
/*
Copyright (c) 2012-2020 Roger Light
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License 2.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
https://www.eclipse.org/legal/epl-2.0/
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
Contributors:
Roger Light - initial implementation and documentation.
*/
#include
void get_password__reset_term(void);
int get_password(const char *prompt, const char *verify_prompt, bool quiet, char *password, size_t len);
#endif
mosquitto-2.0.18/apps/mosquitto_passwd/mosquitto_passwd.c 0000664 0000000 0000000 00000041651 14502137606 0024042 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 2012-2020 Roger Light
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License 2.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
https://www.eclipse.org/legal/epl-2.0/
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include "get_password.h"
#include "password_mosq.h"
#ifdef WIN32
# include
# include
# ifndef __cplusplus
# if defined(_MSC_VER) && _MSC_VER < 1900
# define bool char
# define true 1
# define false 0
# else
# include
# endif
# endif
# define snprintf sprintf_s
# include
# include
#else
# include
# include
# include
# include
#endif
#define MAX_BUFFER_LEN 65500
#define SALT_LEN 12
#include "misc_mosq.h"
struct cb_helper {
const char *line;
const char *username;
const char *password;
int iterations;
bool found;
};
static enum mosquitto_pwhash_type hashtype = pw_sha512_pbkdf2;
#ifdef WIN32
static FILE *mpw_tmpfile(void)
{
return tmpfile();
}
#else
static char unsigned alphanum[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
static unsigned char tmpfile_path[36];
static FILE *mpw_tmpfile(void)
{
int fd;
size_t i;
if(RAND_bytes(tmpfile_path, sizeof(tmpfile_path)) != 1){
return NULL;
}
strcpy((char *)tmpfile_path, "/tmp/");
for(i=strlen((char *)tmpfile_path); iusername)){
/* If this isn't the username to delete, write it to the new file */
fprintf(ftmp, "%s", line);
}else{
/* Don't write the matching username to the file. */
helper->found = true;
}
return 0;
}
static int delete_pwuser(FILE *fptr, FILE *ftmp, const char *username)
{
struct cb_helper helper;
int rc;
memset(&helper, 0, sizeof(helper));
helper.username = username;
rc = pwfile_iterate(fptr, ftmp, delete_pwuser_cb, &helper);
if(helper.found == false){
fprintf(stderr, "Warning: User %s not found in password file.\n", username);
return 1;
}
return rc;
}
/* ======================================================================
* Update a plain text password file to use hashes
* ====================================================================== */
static int update_file_cb(FILE *fptr, FILE *ftmp, const char *username, const char *password, const char *line, struct cb_helper *helper)
{
UNUSED(fptr);
UNUSED(line);
if(helper){
return output_new_password(ftmp, username, password, helper->iterations);
}else{
return output_new_password(ftmp, username, password, PW_DEFAULT_ITERATIONS);
}
}
static int update_file(FILE *fptr, FILE *ftmp)
{
return pwfile_iterate(fptr, ftmp, update_file_cb, NULL);
}
/* ======================================================================
* Update an existing user password / create a new password
* ====================================================================== */
static int update_pwuser_cb(FILE *fptr, FILE *ftmp, const char *username, const char *password, const char *line, struct cb_helper *helper)
{
int rc = 0;
UNUSED(fptr);
UNUSED(password);
if(strcmp(username, helper->username)){
/* If this isn't the matching user, then writing out the exiting line */
fprintf(ftmp, "%s", line);
}else{
/* Write out a new line for our matching username */
helper->found = true;
rc = output_new_password(ftmp, username, helper->password, helper->iterations);
}
return rc;
}
static int update_pwuser(FILE *fptr, FILE *ftmp, const char *username, const char *password, int iterations)
{
struct cb_helper helper;
int rc;
memset(&helper, 0, sizeof(helper));
helper.username = username;
helper.password = password;
helper.iterations = iterations;
rc = pwfile_iterate(fptr, ftmp, update_pwuser_cb, &helper);
if(helper.found){
return rc;
}else{
return output_new_password(ftmp, username, password, iterations);
}
}
static int copy_contents(FILE *src, FILE *dest)
{
char buf[MAX_BUFFER_LEN];
size_t len;
rewind(src);
rewind(dest);
#ifdef WIN32
_chsize(fileno(dest), 0);
#else
if(ftruncate(fileno(dest), 0)) return 1;
#endif
while(!feof(src)){
len = fread(buf, 1, MAX_BUFFER_LEN, src);
if(len > 0){
if(fwrite(buf, 1, len, dest) != len){
return 1;
}
}else{
return !feof(src);
}
}
return 0;
}
static int create_backup(char *backup_file, FILE *fptr)
{
FILE *fbackup;
#ifdef WIN32
fbackup = mosquitto__fopen(backup_file, "wt", true);
#else
int fd;
umask(077);
fd = mkstemp(backup_file);
if(fd < 0){
fprintf(stderr, "Error creating backup password file \"%s\", not continuing.\n", backup_file);
return 1;
}
fbackup = fdopen(fd, "wt");
#endif
if(!fbackup){
fprintf(stderr, "Error creating backup password file \"%s\", not continuing.\n", backup_file);
return 1;
}
if(copy_contents(fptr, fbackup)){
fprintf(stderr, "Error copying data to backup password file \"%s\", not continuing.\n", backup_file);
fclose(fbackup);
return 1;
}
fclose(fbackup);
rewind(fptr);
return 0;
}
static void handle_sigint(int signal)
{
get_password__reset_term();
UNUSED(signal);
exit(0);
}
static bool is_username_valid(const char *username)
{
size_t i;
size_t slen;
if(username){
slen = strlen(username);
if(slen > 65535){
fprintf(stderr, "Error: Username must be less than 65536 characters long.\n");
return false;
}
for(i=0; i 0.\n");
return 1;
}
}else if(!strcmp(argv[idx], "-U")){
do_update_file = true;
}else{
break;
}
}
if(create_new && delete_user){
fprintf(stderr, "Error: -c and -D cannot be used together.\n");
return 1;
}
if(create_new && do_update_file){
fprintf(stderr, "Error: -c and -U cannot be used together.\n");
return 1;
}
if(delete_user && do_update_file){
fprintf(stderr, "Error: -D and -U cannot be used together.\n");
return 1;
}
if(delete_user && batch_mode){
fprintf(stderr, "Error: -b and -D cannot be used together.\n");
return 1;
}
if(create_new){
if(batch_mode){
if(idx+2 >= argc){
fprintf(stderr, "Error: -c argument given but password file, username, or password missing.\n");
return 1;
}else{
password_file_tmp = argv[idx];
username = argv[idx+1];
password_cmd = argv[idx+2];
}
}else{
if(idx+1 >= argc){
fprintf(stderr, "Error: -c argument given but password file or username missing.\n");
return 1;
}else{
password_file_tmp = argv[idx];
username = argv[idx+1];
}
}
}else if(delete_user){
if(idx+1 >= argc){
fprintf(stderr, "Error: -D argument given but password file or username missing.\n");
return 1;
}else{
password_file_tmp = argv[idx];
username = argv[idx+1];
}
}else if(do_update_file){
if(idx+1 != argc){
fprintf(stderr, "Error: -U argument given but password file missing.\n");
return 1;
}else{
password_file_tmp = argv[idx];
}
}else if(batch_mode == true && idx+3 == argc){
password_file_tmp = argv[idx];
username = argv[idx+1];
password_cmd = argv[idx+2];
}else if(batch_mode == false && idx+2 == argc){
password_file_tmp = argv[idx];
username = argv[idx+1];
}else{
print_usage();
return 1;
}
if(!is_username_valid(username)){
return 1;
}
if(password_cmd && strlen(password_cmd) > 65535){
fprintf(stderr, "Error: Password must be less than 65536 characters long.\n");
return 1;
}
#ifdef WIN32
password_file = _fullpath(NULL, password_file_tmp, 0);
if(!password_file){
fprintf(stderr, "Error getting full path for password file.\n");
return 1;
}
#else
password_file = realpath(password_file_tmp, NULL);
if(!password_file){
if(errno == ENOENT){
password_file = strdup(password_file_tmp);
if(!password_file){
fprintf(stderr, "Error: Out of memory.\n");
return 1;
}
}else{
fprintf(stderr, "Error reading password file: %s\n", strerror(errno));
return 1;
}
}
#endif
if(create_new){
if(batch_mode == false){
rc = get_password("Password: ", "Reenter password: ", false, password, MAX_BUFFER_LEN);
if(rc){
free(password_file);
return rc;
}
password_cmd = password;
}
fptr = mosquitto__fopen(password_file, "wt", true);
if(!fptr){
fprintf(stderr, "Error: Unable to open file %s for writing. %s.\n", password_file, strerror(errno));
free(password_file);
return 1;
}
free(password_file);
rc = output_new_password(fptr, username, password_cmd, iterations);
fclose(fptr);
return rc;
}else{
fptr = mosquitto__fopen(password_file, "r+t", true);
if(!fptr){
fprintf(stderr, "Error: Unable to open password file %s. %s.\n", password_file, strerror(errno));
free(password_file);
return 1;
}
size_t len = strlen(password_file) + strlen(".backup.XXXXXX") + 1;
backup_file = malloc(len);
if(!backup_file){
fprintf(stderr, "Error: Out of memory.\n");
free(password_file);
return 1;
}
snprintf(backup_file, len, "%s.backup.XXXXXX", password_file);
free(password_file);
password_file = NULL;
if(create_backup(backup_file, fptr)){
fclose(fptr);
free(backup_file);
return 1;
}
ftmp = mpw_tmpfile();
if(!ftmp){
fprintf(stderr, "Error: Unable to open temporary file. %s.\n", strerror(errno));
fclose(fptr);
free(backup_file);
return 1;
}
if(delete_user){
rc = delete_pwuser(fptr, ftmp, username);
}else if(do_update_file){
rc = update_file(fptr, ftmp);
}else{
if(batch_mode){
/* Update password for individual user */
rc = update_pwuser(fptr, ftmp, username, password_cmd, iterations);
}else{
rc = get_password("Password: ", "Reenter password: ", false, password, MAX_BUFFER_LEN);
if(rc){
fclose(fptr);
fclose(ftmp);
unlink(backup_file);
free(backup_file);
return rc;
}
/* Update password for individual user */
rc = update_pwuser(fptr, ftmp, username, password, iterations);
}
}
if(rc){
fclose(fptr);
fclose(ftmp);
unlink(backup_file);
free(backup_file);
return rc;
}
if(copy_contents(ftmp, fptr)){
fclose(fptr);
fclose(ftmp);
fprintf(stderr, "Error occurred updating password file.\n");
fprintf(stderr, "Password file may be corrupt, check the backup file: %s.\n", backup_file);
free(backup_file);
return 1;
}
fclose(fptr);
fclose(ftmp);
/* Everything was ok so backup no longer needed. May contain old
* passwords so shouldn't be kept around. */
unlink(backup_file);
free(backup_file);
}
return 0;
}
mosquitto-2.0.18/appveyor.yml 0000664 0000000 0000000 00000001403 14502137606 0016240 0 ustar 00root root 0000000 0000000 os: Visual Studio 2015
environment:
CMAKE_ARGS: -DCMAKE_BUILD_TYPE=Release
configuration:
- Release
install:
build:
build_script:
- md build
- cd build
- cmake -DCMAKE_BUILD_TYPE=Release -DWITH_THREADING=no ..
- cmake --build . --config Release
- cd ..
after_build:
- cd installer
- '"%PROGRAMFILES(x86)%\NSIS\makensis" mosquitto.nsi'
artifacts:
- name: Installer
path: 'installer/mosquitto-*-install-windows-x86.exe'
#- path: build\src\Release\mosquitto.exe
#- path: build\src\Release\mosquitto_passwd.exe
#- path: build\lib\Release\mosquitto.dll
#- path: build\lib\Release\mosquitto.lib
#- path: build\client\Release\mosquitto_pub.exe
#- path: build\client\Release\mosquitto_sub.exe
#- path: build\src\Release\mosquitto.exe
mosquitto-2.0.18/buildtest.py 0000775 0000000 0000000 00000002637 14502137606 0016236 0 ustar 00root root 0000000 0000000 #!/usr/bin/python3
build_variants = [
'WITH_ADNS',
'WITH_BRIDGE',
'WITH_CJSON',
'WITH_DOCS',
'WITH_EC',
'WITH_EPOLL',
'WITH_MEMORY_TRACKING',
'WITH_PERSISTENCE',
'WITH_SHARED_LIBRARIES',
'WITH_SOCKS',
'WITH_SRV',
'WITH_STATIC_LIBRARIES',
'WITH_STRIP',
'WITH_SYSTEMD',
'WITH_SYS_TREE',
'WITH_THREADING',
'WITH_TLS',
'WITH_TLS_PSK',
'WITH_UNIX_SOCKETS',
'WITH_WEBSOCKETS',
'WITH_WRAP',
'WITH_XTREPORT',
]
special_variants = [
'WITH_BUNDLED_DEPS',
'WITH_COVERAGE',
]
import os
import random
import subprocess
def run_test(msg, opts):
subprocess.run(["make", "clean"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
print("%s: %s" % (msg, str(opts)))
args = ["make", "-j%d" % (os.cpu_count())] + opts
proc = subprocess.run(args, stdout=subprocess.DEVNULL)
if proc.returncode != 0:
raise RuntimeError("BUILD FAILED: %s" % (' '.join(args)))
def simple_tests():
for bv in build_variants:
for enabled in ["yes", "no"]:
opts = "%s=%s" % (bv, enabled)
run_test("SIMPLE BUILD", [opts])
def random_tests(count=10):
for i in range(1, count):
opts = []
for bv in build_variants:
opts.append("%s=%s" % (bv, random.choice(["yes", "no"])))
run_test("RANDOM BUILD", opts)
if __name__ == "__main__":
simple_tests()
random_tests(100)
mosquitto-2.0.18/client/ 0000775 0000000 0000000 00000000000 14502137606 0015130 5 ustar 00root root 0000000 0000000 mosquitto-2.0.18/client/CMakeLists.txt 0000664 0000000 0000000 00000003357 14502137606 0017700 0 ustar 00root root 0000000 0000000 set(shared_src client_shared.c client_shared.h client_props.c)
if (WITH_SRV)
add_definitions("-DWITH_SRV")
endif (WITH_SRV)
set( CLIENT_INC ${mosquitto_SOURCE_DIR} ${mosquitto_SOURCE_DIR}/include
${STDBOOL_H_PATH} ${STDINT_H_PATH} ${PTHREAD_INCLUDE_DIR}
${OPENSSL_INCLUDE_DIR})
set( CLIENT_DIR ${mosquitto_BINARY_DIR}/lib)
if (CJSON_FOUND)
add_definitions("-DWITH_CJSON")
set( CLIENT_DIR "${CLIENT_DIR};${CJSON_DIR}" )
set( CLIENT_INC "${CLIENT_INC};${CJSON_INCLUDE_DIRS}" )
endif()
include_directories(${CLIENT_INC})
link_directories(${CLIENT_DIR})
add_executable(mosquitto_pub pub_client.c pub_shared.c ${shared_src})
add_executable(mosquitto_sub sub_client.c sub_client_output.c ${shared_src})
add_executable(mosquitto_rr rr_client.c pub_shared.c sub_client_output.c ${shared_src})
if (CJSON_FOUND)
target_link_libraries(mosquitto_pub ${CJSON_LIBRARIES})
target_link_libraries(mosquitto_sub ${CJSON_LIBRARIES})
target_link_libraries(mosquitto_rr ${CJSON_LIBRARIES})
endif()
if (WITH_STATIC_LIBRARIES)
target_link_libraries(mosquitto_pub libmosquitto_static)
target_link_libraries(mosquitto_sub libmosquitto_static)
target_link_libraries(mosquitto_rr libmosquitto_static)
else()
target_link_libraries(mosquitto_pub libmosquitto)
target_link_libraries(mosquitto_sub libmosquitto)
target_link_libraries(mosquitto_rr libmosquitto)
endif()
if (QNX)
target_link_libraries(mosquitto_pub socket)
target_link_libraries(mosquitto_sub socket)
target_link_libraries(mosquitto_rr socket)
endif()
install(TARGETS mosquitto_pub RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
install(TARGETS mosquitto_sub RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
install(TARGETS mosquitto_rr RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
mosquitto-2.0.18/client/Makefile 0000664 0000000 0000000 00000006577 14502137606 0016607 0 ustar 00root root 0000000 0000000 include ../config.mk
.PHONY: all install uninstall reallyclean clean static static_pub static_sub static_rr
ifeq ($(WITH_SHARED_LIBRARIES),yes)
SHARED_DEP:=../lib/libmosquitto.so.${SOVERSION}
endif
ifeq ($(WITH_SHARED_LIBRARIES),yes)
ALL_DEPS:= mosquitto_pub mosquitto_sub mosquitto_rr
else
ifeq ($(WITH_STATIC_LIBRARIES),yes)
ALL_DEPS:= static_pub static_sub static_rr
endif
endif
all : ${ALL_DEPS}
static : static_pub static_sub static_rr
# This makes mosquitto_pub/sub/rr versions that are statically linked with
# libmosquitto only.
static_pub : pub_client.o pub_shared.o client_props.o client_shared.o ../lib/libmosquitto.a
${CROSS_COMPILE}${CC} $^ -o mosquitto_pub ${CLIENT_LDFLAGS} ${STATIC_LIB_DEPS} ${CLIENT_STATIC_LDADD}
static_sub : sub_client.o sub_client_output.o client_props.o client_shared.o ../lib/libmosquitto.a
${CROSS_COMPILE}${CC} $^ -o mosquitto_sub ${CLIENT_LDFLAGS} ${STATIC_LIB_DEPS} ${CLIENT_STATIC_LDADD}
static_rr : rr_client.o client_props.o client_shared.o pub_shared.o sub_client_output.o ../lib/libmosquitto.a
${CROSS_COMPILE}${CC} $^ -o mosquitto_rr ${CLIENT_LDFLAGS} ${STATIC_LIB_DEPS} ${CLIENT_STATIC_LDADD}
mosquitto_pub : pub_client.o pub_shared.o client_shared.o client_props.o
${CROSS_COMPILE}${CC} $(CLIENT_LDFLAGS) $^ -o $@ $(CLIENT_LDADD)
mosquitto_sub : sub_client.o sub_client_output.o client_shared.o client_props.o
${CROSS_COMPILE}${CC} $(CLIENT_LDFLAGS) $^ -o $@ $(CLIENT_LDADD)
mosquitto_rr : rr_client.o client_shared.o client_props.o pub_shared.o sub_client_output.o
${CROSS_COMPILE}${CC} $(CLIENT_LDFLAGS) $^ -o $@ $(CLIENT_LDADD)
pub_client.o : pub_client.c ${SHARED_DEP}
${CROSS_COMPILE}${CC} $(CLIENT_CPPFLAGS) $(CLIENT_CFLAGS) -c $< -o $@
pub_shared.o : pub_shared.c ${SHARED_DEP}
${CROSS_COMPILE}${CC} $(CLIENT_CPPFLAGS) $(CLIENT_CFLAGS) -c $< -o $@
sub_client.o : sub_client.c ${SHARED_DEP}
${CROSS_COMPILE}${CC} $(CLIENT_CPPFLAGS) $(CLIENT_CFLAGS) -c $< -o $@
sub_client_output.o : sub_client_output.c sub_client_output.h ${SHARED_DEP}
${CROSS_COMPILE}${CC} $(CLIENT_CPPFLAGS) $(CLIENT_CFLAGS) -c $< -o $@
rr_client.o : rr_client.c ${SHARED_DEP}
${CROSS_COMPILE}${CC} $(CLIENT_CPPFLAGS) $(CLIENT_CFLAGS) -c $< -o $@
client_shared.o : client_shared.c client_shared.h
${CROSS_COMPILE}${CC} $(CLIENT_CPPFLAGS) $(CLIENT_CFLAGS) -c $< -o $@
client_props.o : client_props.c client_shared.h
${CROSS_COMPILE}${CC} $(CLIENT_CPPFLAGS) $(CLIENT_CFLAGS) -c $< -o $@
# The "testing" target is intended to make it easy to compile a quick client
# for testing purposes. testing.c should not be committed as a file.
testing : testing.o
${CROSS_COMPILE}${CC} $(CLIENT_LDFLAGS) $^ -o $@ $(CLIENT_LDADD) $(CLIENT_LDFLAGS)
testing.o : testing.c
${CROSS_COMPILE}${CC} $(CLIENT_CPPFLAGS) $(CLIENT_CFLAGS) -c $< -o $@
../lib/libmosquitto.so.${SOVERSION} :
$(MAKE) -C ../lib
../lib/libmosquitto.a :
$(MAKE) -C ../lib libmosquitto.a
install : all
$(INSTALL) -d "${DESTDIR}$(prefix)/bin"
$(INSTALL) ${STRIP_OPTS} mosquitto_pub "${DESTDIR}${prefix}/bin/mosquitto_pub"
$(INSTALL) ${STRIP_OPTS} mosquitto_sub "${DESTDIR}${prefix}/bin/mosquitto_sub"
$(INSTALL) ${STRIP_OPTS} mosquitto_rr "${DESTDIR}${prefix}/bin/mosquitto_rr"
uninstall :
-rm -f "${DESTDIR}${prefix}/bin/mosquitto_pub"
-rm -f "${DESTDIR}${prefix}/bin/mosquitto_sub"
-rm -f "${DESTDIR}${prefix}/bin/mosquitto_rr"
reallyclean : clean
clean :
-rm -f *.o mosquitto_pub mosquitto_sub mosquitto_rr *.gcda *.gcno
mosquitto-2.0.18/client/args.txt 0000664 0000000 0000000 00000002255 14502137606 0016631 0 ustar 00root root 0000000 0000000 A - PUB,RR,SUB (bind to address)
a
B
b
C - SUB (message count)
c - PUB,RR,SUB (clean session)
D - PUB,RR,SUB (properties)
d - PUB,RR,SUB (debug log)
E - SUB (exit after subscribe)
e - RR (response topic)
F - RR,SUB (output format)
f - PUB,RR (file input)
G
g
H
h - PUB,RR,SUB (host)
I - PUB,RR,SUB (client id prefix)
i - PUB,RR,SUB (client id)
J
j
K
k - PUB,RR,SUB (keepalive)
L - PUB,RR,SUB (connect url)
l - PUB (stdin input)
M - PUB,RR,SUB (max inflight)
m - PUB,RR (message input)
N - RR,SUB (no end of line)
n - PUB,RR (null message)
O
o - CTRL (options file)
P - PUB,RR,SUB (password)
p - PUB,RR,SUB (port)
Q
q - PUB,RR,SUB (qos)
R - RR,SUB (don't show retained)
r - PUB,RR (retain)
S - PUB,RR,SUB (SRV lookups)
s - PUB,RR (stdin input)
T - SUB (filter out topic)
t - PUB,RR,SUB (topic)
U - SUB (unsubscribe)
u - PUB,RR,SUB (username)
V - PUB,RR,SUB (version output)
v - RR,SUB (verbose)
W - SUB (timeout)
w
X
x - PUB,RR,SUB (session-expiry-interval)
Y
y
Z
z
mosquitto-2.0.18/client/client_props.c 0000664 0000000 0000000 00000013717 14502137606 0020006 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 2018-2020 Roger Light
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License 2.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
https://www.eclipse.org/legal/epl-2.0/
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include
#include
#include
#include
#include
#ifndef WIN32
#include
#include
#else
#include
#include
#define snprintf sprintf_s
#define strncasecmp _strnicmp
#endif
#include "mosquitto.h"
#include "mqtt_protocol.h"
#include "client_shared.h"
enum prop_type
{
PROP_TYPE_BYTE,
PROP_TYPE_INT16,
PROP_TYPE_INT32,
PROP_TYPE_BINARY,
PROP_TYPE_STRING,
PROP_TYPE_STRING_PAIR
};
/* This parses property inputs. It should work for any command type, but is limited at the moment.
*
* Format:
*
* command property value
* command property key value
*
* Example:
*
* publish message-expiry-interval 32
* connect user-property key value
*/
int cfg_parse_property(struct mosq_config *cfg, int argc, char *argv[], int *idx)
{
char *cmdname = NULL, *propname = NULL;
char *key = NULL, *value = NULL;
int cmd, identifier, type;
mosquitto_property **proplist;
int rc;
long tmpl;
size_t szt;
/* idx now points to "command" */
if((*idx)+2 > argc-1){
/* Not enough args */
fprintf(stderr, "Error: --property argument given but not enough arguments specified.\n\n");
return MOSQ_ERR_INVAL;
}
cmdname = argv[*idx];
if(mosquitto_string_to_command(cmdname, &cmd)){
fprintf(stderr, "Error: Invalid command given in --property argument.\n\n");
return MOSQ_ERR_INVAL;
}
propname = argv[(*idx)+1];
if(mosquitto_string_to_property_info(propname, &identifier, &type)){
fprintf(stderr, "Error: Invalid property name given in --property argument.\n\n");
return MOSQ_ERR_INVAL;
}
if(mosquitto_property_check_command(cmd, identifier)){
fprintf(stderr, "Error: %s property not allowed for %s in --property argument.\n\n", propname, cmdname);
return MOSQ_ERR_INVAL;
}
if(identifier == MQTT_PROP_USER_PROPERTY){
if((*idx)+3 > argc-1){
/* Not enough args */
fprintf(stderr, "Error: --property argument given but not enough arguments specified.\n\n");
return MOSQ_ERR_INVAL;
}
key = argv[(*idx)+2];
value = argv[(*idx)+3];
(*idx) += 3;
}else{
value = argv[(*idx)+2];
(*idx) += 2;
}
switch(cmd){
case CMD_CONNECT:
proplist = &cfg->connect_props;
break;
case CMD_PUBLISH:
if(identifier == MQTT_PROP_TOPIC_ALIAS){
cfg->have_topic_alias = true;
}
if(identifier == MQTT_PROP_SUBSCRIPTION_IDENTIFIER){
fprintf(stderr, "Error: %s property not supported for %s in --property argument.\n\n", propname, cmdname);
return MOSQ_ERR_INVAL;
}
proplist = &cfg->publish_props;
break;
case CMD_SUBSCRIBE:
if(identifier != MQTT_PROP_SUBSCRIPTION_IDENTIFIER && identifier != MQTT_PROP_USER_PROPERTY){
fprintf(stderr, "Error: %s property not supported for %s in --property argument.\n\n", propname, cmdname);
return MOSQ_ERR_NOT_SUPPORTED;
}
proplist = &cfg->subscribe_props;
break;
case CMD_UNSUBSCRIBE:
proplist = &cfg->unsubscribe_props;
break;
case CMD_DISCONNECT:
proplist = &cfg->disconnect_props;
break;
case CMD_AUTH:
fprintf(stderr, "Error: %s property not supported for %s in --property argument.\n\n", propname, cmdname);
return MOSQ_ERR_NOT_SUPPORTED;
case CMD_WILL:
proplist = &cfg->will_props;
break;
case CMD_PUBACK:
case CMD_PUBREC:
case CMD_PUBREL:
case CMD_PUBCOMP:
case CMD_SUBACK:
case CMD_UNSUBACK:
fprintf(stderr, "Error: %s property not supported for %s in --property argument.\n\n", propname, cmdname);
return MOSQ_ERR_NOT_SUPPORTED;
default:
return MOSQ_ERR_INVAL;
}
switch(type){
case MQTT_PROP_TYPE_BYTE:
tmpl = atol(value);
if(tmpl < 0 || tmpl > UINT8_MAX){
fprintf(stderr, "Error: Property value (%ld) out of range for property %s.\n\n", tmpl, propname);
return MOSQ_ERR_INVAL;
}
rc = mosquitto_property_add_byte(proplist, identifier, (uint8_t )tmpl);
break;
case MQTT_PROP_TYPE_INT16:
tmpl = atol(value);
if(tmpl < 0 || tmpl > UINT16_MAX){
fprintf(stderr, "Error: Property value (%ld) out of range for property %s.\n\n", tmpl, propname);
return MOSQ_ERR_INVAL;
}
rc = mosquitto_property_add_int16(proplist, identifier, (uint16_t )tmpl);
break;
case MQTT_PROP_TYPE_INT32:
tmpl = atol(value);
if(tmpl < 0 || tmpl > UINT32_MAX){
fprintf(stderr, "Error: Property value (%ld) out of range for property %s.\n\n", tmpl, propname);
return MOSQ_ERR_INVAL;
}
rc = mosquitto_property_add_int32(proplist, identifier, (uint32_t )tmpl);
break;
case MQTT_PROP_TYPE_VARINT:
tmpl = atol(value);
if(tmpl < 0 || tmpl > UINT32_MAX){
fprintf(stderr, "Error: Property value (%ld) out of range for property %s.\n\n", tmpl, propname);
return MOSQ_ERR_INVAL;
}
rc = mosquitto_property_add_varint(proplist, identifier, (uint32_t )tmpl);
break;
case MQTT_PROP_TYPE_BINARY:
szt = strlen(value);
if(szt > UINT16_MAX){
fprintf(stderr, "Error: Property value too long for property %s.\n\n", propname);
return MOSQ_ERR_INVAL;
}
rc = mosquitto_property_add_binary(proplist, identifier, value, (uint16_t )szt);
break;
case MQTT_PROP_TYPE_STRING:
rc = mosquitto_property_add_string(proplist, identifier, value);
break;
case MQTT_PROP_TYPE_STRING_PAIR:
rc = mosquitto_property_add_string_pair(proplist, identifier, key, value);
break;
default:
return MOSQ_ERR_INVAL;
}
if(rc){
fprintf(stderr, "Error adding property %s %d\n", propname, type);
return rc;
}
return MOSQ_ERR_SUCCESS;
}
mosquitto-2.0.18/client/client_shared.c 0000664 0000000 0000000 00000127233 14502137606 0020110 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 2014-2020 Roger Light
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License 2.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
https://www.eclipse.org/legal/epl-2.0/
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include
#include
#include
#include
#include
#include
#ifndef WIN32
#include
#include
#else
#include
#include
#define snprintf sprintf_s
#define strncasecmp _strnicmp
#endif
#include
#include
#include "client_shared.h"
#ifdef WITH_SOCKS
static int mosquitto__parse_socks_url(struct mosq_config *cfg, char *url);
#endif
static int client_config_line_proc(struct mosq_config *cfg, int pub_or_sub, int argc, char *argv[]);
static int check_format(const char *str)
{
size_t i;
size_t len;
len = strlen(str);
for(i=0; i= '0' && str[i+1] <= '9'){
i++;
if(i == len-1){
/* error */
fprintf(stderr, "Error: Incomplete format specifier.\n");
return 1;
}
}
if(str[i+1] == '.'){
/* Precision specifier */
i++;
if(i == len-1){
/* error */
fprintf(stderr, "Error: Incomplete format specifier.\n");
return 1;
}
/* Precision */
while(str[i+1] >= '0' && str[i+1] <= '9'){
i++;
if(i == len-1){
/* error */
fprintf(stderr, "Error: Incomplete format specifier.\n");
return 1;
}
}
}
if(str[i+1] == '%'){
/* Print %, ignore */
}else if(str[i+1] == 'A'){
/* MQTT v5 property topic-alias */
}else if(str[i+1] == 'C'){
/* MQTT v5 property content-type */
}else if(str[i+1] == 'D'){
/* MQTT v5 property correlation-data */
}else if(str[i+1] == 'E'){
/* MQTT v5 property message-expiry-interval */
}else if(str[i+1] == 'F'){
/* MQTT v5 property payload-format-indicator */
}else if(str[i+1] == 'I'){
/* ISO 8601 date+time */
}else if(str[i+1] == 'l'){
/* payload length */
}else if(str[i+1] == 'm'){
/* mid */
}else if(str[i+1] == 'P'){
/* MQTT v5 property user-property */
}else if(str[i+1] == 'p'){
/* payload */
}else if(str[i+1] == 'q'){
/* qos */
}else if(str[i+1] == 'R'){
/* MQTT v5 property response-topic */
}else if(str[i+1] == 'S'){
/* MQTT v5 property subscription-identifier */
}else if(str[i+1] == 'r'){
/* retain */
}else if(str[i+1] == 't'){
/* topic */
}else if(str[i+1] == 'j'){
/* JSON output, escaped payload */
}else if(str[i+1] == 'J'){
/* JSON output, assuming JSON payload */
}else if(str[i+1] == 'U'){
/* Unix time+nanoseconds */
#ifdef WIN32
fprintf(stderr, "Error: The %%U format option is not supported on Windows.\n");
return 1;
#endif
}else if(str[i+1] == 'x' || str[i+1] == 'X'){
/* payload in hex */
}else{
fprintf(stderr, "Error: Invalid format specifier '%c'.\n", str[i+1]);
return 1;
}
i++;
}
}else if(str[i] == '@'){
if(i == len-1){
/* error */
fprintf(stderr, "Error: Incomplete format specifier.\n");
return 1;
}
i++;
}else if(str[i] == '\\'){
if(i == len-1){
/* error */
fprintf(stderr, "Error: Incomplete escape specifier.\n");
return 1;
}else{
switch(str[i+1]){
case '\\': /* '\' */
case '0': /* 0 (NULL) */
case 'a': /* alert */
case 'e': /* escape */
case 'n': /* new line */
case 'r': /* carriage return */
case 't': /* horizontal tab */
case 'v': /* vertical tab */
break;
default:
fprintf(stderr, "Error: Invalid escape specifier '%c'.\n", str[i+1]);
return 1;
}
i++;
}
}
}
return 0;
}
static void init_config(struct mosq_config *cfg, int pub_or_sub)
{
memset(cfg, 0, sizeof(*cfg));
cfg->port = PORT_UNDEFINED;
cfg->max_inflight = 20;
cfg->keepalive = 60;
cfg->clean_session = true;
cfg->eol = true;
cfg->repeat_count = 1;
cfg->repeat_delay.tv_sec = 0;
cfg->repeat_delay.tv_usec = 0;
cfg->random_filter = 10000;
if(pub_or_sub == CLIENT_RR){
cfg->protocol_version = MQTT_PROTOCOL_V5;
cfg->msg_count = 1;
}else{
cfg->protocol_version = MQTT_PROTOCOL_V311;
}
cfg->session_expiry_interval = -1; /* -1 means unset here, the user can't set it to -1. */
}
void client_config_cleanup(struct mosq_config *cfg)
{
int i;
free(cfg->id);
free(cfg->id_prefix);
free(cfg->host);
free(cfg->file_input);
free(cfg->message);
free(cfg->topic);
free(cfg->bind_address);
free(cfg->username);
free(cfg->password);
free(cfg->will_topic);
free(cfg->will_payload);
free(cfg->format);
free(cfg->response_topic);
#ifdef WITH_TLS
free(cfg->cafile);
free(cfg->capath);
free(cfg->certfile);
free(cfg->keyfile);
free(cfg->ciphers);
free(cfg->tls_alpn);
free(cfg->tls_version);
free(cfg->tls_engine);
free(cfg->tls_engine_kpass_sha1);
free(cfg->keyform);
# ifdef FINAL_WITH_TLS_PSK
free(cfg->psk);
free(cfg->psk_identity);
# endif
#endif
if(cfg->topics){
for(i=0; itopic_count; i++){
free(cfg->topics[i]);
}
free(cfg->topics);
}
if(cfg->filter_outs){
for(i=0; ifilter_out_count; i++){
free(cfg->filter_outs[i]);
}
free(cfg->filter_outs);
}
if(cfg->unsub_topics){
for(i=0; iunsub_topic_count; i++){
free(cfg->unsub_topics[i]);
}
free(cfg->unsub_topics);
}
#ifdef WITH_SOCKS
free(cfg->socks5_host);
free(cfg->socks5_username);
free(cfg->socks5_password);
#endif
mosquitto_property_free_all(&cfg->connect_props);
mosquitto_property_free_all(&cfg->publish_props);
mosquitto_property_free_all(&cfg->subscribe_props);
mosquitto_property_free_all(&cfg->unsubscribe_props);
mosquitto_property_free_all(&cfg->disconnect_props);
mosquitto_property_free_all(&cfg->will_props);
}
int client_config_load(struct mosq_config *cfg, int pub_or_sub, int argc, char *argv[])
{
int rc;
FILE *fptr;
char line[1024];
int count;
char *loc = NULL;
size_t len;
char *args[3];
#ifndef WIN32
char *env;
#else
char env[1024];
#endif
args[0] = NULL;
init_config(cfg, pub_or_sub);
/* Default config file */
#ifndef WIN32
env = getenv("XDG_CONFIG_HOME");
if(env){
len = strlen(env) + strlen("/mosquitto_pub") + 1;
loc = malloc(len);
if(!loc){
err_printf(cfg, "Error: Out of memory.\n");
return 1;
}
if(pub_or_sub == CLIENT_PUB){
snprintf(loc, len, "%s/mosquitto_pub", env);
}else if(pub_or_sub == CLIENT_SUB){
snprintf(loc, len, "%s/mosquitto_sub", env);
}else{
snprintf(loc, len, "%s/mosquitto_rr", env);
}
loc[len-1] = '\0';
}else{
env = getenv("HOME");
if(env){
len = strlen(env) + strlen("/.config/mosquitto_pub") + 1;
loc = malloc(len);
if(!loc){
err_printf(cfg, "Error: Out of memory.\n");
return 1;
}
if(pub_or_sub == CLIENT_PUB){
snprintf(loc, len, "%s/.config/mosquitto_pub", env);
}else if(pub_or_sub == CLIENT_SUB){
snprintf(loc, len, "%s/.config/mosquitto_sub", env);
}else{
snprintf(loc, len, "%s/.config/mosquitto_rr", env);
}
loc[len-1] = '\0';
}
}
#else
rc = GetEnvironmentVariable("USERPROFILE", env, 1024);
if(rc > 0 && rc < 1024){
len = strlen(env) + strlen("\\mosquitto_pub.conf") + 1;
loc = malloc(len);
if(!loc){
err_printf(cfg, "Error: Out of memory.\n");
return 1;
}
if(pub_or_sub == CLIENT_PUB){
snprintf(loc, len, "%s\\mosquitto_pub.conf", env);
}else if(pub_or_sub == CLIENT_SUB){
snprintf(loc, len, "%s\\mosquitto_sub.conf", env);
}else{
snprintf(loc, len, "%s\\mosquitto_rr.conf", env);
}
loc[len-1] = '\0';
}
#endif
if(loc){
fptr = fopen(loc, "rt");
if(fptr){
while(fgets(line, 1024, fptr)){
if(line[0] == '#') continue; /* Comments */
while(line[strlen(line)-1] == 10 || line[strlen(line)-1] == 13){
line[strlen(line)-1] = 0;
}
/* All offset by one "args" here, because real argc/argv has
* program name as the first entry. */
args[1] = strtok(line, " ");
if(args[1]){
args[2] = strtok(NULL, "");
if(args[2]){
count = 3;
}else{
count = 2;
}
rc = client_config_line_proc(cfg, pub_or_sub, count, args);
if(rc){
fclose(fptr);
free(loc);
return rc;
}
}
}
fclose(fptr);
}
free(loc);
}
/* Deal with real argc/argv */
rc = client_config_line_proc(cfg, pub_or_sub, argc, argv);
if(rc) return rc;
if(cfg->will_payload && !cfg->will_topic){
fprintf(stderr, "Error: Will payload given, but no will topic given.\n");
return 1;
}
if(cfg->will_retain && !cfg->will_topic){
fprintf(stderr, "Error: Will retain given, but no will topic given.\n");
return 1;
}
#ifdef WITH_TLS
if((cfg->certfile && !cfg->keyfile) || (cfg->keyfile && !cfg->certfile)){
fprintf(stderr, "Error: Both certfile and keyfile must be provided if one of them is set.\n");
return 1;
}
if((cfg->keyform && !cfg->keyfile)){
fprintf(stderr, "Error: If keyform is set, keyfile must be also specified.\n");
return 1;
}
if((cfg->tls_engine_kpass_sha1 && (!cfg->keyform || !cfg->tls_engine))){
fprintf(stderr, "Error: when using tls-engine-kpass-sha1, both tls-engine and keyform must also be provided.\n");
return 1;
}
#endif
#ifdef FINAL_WITH_TLS_PSK
if((cfg->cafile || cfg->capath) && cfg->psk){
fprintf(stderr, "Error: Only one of --psk or --cafile/--capath may be used at once.\n");
return 1;
}
if(cfg->psk && !cfg->psk_identity){
fprintf(stderr, "Error: --psk-identity required if --psk used.\n");
return 1;
}
#endif
if(cfg->protocol_version == 5){
if(cfg->clean_session == false && cfg->session_expiry_interval == -1){
/* User hasn't set session-expiry-interval, but has cleared clean
* session so default to persistent session. */
cfg->session_expiry_interval = UINT32_MAX;
}
if(cfg->session_expiry_interval > 0){
if(cfg->session_expiry_interval == UINT32_MAX && (cfg->id_prefix || !cfg->id)){
fprintf(stderr, "Error: You must provide a client id if you are using an infinite session expiry interval.\n");
return 1;
}
rc = mosquitto_property_add_int32(&cfg->connect_props, MQTT_PROP_SESSION_EXPIRY_INTERVAL, (uint32_t )cfg->session_expiry_interval);
if(rc){
fprintf(stderr, "Error adding property session-expiry-interval\n");
}
}
}else{
if(cfg->clean_session == false && (cfg->id_prefix || !cfg->id)){
fprintf(stderr, "Error: You must provide a client id if you are using the -c option.\n");
return 1;
}
}
if(pub_or_sub == CLIENT_SUB){
if(cfg->topic_count == 0){
fprintf(stderr, "Error: You must specify a topic to subscribe to.\n");
return 1;
}
}
if(!cfg->host){
cfg->host = strdup("localhost");
if(!cfg->host){
err_printf(cfg, "Error: Out of memory.\n");
return 1;
}
}
rc = mosquitto_property_check_all(CMD_CONNECT, cfg->connect_props);
if(rc){
err_printf(cfg, "Error in CONNECT properties: %s\n", mosquitto_strerror(rc));
return 1;
}
rc = mosquitto_property_check_all(CMD_PUBLISH, cfg->publish_props);
if(rc){
err_printf(cfg, "Error in PUBLISH properties: %s\n", mosquitto_strerror(rc));
return 1;
}
rc = mosquitto_property_check_all(CMD_SUBSCRIBE, cfg->subscribe_props);
if(rc){
err_printf(cfg, "Error in SUBSCRIBE properties: %s\n", mosquitto_strerror(rc));
return 1;
}
rc = mosquitto_property_check_all(CMD_UNSUBSCRIBE, cfg->unsubscribe_props);
if(rc){
err_printf(cfg, "Error in UNSUBSCRIBE properties: %s\n", mosquitto_strerror(rc));
return 1;
}
rc = mosquitto_property_check_all(CMD_DISCONNECT, cfg->disconnect_props);
if(rc){
err_printf(cfg, "Error in DISCONNECT properties: %s\n", mosquitto_strerror(rc));
return 1;
}
rc = mosquitto_property_check_all(CMD_WILL, cfg->will_props);
if(rc){
err_printf(cfg, "Error in Will properties: %s\n", mosquitto_strerror(rc));
return 1;
}
return MOSQ_ERR_SUCCESS;
}
static int cfg_add_topic(struct mosq_config *cfg, int type, char *topic, const char *arg)
{
if(mosquitto_validate_utf8(topic, (int )strlen(topic))){
fprintf(stderr, "Error: Malformed UTF-8 in %s argument.\n\n", arg);
return 1;
}
if(type == CLIENT_PUB || type == CLIENT_RR){
if(mosquitto_pub_topic_check(topic) == MOSQ_ERR_INVAL){
fprintf(stderr, "Error: Invalid publish topic '%s', does it contain '+' or '#'?\n", topic);
return 1;
}
cfg->topic = strdup(topic);
}else if(type == CLIENT_RESPONSE_TOPIC){
if(mosquitto_pub_topic_check(topic) == MOSQ_ERR_INVAL){
fprintf(stderr, "Error: Invalid response topic '%s', does it contain '+' or '#'?\n", topic);
return 1;
}
cfg->response_topic = strdup(topic);
}else{
if(mosquitto_sub_topic_check(topic) == MOSQ_ERR_INVAL){
fprintf(stderr, "Error: Invalid subscription topic '%s', are all '+' and '#' wildcards correct?\n", topic);
return 1;
}
cfg->topic_count++;
cfg->topics = realloc(cfg->topics, (size_t )cfg->topic_count*sizeof(char *));
if(!cfg->topics){
err_printf(cfg, "Error: Out of memory.\n");
return 1;
}
cfg->topics[cfg->topic_count-1] = strdup(topic);
}
return 0;
}
/* Process a tokenised single line from a file or set of real argc/argv */
int client_config_line_proc(struct mosq_config *cfg, int pub_or_sub, int argc, char *argv[])
{
int i;
int tmpi;
float f;
size_t szt;
for(i=1; ibind_address = strdup(argv[i+1]);
}
i++;
#ifdef WITH_TLS
}else if(!strcmp(argv[i], "--cafile")){
if(i==argc-1){
fprintf(stderr, "Error: --cafile argument given but no file specified.\n\n");
return 1;
}else{
cfg->cafile = strdup(argv[i+1]);
}
i++;
}else if(!strcmp(argv[i], "--capath")){
if(i==argc-1){
fprintf(stderr, "Error: --capath argument given but no directory specified.\n\n");
return 1;
}else{
cfg->capath = strdup(argv[i+1]);
}
i++;
}else if(!strcmp(argv[i], "--cert")){
if(i==argc-1){
fprintf(stderr, "Error: --cert argument given but no file specified.\n\n");
return 1;
}else{
cfg->certfile = strdup(argv[i+1]);
}
i++;
}else if(!strcmp(argv[i], "--ciphers")){
if(i==argc-1){
fprintf(stderr, "Error: --ciphers argument given but no ciphers specified.\n\n");
return 1;
}else{
cfg->ciphers = strdup(argv[i+1]);
}
i++;
#endif
}else if(!strcmp(argv[i], "-C")){
if(pub_or_sub != CLIENT_SUB){
goto unknown_option;
}else{
if(i==argc-1){
fprintf(stderr, "Error: -C argument given but no count specified.\n\n");
return 1;
}else{
cfg->msg_count = atoi(argv[i+1]);
if(cfg->msg_count < 1){
fprintf(stderr, "Error: Invalid message count \"%d\".\n\n", cfg->msg_count);
return 1;
}
}
i++;
}
}else if(!strcmp(argv[i], "-c") || !strcmp(argv[i], "--disable-clean-session")){
cfg->clean_session = false;
}else if(!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")){
cfg->debug = true;
}else if(!strcmp(argv[i], "-D") || !strcmp(argv[i], "--property")){
i++;
if(cfg_parse_property(cfg, argc, argv, &i)){
return 1;
}
cfg->protocol_version = MQTT_PROTOCOL_V5;
}else if(!strcmp(argv[i], "-e")){
if(pub_or_sub != CLIENT_RR){
goto unknown_option;
}
if(i==argc-1){
fprintf(stderr, "Error: -e argument given but no response topic specified.\n\n");
return 1;
}else{
if(cfg_add_topic(cfg, CLIENT_RESPONSE_TOPIC, argv[i+1], "-e")){
return 1;
}
}
i++;
}else if(!strcmp(argv[i], "-E")){
if(pub_or_sub != CLIENT_SUB){
goto unknown_option;
}
cfg->exit_after_sub = true;
}else if(!strcmp(argv[i], "-f") || !strcmp(argv[i], "--file")){
if(pub_or_sub == CLIENT_SUB){
goto unknown_option;
}
if(cfg->pub_mode != MSGMODE_NONE){
fprintf(stderr, "Error: Only one type of message can be sent at once.\n\n");
return 1;
}else if(i==argc-1){
fprintf(stderr, "Error: -f argument given but no file specified.\n\n");
return 1;
}else{
cfg->pub_mode = MSGMODE_FILE;
cfg->file_input = strdup(argv[i+1]);
if(!cfg->file_input){
err_printf(cfg, "Error: Out of memory.\n");
return 1;
}
}
i++;
}else if(!strcmp(argv[i], "-F")){
if(pub_or_sub == CLIENT_PUB){
goto unknown_option;
}
if(i==argc-1){
fprintf(stderr, "Error: -F argument given but no format specified.\n\n");
return 1;
}else{
cfg->format = strdup(argv[i+1]);
if(!cfg->format){
fprintf(stderr, "Error: Out of memory.\n");
return 1;
}
if(check_format(cfg->format)){
return 1;
}
}
i++;
}else if(!strcmp(argv[i], "--help")){
return 2;
}else if(!strcmp(argv[i], "-h") || !strcmp(argv[i], "--host")){
if(i==argc-1){
fprintf(stderr, "Error: -h argument given but no host specified.\n\n");
return 1;
}else{
cfg->host = strdup(argv[i+1]);
}
i++;
#ifdef WITH_TLS
}else if(!strcmp(argv[i], "--insecure")){
cfg->insecure = true;
#endif
}else if(!strcmp(argv[i], "-i") || !strcmp(argv[i], "--id")){
if(cfg->id_prefix){
fprintf(stderr, "Error: -i and -I argument cannot be used together.\n\n");
return 1;
}
if(i==argc-1){
fprintf(stderr, "Error: -i argument given but no id specified.\n\n");
return 1;
}else{
cfg->id = strdup(argv[i+1]);
}
i++;
}else if(!strcmp(argv[i], "-I") || !strcmp(argv[i], "--id-prefix")){
if(cfg->id){
fprintf(stderr, "Error: -i and -I argument cannot be used together.\n\n");
return 1;
}
if(i==argc-1){
fprintf(stderr, "Error: -I argument given but no id prefix specified.\n\n");
return 1;
}else{
cfg->id_prefix = strdup(argv[i+1]);
}
i++;
}else if(!strcmp(argv[i], "-k") || !strcmp(argv[i], "--keepalive")){
if(i==argc-1){
fprintf(stderr, "Error: -k argument given but no keepalive specified.\n\n");
return 1;
}else{
cfg->keepalive = atoi(argv[i+1]);
if(cfg->keepalive<5 || cfg->keepalive>UINT16_MAX){
fprintf(stderr, "Error: Invalid keepalive given, it must be between 5 and 65535 inclusive.\n\n");
return 1;
}
}
i++;
#ifdef WITH_TLS
}else if(!strcmp(argv[i], "--key")){
if(i==argc-1){
fprintf(stderr, "Error: --key argument given but no file specified.\n\n");
return 1;
}else{
cfg->keyfile = strdup(argv[i+1]);
}
i++;
}else if(!strcmp(argv[i], "--keyform")){
if(i==argc-1){
fprintf(stderr, "Error: --keyform argument given but no keyform specified.\n\n");
return 1;
}else{
cfg->keyform = strdup(argv[i+1]);
}
i++;
#endif
}else if(!strcmp(argv[i], "-L") || !strcmp(argv[i], "--url")){
if(i==argc-1){
fprintf(stderr, "Error: -L argument given but no URL specified.\n\n");
return 1;
} else {
char *url = argv[i+1];
char *topic;
char *tmp;
if(!strncasecmp(url, "mqtt://", 7)) {
url += 7;
cfg->port = 1883;
} else if(!strncasecmp(url, "mqtts://", 8)) {
#ifdef WITH_TLS
url += 8;
cfg->port = 8883;
cfg->tls_use_os_certs = true;
#else
fprintf(stderr, "Error: TLS support not available.\n\n");
return 1;
#endif
} else {
fprintf(stderr, "Error: unsupported URL scheme.\n\n");
return 1;
}
topic = strchr(url, '/');
if(!topic){
fprintf(stderr, "Error: Invalid URL for -L argument specified - topic missing.\n");
return 1;
}
*topic++ = 0;
if(cfg_add_topic(cfg, pub_or_sub, topic, "-L topic"))
return 1;
tmp = strchr(url, '@');
if(tmp) {
char *colon;
*tmp++ = 0;
colon = strchr(url, ':');
if(colon) {
*colon = 0;
cfg->password = strdup(colon + 1);
}
cfg->username = strdup(url);
url = tmp;
}
cfg->host = url;
tmp = strchr(url, ':');
if(tmp) {
*tmp++ = 0;
cfg->port = atoi(tmp);
}
/* Now we've removed the port, time to get the host on the heap */
cfg->host = strdup(cfg->host);
}
i++;
}else if(!strcmp(argv[i], "-l") || !strcmp(argv[i], "--stdin-line")){
if(pub_or_sub != CLIENT_PUB){
goto unknown_option;
}
if(cfg->pub_mode != MSGMODE_NONE){
fprintf(stderr, "Error: Only one type of message can be sent at once.\n\n");
return 1;
}else{
cfg->pub_mode = MSGMODE_STDIN_LINE;
}
}else if(!strcmp(argv[i], "-m") || !strcmp(argv[i], "--message")){
if(pub_or_sub == CLIENT_SUB){
goto unknown_option;
}
if(cfg->pub_mode != MSGMODE_NONE){
fprintf(stderr, "Error: Only one type of message can be sent at once.\n\n");
return 1;
}else if(i==argc-1){
fprintf(stderr, "Error: -m argument given but no message specified.\n\n");
return 1;
}else{
cfg->message = strdup(argv[i+1]);
if(cfg->message == NULL){
fprintf(stderr, "Error: Out of memory.\n\n");
return 1;
}
szt = strlen(cfg->message);
if(szt > MQTT_MAX_PAYLOAD){
fprintf(stderr, "Error: Message length must be less than %u bytes.\n\n", MQTT_MAX_PAYLOAD);
return 1;
}
cfg->msglen = (int )szt;
cfg->pub_mode = MSGMODE_CMD;
}
i++;
}else if(!strcmp(argv[i], "-M")){
if(i==argc-1){
fprintf(stderr, "Error: -M argument given but max_inflight not specified.\n\n");
return 1;
}else{
tmpi = atoi(argv[i+1]);
if(tmpi < 1){
fprintf(stderr, "Error: Maximum inflight messages must be greater than 0.\n\n");
return 1;
}
cfg->max_inflight = (unsigned int )tmpi;
}
i++;
}else if(!strcmp(argv[i], "--nodelay")){
cfg->tcp_nodelay = true;
}else if(!strcmp(argv[i], "-n") || !strcmp(argv[i], "--null-message")){
if(pub_or_sub == CLIENT_SUB){
goto unknown_option;
}
if(cfg->pub_mode != MSGMODE_NONE){
fprintf(stderr, "Error: Only one type of message can be sent at once.\n\n");
return 1;
}else{
cfg->pub_mode = MSGMODE_NULL;
}
}else if(!strcmp(argv[i], "-N")){
if(pub_or_sub == CLIENT_PUB){
goto unknown_option;
}
cfg->eol = false;
}else if(!strcmp(argv[i], "-p") || !strcmp(argv[i], "--port")){
if(i==argc-1){
fprintf(stderr, "Error: -p argument given but no port specified.\n\n");
return 1;
}else{
cfg->port = atoi(argv[i+1]);
if(cfg->port<0 || cfg->port>65535){
fprintf(stderr, "Error: Invalid port given: %d\n", cfg->port);
return 1;
}
}
i++;
}else if(!strcmp(argv[i], "--pretty")){
if(pub_or_sub == CLIENT_PUB){
goto unknown_option;
}
cfg->pretty = true;
}else if(!strcmp(argv[i], "-P") || !strcmp(argv[i], "--pw")){
if(i==argc-1){
fprintf(stderr, "Error: -P argument given but no password specified.\n\n");
return 1;
}else{
cfg->password = strdup(argv[i+1]);
}
i++;
#ifdef WITH_SOCKS
}else if(!strcmp(argv[i], "--proxy")){
if(i==argc-1){
fprintf(stderr, "Error: --proxy argument given but no proxy url specified.\n\n");
return 1;
}else{
if(mosquitto__parse_socks_url(cfg, argv[i+1])){
return 1;
}
i++;
}
#endif
#ifdef FINAL_WITH_TLS_PSK
}else if(!strcmp(argv[i], "--psk")){
if(i==argc-1){
fprintf(stderr, "Error: --psk argument given but no key specified.\n\n");
return 1;
}else{
cfg->psk = strdup(argv[i+1]);
}
i++;
}else if(!strcmp(argv[i], "--psk-identity")){
if(i==argc-1){
fprintf(stderr, "Error: --psk-identity argument given but no identity specified.\n\n");
return 1;
}else{
cfg->psk_identity = strdup(argv[i+1]);
}
i++;
#endif
}else if(!strcmp(argv[i], "-q") || !strcmp(argv[i], "--qos")){
if(i==argc-1){
fprintf(stderr, "Error: -q argument given but no QoS specified.\n\n");
return 1;
}else{
cfg->qos = atoi(argv[i+1]);
if(cfg->qos<0 || cfg->qos>2){
fprintf(stderr, "Error: Invalid QoS given: %d\n", cfg->qos);
return 1;
}
}
i++;
}else if(!strcmp(argv[i], "--quiet")){
cfg->quiet = true;
}else if(!strcmp(argv[i], "-r") || !strcmp(argv[i], "--retain")){
if(pub_or_sub != CLIENT_PUB){
goto unknown_option;
}
cfg->retain = 1;
}else if(!strcmp(argv[i], "-R")){
if(pub_or_sub == CLIENT_PUB){
goto unknown_option;
}
cfg->no_retain = true;
cfg->sub_opts |= MQTT_SUB_OPT_SEND_RETAIN_NEVER;
}else if(!strcmp(argv[i], "--random-filter")){
if(pub_or_sub != CLIENT_SUB){
goto unknown_option;
}
if(i==argc-1){
fprintf(stderr, "Error: --random-filter argument given but no chance specified.\n\n");
return 1;
}else{
cfg->random_filter = (int)(10.0*atof(argv[i+1]));
if(cfg->random_filter > 10000 || cfg->random_filter < 1){
fprintf(stderr, "Error: --random-filter chance must be between 0.1-100.0\n\n");
return 1;
}
}
i++;
}else if(!strcmp(argv[i], "--remove-retained")){
if(pub_or_sub != CLIENT_SUB){
goto unknown_option;
}
cfg->remove_retained = true;
}else if(!strcmp(argv[i], "--repeat")){
if(pub_or_sub != CLIENT_PUB){
goto unknown_option;
}
if(i==argc-1){
fprintf(stderr, "Error: --repeat argument given but no count specified.\n\n");
return 1;
}else{
cfg->repeat_count = atoi(argv[i+1]);
if(cfg->repeat_count < 1){
fprintf(stderr, "Error: --repeat argument must be >0.\n\n");
return 1;
}
}
i++;
}else if(!strcmp(argv[i], "--repeat-delay")){
if(pub_or_sub != CLIENT_PUB){
goto unknown_option;
}
if(i==argc-1){
fprintf(stderr, "Error: --repeat-delay argument given but no time specified.\n\n");
return 1;
}else{
f = (float )atof(argv[i+1]);
if(f < 0.0f){
fprintf(stderr, "Error: --repeat-delay argument must be >=0.0.\n\n");
return 1;
}
f *= 1.0e6f;
cfg->repeat_delay.tv_sec = (int)f/1000000;
cfg->repeat_delay.tv_usec = (int)f%1000000;
}
i++;
}else if(!strcmp(argv[i], "--retain-as-published")){
if(pub_or_sub == CLIENT_PUB){
goto unknown_option;
}
cfg->sub_opts |= MQTT_SUB_OPT_RETAIN_AS_PUBLISHED;
}else if(!strcmp(argv[i], "--retained-only")){
if(pub_or_sub != CLIENT_SUB){
goto unknown_option;
}
cfg->retained_only = true;
}else if(!strcmp(argv[i], "-s") || !strcmp(argv[i], "--stdin-file")){
if(pub_or_sub == CLIENT_SUB){
goto unknown_option;
}
if(cfg->pub_mode != MSGMODE_NONE){
fprintf(stderr, "Error: Only one type of message can be sent at once.\n\n");
return 1;
}else{
cfg->pub_mode = MSGMODE_STDIN_FILE;
}
#ifdef WITH_SRV
}else if(!strcmp(argv[i], "-S")){
cfg->use_srv = true;
#endif
}else if(!strcmp(argv[i], "-t") || !strcmp(argv[i], "--topic")){
if(i==argc-1){
fprintf(stderr, "Error: -t argument given but no topic specified.\n\n");
return 1;
}else{
if(cfg_add_topic(cfg, pub_or_sub, argv[i + 1], "-t"))
return 1;
i++;
}
}else if(!strcmp(argv[i], "-T") || !strcmp(argv[i], "--filter-out")){
if(pub_or_sub != CLIENT_SUB){
goto unknown_option;
}
if(i==argc-1){
fprintf(stderr, "Error: -T argument given but no topic filter specified.\n\n");
return 1;
}else{
if(mosquitto_validate_utf8(argv[i+1], (int )strlen(argv[i+1]))){
fprintf(stderr, "Error: Malformed UTF-8 in -T argument.\n\n");
return 1;
}
if(mosquitto_sub_topic_check(argv[i+1]) == MOSQ_ERR_INVAL){
fprintf(stderr, "Error: Invalid filter topic '%s', are all '+' and '#' wildcards correct?\n", argv[i+1]);
return 1;
}
cfg->filter_out_count++;
cfg->filter_outs = realloc(cfg->filter_outs, (size_t )cfg->filter_out_count*sizeof(char *));
if(!cfg->filter_outs){
fprintf(stderr, "Error: Out of memory.\n");
return 1;
}
cfg->filter_outs[cfg->filter_out_count-1] = strdup(argv[i+1]);
}
i++;
#ifdef WITH_TLS
}else if(!strcmp(argv[i], "--tls-alpn")){
if(i==argc-1){
fprintf(stderr, "Error: --tls-alpn argument given but no protocol specified.\n\n");
return 1;
}else{
cfg->tls_alpn = strdup(argv[i+1]);
}
i++;
}else if(!strcmp(argv[i], "--tls-engine")){
if(i==argc-1){
fprintf(stderr, "Error: --tls-engine argument given but no engine_id specified.\n\n");
return 1;
}else{
cfg->tls_engine = strdup(argv[i+1]);
}
i++;
}else if(!strcmp(argv[i], "--tls-engine-kpass-sha1")){
if(i==argc-1){
fprintf(stderr, "Error: --tls-engine-kpass-sha1 argument given but no kpass sha1 specified.\n\n");
return 1;
}else{
cfg->tls_engine_kpass_sha1 = strdup(argv[i+1]);
}
i++;
}else if(!strcmp(argv[i], "--tls-use-os-certs")){
cfg->tls_use_os_certs = true;
}else if(!strcmp(argv[i], "--tls-version")){
if(i==argc-1){
fprintf(stderr, "Error: --tls-version argument given but no version specified.\n\n");
return 1;
}else{
cfg->tls_version = strdup(argv[i+1]);
}
i++;
#endif
}else if(!strcmp(argv[i], "-U") || !strcmp(argv[i], "--unsubscribe")){
if(pub_or_sub != CLIENT_SUB){
goto unknown_option;
}
if(i==argc-1){
fprintf(stderr, "Error: -U argument given but no unsubscribe topic specified.\n\n");
return 1;
}else{
if(mosquitto_validate_utf8(argv[i+1], (int )strlen(argv[i+1]))){
fprintf(stderr, "Error: Malformed UTF-8 in -U argument.\n\n");
return 1;
}
if(mosquitto_sub_topic_check(argv[i+1]) == MOSQ_ERR_INVAL){
fprintf(stderr, "Error: Invalid unsubscribe topic '%s', are all '+' and '#' wildcards correct?\n", argv[i+1]);
return 1;
}
cfg->unsub_topic_count++;
cfg->unsub_topics = realloc(cfg->unsub_topics, (size_t )cfg->unsub_topic_count*sizeof(char *));
if(!cfg->unsub_topics){
fprintf(stderr, "Error: Out of memory.\n");
return 1;
}
cfg->unsub_topics[cfg->unsub_topic_count-1] = strdup(argv[i+1]);
}
i++;
}else if(!strcmp(argv[i], "-u") || !strcmp(argv[i], "--username")){
if(i==argc-1){
fprintf(stderr, "Error: -u argument given but no username specified.\n\n");
return 1;
}else{
cfg->username = strdup(argv[i+1]);
}
i++;
}else if(!strcmp(argv[i], "--unix")){
if(i==argc-1){
fprintf(stderr, "Error: --unix argument given but no socket path specified.\n\n");
return 1;
}else{
cfg->host = strdup(argv[i+1]);
cfg->port = 0;
}
i++;
}else if(!strcmp(argv[i], "-V") || !strcmp(argv[i], "--protocol-version")){
if(i==argc-1){
fprintf(stderr, "Error: --protocol-version argument given but no version specified.\n\n");
return 1;
}else{
if(!strcmp(argv[i+1], "mqttv31") || !strcmp(argv[i+1], "31")){
cfg->protocol_version = MQTT_PROTOCOL_V31;
}else if(!strcmp(argv[i+1], "mqttv311") || !strcmp(argv[i+1], "311")){
cfg->protocol_version = MQTT_PROTOCOL_V311;
}else if(!strcmp(argv[i+1], "mqttv5") || !strcmp(argv[i+1], "5")){
cfg->protocol_version = MQTT_PROTOCOL_V5;
}else{
fprintf(stderr, "Error: Invalid protocol version argument given.\n\n");
return 1;
}
i++;
}
}else if(!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose")){
if(pub_or_sub == CLIENT_PUB){
goto unknown_option;
}
cfg->verbose = 1;
}else if(!strcmp(argv[i], "--version")){
return 3;
}else if(!strcmp(argv[i], "-W")){
if(pub_or_sub == CLIENT_PUB){
goto unknown_option;
}else{
if(i==argc-1){
fprintf(stderr, "Error: -W argument given but no timeout specified.\n\n");
return 1;
}else{
tmpi = atoi(argv[i+1]);
if(tmpi < 1){
fprintf(stderr, "Error: Invalid timeout \"%d\".\n\n", tmpi);
return 1;
}
cfg->timeout = (unsigned int )tmpi;
}
i++;
}
}else if(!strcmp(argv[i], "--will-payload")){
if(i==argc-1){
fprintf(stderr, "Error: --will-payload argument given but no will payload specified.\n\n");
return 1;
}else{
cfg->will_payload = strdup(argv[i+1]);
cfg->will_payloadlen = (int )strlen(cfg->will_payload);
}
i++;
}else if(!strcmp(argv[i], "--will-qos")){
if(i==argc-1){
fprintf(stderr, "Error: --will-qos argument given but no will QoS specified.\n\n");
return 1;
}else{
cfg->will_qos = atoi(argv[i+1]);
if(cfg->will_qos < 0 || cfg->will_qos > 2){
fprintf(stderr, "Error: Invalid will QoS %d.\n\n", cfg->will_qos);
return 1;
}
}
i++;
}else if(!strcmp(argv[i], "--will-retain")){
cfg->will_retain = true;
}else if(!strcmp(argv[i], "--will-topic")){
if(i==argc-1){
fprintf(stderr, "Error: --will-topic argument given but no will topic specified.\n\n");
return 1;
}else{
if(mosquitto_validate_utf8(argv[i+1], (int )strlen(argv[i+1]))){
fprintf(stderr, "Error: Malformed UTF-8 in --will-topic argument.\n\n");
return 1;
}
if(mosquitto_pub_topic_check(argv[i+1]) == MOSQ_ERR_INVAL){
fprintf(stderr, "Error: Invalid will topic '%s', does it contain '+' or '#'?\n", argv[i+1]);
return 1;
}
cfg->will_topic = strdup(argv[i+1]);
}
i++;
}else if(!strcmp(argv[i], "-x")){
if(i==argc-1){
fprintf(stderr, "Error: -x argument given but no session expiry interval specified.\n\n");
return 1;
}else{
if(!strcmp(argv[i+1], "∞")){
cfg->session_expiry_interval = UINT32_MAX;
}else{
char *endptr = NULL;
cfg->session_expiry_interval = strtol(argv[i+1], &endptr, 0);
if(endptr == argv[i+1] || endptr[0] != '\0'){
/* Entirety of argument wasn't a number */
fprintf(stderr, "Error: session-expiry-interval not a number.\n\n");
return 1;
}
if(cfg->session_expiry_interval > UINT32_MAX || cfg->session_expiry_interval < -1){
fprintf(stderr, "Error: session-expiry-interval out of range.\n\n");
return 1;
}
if(cfg->session_expiry_interval == -1){
/* Convenience value for infinity. */
cfg->session_expiry_interval = UINT32_MAX;
}
}
}
i++;
}else{
goto unknown_option;
}
}
return MOSQ_ERR_SUCCESS;
unknown_option:
fprintf(stderr, "Error: Unknown option '%s'.\n",argv[i]);
return 1;
}
int client_opts_set(struct mosquitto *mosq, struct mosq_config *cfg)
{
#if defined(WITH_TLS) || defined(WITH_SOCKS)
int rc;
#endif
mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, cfg->protocol_version);
if(cfg->will_topic && mosquitto_will_set_v5(mosq, cfg->will_topic,
cfg->will_payloadlen, cfg->will_payload, cfg->will_qos,
cfg->will_retain, cfg->will_props)){
err_printf(cfg, "Error: Problem setting will.\n");
mosquitto_lib_cleanup();
return 1;
}
cfg->will_props = NULL;
if((cfg->username || cfg->password) && mosquitto_username_pw_set(mosq, cfg->username, cfg->password)){
err_printf(cfg, "Error: Problem setting username and/or password.\n");
mosquitto_lib_cleanup();
return 1;
}
#ifdef WITH_TLS
if(cfg->keyform && mosquitto_string_option(mosq, MOSQ_OPT_TLS_KEYFORM, cfg->keyform)){
err_printf(cfg, "Error: Problem setting key form, it must be one of 'pem' or 'engine'.\n");
mosquitto_lib_cleanup();
return 1;
}
if(cfg->cafile || cfg->capath){
rc = mosquitto_tls_set(mosq, cfg->cafile, cfg->capath, cfg->certfile, cfg->keyfile, NULL);
if(rc){
if(rc == MOSQ_ERR_INVAL){
err_printf(cfg, "Error: Problem setting TLS options: File not found.\n");
}else{
err_printf(cfg, "Error: Problem setting TLS options: %s.\n", mosquitto_strerror(rc));
}
mosquitto_lib_cleanup();
return 1;
}
# ifdef FINAL_WITH_TLS_PSK
}else if(cfg->psk){
if(mosquitto_tls_psk_set(mosq, cfg->psk, cfg->psk_identity, NULL)){
err_printf(cfg, "Error: Problem setting TLS-PSK options.\n");
mosquitto_lib_cleanup();
return 1;
}
# endif
}else if(cfg->port == 8883){
mosquitto_int_option(mosq, MOSQ_OPT_TLS_USE_OS_CERTS, 1);
}
if(cfg->tls_use_os_certs){
mosquitto_int_option(mosq, MOSQ_OPT_TLS_USE_OS_CERTS, 1);
}
if(cfg->insecure && mosquitto_tls_insecure_set(mosq, true)){
err_printf(cfg, "Error: Problem setting TLS insecure option.\n");
mosquitto_lib_cleanup();
return 1;
}
if(cfg->tls_engine && mosquitto_string_option(mosq, MOSQ_OPT_TLS_ENGINE, cfg->tls_engine)){
err_printf(cfg, "Error: Problem setting TLS engine, is %s a valid engine?\n", cfg->tls_engine);
mosquitto_lib_cleanup();
return 1;
}
if(cfg->tls_engine_kpass_sha1 && mosquitto_string_option(mosq, MOSQ_OPT_TLS_ENGINE_KPASS_SHA1, cfg->tls_engine_kpass_sha1)){
err_printf(cfg, "Error: Problem setting TLS engine key pass sha, is it a 40 character hex string?\n");
mosquitto_lib_cleanup();
return 1;
}
if(cfg->tls_alpn && mosquitto_string_option(mosq, MOSQ_OPT_TLS_ALPN, cfg->tls_alpn)){
err_printf(cfg, "Error: Problem setting TLS ALPN protocol.\n");
mosquitto_lib_cleanup();
return 1;
}
if((cfg->tls_version || cfg->ciphers) && mosquitto_tls_opts_set(mosq, 1, cfg->tls_version, cfg->ciphers)){
err_printf(cfg, "Error: Problem setting TLS options, check the options are valid.\n");
mosquitto_lib_cleanup();
return 1;
}
#endif
mosquitto_max_inflight_messages_set(mosq, cfg->max_inflight);
#ifdef WITH_SOCKS
if(cfg->socks5_host){
rc = mosquitto_socks5_set(mosq, cfg->socks5_host, cfg->socks5_port, cfg->socks5_username, cfg->socks5_password);
if(rc){
mosquitto_lib_cleanup();
return rc;
}
}
#endif
if(cfg->tcp_nodelay){
mosquitto_int_option(mosq, MOSQ_OPT_TCP_NODELAY, 1);
}
if(cfg->msg_count > 0 && cfg->msg_count < 20){
/* 20 is the default "receive maximum"
* If we don't set this, then we can receive > msg_count messages
* before we quit.*/
mosquitto_int_option(mosq, MOSQ_OPT_RECEIVE_MAXIMUM, cfg->msg_count);
}
return MOSQ_ERR_SUCCESS;
}
int client_id_generate(struct mosq_config *cfg)
{
if(cfg->id_prefix){
cfg->id = malloc(strlen(cfg->id_prefix)+10);
if(!cfg->id){
err_printf(cfg, "Error: Out of memory.\n");
mosquitto_lib_cleanup();
return 1;
}
snprintf(cfg->id, strlen(cfg->id_prefix)+10, "%s%d", cfg->id_prefix, getpid());
}
return MOSQ_ERR_SUCCESS;
}
int client_connect(struct mosquitto *mosq, struct mosq_config *cfg)
{
#ifndef WIN32
char *err;
#else
char err[1024];
#endif
int rc;
int port;
if(cfg->port == PORT_UNDEFINED){
#ifdef WITH_TLS
if(cfg->cafile || cfg->capath
# ifdef FINAL_WITH_TLS_PSK
|| cfg->psk
# endif
){
port = 8883;
}else
#endif
{
port = 1883;
}
}else{
port = cfg->port;
}
#ifdef WITH_SRV
if(cfg->use_srv){
rc = mosquitto_connect_srv(mosq, cfg->host, cfg->keepalive, cfg->bind_address);
}else{
rc = mosquitto_connect_bind_v5(mosq, cfg->host, port, cfg->keepalive, cfg->bind_address, cfg->connect_props);
}
#else
rc = mosquitto_connect_bind_v5(mosq, cfg->host, port, cfg->keepalive, cfg->bind_address, cfg->connect_props);
#endif
if(rc>0){
if(rc == MOSQ_ERR_ERRNO){
#ifndef WIN32
err = strerror(errno);
#else
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, errno, 0, (LPTSTR)&err, 1024, NULL);
#endif
err_printf(cfg, "Error: %s\n", err);
}else{
err_printf(cfg, "Unable to connect (%s).\n", mosquitto_strerror(rc));
}
mosquitto_lib_cleanup();
return rc;
}
return MOSQ_ERR_SUCCESS;
}
#ifdef WITH_SOCKS
/* Convert %25 -> %, %3a, %3A -> :, %40 -> @ */
static int mosquitto__urldecode(char *str)
{
size_t i, j;
size_t len;
if(!str) return 0;
if(!strchr(str, '%')) return 0;
len = strlen(str);
for(i=0; i= len){
return 1;
}
if(str[i+1] == '2' && str[i+2] == '5'){
str[i] = '%';
len -= 2;
for(j=i+1; j start){
len = i-start;
if(host){
/* Have already seen a @ , so this must be of form
* socks5h://username[:password]@host:port */
port = malloc(len + 1);
if(!port){
err_printf(cfg, "Error: Out of memory.\n");
goto cleanup;
}
memcpy(port, &(str[start]), len);
port[len] = '\0';
}else if(username_or_host){
/* Haven't seen a @ before, so must be of form
* socks5h://host:port */
host = username_or_host;
username_or_host = NULL;
port = malloc(len + 1);
if(!port){
err_printf(cfg, "Error: Out of memory.\n");
goto cleanup;
}
memcpy(port, &(str[start]), len);
port[len] = '\0';
}else{
host = malloc(len + 1);
if(!host){
err_printf(cfg, "Error: Out of memory.\n");
goto cleanup;
}
memcpy(host, &(str[start]), len);
host[len] = '\0';
}
}
if(!host){
err_printf(cfg, "Error: Invalid proxy.\n");
goto cleanup;
}
if(mosquitto__urldecode(username)){
goto cleanup;
}
if(mosquitto__urldecode(password)){
goto cleanup;
}
if(port){
port_int = atoi(port);
if(port_int < 1 || port_int > 65535){
err_printf(cfg, "Error: Invalid proxy port %d\n", port_int);
goto cleanup;
}
free(port);
}else{
port_int = 1080;
}
cfg->socks5_username = username;
cfg->socks5_password = password;
cfg->socks5_host = host;
cfg->socks5_port = port_int;
return 0;
cleanup:
free(username_or_host);
free(username);
free(password);
free(host);
free(port);
return 1;
}
#endif
void err_printf(const struct mosq_config *cfg, const char *fmt, ...)
{
va_list va;
if(cfg->quiet) return;
va_start(va, fmt);
vfprintf(stderr, fmt, va);
va_end(va);
}
mosquitto-2.0.18/client/client_shared.h 0000664 0000000 0000000 00000006736 14502137606 0020121 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 2014-2020 Roger Light
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License 2.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
https://www.eclipse.org/legal/epl-2.0/
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
Contributors:
Roger Light - initial implementation and documentation.
*/
#ifndef CLIENT_CONFIG_H
#define CLIENT_CONFIG_H
#include
#ifdef WIN32
# include
#else
# include
#endif
#ifndef __GNUC__
#define __attribute__(attrib)
#endif
/* pub_client.c modes */
#define MSGMODE_NONE 0
#define MSGMODE_CMD 1
#define MSGMODE_STDIN_LINE 2
#define MSGMODE_STDIN_FILE 3
#define MSGMODE_FILE 4
#define MSGMODE_NULL 5
#define CLIENT_PUB 1
#define CLIENT_SUB 2
#define CLIENT_RR 3
#define CLIENT_RESPONSE_TOPIC 4
#define PORT_UNDEFINED -1
#define PORT_UNIX 0
struct mosq_config {
char *id;
char *id_prefix;
int protocol_version;
int keepalive;
char *host;
int port;
int qos;
bool retain;
int pub_mode; /* pub, rr */
char *file_input; /* pub, rr */
char *message; /* pub, rr */
int msglen; /* pub, rr */
char *topic; /* pub, rr */
char *bind_address;
int repeat_count; /* pub */
struct timeval repeat_delay; /* pub */
#ifdef WITH_SRV
bool use_srv;
#endif
bool debug;
bool quiet;
unsigned int max_inflight;
char *username;
char *password;
char *will_topic;
char *will_payload;
int will_payloadlen;
int will_qos;
bool will_retain;
#ifdef WITH_TLS
char *cafile;
char *capath;
char *certfile;
char *keyfile;
char *ciphers;
bool insecure;
char *tls_alpn;
char *tls_version;
char *tls_engine;
char *tls_engine_kpass_sha1;
char *keyform;
bool tls_use_os_certs;
# ifdef FINAL_WITH_TLS_PSK
char *psk;
char *psk_identity;
# endif
#endif
bool clean_session;
char **topics; /* sub, rr */
int topic_count; /* sub, rr */
bool exit_after_sub; /* sub */
bool no_retain; /* sub */
bool retained_only; /* sub */
bool remove_retained; /* sub */
char **filter_outs; /* sub */
int filter_out_count; /* sub */
char **unsub_topics; /* sub */
int unsub_topic_count; /* sub */
bool verbose; /* sub */
bool eol; /* sub */
int msg_count; /* sub */
char *format; /* sub, rr */
bool pretty; /* sub, rr */
unsigned int timeout; /* sub */
int sub_opts; /* sub */
long session_expiry_interval;
int random_filter; /* sub */
#ifdef WITH_SOCKS
char *socks5_host;
int socks5_port;
char *socks5_username;
char *socks5_password;
#endif
mosquitto_property *connect_props;
mosquitto_property *publish_props;
mosquitto_property *subscribe_props;
mosquitto_property *unsubscribe_props;
mosquitto_property *disconnect_props;
mosquitto_property *will_props;
bool have_topic_alias; /* pub */
char *response_topic; /* rr */
bool tcp_nodelay;
};
int client_config_load(struct mosq_config *config, int pub_or_sub, int argc, char *argv[]);
void client_config_cleanup(struct mosq_config *cfg);
int client_opts_set(struct mosquitto *mosq, struct mosq_config *cfg);
int client_id_generate(struct mosq_config *cfg);
int client_connect(struct mosquitto *mosq, struct mosq_config *cfg);
int cfg_parse_property(struct mosq_config *cfg, int argc, char *argv[], int *idx);
void err_printf(const struct mosq_config *cfg, const char *fmt, ...) __attribute__((format(printf, 2, 3)));
#endif
mosquitto-2.0.18/client/pub_client.c 0000664 0000000 0000000 00000046626 14502137606 0017436 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 2009-2020 Roger Light
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License 2.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
https://www.eclipse.org/legal/epl-2.0/
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include
#include
#include
#include
#include
#ifndef WIN32
#include
#include
#else
#include
#include
#define snprintf sprintf_s
#endif
#include
#include
#include "client_shared.h"
#include "pub_shared.h"
/* Global variables for use in callbacks. See sub_client.c for an example of
* using a struct to hold variables for use in callbacks. */
static bool first_publish = true;
static int last_mid = -1;
static int last_mid_sent = -1;
static char *line_buf = NULL;
static int line_buf_len = 1024;
static bool disconnect_sent = false;
static int publish_count = 0;
static bool ready_for_repeat = false;
static volatile int status = STATUS_CONNECTING;
static int connack_result = 0;
#ifdef WIN32
static uint64_t next_publish_tv;
static void set_repeat_time(void)
{
uint64_t ticks = GetTickCount64();
next_publish_tv = ticks + cfg.repeat_delay.tv_sec*1000 + cfg.repeat_delay.tv_usec/1000;
}
static int check_repeat_time(void)
{
uint64_t ticks = GetTickCount64();
if(ticks > next_publish_tv){
return 1;
}else{
return 0;
}
}
#else
static struct timeval next_publish_tv;
static void set_repeat_time(void)
{
gettimeofday(&next_publish_tv, NULL);
next_publish_tv.tv_sec += cfg.repeat_delay.tv_sec;
next_publish_tv.tv_usec += cfg.repeat_delay.tv_usec;
next_publish_tv.tv_sec += next_publish_tv.tv_usec/1000000;
next_publish_tv.tv_usec = next_publish_tv.tv_usec%1000000;
}
static int check_repeat_time(void)
{
struct timeval tv;
gettimeofday(&tv, NULL);
if(tv.tv_sec > next_publish_tv.tv_sec){
return 1;
}else if(tv.tv_sec == next_publish_tv.tv_sec
&& tv.tv_usec > next_publish_tv.tv_usec){
return 1;
}
return 0;
}
#endif
void my_disconnect_callback(struct mosquitto *mosq, void *obj, int rc, const mosquitto_property *properties)
{
UNUSED(mosq);
UNUSED(obj);
UNUSED(rc);
UNUSED(properties);
if(rc == 0){
status = STATUS_DISCONNECTED;
}
}
int my_publish(struct mosquitto *mosq, int *mid, const char *topic, int payloadlen, void *payload, int qos, bool retain)
{
ready_for_repeat = false;
if(cfg.protocol_version == MQTT_PROTOCOL_V5 && cfg.have_topic_alias && first_publish == false){
return mosquitto_publish_v5(mosq, mid, NULL, payloadlen, payload, qos, retain, cfg.publish_props);
}else{
first_publish = false;
return mosquitto_publish_v5(mosq, mid, topic, payloadlen, payload, qos, retain, cfg.publish_props);
}
}
void my_connect_callback(struct mosquitto *mosq, void *obj, int result, int flags, const mosquitto_property *properties)
{
int rc = MOSQ_ERR_SUCCESS;
UNUSED(obj);
UNUSED(flags);
UNUSED(properties);
connack_result = result;
if(!result){
first_publish = true;
switch(cfg.pub_mode){
case MSGMODE_CMD:
case MSGMODE_FILE:
case MSGMODE_STDIN_FILE:
rc = my_publish(mosq, &mid_sent, cfg.topic, cfg.msglen, cfg.message, cfg.qos, cfg.retain);
break;
case MSGMODE_NULL:
rc = my_publish(mosq, &mid_sent, cfg.topic, 0, NULL, cfg.qos, cfg.retain);
break;
case MSGMODE_STDIN_LINE:
status = STATUS_CONNACK_RECVD;
break;
}
if(rc){
switch(rc){
case MOSQ_ERR_INVAL:
err_printf(&cfg, "Error: Invalid input. Does your topic contain '+' or '#'?\n");
break;
case MOSQ_ERR_NOMEM:
err_printf(&cfg, "Error: Out of memory when trying to publish message.\n");
break;
case MOSQ_ERR_NO_CONN:
err_printf(&cfg, "Error: Client not connected when trying to publish.\n");
break;
case MOSQ_ERR_PROTOCOL:
err_printf(&cfg, "Error: Protocol error when communicating with broker.\n");
break;
case MOSQ_ERR_PAYLOAD_SIZE:
err_printf(&cfg, "Error: Message payload is too large.\n");
break;
case MOSQ_ERR_QOS_NOT_SUPPORTED:
err_printf(&cfg, "Error: Message QoS not supported on broker, try a lower QoS.\n");
break;
}
mosquitto_disconnect_v5(mosq, 0, cfg.disconnect_props);
}
}else{
if(result){
if(cfg.protocol_version == MQTT_PROTOCOL_V5){
if(result == MQTT_RC_UNSUPPORTED_PROTOCOL_VERSION){
err_printf(&cfg, "Connection error: %s. Try connecting to an MQTT v5 broker, or use MQTT v3.x mode.\n", mosquitto_reason_string(result));
}else{
err_printf(&cfg, "Connection error: %s\n", mosquitto_reason_string(result));
}
}else{
err_printf(&cfg, "Connection error: %s\n", mosquitto_connack_string(result));
}
/* let the loop know that this is an unrecoverable connection */
status = STATUS_NOHOPE;
}
}
}
void my_publish_callback(struct mosquitto *mosq, void *obj, int mid, int reason_code, const mosquitto_property *properties)
{
char *reason_string = NULL;
UNUSED(obj);
UNUSED(properties);
last_mid_sent = mid;
if(reason_code > 127){
err_printf(&cfg, "Warning: Publish %d failed: %s.\n", mid, mosquitto_reason_string(reason_code));
mosquitto_property_read_string(properties, MQTT_PROP_REASON_STRING, &reason_string, false);
if(reason_string){
err_printf(&cfg, "%s\n", reason_string);
free(reason_string);
}
}
publish_count++;
if(cfg.pub_mode == MSGMODE_STDIN_LINE){
if(mid == last_mid){
mosquitto_disconnect_v5(mosq, 0, cfg.disconnect_props);
disconnect_sent = true;
}
}else if(publish_count < cfg.repeat_count){
ready_for_repeat = true;
set_repeat_time();
}else if(disconnect_sent == false){
mosquitto_disconnect_v5(mosq, 0, cfg.disconnect_props);
disconnect_sent = true;
}
}
int pub_shared_init(void)
{
line_buf = malloc((size_t )line_buf_len);
if(!line_buf){
err_printf(&cfg, "Error: Out of memory.\n");
return 1;
}
return 0;
}
static int pub_stdin_line_loop(struct mosquitto *mosq)
{
char *buf2;
int buf_len_actual = 0;
int pos;
int rc = MOSQ_ERR_SUCCESS;
int read_len;
bool stdin_finished = false;
mosquitto_loop_start(mosq);
stdin_finished = false;
do{
if(status == STATUS_CONNECTING){
#ifdef WIN32
Sleep(100);
#else
struct timespec ts;
ts.tv_sec = 0;
ts.tv_nsec = 100000000;
nanosleep(&ts, NULL);
#endif
}
if(status == STATUS_NOHOPE){
return MOSQ_ERR_CONN_REFUSED;
}
if(status == STATUS_CONNACK_RECVD){
pos = 0;
read_len = line_buf_len;
while(status == STATUS_CONNACK_RECVD && fgets(&line_buf[pos], read_len, stdin)){
buf_len_actual = (int )strlen(line_buf);
if(line_buf[buf_len_actual-1] == '\n'){
line_buf[buf_len_actual-1] = '\0';
rc = my_publish(mosq, &mid_sent, cfg.topic, buf_len_actual-1, line_buf, cfg.qos, cfg.retain);
pos = 0;
if(rc != MOSQ_ERR_SUCCESS && rc != MOSQ_ERR_NO_CONN){
return rc;
}
break;
}else{
line_buf_len += 1024;
pos += read_len-1;
read_len = 1024;
buf2 = realloc(line_buf, (size_t )line_buf_len);
if(!buf2){
err_printf(&cfg, "Error: Out of memory.\n");
return MOSQ_ERR_NOMEM;
}
line_buf = buf2;
}
}
if(pos != 0){
rc = my_publish(mosq, &mid_sent, cfg.topic, buf_len_actual, line_buf, cfg.qos, cfg.retain);
if(rc){
if(cfg.qos>0) return rc;
}
}
if(feof(stdin)){
if(mid_sent == -1){
/* Empty file */
mosquitto_disconnect_v5(mosq, 0, cfg.disconnect_props);
disconnect_sent = true;
status = STATUS_DISCONNECTING;
}else{
last_mid = mid_sent;
status = STATUS_WAITING;
}
stdin_finished = true;
}else if(status == STATUS_DISCONNECTED){
/* Not end of stdin, so we've lost our connection and must
* reconnect */
}
}
if(status == STATUS_WAITING){
if(last_mid_sent == last_mid && disconnect_sent == false){
mosquitto_disconnect_v5(mosq, 0, cfg.disconnect_props);
disconnect_sent = true;
}
#ifdef WIN32
Sleep(100);
#else
struct timespec ts;
ts.tv_sec = 0;
ts.tv_nsec = 100000000;
nanosleep(&ts, NULL);
#endif
}
}while(stdin_finished == false);
mosquitto_loop_stop(mosq, false);
if(status == STATUS_DISCONNECTED){
return MOSQ_ERR_SUCCESS;
}else{
return rc;
}
}
static int pub_other_loop(struct mosquitto *mosq)
{
int rc;
int loop_delay = 1000;
if(cfg.repeat_count > 1 && (cfg.repeat_delay.tv_sec == 0 || cfg.repeat_delay.tv_usec != 0)){
loop_delay = (int )cfg.repeat_delay.tv_usec / 2000;
}
do{
rc = mosquitto_loop(mosq, loop_delay, 1);
if(ready_for_repeat && check_repeat_time()){
rc = MOSQ_ERR_SUCCESS;
switch(cfg.pub_mode){
case MSGMODE_CMD:
case MSGMODE_FILE:
case MSGMODE_STDIN_FILE:
rc = my_publish(mosq, &mid_sent, cfg.topic, cfg.msglen, cfg.message, cfg.qos, cfg.retain);
break;
case MSGMODE_NULL:
rc = my_publish(mosq, &mid_sent, cfg.topic, 0, NULL, cfg.qos, cfg.retain);
break;
}
if(rc != MOSQ_ERR_SUCCESS && rc != MOSQ_ERR_NO_CONN){
err_printf(&cfg, "Error sending repeat publish: %s", mosquitto_strerror(rc));
}
}
}while(rc == MOSQ_ERR_SUCCESS);
if(status == STATUS_DISCONNECTED){
return MOSQ_ERR_SUCCESS;
}else{
return rc;
}
}
int pub_shared_loop(struct mosquitto *mosq)
{
if(cfg.pub_mode == MSGMODE_STDIN_LINE){
return pub_stdin_line_loop(mosq);
}else{
return pub_other_loop(mosq);
}
}
void pub_shared_cleanup(void)
{
free(line_buf);
}
static void print_version(void)
{
int major, minor, revision;
mosquitto_lib_version(&major, &minor, &revision);
printf("mosquitto_pub version %s running on libmosquitto %d.%d.%d.\n", VERSION, major, minor, revision);
}
static void print_usage(void)
{
int major, minor, revision;
mosquitto_lib_version(&major, &minor, &revision);
printf("mosquitto_pub is a simple mqtt client that will publish a message on a single topic and exit.\n");
printf("mosquitto_pub version %s running on libmosquitto %d.%d.%d.\n\n", VERSION, major, minor, revision);
printf("Usage: mosquitto_pub {[-h host] [--unix path] [-p port] [-u username] [-P password] -t topic | -L URL}\n");
printf(" {-f file | -l | -n | -m message}\n");
printf(" [-c] [-k keepalive] [-q qos] [-r] [--repeat N] [--repeat-delay time] [-x session-expiry]\n");
#ifdef WITH_SRV
printf(" [-A bind_address] [--nodelay] [-S]\n");
#else
printf(" [-A bind_address] [--nodelay]\n");
#endif
printf(" [-i id] [-I id_prefix]\n");
printf(" [-d] [--quiet]\n");
printf(" [-M max_inflight]\n");
printf(" [-u username [-P password]]\n");
printf(" [--will-topic [--will-payload payload] [--will-qos qos] [--will-retain]]\n");
#ifdef WITH_TLS
printf(" [{--cafile file | --capath dir} [--cert file] [--key file]\n");
printf(" [--ciphers ciphers] [--insecure]\n");
printf(" [--tls-alpn protocol]\n");
printf(" [--tls-engine engine] [--keyform keyform] [--tls-engine-kpass-sha1]]\n");
printf(" [--tls-use-os-certs]\n");
#ifdef FINAL_WITH_TLS_PSK
printf(" [--psk hex-key --psk-identity identity [--ciphers ciphers]]\n");
#endif
#endif
#ifdef WITH_SOCKS
printf(" [--proxy socks-url]\n");
#endif
printf(" [--property command identifier value]\n");
printf(" [-D command identifier value]\n");
printf(" mosquitto_pub --help\n\n");
printf(" -A : bind the outgoing socket to this host/ip address. Use to control which interface\n");
printf(" the client communicates over.\n");
printf(" -d : enable debug messages.\n");
printf(" -c : disable clean session/enable persistent client mode\n");
printf(" When this argument is used, the broker will be instructed not to clean existing sessions\n");
printf(" for the same client id when the client connects, and sessions will never expire when the\n");
printf(" client disconnects. MQTT v5 clients can change their session expiry interval with the -x\n");
printf(" argument.\n");
printf(" -D : Define MQTT v5 properties. See the documentation for more details.\n");
printf(" -f : send the contents of a file as the message.\n");
printf(" -h : mqtt host to connect to. Defaults to localhost.\n");
printf(" -i : id to use for this client. Defaults to mosquitto_pub_ appended with the process id.\n");
printf(" -I : define the client id as id_prefix appended with the process id. Useful for when the\n");
printf(" broker is using the clientid_prefixes option.\n");
printf(" -k : keep alive in seconds for this client. Defaults to 60.\n");
printf(" -L : specify user, password, hostname, port and topic as a URL in the form:\n");
printf(" mqtt(s)://[username[:password]@]host[:port]/topic\n");
printf(" -l : read messages from stdin, sending a separate message for each line.\n");
printf(" -m : message payload to send.\n");
printf(" -M : the maximum inflight messages for QoS 1/2..\n");
printf(" -n : send a null (zero length) message.\n");
printf(" -p : network port to connect to. Defaults to 1883 for plain MQTT and 8883 for MQTT over TLS.\n");
printf(" -P : provide a password\n");
printf(" -q : quality of service level to use for all messages. Defaults to 0.\n");
printf(" -r : message should be retained.\n");
printf(" -s : read message from stdin, sending the entire input as a message.\n");
#ifdef WITH_SRV
printf(" -S : use SRV lookups to determine which host to connect to.\n");
#endif
printf(" -t : mqtt topic to publish to.\n");
printf(" -u : provide a username\n");
printf(" -V : specify the version of the MQTT protocol to use when connecting.\n");
printf(" Can be mqttv5, mqttv311 or mqttv31. Defaults to mqttv311.\n");
printf(" -x : Set the session-expiry-interval property on the CONNECT packet. Applies to MQTT v5\n");
printf(" clients only. Set to 0-4294967294 to specify the session will expire in that many\n");
printf(" seconds after the client disconnects, or use -1, 4294967295, or ∞ for a session\n");
printf(" that does not expire. Defaults to -1 if -c is also given, or 0 if -c not given.\n");
printf(" --help : display this message.\n");
printf(" --nodelay : disable Nagle's algorithm, to reduce socket sending latency at the possible\n");
printf(" expense of more packets being sent.\n");
printf(" --quiet : don't print error messages.\n");
printf(" --repeat : if publish mode is -f, -m, or -s, then repeat the publish N times.\n");
printf(" --repeat-delay : if using --repeat, wait time seconds between publishes. Defaults to 0.\n");
printf(" --unix : connect to a broker through a unix domain socket instead of a TCP socket,\n");
printf(" e.g. /tmp/mosquitto.sock\n");
printf(" --will-payload : payload for the client Will, which is sent by the broker in case of\n");
printf(" unexpected disconnection. If not given and will-topic is set, a zero\n");
printf(" length message will be sent.\n");
printf(" --will-qos : QoS level for the client Will.\n");
printf(" --will-retain : if given, make the client Will retained.\n");
printf(" --will-topic : the topic on which to publish the client Will.\n");
#ifdef WITH_TLS
printf(" --cafile : path to a file containing trusted CA certificates to enable encrypted\n");
printf(" communication.\n");
printf(" --capath : path to a directory containing trusted CA certificates to enable encrypted\n");
printf(" communication.\n");
printf(" --cert : client certificate for authentication, if required by server.\n");
printf(" --key : client private key for authentication, if required by server.\n");
printf(" --keyform : keyfile type, can be either \"pem\" or \"engine\".\n");
printf(" --ciphers : openssl compatible list of TLS ciphers to support.\n");
printf(" --tls-version : TLS protocol version, can be one of tlsv1.3 tlsv1.2 or tlsv1.1.\n");
printf(" Defaults to tlsv1.2 if available.\n");
printf(" --insecure : do not check that the server certificate hostname matches the remote\n");
printf(" hostname. Using this option means that you cannot be sure that the\n");
printf(" remote host is the server you wish to connect to and so is insecure.\n");
printf(" Do not use this option in a production environment.\n");
printf(" --tls-engine : If set, enables the use of a TLS engine device.\n");
printf(" --tls-engine-kpass-sha1 : SHA1 of the key password to be used with the selected SSL engine.\n");
printf(" --tls-use-os-certs : Load and trust OS provided CA certificates.\n");
# ifdef FINAL_WITH_TLS_PSK
printf(" --psk : pre-shared-key in hexadecimal (no leading 0x) to enable TLS-PSK mode.\n");
printf(" --psk-identity : client identity string for TLS-PSK mode.\n");
# endif
#endif
#ifdef WITH_SOCKS
printf(" --proxy : SOCKS5 proxy URL of the form:\n");
printf(" socks5h://[username[:password]@]hostname[:port]\n");
printf(" Only \"none\" and \"username\" authentication is supported.\n");
#endif
printf("\nSee https://mosquitto.org/ for more information.\n\n");
}
int main(int argc, char *argv[])
{
struct mosquitto *mosq = NULL;
int rc;
mosquitto_lib_init();
if(pub_shared_init()) return 1;
rc = client_config_load(&cfg, CLIENT_PUB, argc, argv);
if(rc){
if(rc == 2){
/* --help */
print_usage();
}else if(rc == 3){
print_version();
}else{
fprintf(stderr, "\nUse 'mosquitto_pub --help' to see usage.\n");
}
goto cleanup;
}
#ifndef WITH_THREADING
if(cfg.pub_mode == MSGMODE_STDIN_LINE){
fprintf(stderr, "Error: '-l' mode not available, threading support has not been compiled in.\n");
goto cleanup;
}
#endif
if(cfg.pub_mode == MSGMODE_STDIN_FILE){
if(load_stdin()){
err_printf(&cfg, "Error loading input from stdin.\n");
goto cleanup;
}
}else if(cfg.file_input){
if(load_file(cfg.file_input)){
err_printf(&cfg, "Error loading input file \"%s\".\n", cfg.file_input);
goto cleanup;
}
}
if(!cfg.topic || cfg.pub_mode == MSGMODE_NONE){
fprintf(stderr, "Error: Both topic and message must be supplied.\n");
print_usage();
goto cleanup;
}
if(client_id_generate(&cfg)){
goto cleanup;
}
mosq = mosquitto_new(cfg.id, cfg.clean_session, NULL);
if(!mosq){
switch(errno){
case ENOMEM:
err_printf(&cfg, "Error: Out of memory.\n");
break;
case EINVAL:
err_printf(&cfg, "Error: Invalid id.\n");
break;
}
goto cleanup;
}
if(cfg.debug){
mosquitto_log_callback_set(mosq, my_log_callback);
}
mosquitto_connect_v5_callback_set(mosq, my_connect_callback);
mosquitto_disconnect_v5_callback_set(mosq, my_disconnect_callback);
mosquitto_publish_v5_callback_set(mosq, my_publish_callback);
if(client_opts_set(mosq, &cfg)){
goto cleanup;
}
rc = client_connect(mosq, &cfg);
if(rc){
goto cleanup;
}
rc = pub_shared_loop(mosq);
if(cfg.message && cfg.pub_mode == MSGMODE_FILE){
free(cfg.message);
cfg.message = NULL;
}
mosquitto_destroy(mosq);
mosquitto_lib_cleanup();
client_config_cleanup(&cfg);
pub_shared_cleanup();
if(rc){
err_printf(&cfg, "Error: %s\n", mosquitto_strerror(rc));
}
if(connack_result){
return connack_result;
}else{
return rc;
}
cleanup:
mosquitto_lib_cleanup();
client_config_cleanup(&cfg);
pub_shared_cleanup();
return 1;
}
mosquitto-2.0.18/client/pub_shared.c 0000664 0000000 0000000 00000005743 14502137606 0017421 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 2009-2020 Roger Light
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License 2.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
https://www.eclipse.org/legal/epl-2.0/
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include
#include
#include
#include
#include
#ifndef WIN32
#include
#else
#include
#include
#define snprintf sprintf_s
#endif
#include
#include
#include "client_shared.h"
#include "pub_shared.h"
/* Global variables for use in callbacks. See sub_client.c for an example of
* using a struct to hold variables for use in callbacks. */
int mid_sent = -1;
struct mosq_config cfg;
void my_log_callback(struct mosquitto *mosq, void *obj, int level, const char *str)
{
UNUSED(mosq);
UNUSED(obj);
UNUSED(level);
printf("%s\n", str);
}
int load_stdin(void)
{
size_t pos = 0, rlen;
char buf[1024];
char *aux_message = NULL;
cfg.pub_mode = MSGMODE_STDIN_FILE;
while(!feof(stdin)){
rlen = fread(buf, 1, 1024, stdin);
aux_message = realloc(cfg.message, pos+rlen);
if(!aux_message){
err_printf(&cfg, "Error: Out of memory.\n");
free(cfg.message);
return 1;
} else
{
cfg.message = aux_message;
}
memcpy(&(cfg.message[pos]), buf, rlen);
pos += rlen;
}
if(pos > MQTT_MAX_PAYLOAD){
err_printf(&cfg, "Error: Message length must be less than %u bytes.\n\n", MQTT_MAX_PAYLOAD);
free(cfg.message);
return 1;
}
cfg.msglen = (int )pos;
if(!cfg.msglen){
err_printf(&cfg, "Error: Zero length input.\n");
return 1;
}
return 0;
}
int load_file(const char *filename)
{
size_t pos, rlen;
FILE *fptr = NULL;
long flen;
fptr = fopen(filename, "rb");
if(!fptr){
err_printf(&cfg, "Error: Unable to open file \"%s\".\n", filename);
return 1;
}
cfg.pub_mode = MSGMODE_FILE;
fseek(fptr, 0, SEEK_END);
flen = ftell(fptr);
if(flen > MQTT_MAX_PAYLOAD){
fclose(fptr);
err_printf(&cfg, "Error: File must be less than %u bytes.\n\n", MQTT_MAX_PAYLOAD);
free(cfg.message);
return 1;
}else if(flen == 0){
fclose(fptr);
cfg.message = NULL;
cfg.msglen = 0;
return 0;
}else if(flen < 0){
fclose(fptr);
err_printf(&cfg, "Error: Unable to determine size of file \"%s\".\n", filename);
return 1;
}
cfg.msglen = (int )flen;
fseek(fptr, 0, SEEK_SET);
cfg.message = malloc((size_t )cfg.msglen);
if(!cfg.message){
fclose(fptr);
err_printf(&cfg, "Error: Out of memory.\n");
return 1;
}
pos = 0;
while(pos < (size_t)cfg.msglen){
rlen = fread(&(cfg.message[pos]), sizeof(char), (size_t )cfg.msglen-pos, fptr);
pos += rlen;
}
fclose(fptr);
return 0;
}
mosquitto-2.0.18/client/pub_shared.h 0000664 0000000 0000000 00000003063 14502137606 0017417 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 2009-2020 Roger Light
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License 2.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
https://www.eclipse.org/legal/epl-2.0/
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
Contributors:
Roger Light - initial implementation and documentation.
*/
#ifndef PUB_SHARED_H
#define PUB_SHARED_H
#define STATUS_CONNECTING 0
#define STATUS_CONNACK_RECVD 1
#define STATUS_WAITING 2
#define STATUS_DISCONNECTING 3
#define STATUS_DISCONNECTED 4
#define STATUS_NOHOPE 5
extern int mid_sent;
extern struct mosq_config cfg;
void my_connect_callback(struct mosquitto *mosq, void *obj, int result, int flags, const mosquitto_property *properties);
void my_disconnect_callback(struct mosquitto *mosq, void *obj, int rc, const mosquitto_property *properties);
void my_publish_callback(struct mosquitto *mosq, void *obj, int mid, int reason_code, const mosquitto_property *properties);
void my_log_callback(struct mosquitto *mosq, void *obj, int level, const char *str);
int load_stdin(void);
int load_file(const char *filename);
int my_publish(struct mosquitto *mosq, int *mid, const char *topic, int payloadlen, void *payload, int qos, bool retain);
int pub_shared_init(void);
int pub_shared_loop(struct mosquitto *mosq);
void pub_shared_cleanup(void);
#endif
mosquitto-2.0.18/client/pub_test_properties 0000775 0000000 0000000 00000001542 14502137606 0021161 0 ustar 00root root 0000000 0000000 LD_LIBRARY_PATH=../lib ./mosquitto_pub \
\
-t asdf -V mqttv5 -m '{"key":"value"}' \
\
-D connect authentication-data password \
-D connect authentication-method something \
-D connect maximum-packet-size 0191 \
-D connect receive-maximum 1000 \
-D connect request-problem-information 1 \
-D connect request-response-information 1 \
-D connect session-expiry-interval 39 \
-D connect topic-alias-maximum 123 \
-D connect user-property connect up \
\
-D publish content-type application/json \
-D publish correlation-data some-data \
-D publish message-expiry-interval 59 \
-D publish payload-format-indicator 1 \
-D publish response-topic /dev/null \
-D publish topic-alias 4 \
-D publish user-property publish up \
\
-D disconnect reason-string "reason" \
-D disconnect session-expiry-interval 40 \
-D disconnect user-property disconnect up
mosquitto-2.0.18/client/rr_client.c 0000664 0000000 0000000 00000034062 14502137606 0017262 0 ustar 00root root 0000000 0000000 /*
Copyright (c) 2009-2020 Roger Light
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License 2.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
https://www.eclipse.org/legal/epl-2.0/
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include
#include
#include
#include
#include