2014-05-07 22:27:00 +00:00
|
|
|
/*
|
|
|
|
Copyright (c) 2009-2014 Roger Light <roger@atchoo.org>
|
|
|
|
|
|
|
|
All rights reserved. This program and the accompanying materials
|
|
|
|
are made available under the terms of the Eclipse Public License v1.0
|
|
|
|
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 <errno.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#ifndef WIN32
|
|
|
|
#include <netdb.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#else
|
|
|
|
#include <winsock2.h>
|
|
|
|
#include <ws2tcpip.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#include <mosquitto.h>
|
|
|
|
#include <mosquitto_broker.h>
|
|
|
|
#include <mosquitto_internal.h>
|
|
|
|
#include <net_mosq.h>
|
|
|
|
#include <memory_mosq.h>
|
|
|
|
#include <send_mosq.h>
|
|
|
|
#include <time_mosq.h>
|
|
|
|
#include <tls_mosq.h>
|
|
|
|
#include <util_mosq.h>
|
|
|
|
#include <will_mosq.h>
|
|
|
|
|
|
|
|
#ifdef WITH_BRIDGE
|
|
|
|
|
|
|
|
int mqtt3_bridge_new(struct mosquitto_db *db, struct _mqtt3_bridge *bridge)
|
|
|
|
{
|
|
|
|
struct mosquitto *new_context = NULL;
|
2014-11-18 23:34:54 +00:00
|
|
|
struct mosquitto **bridges;
|
2014-05-07 22:27:00 +00:00
|
|
|
char hostname[256];
|
|
|
|
int len;
|
2014-06-10 22:30:15 +00:00
|
|
|
char *id, *local_id;
|
2014-05-07 22:27:00 +00:00
|
|
|
|
|
|
|
assert(db);
|
|
|
|
assert(bridge);
|
|
|
|
|
2014-08-16 20:31:12 +00:00
|
|
|
if(!bridge->remote_clientid){
|
2014-05-07 22:27:00 +00:00
|
|
|
if(!gethostname(hostname, 256)){
|
|
|
|
len = strlen(hostname) + strlen(bridge->name) + 2;
|
|
|
|
id = _mosquitto_malloc(len);
|
|
|
|
if(!id){
|
|
|
|
return MOSQ_ERR_NOMEM;
|
|
|
|
}
|
|
|
|
snprintf(id, len, "%s.%s", hostname, bridge->name);
|
|
|
|
}else{
|
|
|
|
return 1;
|
|
|
|
}
|
2014-08-16 20:31:12 +00:00
|
|
|
bridge->remote_clientid = id;
|
2014-05-07 22:27:00 +00:00
|
|
|
}
|
2014-06-10 22:30:15 +00:00
|
|
|
if(bridge->local_clientid){
|
2014-06-28 00:38:58 +00:00
|
|
|
local_id = _mosquitto_strdup(bridge->local_clientid);
|
2014-07-04 20:32:27 +00:00
|
|
|
if(!local_id){
|
|
|
|
return MOSQ_ERR_NOMEM;
|
|
|
|
}
|
2014-06-10 22:30:15 +00:00
|
|
|
}else{
|
2014-08-16 20:31:12 +00:00
|
|
|
len = strlen(bridge->remote_clientid) + strlen("local.") + 2;
|
2014-06-10 22:30:15 +00:00
|
|
|
local_id = _mosquitto_malloc(len);
|
|
|
|
if(!local_id){
|
|
|
|
return MOSQ_ERR_NOMEM;
|
|
|
|
}
|
2014-08-16 20:31:12 +00:00
|
|
|
snprintf(local_id, len, "local.%s", bridge->remote_clientid);
|
2014-07-04 20:32:27 +00:00
|
|
|
bridge->local_clientid = _mosquitto_strdup(local_id);
|
|
|
|
if(!bridge->local_clientid){
|
|
|
|
_mosquitto_free(local_id);
|
|
|
|
return MOSQ_ERR_NOMEM;
|
|
|
|
}
|
2014-05-07 22:27:00 +00:00
|
|
|
}
|
|
|
|
|
2014-06-23 16:57:35 +00:00
|
|
|
HASH_FIND(hh_id, db->contexts_by_id, local_id, strlen(local_id), new_context);
|
|
|
|
if(new_context){
|
|
|
|
/* (possible from persistent db) */
|
2014-07-03 20:55:25 +00:00
|
|
|
_mosquitto_free(local_id);
|
2014-06-23 16:57:35 +00:00
|
|
|
}else{
|
2014-05-07 22:27:00 +00:00
|
|
|
/* id wasn't found, so generate a new context */
|
2014-06-23 22:47:48 +00:00
|
|
|
new_context = mqtt3_context_init(db, -1);
|
2014-05-07 22:27:00 +00:00
|
|
|
if(!new_context){
|
2014-09-18 23:56:37 +00:00
|
|
|
_mosquitto_free(local_id);
|
2014-05-07 22:27:00 +00:00
|
|
|
return MOSQ_ERR_NOMEM;
|
|
|
|
}
|
2014-07-03 20:55:25 +00:00
|
|
|
new_context->id = local_id;
|
2014-06-23 16:57:35 +00:00
|
|
|
HASH_ADD_KEYPTR(hh_id, db->contexts_by_id, new_context->id, strlen(new_context->id), new_context);
|
2014-05-07 22:27:00 +00:00
|
|
|
}
|
|
|
|
new_context->bridge = bridge;
|
|
|
|
new_context->is_bridge = true;
|
|
|
|
|
2014-08-16 20:31:12 +00:00
|
|
|
new_context->username = new_context->bridge->remote_username;
|
|
|
|
new_context->password = new_context->bridge->remote_password;
|
2014-05-07 22:27:00 +00:00
|
|
|
|
|
|
|
#ifdef WITH_TLS
|
|
|
|
new_context->tls_cafile = new_context->bridge->tls_cafile;
|
|
|
|
new_context->tls_capath = new_context->bridge->tls_capath;
|
|
|
|
new_context->tls_certfile = new_context->bridge->tls_certfile;
|
|
|
|
new_context->tls_keyfile = new_context->bridge->tls_keyfile;
|
2014-05-08 21:48:13 +00:00
|
|
|
new_context->tls_cert_reqs = SSL_VERIFY_PEER;
|
2014-05-07 22:27:00 +00:00
|
|
|
new_context->tls_version = new_context->bridge->tls_version;
|
|
|
|
new_context->tls_insecure = new_context->bridge->tls_insecure;
|
|
|
|
#ifdef REAL_WITH_TLS_PSK
|
|
|
|
new_context->tls_psk_identity = new_context->bridge->tls_psk_identity;
|
|
|
|
new_context->tls_psk = new_context->bridge->tls_psk;
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
bridge->try_private_accepted = true;
|
2015-01-27 17:12:36 +00:00
|
|
|
new_context->protocol = bridge->protocol_version;
|
2014-05-07 22:27:00 +00:00
|
|
|
|
2014-11-18 23:34:54 +00:00
|
|
|
bridges = _mosquitto_realloc(db->bridges, (db->bridge_count+1)*sizeof(struct mosquitto *));
|
|
|
|
if(bridges){
|
|
|
|
db->bridges = bridges;
|
|
|
|
db->bridge_count++;
|
|
|
|
db->bridges[db->bridge_count-1] = new_context;
|
|
|
|
}else{
|
|
|
|
return MOSQ_ERR_NOMEM;
|
|
|
|
}
|
2014-06-23 16:57:35 +00:00
|
|
|
|
2014-05-07 22:27:00 +00:00
|
|
|
return mqtt3_bridge_connect(db, new_context);
|
|
|
|
}
|
|
|
|
|
|
|
|
int mqtt3_bridge_connect(struct mosquitto_db *db, struct mosquitto *context)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
int i;
|
|
|
|
char *notification_topic;
|
|
|
|
int notification_topic_len;
|
|
|
|
uint8_t notification_payload;
|
2014-08-16 22:14:41 +00:00
|
|
|
int lr, ll;
|
2014-05-07 22:27:00 +00:00
|
|
|
|
|
|
|
if(!context || !context->bridge) return MOSQ_ERR_INVAL;
|
|
|
|
|
|
|
|
context->state = mosq_cs_new;
|
2015-03-29 09:43:08 +00:00
|
|
|
context->sock = INVALID_SOCKET;
|
2014-05-07 22:27:00 +00:00
|
|
|
context->last_msg_in = mosquitto_time();
|
|
|
|
context->last_msg_out = mosquitto_time();
|
|
|
|
context->keepalive = context->bridge->keepalive;
|
|
|
|
context->clean_session = context->bridge->clean_session;
|
|
|
|
context->in_packet.payload = NULL;
|
|
|
|
context->ping_t = 0;
|
|
|
|
context->bridge->lazy_reconnect = false;
|
|
|
|
mqtt3_bridge_packet_cleanup(context);
|
2014-11-17 23:46:02 +00:00
|
|
|
mqtt3_db_message_reconnect_reset(db, context);
|
2014-05-07 22:27:00 +00:00
|
|
|
|
|
|
|
if(context->clean_session){
|
2014-11-17 23:46:02 +00:00
|
|
|
mqtt3_db_messages_delete(db, context);
|
2014-05-07 22:27:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Delete all local subscriptions even for clean_session==false. We don't
|
|
|
|
* remove any messages and the next loop carries out the resubscription
|
|
|
|
* anyway. This means any unwanted subs will be removed.
|
|
|
|
*/
|
2014-10-09 22:28:16 +00:00
|
|
|
mqtt3_subs_clean_session(db, context);
|
2014-05-07 22:27:00 +00:00
|
|
|
|
|
|
|
for(i=0; i<context->bridge->topic_count; i++){
|
|
|
|
if(context->bridge->topics[i].direction == bd_out || context->bridge->topics[i].direction == bd_both){
|
|
|
|
_mosquitto_log_printf(NULL, MOSQ_LOG_DEBUG, "Bridge %s doing local SUBSCRIBE on topic %s", context->id, context->bridge->topics[i].local_topic);
|
|
|
|
if(mqtt3_sub_add(db, context, context->bridge->topics[i].local_topic, context->bridge->topics[i].qos, &db->subs)) return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(context->bridge->notifications){
|
|
|
|
if(context->bridge->notification_topic){
|
2014-09-17 21:40:49 +00:00
|
|
|
notification_payload = '1';
|
2014-05-07 22:27:00 +00:00
|
|
|
mqtt3_db_messages_easy_queue(db, context, context->bridge->notification_topic, 1, 1, ¬ification_payload, 1);
|
2014-09-17 21:40:49 +00:00
|
|
|
notification_payload = '0';
|
2014-05-07 22:27:00 +00:00
|
|
|
rc = _mosquitto_will_set(context, context->bridge->notification_topic, 1, ¬ification_payload, 1, true);
|
|
|
|
if(rc != MOSQ_ERR_SUCCESS){
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
}else{
|
2014-08-16 22:14:41 +00:00
|
|
|
ll = strlen(context->bridge->local_clientid);
|
|
|
|
lr = strlen(context->bridge->remote_clientid);
|
|
|
|
if(ll > lr){
|
|
|
|
notification_topic_len = ll+strlen("$SYS/broker/connection//state");
|
|
|
|
}else{
|
|
|
|
notification_topic_len = lr+strlen("$SYS/broker/connection//state");
|
|
|
|
}
|
2014-05-07 22:27:00 +00:00
|
|
|
notification_topic = _mosquitto_malloc(sizeof(char)*(notification_topic_len+1));
|
|
|
|
if(!notification_topic) return MOSQ_ERR_NOMEM;
|
|
|
|
|
2014-08-16 22:14:41 +00:00
|
|
|
snprintf(notification_topic, notification_topic_len+1, "$SYS/broker/connection/%s/state", context->bridge->local_clientid);
|
2014-09-17 21:40:49 +00:00
|
|
|
notification_payload = '1';
|
2014-05-07 22:27:00 +00:00
|
|
|
mqtt3_db_messages_easy_queue(db, context, notification_topic, 1, 1, ¬ification_payload, 1);
|
2014-08-16 22:14:41 +00:00
|
|
|
|
|
|
|
snprintf(notification_topic, notification_topic_len+1, "$SYS/broker/connection/%s/state", context->bridge->remote_clientid);
|
2014-09-17 21:40:49 +00:00
|
|
|
notification_payload = '0';
|
2014-05-07 22:27:00 +00:00
|
|
|
rc = _mosquitto_will_set(context, notification_topic, 1, ¬ification_payload, 1, true);
|
2014-08-16 22:14:41 +00:00
|
|
|
_mosquitto_free(notification_topic);
|
2014-05-07 22:27:00 +00:00
|
|
|
if(rc != MOSQ_ERR_SUCCESS){
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_mosquitto_log_printf(NULL, MOSQ_LOG_NOTICE, "Connecting bridge %s (%s:%d)", context->bridge->name, context->bridge->addresses[context->bridge->cur_address].address, context->bridge->addresses[context->bridge->cur_address].port);
|
2014-10-24 20:28:24 +00:00
|
|
|
rc = _mosquitto_socket_connect(context, context->bridge->addresses[context->bridge->cur_address].address, context->bridge->addresses[context->bridge->cur_address].port, NULL, false);
|
|
|
|
if(rc > 0 ){
|
2014-05-07 22:27:00 +00:00
|
|
|
if(rc == MOSQ_ERR_TLS){
|
|
|
|
return rc; /* Error already printed */
|
|
|
|
}else if(rc == MOSQ_ERR_ERRNO){
|
|
|
|
_mosquitto_log_printf(NULL, MOSQ_LOG_ERR, "Error creating bridge: %s.", strerror(errno));
|
|
|
|
}else if(rc == MOSQ_ERR_EAI){
|
|
|
|
_mosquitto_log_printf(NULL, MOSQ_LOG_ERR, "Error creating bridge: %s.", gai_strerror(errno));
|
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2014-06-23 16:57:35 +00:00
|
|
|
HASH_ADD(hh_sock, db->contexts_by_sock, sock, sizeof(context->sock), context);
|
2014-10-24 20:28:24 +00:00
|
|
|
|
|
|
|
if(rc == MOSQ_ERR_CONN_PENDING){
|
|
|
|
context->state = mosq_cs_connect_pending;
|
|
|
|
}
|
2014-05-07 22:27:00 +00:00
|
|
|
rc = _mosquitto_send_connect(context, context->keepalive, context->clean_session);
|
|
|
|
if(rc == MOSQ_ERR_SUCCESS){
|
|
|
|
return MOSQ_ERR_SUCCESS;
|
|
|
|
}else if(rc == MOSQ_ERR_ERRNO && errno == ENOTCONN){
|
|
|
|
return MOSQ_ERR_SUCCESS;
|
|
|
|
}else{
|
|
|
|
if(rc == MOSQ_ERR_TLS){
|
|
|
|
return rc; /* Error already printed */
|
|
|
|
}else if(rc == MOSQ_ERR_ERRNO){
|
|
|
|
_mosquitto_log_printf(NULL, MOSQ_LOG_ERR, "Error creating bridge: %s.", strerror(errno));
|
|
|
|
}else if(rc == MOSQ_ERR_EAI){
|
|
|
|
_mosquitto_log_printf(NULL, MOSQ_LOG_ERR, "Error creating bridge: %s.", gai_strerror(errno));
|
|
|
|
}
|
2014-06-23 16:57:35 +00:00
|
|
|
_mosquitto_socket_close(db, context);
|
2014-05-07 22:27:00 +00:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void mqtt3_bridge_packet_cleanup(struct mosquitto *context)
|
|
|
|
{
|
|
|
|
struct _mosquitto_packet *packet;
|
|
|
|
if(!context) return;
|
|
|
|
|
2014-09-22 23:32:34 +00:00
|
|
|
if(context->current_out_packet){
|
|
|
|
_mosquitto_packet_cleanup(context->current_out_packet);
|
|
|
|
_mosquitto_free(context->current_out_packet);
|
|
|
|
context->current_out_packet = NULL;
|
|
|
|
}
|
2014-05-07 22:27:00 +00:00
|
|
|
while(context->out_packet){
|
|
|
|
_mosquitto_packet_cleanup(context->out_packet);
|
|
|
|
packet = context->out_packet;
|
|
|
|
context->out_packet = context->out_packet->next;
|
|
|
|
_mosquitto_free(packet);
|
|
|
|
}
|
2014-09-22 23:32:34 +00:00
|
|
|
context->out_packet = NULL;
|
|
|
|
context->out_packet_last = NULL;
|
2014-05-07 22:27:00 +00:00
|
|
|
|
|
|
|
_mosquitto_packet_cleanup(&(context->in_packet));
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|