2014-05-07 22:27:00 +00:00
|
|
|
/*
|
2015-04-19 21:10:59 +00:00
|
|
|
Copyright (c) 2009-2015 Roger Light <roger@atchoo.org>
|
2014-05-07 22:27:00 +00:00
|
|
|
|
|
|
|
All rights reserved. This program and the accompanying materials
|
|
|
|
are made available under the terms of the Eclipse Public License v1.0
|
|
|
|
and Eclipse Distribution License v1.0 which accompany this distribution.
|
|
|
|
|
|
|
|
The Eclipse Public License is available at
|
|
|
|
http://www.eclipse.org/legal/epl-v10.html
|
|
|
|
and the Eclipse Distribution License is available at
|
|
|
|
http://www.eclipse.org/org/documents/edl-v10.php.
|
|
|
|
|
|
|
|
Contributors:
|
|
|
|
Roger Light - initial implementation and documentation.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#ifdef WIN32
|
|
|
|
#include <winsock2.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
#include <mosquitto.h>
|
|
|
|
#include <memory_mosq.h>
|
|
|
|
#include <net_mosq.h>
|
|
|
|
#include <send_mosq.h>
|
|
|
|
#include <time_mosq.h>
|
|
|
|
#include <tls_mosq.h>
|
|
|
|
#include <util_mosq.h>
|
|
|
|
|
|
|
|
#ifdef WITH_BROKER
|
|
|
|
#include <mosquitto_broker.h>
|
|
|
|
#endif
|
|
|
|
|
2014-05-06 09:47:00 +00:00
|
|
|
#ifdef WITH_WEBSOCKETS
|
|
|
|
#include <libwebsockets.h>
|
|
|
|
#endif
|
|
|
|
|
2015-04-19 21:10:59 +00:00
|
|
|
int mosquitto__packet_alloc(struct mosquitto__packet *packet)
|
2014-05-07 22:27:00 +00:00
|
|
|
{
|
|
|
|
uint8_t remaining_bytes[5], byte;
|
|
|
|
uint32_t remaining_length;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
assert(packet);
|
|
|
|
|
|
|
|
remaining_length = packet->remaining_length;
|
|
|
|
packet->payload = NULL;
|
|
|
|
packet->remaining_count = 0;
|
|
|
|
do{
|
|
|
|
byte = remaining_length % 128;
|
|
|
|
remaining_length = remaining_length / 128;
|
|
|
|
/* If there are more digits to encode, set the top bit of this digit */
|
|
|
|
if(remaining_length > 0){
|
|
|
|
byte = byte | 0x80;
|
|
|
|
}
|
|
|
|
remaining_bytes[packet->remaining_count] = byte;
|
|
|
|
packet->remaining_count++;
|
|
|
|
}while(remaining_length > 0 && packet->remaining_count < 5);
|
|
|
|
if(packet->remaining_count == 5) return MOSQ_ERR_PAYLOAD_SIZE;
|
|
|
|
packet->packet_length = packet->remaining_length + 1 + packet->remaining_count;
|
2014-05-06 09:47:00 +00:00
|
|
|
#ifdef WITH_WEBSOCKETS
|
2015-04-19 21:10:59 +00:00
|
|
|
packet->payload = mosquitto__malloc(sizeof(uint8_t)*packet->packet_length + LWS_SEND_BUFFER_PRE_PADDING + LWS_SEND_BUFFER_POST_PADDING);
|
2014-05-06 09:47:00 +00:00
|
|
|
#else
|
2015-04-19 21:10:59 +00:00
|
|
|
packet->payload = mosquitto__malloc(sizeof(uint8_t)*packet->packet_length);
|
2014-05-06 09:47:00 +00:00
|
|
|
#endif
|
2014-05-07 22:27:00 +00:00
|
|
|
if(!packet->payload) return MOSQ_ERR_NOMEM;
|
|
|
|
|
|
|
|
packet->payload[0] = packet->command;
|
|
|
|
for(i=0; i<packet->remaining_count; i++){
|
|
|
|
packet->payload[i+1] = remaining_bytes[i];
|
|
|
|
}
|
|
|
|
packet->pos = 1 + packet->remaining_count;
|
|
|
|
|
|
|
|
return MOSQ_ERR_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2014-06-23 16:57:35 +00:00
|
|
|
#ifdef WITH_BROKER
|
2015-04-19 21:10:59 +00:00
|
|
|
void mosquitto__check_keepalive(struct mosquitto_db *db, struct mosquitto *mosq)
|
2014-06-23 16:57:35 +00:00
|
|
|
#else
|
2015-04-19 21:10:59 +00:00
|
|
|
void mosquitto__check_keepalive(struct mosquitto *mosq)
|
2014-06-23 16:57:35 +00:00
|
|
|
#endif
|
2014-05-07 22:27:00 +00:00
|
|
|
{
|
|
|
|
time_t last_msg_out;
|
|
|
|
time_t last_msg_in;
|
|
|
|
time_t now = mosquitto_time();
|
|
|
|
#ifndef WITH_BROKER
|
|
|
|
int rc;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
assert(mosq);
|
|
|
|
#if defined(WITH_BROKER) && defined(WITH_BRIDGE)
|
|
|
|
/* Check if a lazy bridge should be timed out due to idle. */
|
|
|
|
if(mosq->bridge && mosq->bridge->start_type == bst_lazy
|
|
|
|
&& mosq->sock != INVALID_SOCKET
|
|
|
|
&& now - mosq->last_msg_out >= mosq->bridge->idle_timeout){
|
|
|
|
|
2015-04-19 21:10:59 +00:00
|
|
|
mosquitto__log_printf(NULL, MOSQ_LOG_NOTICE, "Bridge connection %s has exceeded idle timeout, disconnecting.", mosq->id);
|
|
|
|
mosquitto__socket_close(db, mosq);
|
2014-05-07 22:27:00 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
pthread_mutex_lock(&mosq->msgtime_mutex);
|
|
|
|
last_msg_out = mosq->last_msg_out;
|
|
|
|
last_msg_in = mosq->last_msg_in;
|
|
|
|
pthread_mutex_unlock(&mosq->msgtime_mutex);
|
|
|
|
if(mosq->keepalive && mosq->sock != INVALID_SOCKET &&
|
|
|
|
(now - last_msg_out >= mosq->keepalive || now - last_msg_in >= mosq->keepalive)){
|
|
|
|
|
|
|
|
if(mosq->state == mosq_cs_connected && mosq->ping_t == 0){
|
2015-04-19 21:10:59 +00:00
|
|
|
mosquitto__send_pingreq(mosq);
|
2014-05-07 22:27:00 +00:00
|
|
|
/* Reset last msg times to give the server time to send a pingresp */
|
|
|
|
pthread_mutex_lock(&mosq->msgtime_mutex);
|
|
|
|
mosq->last_msg_in = now;
|
|
|
|
mosq->last_msg_out = now;
|
|
|
|
pthread_mutex_unlock(&mosq->msgtime_mutex);
|
|
|
|
}else{
|
|
|
|
#ifdef WITH_BROKER
|
|
|
|
if(mosq->listener){
|
|
|
|
mosq->listener->client_count--;
|
|
|
|
assert(mosq->listener->client_count >= 0);
|
|
|
|
}
|
|
|
|
mosq->listener = NULL;
|
2015-04-19 21:10:59 +00:00
|
|
|
mosquitto__socket_close(db, mosq);
|
2014-06-23 16:57:35 +00:00
|
|
|
#else
|
2015-04-19 21:10:59 +00:00
|
|
|
mosquitto__socket_close(mosq);
|
2014-05-07 22:27:00 +00:00
|
|
|
pthread_mutex_lock(&mosq->state_mutex);
|
|
|
|
if(mosq->state == mosq_cs_disconnecting){
|
|
|
|
rc = MOSQ_ERR_SUCCESS;
|
|
|
|
}else{
|
|
|
|
rc = 1;
|
|
|
|
}
|
|
|
|
pthread_mutex_unlock(&mosq->state_mutex);
|
|
|
|
pthread_mutex_lock(&mosq->callback_mutex);
|
|
|
|
if(mosq->on_disconnect){
|
|
|
|
mosq->in_callback = true;
|
|
|
|
mosq->on_disconnect(mosq, mosq->userdata, rc);
|
|
|
|
mosq->in_callback = false;
|
|
|
|
}
|
|
|
|
pthread_mutex_unlock(&mosq->callback_mutex);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-19 21:10:59 +00:00
|
|
|
uint16_t mosquitto__mid_generate(struct mosquitto *mosq)
|
2014-05-07 22:27:00 +00:00
|
|
|
{
|
|
|
|
assert(mosq);
|
|
|
|
|
|
|
|
mosq->last_mid++;
|
|
|
|
if(mosq->last_mid == 0) mosq->last_mid++;
|
|
|
|
|
|
|
|
return mosq->last_mid;
|
|
|
|
}
|
|
|
|
|
2014-06-08 21:51:36 +00:00
|
|
|
/* Check that a topic used for publishing is valid.
|
|
|
|
* Search for + or # in a topic. Return MOSQ_ERR_INVAL if found.
|
2014-05-07 22:27:00 +00:00
|
|
|
* Also returns MOSQ_ERR_INVAL if the topic string is too long.
|
|
|
|
* Returns MOSQ_ERR_SUCCESS if everything is fine.
|
|
|
|
*/
|
2014-09-10 14:57:20 +00:00
|
|
|
int mosquitto_pub_topic_check(const char *str)
|
2014-05-07 22:27:00 +00:00
|
|
|
{
|
|
|
|
int len = 0;
|
|
|
|
while(str && str[0]){
|
|
|
|
if(str[0] == '+' || str[0] == '#'){
|
|
|
|
return MOSQ_ERR_INVAL;
|
|
|
|
}
|
|
|
|
len++;
|
|
|
|
str = &str[1];
|
|
|
|
}
|
|
|
|
if(len > 65535) return MOSQ_ERR_INVAL;
|
|
|
|
|
|
|
|
return MOSQ_ERR_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2014-06-08 21:51:36 +00:00
|
|
|
/* Check that a topic used for subscriptions is valid.
|
|
|
|
* Search for + or # in a topic, check they aren't in invalid positions such as
|
|
|
|
* foo/#/bar, foo/+bar or foo/bar#.
|
2014-05-07 22:27:00 +00:00
|
|
|
* Return MOSQ_ERR_INVAL if invalid position found.
|
|
|
|
* Also returns MOSQ_ERR_INVAL if the topic string is too long.
|
|
|
|
* Returns MOSQ_ERR_SUCCESS if everything is fine.
|
|
|
|
*/
|
2014-09-10 14:57:20 +00:00
|
|
|
int mosquitto_sub_topic_check(const char *str)
|
2014-05-07 22:27:00 +00:00
|
|
|
{
|
|
|
|
char c = '\0';
|
|
|
|
int len = 0;
|
|
|
|
while(str && str[0]){
|
|
|
|
if(str[0] == '+'){
|
|
|
|
if((c != '\0' && c != '/') || (str[1] != '\0' && str[1] != '/')){
|
|
|
|
return MOSQ_ERR_INVAL;
|
|
|
|
}
|
|
|
|
}else if(str[0] == '#'){
|
|
|
|
if((c != '\0' && c != '/') || str[1] != '\0'){
|
|
|
|
return MOSQ_ERR_INVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
len++;
|
|
|
|
c = str[0];
|
|
|
|
str = &str[1];
|
|
|
|
}
|
|
|
|
if(len > 65535) return MOSQ_ERR_INVAL;
|
|
|
|
|
|
|
|
return MOSQ_ERR_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Does a topic match a subscription? */
|
|
|
|
int mosquitto_topic_matches_sub(const char *sub, const char *topic, bool *result)
|
|
|
|
{
|
|
|
|
int slen, tlen;
|
|
|
|
int spos, tpos;
|
|
|
|
bool multilevel_wildcard = false;
|
|
|
|
|
|
|
|
if(!sub || !topic || !result) return MOSQ_ERR_INVAL;
|
|
|
|
|
|
|
|
slen = strlen(sub);
|
|
|
|
tlen = strlen(topic);
|
|
|
|
|
|
|
|
if(slen && tlen){
|
|
|
|
if((sub[0] == '$' && topic[0] != '$')
|
|
|
|
|| (topic[0] == '$' && sub[0] != '$')){
|
|
|
|
|
|
|
|
*result = false;
|
|
|
|
return MOSQ_ERR_SUCCESS;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
spos = 0;
|
|
|
|
tpos = 0;
|
|
|
|
|
|
|
|
while(spos < slen && tpos < tlen){
|
|
|
|
if(sub[spos] == topic[tpos]){
|
2014-05-08 21:56:16 +00:00
|
|
|
if(tpos == tlen-1){
|
|
|
|
/* Check for e.g. foo matching foo/# */
|
|
|
|
if(spos == slen-3
|
|
|
|
&& sub[spos+1] == '/'
|
|
|
|
&& sub[spos+2] == '#'){
|
|
|
|
*result = true;
|
|
|
|
multilevel_wildcard = true;
|
|
|
|
return MOSQ_ERR_SUCCESS;
|
|
|
|
}
|
|
|
|
}
|
2014-05-07 22:27:00 +00:00
|
|
|
spos++;
|
|
|
|
tpos++;
|
|
|
|
if(spos == slen && tpos == tlen){
|
|
|
|
*result = true;
|
|
|
|
return MOSQ_ERR_SUCCESS;
|
|
|
|
}else if(tpos == tlen && spos == slen-1 && sub[spos] == '+'){
|
|
|
|
spos++;
|
|
|
|
*result = true;
|
|
|
|
return MOSQ_ERR_SUCCESS;
|
|
|
|
}
|
|
|
|
}else{
|
|
|
|
if(sub[spos] == '+'){
|
|
|
|
spos++;
|
|
|
|
while(tpos < tlen && topic[tpos] != '/'){
|
|
|
|
tpos++;
|
|
|
|
}
|
|
|
|
if(tpos == tlen && spos == slen){
|
|
|
|
*result = true;
|
|
|
|
return MOSQ_ERR_SUCCESS;
|
|
|
|
}
|
|
|
|
}else if(sub[spos] == '#'){
|
|
|
|
multilevel_wildcard = true;
|
|
|
|
if(spos+1 != slen){
|
|
|
|
*result = false;
|
|
|
|
return MOSQ_ERR_SUCCESS;
|
|
|
|
}else{
|
|
|
|
*result = true;
|
|
|
|
return MOSQ_ERR_SUCCESS;
|
|
|
|
}
|
|
|
|
}else{
|
|
|
|
*result = false;
|
|
|
|
return MOSQ_ERR_SUCCESS;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(multilevel_wildcard == false && (tpos < tlen || spos < slen)){
|
|
|
|
*result = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return MOSQ_ERR_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef REAL_WITH_TLS_PSK
|
2015-04-19 21:10:59 +00:00
|
|
|
int mosquitto__hex2bin(const char *hex, unsigned char *bin, int bin_max_len)
|
2014-05-07 22:27:00 +00:00
|
|
|
{
|
|
|
|
BIGNUM *bn = NULL;
|
|
|
|
int len;
|
|
|
|
|
|
|
|
if(BN_hex2bn(&bn, hex) == 0){
|
|
|
|
if(bn) BN_free(bn);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if(BN_num_bytes(bn) > bin_max_len){
|
|
|
|
BN_free(bn);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
len = BN_bn2bin(bn, bin);
|
|
|
|
BN_free(bn);
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2015-04-19 21:10:59 +00:00
|
|
|
FILE *mosquitto__fopen(const char *path, const char *mode)
|
2014-05-07 22:27:00 +00:00
|
|
|
{
|
|
|
|
#ifdef WIN32
|
2015-03-27 21:47:27 +00:00
|
|
|
char buf[4096];
|
2014-05-07 22:27:00 +00:00
|
|
|
int rc;
|
2015-03-27 21:47:27 +00:00
|
|
|
rc = ExpandEnvironmentStrings(path, buf, 4096);
|
|
|
|
if(rc == 0 || rc > 4096){
|
2014-05-07 22:27:00 +00:00
|
|
|
return NULL;
|
|
|
|
}else{
|
|
|
|
return fopen(buf, mode);
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
return fopen(path, mode);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|