mosquitto/lib/util_mosq.c

456 lines
10 KiB
C
Raw Normal View History

2014-05-07 22:27:00 +00:00
/*
2016-07-08 08:42:24 +00:00
Copyright (c) 2009-2016 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.
2018-02-13 14:16:47 +00:00
2014-05-07 22:27:00 +00:00
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.
2018-02-13 14:16:47 +00:00
2014-05-07 22:27:00 +00:00
Contributors:
Roger Light - initial implementation and documentation.
*/
#include <assert.h>
#include <string.h>
#ifdef WIN32
2017-06-26 13:53:33 +00:00
# include <winsock2.h>
# include <aclapi.h>
# include <io.h>
# include <lmcons.h>
#else
# include <sys/stat.h>
2014-05-07 22:27:00 +00:00
#endif
2015-04-29 20:37:47 +00:00
#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"
2014-05-07 22:27:00 +00:00
#ifdef WITH_BROKER
#include "mosquitto_broker_internal.h"
2014-05-07 22:27:00 +00:00
#endif
#ifdef WITH_WEBSOCKETS
#include <libwebsockets.h>
#endif
#ifdef WITH_BROKER
void mosquitto__check_keepalive(struct mosquitto_db *db, struct mosquitto *mosq)
#else
void mosquitto__check_keepalive(struct mosquitto *mosq)
#endif
2014-05-07 22:27:00 +00:00
{
time_t next_msg_out;
2014-05-07 22:27:00 +00:00
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->next_msg_out - mosq->keepalive >= mosq->bridge->idle_timeout){
2014-05-07 22:27:00 +00:00
2015-05-18 07:53:21 +00:00
log__printf(NULL, MOSQ_LOG_NOTICE, "Bridge connection %s has exceeded idle timeout, disconnecting.", mosq->id);
2015-05-18 08:29:22 +00:00
net__socket_close(db, mosq);
2014-05-07 22:27:00 +00:00
return;
}
#endif
pthread_mutex_lock(&mosq->msgtime_mutex);
next_msg_out = mosq->next_msg_out;
2014-05-07 22:27:00 +00:00
last_msg_in = mosq->last_msg_in;
pthread_mutex_unlock(&mosq->msgtime_mutex);
if(mosq->keepalive && mosq->sock != INVALID_SOCKET &&
(now >= next_msg_out || now - last_msg_in >= mosq->keepalive)){
2014-05-07 22:27:00 +00:00
if(mosq->state == mosq_cs_connected && mosq->ping_t == 0){
2015-05-16 14:24:24 +00:00
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->next_msg_out = now + mosq->keepalive;
2014-05-07 22:27:00 +00:00
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-05-18 08:29:22 +00:00
net__socket_close(db, mosq);
#else
2015-05-18 08:29:22 +00:00
net__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
}
}
}
uint16_t mosquitto__mid_generate(struct mosquitto *mosq)
2014-05-07 22:27:00 +00:00
{
/* FIXME - this would be better with atomic increment, but this is safer
* for now for a bug fix release.
*
* If this is changed to use atomic increment, callers of this function
* will have to be aware that they may receive a 0 result, which may not be
* used as a mid.
*/
uint16_t mid;
2014-05-07 22:27:00 +00:00
assert(mosq);
pthread_mutex_lock(&mosq->mid_mutex);
2014-05-07 22:27:00 +00:00
mosq->last_mid++;
if(mosq->last_mid == 0) mosq->last_mid++;
mid = mosq->last_mid;
pthread_mutex_unlock(&mosq->mid_mutex);
2018-02-13 14:16:47 +00:00
return mid;
2014-05-07 22:27:00 +00:00
}
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.
*/
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;
}
int mosquitto_pub_topic_check2(const char *str, size_t len)
{
int i;
if(len > 65535) return MOSQ_ERR_INVAL;
for(i=0; i<len; i++){
if(str[i] == '+' || str[i] == '#'){
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.
*/
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;
}
int mosquitto_sub_topic_check2(const char *str, size_t len)
{
char c = '\0';
int i;
if(len > 65535) return MOSQ_ERR_INVAL;
for(i=0; i<len; i++){
if(str[i] == '+'){
if((c != '\0' && c != '/') || (i<len-1 && str[i+1] != '/')){
return MOSQ_ERR_INVAL;
}
}else if(str[i] == '#'){
if((c != '\0' && c != '/') || i<len-1){
return MOSQ_ERR_INVAL;
}
}
c = str[i];
}
return MOSQ_ERR_SUCCESS;
}
2014-05-07 22:27:00 +00:00
int mosquitto_topic_matches_sub(const char *sub, const char *topic, bool *result)
{
int slen, tlen;
if(!result) return MOSQ_ERR_INVAL;
*result = false;
if(!sub || !topic){
return MOSQ_ERR_INVAL;
}
2014-05-07 22:27:00 +00:00
slen = strlen(sub);
tlen = strlen(topic);
return mosquitto_topic_matches_sub2(sub, slen, topic, tlen, result);
}
/* Does a topic match a subscription? */
int mosquitto_topic_matches_sub2(const char *sub, size_t sublen, const char *topic, size_t topiclen, bool *result)
{
int spos, tpos;
bool multilevel_wildcard = false;
2018-02-13 14:16:47 +00:00
if(!result) return MOSQ_ERR_INVAL;
*result = false;
if(!sub || !topic){
2016-06-02 19:53:09 +00:00
return MOSQ_ERR_INVAL;
}
if(!sublen || !topiclen){
2016-06-02 19:53:09 +00:00
*result = false;
return MOSQ_ERR_INVAL;
}
if(sublen && topiclen){
2014-05-07 22:27:00 +00:00
if((sub[0] == '$' && topic[0] != '$')
|| (topic[0] == '$' && sub[0] != '$')){
return MOSQ_ERR_SUCCESS;
}
}
spos = 0;
tpos = 0;
while(spos < sublen && tpos <= topiclen){
2014-05-07 22:27:00 +00:00
if(sub[spos] == topic[tpos]){
if(tpos == topiclen-1){
/* Check for e.g. foo matching foo/# */
2018-02-13 14:16:47 +00:00
if(spos == sublen-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 == sublen && tpos == topiclen){
2014-05-07 22:27:00 +00:00
*result = true;
return MOSQ_ERR_SUCCESS;
}else if(tpos == topiclen && spos == sublen-1 && sub[spos] == '+'){
2016-06-02 19:53:09 +00:00
if(spos > 0 && sub[spos-1] != '/'){
return MOSQ_ERR_INVAL;
}
2014-05-07 22:27:00 +00:00
spos++;
*result = true;
return MOSQ_ERR_SUCCESS;
}
}else{
if(sub[spos] == '+'){
2016-06-02 19:53:09 +00:00
/* Check for bad "+foo" or "a/+foo" subscription */
if(spos > 0 && sub[spos-1] != '/'){
return MOSQ_ERR_INVAL;
}
/* Check for bad "foo+" or "foo+/a" subscription */
if(spos < sublen-1 && sub[spos+1] != '/'){
2016-06-02 19:53:09 +00:00
return MOSQ_ERR_INVAL;
}
2014-05-07 22:27:00 +00:00
spos++;
while(tpos < topiclen && topic[tpos] != '/'){
2014-05-07 22:27:00 +00:00
tpos++;
}
if(tpos == topiclen && spos == sublen){
2014-05-07 22:27:00 +00:00
*result = true;
return MOSQ_ERR_SUCCESS;
}
}else if(sub[spos] == '#'){
2016-06-02 19:53:09 +00:00
if(spos > 0 && sub[spos-1] != '/'){
return MOSQ_ERR_INVAL;
}
2014-05-07 22:27:00 +00:00
multilevel_wildcard = true;
if(spos+1 != sublen){
2016-06-02 19:53:09 +00:00
return MOSQ_ERR_INVAL;
2014-05-07 22:27:00 +00:00
}else{
*result = true;
return MOSQ_ERR_SUCCESS;
}
}else{
/* Check for e.g. foo/bar matching foo/+/# */
if(spos > 0
2018-02-13 14:16:47 +00:00
&& spos+2 == sublen
&& tpos == topiclen
&& sub[spos-1] == '+'
&& sub[spos] == '/'
&& sub[spos+1] == '#')
{
*result = true;
multilevel_wildcard = true;
return MOSQ_ERR_SUCCESS;
}
2014-05-07 22:27:00 +00:00
return MOSQ_ERR_SUCCESS;
}
}
}
if(multilevel_wildcard == false && (tpos < topiclen || spos < sublen)){
2014-05-07 22:27:00 +00:00
*result = false;
}
return MOSQ_ERR_SUCCESS;
}
#ifdef REAL_WITH_TLS_PSK
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;
int leading_zero = 0;
int start = 0;
int i = 0;
/* Count the number of leading zero */
for(i=0; i<strlen(hex); i=i+2) {
if(strncmp(hex + i, "00", 2) == 0) {
leading_zero++;
/* output leading zero to bin */
bin[start++] = 0;
}else{
break;
}
}
2014-05-07 22:27:00 +00:00
if(BN_hex2bn(&bn, hex) == 0){
if(bn) BN_free(bn);
return 0;
}
if(BN_num_bytes(bn) + leading_zero > bin_max_len){
2014-05-07 22:27:00 +00:00
BN_free(bn);
return 0;
}
len = BN_bn2bin(bn, bin + leading_zero);
2014-05-07 22:27:00 +00:00
BN_free(bn);
return len + leading_zero;
2014-05-07 22:27:00 +00:00
}
#endif
2017-07-16 21:52:01 +00:00
FILE *mosquitto__fopen(const char *path, const char *mode, bool restrict_read)
2014-05-07 22:27:00 +00:00
{
#ifdef WIN32
char buf[4096];
2014-05-07 22:27:00 +00:00
int rc;
rc = ExpandEnvironmentStrings(path, buf, 4096);
if(rc == 0 || rc > 4096){
2014-05-07 22:27:00 +00:00
return NULL;
}else{
2017-06-26 13:53:33 +00:00
if (restrict_read) {
HANDLE hfile;
SECURITY_ATTRIBUTES sec;
EXPLICIT_ACCESS ea;
PACL pacl = NULL;
char username[UNLEN + 1];
int ulen = UNLEN;
SECURITY_DESCRIPTOR sd;
GetUserName(username, &ulen);
if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) {
return NULL;
}
BuildExplicitAccessWithName(&ea, username, GENERIC_ALL, SET_ACCESS, NO_INHERITANCE);
if (SetEntriesInAcl(1, &ea, NULL, &pacl) != ERROR_SUCCESS) {
return NULL;
}
if (!SetSecurityDescriptorDacl(&sd, TRUE, pacl, FALSE)) {
LocalFree(pacl);
return NULL;
}
sec.nLength = sizeof(SECURITY_ATTRIBUTES);
sec.bInheritHandle = FALSE;
sec.lpSecurityDescriptor = &sd;
hfile = CreateFile(buf, GENERIC_READ | GENERIC_WRITE, 0,
&sec,
CREATE_NEW,
FILE_ATTRIBUTE_NORMAL,
NULL);
LocalFree(pacl);
int fd = _open_osfhandle((intptr_t)hfile, 0);
if (fd < 0) {
return NULL;
}
FILE *fptr = _fdopen(fd, mode);
if (!fptr) {
_close(fd);
return NULL;
}
return fptr;
}else {
return fopen(buf, mode);
}
2014-05-07 22:27:00 +00:00
}
#else
2017-06-26 13:53:33 +00:00
if (restrict_read) {
2017-06-27 08:57:47 +00:00
FILE *fptr;
mode_t old_mask;
2017-06-27 10:21:34 +00:00
old_mask = umask(0077);
2017-06-27 08:57:47 +00:00
fptr = fopen(path, mode);
umask(old_mask);
return fptr;
}else{
return fopen(path, mode);
2017-06-26 13:53:33 +00:00
}
2014-05-07 22:27:00 +00:00
#endif
}