From 64fa07b391ed93461d2ed559b9b0dd12bcd04feb Mon Sep 17 00:00:00 2001 From: tomoaki Date: Fri, 13 May 2016 19:18:47 +0900 Subject: [PATCH] First Commit of MQTT-SN Gateway Add new SensorNetwork XBee Add a sensor network type in a start message. Update MQTTSNClient to avoid build warning. Update WiringPi's functions to my original ones. BugFix check msgId before adding waitdTopicId table. BugFix Process termination procedures Update print curent time in millseconds. update move currentDateTime() to linux directory. Bugfix: blink blue lightiIndicator. Bugfix: Register returns wrong id. change a status of the client to Disconnected. change client status procedure Update README BugFix: change Network Disconnect procedures. Signed-off-by: tomoaki --- .cproject | 83 +- .project | 1 + MQTTSNClient/src/linux/linux.cpp | 4 +- MQTTSNGateway/src/MQTTConnection.h | 559 -------- MQTTSNGateway/src/MQTTGWConnectionHandler.cpp | 106 ++ MQTTSNGateway/src/MQTTGWConnectionHandler.h | 42 + MQTTSNGateway/src/MQTTGWPacket.cpp | 530 ++++++++ MQTTSNGateway/src/MQTTGWPacket.h | 218 ++++ MQTTSNGateway/src/MQTTGWPublishHandler.cpp | 217 ++++ MQTTSNGateway/src/MQTTGWPublishHandler.h | 45 + MQTTSNGateway/src/MQTTGWSubscribeHandler.cpp | 74 ++ MQTTSNGateway/src/MQTTGWSubscribeHandler.h | 41 + MQTTSNGateway/src/MQTTSNGWBrokerRecvTask.cpp | 195 +++ MQTTSNGateway/src/MQTTSNGWBrokerRecvTask.h | 49 + MQTTSNGateway/src/MQTTSNGWBrokerSendTask.cpp | 162 +++ MQTTSNGateway/src/MQTTSNGWBrokerSendTask.h | 47 + MQTTSNGateway/src/MQTTSNGWClient.cpp | 1142 +++++++++++++++++ MQTTSNGateway/src/MQTTSNGWClient.h | 347 +++++ MQTTSNGateway/src/MQTTSNGWClientRecvTask.cpp | 172 +++ MQTTSNGateway/src/MQTTSNGWClientRecvTask.h | 46 + MQTTSNGateway/src/MQTTSNGWClientSendTask.cpp | 105 ++ MQTTSNGateway/src/MQTTSNGWClientSendTask.h | 45 + .../src/MQTTSNGWConnectionHandler.cpp | 251 ++++ MQTTSNGateway/src/MQTTSNGWConnectionHandler.h | 46 + MQTTSNGateway/src/MQTTSNGWDefines.h | 62 + MQTTSNGateway/src/MQTTSNGWLogmonitor.cpp | 50 + MQTTSNGateway/src/MQTTSNGWLogmonitor.h | 33 + MQTTSNGateway/src/MQTTSNGWPacket.cpp | 410 ++++++ MQTTSNGateway/src/MQTTSNGWPacket.h | 88 ++ .../src/MQTTSNGWPacketHandleTask.cpp | 236 ++++ MQTTSNGateway/src/MQTTSNGWPacketHandleTask.h | 65 + MQTTSNGateway/src/MQTTSNGWProcess.cpp | 333 +++++ MQTTSNGateway/src/MQTTSNGWProcess.h | 248 ++++ MQTTSNGateway/src/MQTTSNGWPublishHandler.cpp | 195 +++ MQTTSNGateway/src/MQTTSNGWPublishHandler.h | 40 + .../src/MQTTSNGWSubscribeHandler.cpp | 192 +++ MQTTSNGateway/src/MQTTSNGWSubscribeHandler.h | 44 + MQTTSNGateway/src/MQTTSNGateway.cpp | 328 +++++ MQTTSNGateway/src/MQTTSNGateway.h | 408 +++--- MQTTSNGateway/src/linux/Network.cpp | 563 ++++++++ MQTTSNGateway/src/linux/Network.h | 95 ++ MQTTSNGateway/src/linux/Threading.cpp | 502 ++++++++ MQTTSNGateway/src/linux/Threading.h | 159 ++- MQTTSNGateway/src/linux/build.sh | 2 - MQTTSNGateway/src/linux/linux.cpp | 392 +++--- MQTTSNGateway/src/linux/linux.h | 70 + MQTTSNGateway/src/linux/main.cpp | 24 - MQTTSNGateway/src/linux/udp/SensorNetwork.cpp | 336 +++++ MQTTSNGateway/src/linux/udp/SensorNetwork.h | 114 ++ .../src/linux/xbee/SensorNetwork.cpp | 434 +++++++ MQTTSNGateway/src/linux/xbee/SensorNetwork.h | 141 ++ MQTTSNGateway/src/main.cpp | 33 - MQTTSNGateway/src/mainGateway.cpp | 46 + MQTTSNGateway/src/mainLogmonitor.cpp | 33 + MQTTSNPacket/src/MQTTSNConnectClient.c | 5 +- MQTTSNPacket/src/MQTTSNDeserializePublish.c | 2 +- MQTTSNPacket/src/MQTTSNPacket.c | 9 +- MQTTSNPacket/src/MQTTSNPacket.h | 12 +- MQTTSNPacket/src/MQTTSNPublish.h | 1 + MQTTSNPacket/src/MQTTSNSubscribeServer.c | 2 +- MQTTSNPacket/src/MQTTSNUnsubscribeServer.c | 2 +- Makefile | 106 ++ README.md | 2 +- 63 files changed, 9206 insertions(+), 1138 deletions(-) delete mode 100644 MQTTSNGateway/src/MQTTConnection.h create mode 100644 MQTTSNGateway/src/MQTTGWConnectionHandler.cpp create mode 100644 MQTTSNGateway/src/MQTTGWConnectionHandler.h create mode 100644 MQTTSNGateway/src/MQTTGWPacket.cpp create mode 100644 MQTTSNGateway/src/MQTTGWPacket.h create mode 100644 MQTTSNGateway/src/MQTTGWPublishHandler.cpp create mode 100644 MQTTSNGateway/src/MQTTGWPublishHandler.h create mode 100644 MQTTSNGateway/src/MQTTGWSubscribeHandler.cpp create mode 100644 MQTTSNGateway/src/MQTTGWSubscribeHandler.h create mode 100644 MQTTSNGateway/src/MQTTSNGWBrokerRecvTask.cpp create mode 100644 MQTTSNGateway/src/MQTTSNGWBrokerRecvTask.h create mode 100644 MQTTSNGateway/src/MQTTSNGWBrokerSendTask.cpp create mode 100644 MQTTSNGateway/src/MQTTSNGWBrokerSendTask.h create mode 100644 MQTTSNGateway/src/MQTTSNGWClient.cpp create mode 100644 MQTTSNGateway/src/MQTTSNGWClient.h create mode 100644 MQTTSNGateway/src/MQTTSNGWClientRecvTask.cpp create mode 100644 MQTTSNGateway/src/MQTTSNGWClientRecvTask.h create mode 100644 MQTTSNGateway/src/MQTTSNGWClientSendTask.cpp create mode 100644 MQTTSNGateway/src/MQTTSNGWClientSendTask.h create mode 100644 MQTTSNGateway/src/MQTTSNGWConnectionHandler.cpp create mode 100644 MQTTSNGateway/src/MQTTSNGWConnectionHandler.h create mode 100644 MQTTSNGateway/src/MQTTSNGWDefines.h create mode 100644 MQTTSNGateway/src/MQTTSNGWLogmonitor.cpp create mode 100644 MQTTSNGateway/src/MQTTSNGWLogmonitor.h create mode 100644 MQTTSNGateway/src/MQTTSNGWPacket.cpp create mode 100644 MQTTSNGateway/src/MQTTSNGWPacket.h create mode 100644 MQTTSNGateway/src/MQTTSNGWPacketHandleTask.cpp create mode 100644 MQTTSNGateway/src/MQTTSNGWPacketHandleTask.h create mode 100644 MQTTSNGateway/src/MQTTSNGWProcess.cpp create mode 100644 MQTTSNGateway/src/MQTTSNGWProcess.h create mode 100644 MQTTSNGateway/src/MQTTSNGWPublishHandler.cpp create mode 100644 MQTTSNGateway/src/MQTTSNGWPublishHandler.h create mode 100644 MQTTSNGateway/src/MQTTSNGWSubscribeHandler.cpp create mode 100644 MQTTSNGateway/src/MQTTSNGWSubscribeHandler.h create mode 100644 MQTTSNGateway/src/MQTTSNGateway.cpp create mode 100644 MQTTSNGateway/src/linux/Network.cpp create mode 100644 MQTTSNGateway/src/linux/Network.h create mode 100644 MQTTSNGateway/src/linux/Threading.cpp delete mode 100644 MQTTSNGateway/src/linux/build.sh create mode 100644 MQTTSNGateway/src/linux/linux.h delete mode 100644 MQTTSNGateway/src/linux/main.cpp create mode 100644 MQTTSNGateway/src/linux/udp/SensorNetwork.cpp create mode 100644 MQTTSNGateway/src/linux/udp/SensorNetwork.h create mode 100644 MQTTSNGateway/src/linux/xbee/SensorNetwork.cpp create mode 100644 MQTTSNGateway/src/linux/xbee/SensorNetwork.h delete mode 100644 MQTTSNGateway/src/main.cpp create mode 100644 MQTTSNGateway/src/mainGateway.cpp create mode 100644 MQTTSNGateway/src/mainLogmonitor.cpp create mode 100644 Makefile diff --git a/.cproject b/.cproject index 6dc5729..e99b880 100644 --- a/.cproject +++ b/.cproject @@ -1,4 +1,5 @@ - + + @@ -22,6 +23,16 @@ + - + + + + + + + - + @@ -69,6 +90,15 @@ + - + + + + + + + - + + + + @@ -98,23 +141,29 @@ + + + + + + + + + + - + + + + + + + - - - - - - - - - - - \ No newline at end of file + diff --git a/.project b/.project index 09b1b50..e1e29d1 100644 --- a/.project +++ b/.project @@ -20,6 +20,7 @@ org.eclipse.cdt.core.cnature + org.eclipse.cdt.core.ccnature org.eclipse.cdt.managedbuilder.core.managedBuildNature org.eclipse.cdt.managedbuilder.core.ScannerConfigNature diff --git a/MQTTSNClient/src/linux/linux.cpp b/MQTTSNClient/src/linux/linux.cpp index 222acda..0e9872c 100644 --- a/MQTTSNClient/src/linux/linux.cpp +++ b/MQTTSNClient/src/linux/linux.cpp @@ -53,7 +53,7 @@ public: rc = errno; } //} - return errno; + return rc; } int connect(const char* hostname, int port) @@ -97,7 +97,7 @@ public: mysock = socket(family, type, 0); if (mysock != -1) { - int opt = 1; + //int opt = 1; //if (setsockopt(mysock, SOL_SOCKET, SO_NOSIGPIPE, (void*)&opt, sizeof(opt)) != 0) // printf("Could not set SO_NOSIGPIPE for socket %d", mysock); diff --git a/MQTTSNGateway/src/MQTTConnection.h b/MQTTSNGateway/src/MQTTConnection.h deleted file mode 100644 index e2df956..0000000 --- a/MQTTSNGateway/src/MQTTConnection.h +++ /dev/null @@ -1,559 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014, 2016 IBM Corp. - * - * 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: - * Ian Craggs - initial API and implementation and/or initial documentation - *******************************************************************************/ - -#if !defined(MQTTCONNECTION_H) -#define MQTTCONNECTION_H - -#include "FP.h" -#include "MQTTPacket.h" -#include "stdio.h" -#include "MQTTLogging.h" - -#if !defined(MQTTCLIENT_QOS1) - #define MQTTCLIENT_QOS1 1 -#endif -#if !defined(MQTTCLIENT_QOS2) - #define MQTTCLIENT_QOS2 0 -#endif - -namespace MQTT -{ - - -enum QoS { QOS0, QOS1, QOS2 }; - -// all failure return codes must be negative -enum returnCode { BUFFER_OVERFLOW = -2, FAILURE = -1, SUCCESS = 0 }; - - -struct Message -{ - enum QoS qos; - bool retained; - bool dup; - unsigned short id; - void *payload; - size_t payloadlen; -}; - - -class PacketId -{ -public: - PacketId() - { - next = 0; - } - - int getNext() - { - return next = (next == MAX_PACKET_ID) ? 1 : ++next; - } - -private: - static const int MAX_PACKET_ID = 65535; - int next; -}; - - -/** - * @class Client - * @brief blocking, non-threaded MQTT client API - * - * This version of the API blocks on all method calls, until they are complete. This means that only one - * MQTT request can be in process at any one time. - * @param Network a network class which supports send, receive - * @param Timer a timer class with the methods: - */ -template -class Connection -{ - -public: - - /** Construct the client - * @param network - pointer to an instance of the Network class - must be connected to the endpoint - * before calling MQTT connect - * @param limits an instance of the Limit class - to alter limits as required - */ - Connection(unsigned int command_timeout_ms = 30000); - - - static void run(void const* arg); - - - /** MQTT Disconnect - send an MQTT disconnect packet, and clean up any state - * @return success code - - */ - int disconnect(); - - /** Is the client connected? - * @return flag - is the client connected or not? - */ - bool isConnected() - { - return isconnected; - } - - int sendPacket(int length, Timer& timer); - unsigned char sendbuf[MAX_MQTT_PACKET_SIZE]; - -private: - - int connect(MQTTPacket_connectData&); - int connect(); - - void cleanSession(); - int cycle(Timer& timer); - int waitfor(int packet_type, Timer& timer); - int keepalive(); - int publish(int len, Timer& timer, enum QoS qos); - - int decodePacket(int* value, int timeout); - int readPacket(Timer& timer); - - Network ipstack; - unsigned long command_timeout_ms; - unsigned char readbuf[MAX_MQTT_PACKET_SIZE]; - - Timer last_sent, last_received; - unsigned int keepAliveInterval; - bool ping_outstanding; - bool cleansession; - - PacketId packetid; - - bool isconnected; - -#if MQTTCLIENT_QOS1 || MQTTCLIENT_QOS2 - unsigned char pubbuf[MAX_MQTT_PACKET_SIZE]; // store the last publish for sending on reconnect - int inflightLen; - unsigned short inflightMsgid; - enum QoS inflightQoS; -#endif - -#if MQTTCLIENT_QOS2 - bool pubrel; - #if !defined(MAX_INCOMING_QOS2_MESSAGES) - #define MAX_INCOMING_QOS2_MESSAGES 10 - #endif - unsigned short incomingQoS2messages[MAX_INCOMING_QOS2_MESSAGES]; - bool isQoS2msgidFree(unsigned short id); - bool useQoS2msgid(unsigned short id); - void freeQoS2msgid(unsigned short id); -#endif - -}; - -} - - -template -void MQTT::Connection::run(void const* arg) -{ - - - - -} - - -template -void MQTT::Connection::cleanSession() -{ - ping_outstanding = false; - isconnected = false; - -#if MQTTCLIENT_QOS1 || MQTTCLIENT_QOS2 - inflightMsgid = 0; - inflightQoS = QOS0; -#endif - -#if MQTTCLIENT_QOS2 - pubrel = false; - for (int i = 0; i < MAX_INCOMING_QOS2_MESSAGES; ++i) - incomingQoS2messages[i] = 0; -#endif -} - - -template -MQTT::Connection::Connection(unsigned int command_timeout_ms) : packetid() -{ - last_sent = Timer(); - last_received = Timer(); - this->command_timeout_ms = command_timeout_ms; - cleanSession(); -} - - -#if MQTTCLIENT_QOS2 -template -bool MQTT::Connection::isQoS2msgidFree(unsigned short id) -{ - for (int i = 0; i < MAX_INCOMING_QOS2_MESSAGES; ++i) - { - if (incomingQoS2messages[i] == id) - return false; - } - return true; -} - - -template -bool MQTT::Connection::useQoS2msgid(unsigned short id) -{ - for (int i = 0; i < MAX_INCOMING_QOS2_MESSAGES; ++i) - { - if (incomingQoS2messages[i] == 0) - { - incomingQoS2messages[i] = id; - return true; - } - } - return false; -} - - -template -void MQTT::Connection::freeQoS2msgid(unsigned short id) -{ - for (int i = 0; i < MAX_INCOMING_QOS2_MESSAGES; ++i) - { - if (incomingQoS2messages[i] == id) - { - incomingQoS2messages[i] = 0; - return; - } - } -} -#endif - - -template -int MQTT::Connection::sendPacket(int length, Timer& timer) -{ - int rc = FAILURE, - sent = 0; - - while (sent < length && !timer.expired()) - { - rc = ipstack.write(&sendbuf[sent], length - sent, timer.left_ms()); - if (rc < 0) // there was an error writing the data - break; - sent += rc; - } - if (sent == length) - { - if (this->keepAliveInterval > 0) - last_sent.countdown(this->keepAliveInterval); // record the fact that we have successfully sent the packet - rc = SUCCESS; - } - else - rc = FAILURE; - -#if defined(MQTT_DEBUG) - char printbuf[150]; - DEBUG("Rc %d from sending packet %s\n", rc, MQTTFormat_toServerString(printbuf, sizeof(printbuf), sendbuf, length)); -#endif - return rc; -} - - -template -int MQTT::Connection::decodePacket(int* value, int timeout) -{ - unsigned char c; - int multiplier = 1; - int len = 0; - const int MAX_NO_OF_REMAINING_LENGTH_BYTES = 4; - - *value = 0; - do - { - int rc = MQTTPACKET_READ_ERROR; - - if (++len > MAX_NO_OF_REMAINING_LENGTH_BYTES) - { - rc = MQTTPACKET_READ_ERROR; /* bad data */ - goto exit; - } - rc = ipstack.read(&c, 1, timeout); - if (rc != 1) - goto exit; - *value += (c & 127) * multiplier; - multiplier *= 128; - } while ((c & 128) != 0); -exit: - return len; -} - - -/** - * If any read fails in this method, then we should disconnect from the network, as on reconnect - * the packets can be retried. - * @param timeout the max time to wait for the packet read to complete, in milliseconds - * @return the MQTT packet type, or -1 if none - */ -template -int MQTT::Connection::readPacket(Timer& timer) -{ - int rc = FAILURE; - MQTTHeader header = {0}; - int len = 0; - int rem_len = 0; - - /* 1. read the header byte. This has the packet type in it */ - if (ipstack.read(readbuf, 1, timer.left_ms()) != 1) - goto exit; - - len = 1; - /* 2. read the remaining length. This is variable in itself */ - decodePacket(&rem_len, timer.left_ms()); - len += MQTTPacket_encode(readbuf + 1, rem_len); /* put the original remaining length into the buffer */ - - if (rem_len > (MAX_MQTT_PACKET_SIZE - len)) - { - rc = BUFFER_OVERFLOW; - goto exit; - } - - /* 3. read the rest of the buffer using a callback to supply the rest of the data */ - if (rem_len > 0 && (ipstack.read(readbuf + len, rem_len, timer.left_ms()) != rem_len)) - goto exit; - - header.byte = readbuf[0]; - rc = header.bits.type; - if (this->keepAliveInterval > 0) - last_received.countdown(this->keepAliveInterval); // record the fact that we have successfully received a packet -exit: - -#if defined(MQTT_DEBUG) - if (rc >= 0) - { - char printbuf[50]; - DEBUG("Rc %d from receiving packet %s\n", rc, MQTTFormat_toClientString(printbuf, sizeof(printbuf), readbuf, len)); - } -#endif - return rc; -} - - -template -int MQTT::Connection::cycle(Timer& timer) -{ - /* get one piece of work off the wire and one pass through */ - - // read the socket, see what work is due - int packet_type = readPacket(timer); - - int len = 0, - rc = SUCCESS; - - switch (packet_type) - { - case FAILURE: - case BUFFER_OVERFLOW: - rc = packet_type; - break; - case CONNACK: - case PUBACK: - case SUBACK: - break; - case PUBLISH: - { - MQTTString topicName = MQTTString_initializer; - Message msg; - int intQoS; - if (MQTTDeserialize_publish((unsigned char*)&msg.dup, &intQoS, (unsigned char*)&msg.retained, (unsigned short*)&msg.id, &topicName, - (unsigned char**)&msg.payload, (int*)&msg.payloadlen, readbuf, MAX_MQTT_PACKET_SIZE) != 1) - goto exit; - msg.qos = (enum QoS)intQoS; -#if MQTTCLIENT_QOS2 - if (msg.qos != QOS2) -#endif - ; //deliverMessage(topicName, msg); -#if MQTTCLIENT_QOS2 - else if (isQoS2msgidFree(msg.id)) - { - if (useQoS2msgid(msg.id)) - ; //deliverMessage(topicName, msg); - else - WARN("Maximum number of incoming QoS2 messages exceeded"); - } -#endif -#if MQTTCLIENT_QOS1 || MQTTCLIENT_QOS2 - if (msg.qos != QOS0) - { - if (msg.qos == QOS1) - len = MQTTSerialize_ack(sendbuf, MAX_MQTT_PACKET_SIZE, PUBACK, 0, msg.id); - else if (msg.qos == QOS2) - len = MQTTSerialize_ack(sendbuf, MAX_MQTT_PACKET_SIZE, PUBREC, 0, msg.id); - if (len <= 0) - rc = FAILURE; - else - rc = sendPacket(len, timer); - if (rc == FAILURE) - goto exit; // there was a problem - } - break; -#endif - } -#if MQTTCLIENT_QOS2 - case PUBREC: - case PUBREL: - unsigned short mypacketid; - unsigned char dup, type; - if (MQTTDeserialize_ack(&type, &dup, &mypacketid, readbuf, MAX_MQTT_PACKET_SIZE) != 1) - rc = FAILURE; - else if ((len = MQTTSerialize_ack(sendbuf, MAX_MQTT_PACKET_SIZE, - (packet_type == PUBREC) ? PUBREL : PUBCOMP, 0, mypacketid)) <= 0) - rc = FAILURE; - else if ((rc = sendPacket(len, timer)) != SUCCESS) // send the PUBREL packet - rc = FAILURE; // there was a problem - if (rc == FAILURE) - goto exit; // there was a problem - if (packet_type == PUBREL) - freeQoS2msgid(mypacketid); - break; - - case PUBCOMP: - break; -#endif - case PINGRESP: - ping_outstanding = false; - break; - } - keepalive(); -exit: - if (rc == SUCCESS) - rc = packet_type; - return rc; -} - - -template -int MQTT::Connection::keepalive() -{ - int rc = FAILURE; - - if (keepAliveInterval == 0) - { - rc = SUCCESS; - goto exit; - } - - if (last_sent.expired() || last_received.expired()) - { - if (!ping_outstanding) - { - Timer timer(1000); - int len = MQTTSerialize_pingreq(sendbuf, MAX_MQTT_PACKET_SIZE); - if (len > 0 && (rc = sendPacket(len, timer)) == SUCCESS) // send the ping packet - ping_outstanding = true; - } - } - -exit: - return rc; -} - - -template -int MQTT::Connection::connect(MQTTPacket_connectData& options) -{ - Timer connect_timer(command_timeout_ms); - int rc = FAILURE; - int len = 0; - - if (isconnected) // don't send connect packet again if we are already connected - goto exit; - - this->keepAliveInterval = options.keepAliveInterval; - this->cleansession = options.cleansession; - if ((len = MQTTSerialize_connect(sendbuf, MAX_MQTT_PACKET_SIZE, &options)) <= 0) - goto exit; - if ((rc = sendPacket(len, connect_timer)) != SUCCESS) // send the connect packet - goto exit; // there was a problem - - if (this->keepAliveInterval > 0) - last_received.countdown(this->keepAliveInterval); - // this will be a blocking call, wait for the connack - if (waitfor(CONNACK, connect_timer) == CONNACK) - { - unsigned char connack_rc = 255; - bool sessionPresent = false; - if (MQTTDeserialize_connack((unsigned char*)&sessionPresent, &connack_rc, readbuf, MAX_MQTT_PACKET_SIZE) == 1) - rc = connack_rc; - else - rc = FAILURE; - } - else - rc = FAILURE; - -#if MQTTCLIENT_QOS2 - // resend any inflight publish - if (inflightMsgid > 0 && inflightQoS == QOS2 && pubrel) - { - if ((len = MQTTSerialize_ack(sendbuf, MAX_MQTT_PACKET_SIZE, PUBREL, 0, inflightMsgid)) <= 0) - rc = FAILURE; - else - rc = publish(len, connect_timer, inflightQoS); - } - else -#endif -#if MQTTCLIENT_QOS1 || MQTTCLIENT_QOS2 - if (inflightMsgid > 0) - { - memcpy(sendbuf, pubbuf, MAX_MQTT_PACKET_SIZE); - rc = publish(inflightLen, connect_timer, inflightQoS); - } -#endif - -exit: - if (rc == SUCCESS) - isconnected = true; - return rc; -} - - -template -int MQTT::Connection::connect() -{ - MQTTPacket_connectData default_options = MQTTPacket_connectData_initializer; - return connect(default_options); -} - - - -template -int MQTT::Connection::disconnect() -{ - int rc = FAILURE; - Timer timer(command_timeout_ms); // we might wait for incomplete incoming publishes to complete - int len = MQTTSerialize_disconnect(sendbuf, MAX_MQTT_PACKET_SIZE); - if (len > 0) - rc = sendPacket(len, timer); // send the disconnect packet - - if (cleansession) - cleanSession(); - else - isconnected = false; - return rc; -} - - -#endif diff --git a/MQTTSNGateway/src/MQTTGWConnectionHandler.cpp b/MQTTSNGateway/src/MQTTGWConnectionHandler.cpp new file mode 100644 index 0000000..88263da --- /dev/null +++ b/MQTTSNGateway/src/MQTTGWConnectionHandler.cpp @@ -0,0 +1,106 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * 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: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ + +#include "MQTTGWConnectionHandler.h" +#include "MQTTGWPacket.h" + +using namespace std; +using namespace MQTTSNGW; + +MQTTGWConnectionHandler::MQTTGWConnectionHandler(Gateway* gateway) +{ + _gateway = gateway; +} + +MQTTGWConnectionHandler::~MQTTGWConnectionHandler() +{ + +} + +void MQTTGWConnectionHandler::handleConnack(Client* client, MQTTGWPacket* packet) +{ + uint8_t rc = MQTT_SERVER_UNAVAILABLE; + Connack resp; + packet->getCONNACK(&resp); + + /* convert MQTT ReturnCode to MQTT-SN one */ + if (resp.rc == MQTT_CONNECTION_ACCEPTED) + { + rc = MQTTSN_RC_ACCEPTED; + } + else if (resp.rc == MQTT_UNACCEPTABLE_PROTOCOL_VERSION) + { + rc = MQTTSN_RC_NOT_SUPPORTED; + WRITELOG(" ClientID : %s Requested Protocol version is not supported.\n", client->getClientId()); + } + else if (resp.rc == MQTT_IDENTIFIER_REJECTED) + { + rc = MQTTSN_RC_NOT_SUPPORTED; + WRITELOG(" ClientID : %s ClientID is collect UTF-8 but not allowed by the Server.\n", + client->getClientId()); + } + else if (resp.rc == MQTT_SERVER_UNAVAILABLE) + { + rc = MQTTSN_RC_REJECTED_CONGESTED; + WRITELOG(" ClientID : %s The Network Connection has been made but the MQTT service is unavailable.\n", + client->getClientId()); + } + else if (resp.rc == MQTT_BAD_USERNAME_OR_PASSWORD) + { + rc = MQTTSN_RC_NOT_SUPPORTED; + WRITELOG(" Gateway Configuration Error: The data in the user name or password is malformed.\n"); + } + else if (resp.rc == MQTT_NOT_AUTHORIZED) + { + rc = MQTTSN_RC_NOT_SUPPORTED; + WRITELOG(" Gateway Configuration Error: The Client is not authorized to connect.\n"); + } + + MQTTSNPacket* snPacket = new MQTTSNPacket(); + snPacket->setCONNACK(rc); + + Event* ev1 = new Event(); + ev1->setClientSendEvent(client, snPacket); + client->connackSended(rc); // update the client's status + _gateway->getClientSendQue()->post(ev1); + + MQTTSNPacket* sleepPacket = 0; + while ( (sleepPacket = client->getClientSleepPacket()) ) + { + Event* ev1 = new Event(); + ev1->setClientSendEvent(client, sleepPacket); + _gateway->getClientSendQue()->post(ev1); + } +} + +void MQTTGWConnectionHandler::handlePingresp(Client* client, MQTTGWPacket* packet) +{ + MQTTSNPacket* snPacket = new MQTTSNPacket(); + snPacket->setPINGRESP(); + Event* ev1 = new Event(); + ev1->setClientSendEvent(client, snPacket); + _gateway->getClientSendQue()->post(ev1); +} + +void MQTTGWConnectionHandler::handleDisconnect(Client* client, MQTTGWPacket* packet) +{ + MQTTSNPacket* snPacket = new MQTTSNPacket(); + snPacket->setDISCONNECT(0); + client->disconnected(); + Event* ev1 = new Event(); + ev1->setClientSendEvent(client, snPacket); +} + diff --git a/MQTTSNGateway/src/MQTTGWConnectionHandler.h b/MQTTSNGateway/src/MQTTGWConnectionHandler.h new file mode 100644 index 0000000..8d8af3c --- /dev/null +++ b/MQTTSNGateway/src/MQTTGWConnectionHandler.h @@ -0,0 +1,42 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * 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: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ +#ifndef MQTTGWCONNECTIONHANDLER_H_ +#define MQTTGWCONNECTIONHANDLER_H_ + +#include "MQTTSNGWDefines.h" +#include "MQTTSNGWPacket.h" +#include "MQTTSNGateway.h" + +namespace MQTTSNGW +{ + +class MQTTGWConnectionHandler +{ +public: + MQTTGWConnectionHandler(Gateway* gateway); + ~MQTTGWConnectionHandler(); + void handleConnack(Client* client, MQTTGWPacket* packet); + void handlePingresp(Client* client, MQTTGWPacket* packet); + void handleDisconnect(Client* client, MQTTGWPacket* packet); + +private: + Gateway* _gateway; +}; + +} + + +#endif /* MQTTGWCONNECTIONHANDLER_H_ */ diff --git a/MQTTSNGateway/src/MQTTGWPacket.cpp b/MQTTSNGateway/src/MQTTGWPacket.cpp new file mode 100644 index 0000000..858bf35 --- /dev/null +++ b/MQTTSNGateway/src/MQTTGWPacket.cpp @@ -0,0 +1,530 @@ +/************************************************************************************** + * Copyright (c) 2009, 2014 IBM Corp. + * + * 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: + * Ian Craggs - initial API and implementation and/or initial documentation + * Tomoaki Yamaguchi - modify codes for MATT-SN Gateway + **************************************************************************************/ + +#include "MQTTGWPacket.h" +#include + +using namespace MQTTSNGW; + +#define MAX_NO_OF_REMAINING_LENGTH_BYTES 3 +/** + * List of the predefined MQTT v3 packet names. + */ +static const char* mqtt_packet_names[] = +{ "RESERVED", "CONNECT", "CONNACK", "PUBLISH", "PUBACK", "PUBREC", "PUBREL", "PUBCOMP", "SUBSCRIBE", "SUBACK", + "UNSUBSCRIBE", "UNSUBACK", "PINGREQ", "PINGRESP", "DISCONNECT" }; + +/** + * Encodes the message length according to the MQTT algorithm + * @param buf the buffer into which the encoded data is written + * @param length the length to be encoded + * @return the number of bytes written to buffer + */ +int MQTTPacket_encode(char* buf, int length) +{ + int rc = 0; + do + { + char d = length % 128; + length /= 128; + /* if there are more digits to encode, set the top bit of this digit */ + if (length > 0) + d |= 0x80; + buf[rc++] = d; + } while (length > 0); + return rc; +} + +/** + * Calculates an integer from two bytes read from the input buffer + * @param pptr pointer to the input buffer - incremented by the number of bytes used & returned + * @return the integer value calculated + */ +int readInt(char** pptr) +{ + char* ptr = *pptr; + int len = 256 * ((unsigned char) (*ptr)) + (unsigned char) (*(ptr + 1)); + *pptr += 2; + return len; +} + +/** + * Reads a "UTF" string from the input buffer. UTF as in the MQTT v3 spec which really means + * a length delimited string. So it reads the two byte length then the data according to + * that length. The end of the buffer is provided too, so we can prevent buffer overruns caused + * by an incorrect length. + * @param pptr pointer to the input buffer - incremented by the number of bytes used & returned + * @param enddata pointer to the end of the buffer not to be read beyond + * @param len returns the calculcated value of the length bytes read + * @return an allocated C string holding the characters read, or NULL if the length read would + * have caused an overrun. + * + */ +char* readUTFlen(char** pptr, char* enddata, int* len) +{ + char* string = NULL; + + if (enddata - (*pptr) > 1) /* enough length to read the integer? */ + { + *len = readInt(pptr); + if (&(*pptr)[*len] <= enddata) + { + string = (char*)calloc(*len + 1, 1); + memcpy(string, *pptr, (size_t)*len); + string[*len] = '\0'; + *pptr += *len; + } + } + return string; +} + +/** + * Reads a "UTF" string from the input buffer. UTF as in the MQTT v3 spec which really means + * a length delimited string. So it reads the two byte length then the data according to + * that length. The end of the buffer is provided too, so we can prevent buffer overruns caused + * by an incorrect length. + * @param pptr pointer to the input buffer - incremented by the number of bytes used & returned + * @param enddata pointer to the end of the buffer not to be read beyond + * @return an allocated C string holding the characters read, or NULL if the length read would + * have caused an overrun. + */ +char* readUTF(char** pptr, char* enddata) +{ + int len; + return readUTFlen(pptr, enddata, &len); +} + +/** + * Reads one character from the input buffer. + * @param pptr pointer to the input buffer - incremented by the number of bytes used & returned + * @return the character read + */ +unsigned char readChar(char** pptr) +{ + unsigned char c = **pptr; + (*pptr)++; + return c; +} + +/** + * Writes one character to an output buffer. + * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned + * @param c the character to write + */ +void writeChar(unsigned char** pptr, char c) +{ + **pptr = c; + (*pptr)++; +} + +/** + * Writes an integer as 2 bytes to an output buffer. + * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned + * @param anInt the integer to write + */ +void writeInt(unsigned char** pptr, int anInt) +{ + **pptr = (unsigned char)(anInt / 256); + (*pptr)++; + **pptr = (unsigned char)(anInt % 256); + (*pptr)++; +} + +/** + * Writes a "UTF" string to an output buffer. Converts C string to length-delimited. + * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned + * @param string the C string to write + */ +void writeUTF(unsigned char** pptr, const char* string) +{ + int len = (int)strlen(string); + writeInt(pptr, len); + memcpy(*pptr, string, (size_t)len); + *pptr += len; +} + +/** + * Lapper class of MQTTPacket + * + */ +MQTTGWPacket::MQTTGWPacket() +{ + _data = 0; + _header.byte = 0; + _remainingLength = 0; +} + +MQTTGWPacket::~MQTTGWPacket() +{ + if (_data) + { + free(_data); + } +} + +int MQTTGWPacket::recv(Network* network) +{ + int len = 0; + int multiplier = 1; + unsigned char c; + + /* read First Byte of Packet */ + if (network->recv((unsigned char*)&_header.byte, 1) == -1) + { + return -1; + } + /* read RemainingLength */ + do + { + if (++len > MAX_NO_OF_REMAINING_LENGTH_BYTES) + { + return -2; + } + if (network->recv(&c, 1) == -1) + { + return -2; + } + _remainingLength += (c & 127) * multiplier; + multiplier *= 128; + } while ((c & 128) != 0); + + /* allocate buffer */ + _data = (unsigned char*)calloc(_remainingLength, 1); + if ( !_data ) + { + return -3; + } + + /* read Payload */ + int remlen = network->recv(_data, _remainingLength); + + if (remlen == -1 || remlen != _remainingLength ) + { + return -2; + } + return 1 + len + _remainingLength; +} + +int MQTTGWPacket::send(Network* network) +{ + unsigned char buf[MQTTSNGW_MAX_PACKET_SIZE]; + memset(buf, 0, MQTTSNGW_MAX_PACKET_SIZE); + int len = getPacketData(buf); + return network->send(buf, len); + +} + +int MQTTGWPacket::getAck(Ack* ack) +{ + if (PUBACK != _header.bits.type && PUBREC != _header.bits.type && PUBREL != _header.bits.type + && PUBCOMP != _header.bits.type && UNSUBACK != _header.bits.type) + { + return 0; + } + char* ptr = (char*) _data; + ack->header.byte = _header.byte; + ack->msgId = readInt((char**) &ptr); + return 1; +} + +int MQTTGWPacket::getCONNACK(Connack* resp) +{ + if (_header.bits.type != CONNACK) + { + return 0; + } + char* ptr = (char*) _data; + resp->header.byte = _header.byte; + resp->flags.all = *ptr++; + resp->rc = readChar(&ptr); + return 1; +} + +int MQTTGWPacket::getSUBACK(unsigned short* msgId, unsigned char* rc) +{ + if (_header.bits.type != SUBACK) + { + return 0; + } + char *ptr = (char*) _data; + *msgId = readInt((char**) &ptr); + *rc = readChar(&ptr); + return 1; +} + +int MQTTGWPacket::getPUBLISH(Publish* pub) +{ + if (_header.bits.type != PUBLISH) + { + return 0; + } + char* ptr = (char*) _data; + pub->header = _header; + pub->topiclen = readInt((char**) &ptr); + pub->topic = (char*) _data + 2; + ptr += pub->topiclen; + pub->msgId = readInt(&ptr); + pub->payload = ptr; + pub->payloadlen = _remainingLength - pub->topiclen - 4; + return 1; +} + +int MQTTGWPacket::setCONNECT(Connect* connect, unsigned char* username, unsigned char* password) +{ + clearData(); + _header = connect->header; + + _remainingLength = ((connect->version == 3) ? 12 : 10) + (int)strlen(connect->clientID) + 2; + if (connect->flags.bits.will) + { + _remainingLength += (int)strlen(connect->willTopic) + 2 + (int)strlen(connect->willMsg) + 2; + } + if ( connect->flags.bits.username ) + { + _remainingLength += (int)strlen((char*) username) + 2; + } + if (connect->flags.bits.password) + { + _remainingLength += (int)strlen((char*) password) + 2; + } + + _data = (unsigned char*)calloc(_remainingLength, 1); + unsigned char* ptr = _data; + + if (connect->version == 3) + { + writeUTF(&ptr, "MQIsdp"); + writeChar(&ptr, (char) 3); + } + else if (connect->version == 4) + { + writeUTF(&ptr, "MQTT"); + writeChar(&ptr, (char) 4); + } + else + { + return 0; + } + + writeChar(&ptr, connect->flags.all); + writeInt(&ptr, connect->keepAliveTimer); + writeUTF(&ptr, connect->clientID); + if (connect->flags.bits.will) + { + writeUTF(&ptr, connect->willTopic); + writeUTF(&ptr, connect->willMsg); + } + + if (connect->flags.bits.username) + { + writeUTF(&ptr, (const char*) username); + } + if (connect->flags.bits.password) + { + writeUTF(&ptr, (const char*) password); + } + return 1; +} + +int MQTTGWPacket::setSUBSCRIBE(const char* topic, unsigned char qos, unsigned short msgId) +{ + clearData(); + _header.byte = 0; + _header.bits.type = SUBSCRIBE; + _header.bits.qos = 1; // Reserved + _remainingLength = (int)strlen(topic) + 5; + _data = (unsigned char*)calloc(_remainingLength, 1); + if (_data) + { + unsigned char* ptr = _data; + writeInt(&ptr, msgId); + writeUTF(&ptr, topic); + writeChar(&ptr, (char) qos); + return 1; + } + clearData(); + return 0; +} + +int MQTTGWPacket::setUNSUBSCRIBE(const char* topic, unsigned short msgid) +{ + clearData(); + _header.byte = 0; + _header.bits.type = UNSUBSCRIBE; + _header.bits.qos = 1; + _remainingLength = (int)strlen(topic) + 4; + _data = (unsigned char*)calloc(_remainingLength, 1); + if (_data) + { + unsigned char* ptr = _data; + writeInt(&ptr, msgid); + writeUTF(&ptr, topic); + return 1; + } + clearData(); + return 0; + +} + +int MQTTGWPacket::setPUBLISH(Publish* pub) +{ + clearData(); + _header.byte = pub->header.byte; + _header.bits.type = PUBLISH; + _remainingLength = 4 + pub->topiclen + pub->payloadlen; + _data = (unsigned char*)calloc(_remainingLength, 1); + if (_data) + { + unsigned char* ptr = _data; + writeInt(&ptr, pub->topiclen); + memcpy(ptr, pub->topic, pub->topiclen); + ptr += pub->topiclen; + writeInt(&ptr, pub->msgId); + memcpy(ptr, pub->payload, pub->payloadlen); + return 1; + } + else + { + clearData(); + return 0; + } +} + +int MQTTGWPacket::setAck(unsigned char msgType, unsigned short msgid) +{ + clearData(); + _remainingLength = 2; + _header.bits.type = msgType; + _header.bits.qos = (msgType == PUBREL) ? 1 : 0; + + _data = (unsigned char*)calloc(_remainingLength, 1); + if (_data) + { + unsigned char* data = _data; + writeInt(&data, msgid); + return 1; + } + return 0; +} + +int MQTTGWPacket::setHeader(unsigned char msgType) +{ + clearData(); + if (msgType < CONNECT || msgType > DISCONNECT) + { + return 0; + } + _header.bits.type = msgType; + return 0; +} + +int MQTTGWPacket::getType(void) +{ + return _header.bits.type; +} + +const char* MQTTGWPacket::getName(void) +{ + return getType() > DISCONNECT ? "UNKNOWN" : mqtt_packet_names[getType()]; +} + +int MQTTGWPacket::getPacketData(unsigned char* buf) +{ + unsigned char* ptr = buf; + *ptr++ = _header.byte; + int len = MQTTPacket_encode((char*)ptr, _remainingLength); + ptr += len; + memcpy(ptr, _data, _remainingLength); + return 1 + len + _remainingLength; +} + +int MQTTGWPacket::getPacketLength(void) +{ + char buf[4]; + return 1 + MQTTPacket_encode(buf, _remainingLength) + _remainingLength; +} + +void MQTTGWPacket::clearData(void) +{ + if (_data) + { + free(_data); + } + _header.byte = 0; + _remainingLength = 0; +} + +char* MQTTGWPacket::getMsgId(char* pbuf) +{ + int type = getType(); + + switch ( type ) + { + case PUBLISH: + Publish pub; + getPUBLISH(&pub); + if ( _header.bits.dup ) + { + sprintf(pbuf, "+%04X", pub.msgId); + } + else + { + sprintf(pbuf, " %04X", pub.msgId); + } + break; + case PUBACK: + case PUBREC: + case PUBREL: + case PUBCOMP: + case SUBSCRIBE: + case UNSUBSCRIBE: + case SUBACK: + case UNSUBACK: + sprintf(pbuf, " %02X%02X", _data[0], _data[1]); + break; + default: + sprintf(pbuf, " "); + break; + } + return pbuf; +} + +char* MQTTGWPacket::print(char* pbuf) +{ + char* ptr = pbuf; + char** pptr = &pbuf; + char digit[4]; + + sprintf(*pptr, " %02X",(const unsigned char)_header.byte); + *pptr += 3; + int len = MQTTPacket_encode((char*) digit, _remainingLength); + for (int i = 0; i < len; i++) + { + sprintf(*pptr, " %02X", digit[i]); + *pptr += 3; + } + + int size = _remainingLength > SIZEOF_LOG_PACKET ? SIZEOF_LOG_PACKET : _remainingLength; + for (int i = 0; i < size; i++) + { + sprintf(*pptr, " %02X", *(_data + i)); + *pptr += 3; + } + **pptr = 0; + return ptr; +} + diff --git a/MQTTSNGateway/src/MQTTGWPacket.h b/MQTTSNGateway/src/MQTTGWPacket.h new file mode 100644 index 0000000..ecba6bb --- /dev/null +++ b/MQTTSNGateway/src/MQTTGWPacket.h @@ -0,0 +1,218 @@ +/******************************************************************************* + * Copyright (c) 2009, 2014 IBM Corp. + * + * 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: + * Ian Craggs - initial API and implementation and/or initial documentation + * Ian Craggs, Allan Stockdill-Mander - SSL updates + * Ian Craggs - MQTT 3.1.1 support + * Tomoaki Yamaguchi - modify codes for MATT-SN Gateway + *******************************************************************************/ + +#ifndef MQTTGWPACKET_H_ +#define MQTTGWPACKET_H_ + +#include "Network.h" + +namespace MQTTSNGW +{ + +typedef void* (*pf)(unsigned char, char*, size_t); + +#define BAD_MQTT_PACKET -4 + +enum msgTypes +{ + CONNECT = 1, CONNACK, PUBLISH, PUBACK, PUBREC, PUBREL, + PUBCOMP, SUBSCRIBE, SUBACK, UNSUBSCRIBE, UNSUBACK, + PINGREQ, PINGRESP, DISCONNECT +}; + + +/** + * Bitfields for the MQTT header byte. + */ +typedef union +{ + /*unsigned*/ char byte; /**< the whole byte */ +#if defined(REVERSED) + struct + { + unsigned int type : 4; /**< message type nibble */ + bool dup : 1; /**< DUP flag bit */ + unsigned int qos : 2; /**< QoS value, 0, 1 or 2 */ + bool retain : 1; /**< retained flag bit */ + } bits; +#else + struct + { + bool retain : 1; /**< retained flag bit */ + unsigned int qos : 2; /**< QoS value, 0, 1 or 2 */ + bool dup : 1; /**< DUP flag bit */ + unsigned int type : 4; /**< message type nibble */ + } bits; +#endif +} Header; + + +/** + * Data for a connect packet. + */ + +enum MQTT_connackCodes{ + MQTT_CONNECTION_ACCEPTED , + MQTT_UNACCEPTABLE_PROTOCOL_VERSION, + MQTT_IDENTIFIER_REJECTED, + MQTT_SERVER_UNAVAILABLE, + MQTT_BAD_USERNAME_OR_PASSWORD, + MQTT_NOT_AUTHORIZED +}; + +typedef struct +{ + Header header; /**< MQTT header byte */ + union + { + unsigned char all; /**< all connect flags */ +#if defined(REVERSED) + struct + { + bool username : 1; /**< 3.1 user name */ + bool password : 1; /**< 3.1 password */ + bool willRetain : 1; /**< will retain setting */ + unsigned int willQoS : 2; /**< will QoS value */ + bool will : 1; /**< will flag */ + bool cleanstart : 1; /**< cleansession flag */ + int : 1; /**< unused */ + } bits; +#else + struct + { + int : 1; /**< unused */ + bool cleanstart : 1; /**< cleansession flag */ + bool will : 1; /**< will flag */ + unsigned int willQoS : 2; /**< will QoS value */ + bool willRetain : 1; /**< will retain setting */ + bool password : 1; /**< 3.1 password */ + bool username : 1; /**< 3.1 user name */ + } bits; +#endif + } flags; /**< connect flags byte */ + + char *Protocol, /**< MQTT protocol name */ + *clientID, /**< string client id */ + *willTopic, /**< will topic */ + *willMsg; /**< will payload */ + + int keepAliveTimer; /**< keepalive timeout value in seconds */ + unsigned char version; /**< MQTT version number */ +} Connect; + +/** + * Data for a willMessage. + */ +typedef struct +{ + char* topic; + char* msg; + int retained; + int qos; +}willMessages; + +/** + * Data for a connack packet. + */ +typedef struct +{ + Header header; /**< MQTT header byte */ + union + { + unsigned char all; /**< all connack flags */ +#if defined(REVERSED) + struct + { + unsigned int reserved : 7; /**< message type nibble */ + bool sessionPresent : 1; /**< was a session found on the server? */ + } bits; +#else + struct + { + bool sessionPresent : 1; /**< was a session found on the server? */ + unsigned int reserved : 7; /**< message type nibble */ + } bits; +#endif + } flags; /**< connack flags byte */ + char rc; /**< connack return code */ +} Connack; + + +/** + * Data for a publish packet. + */ +typedef struct +{ + Header header; /**< MQTT header byte */ + char* topic; /**< topic string */ + int topiclen; + int msgId; /**< MQTT message id */ + char* payload; /**< binary payload, length delimited */ + int payloadlen; /**< payload length */ +} Publish; + + +/** + * Data for one of the ack packets. + */ +typedef struct +{ + Header header; /**< MQTT header byte */ + int msgId; /**< MQTT message id */ +} Ack; + +/** + * Class MQTT Packet + */ +class MQTTGWPacket +{ +public: + MQTTGWPacket(); + ~MQTTGWPacket(); + int recv(Network* network); + int send(Network* network); + int getType(void); + int getPacketData(unsigned char* buf); + int getPacketLength(void); + const char* getName(void); + + int getAck(Ack* ack); + int getCONNACK(Connack* resp); + int getSUBACK(unsigned short* msgId, unsigned char* rc); + int getPUBLISH(Publish* pub); + + int setCONNECT(Connect* conect, unsigned char* username, unsigned char* password); + int setPUBLISH(Publish* pub); + int setAck(unsigned char msgType, unsigned short msgid); + int setHeader(unsigned char msgType); + int setSUBSCRIBE(const char* topic, unsigned char qos, unsigned short msgId); + int setUNSUBSCRIBE(const char* topics, unsigned short msgid); + char* getMsgId(char* buf); + char* print(char* buf); + +private: + void clearData(void); + Header _header; + int _remainingLength; + unsigned char* _data; +}; + +} + +#endif /* MQTTGWPACKET_H_ */ diff --git a/MQTTSNGateway/src/MQTTGWPublishHandler.cpp b/MQTTSNGateway/src/MQTTGWPublishHandler.cpp new file mode 100644 index 0000000..cebb633 --- /dev/null +++ b/MQTTSNGateway/src/MQTTGWPublishHandler.cpp @@ -0,0 +1,217 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * 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: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ + +#include "MQTTGWPublishHandler.h" +#include "MQTTSNGateway.h" +#include "MQTTSNPacket.h" +#include + +using namespace std; +using namespace MQTTSNGW; + +char* currentDateTime(void); + +MQTTGWPublishHandler::MQTTGWPublishHandler(Gateway* gateway) +{ + _gateway = gateway; +} + +MQTTGWPublishHandler::~MQTTGWPublishHandler() +{ + +} + +void MQTTGWPublishHandler::handlePublish(Client* client, MQTTGWPacket* packet) +{ + if ( !client->isActive() && !client->isSleep() ) + { + WRITELOG(" The client is neither active nor sleep %s\n", client->getStatus()); + return; + } + + Publish pub; + packet->getPUBLISH(&pub); + + MQTTSNPacket* snPacket = new MQTTSNPacket(); + + /* create MQTTSN_topicid */ + MQTTSN_topicid topicId; + + if (pub.topiclen == 2) + { + topicId.type = MQTTSN_TOPIC_TYPE_SHORT; + *(topicId.data.short_name) = *pub.topic; + *(topicId.data.short_name + 1) = *(pub.topic + 1); + } + else + { + topicId.type = MQTTSN_TOPIC_TYPE_NORMAL; + topicId.data.long_.len = pub.topiclen; + topicId.data.long_.name = pub.topic; + unsigned short id = client->getTopics()->getTopicId(&topicId); + topicId.data.id = id; + } + + if (topicId.data.id == 0) + { + /* This message might be subscribed with wild card. */ + Topic* topic = client->getTopics()->match(&topicId); + if (topic == 0) + { + WRITELOG(" Invalid Topic. PUBLISH message is canceled.\n"); + if (pub.header.bits.qos == 1) + { + replyACK(client, &pub, PUBACK); + } + else if ( pub.header.bits.qos == 2 ) + { + replyACK(client, &pub, PUBREC); + } + return; + } + + /* add the Topic and get a TopicId */ + topic = client->getTopics()->add(&topicId); + uint16_t id = topic->getTopicId(); + + if (id > 0) + { + /* create REGACK */ + MQTTSNPacket* regPacket = new MQTTSNPacket(); + + MQTTSNString topicName; + topicName.lenstring.len = topicId.data.long_.len; + topicName.lenstring.data = topicId.data.long_.name; + + uint16_t regackMsgId = client->getNextSnMsgId(); + regPacket->setREGISTER(id, regackMsgId, &topicName); + + if (client->isSleep()) + { + client->setClientSleepPacket(regPacket); + WRITELOG(FORMAT_BL_NL, currentDateTime(), regPacket->getName(), + RIGHTARROW, client->getClientId(), "is sleeping. REGISTER was saved."); + } + else if (client->isActive()) + { + /* send REGISTER */ + Event* evrg = new Event(); + evrg->setClientSendEvent(client, regPacket); + _gateway->getClientSendQue()->post(evrg); + } + + /* send PUBLISH */ + topicId.data.id = id; + snPacket->setPUBLISH((uint8_t) pub.header.bits.dup, (int) pub.header.bits.qos, + (uint8_t) pub.header.bits.retain, (uint16_t) pub.msgId, topicId, (uint8_t*) pub.payload, + pub.payloadlen); + client->getWaitREGACKPacketList()->setPacket(snPacket, regackMsgId); + } + else + { + WRITELOG("\x1b[0m\x1b[31mMQTTGWPublishHandler Can't create a Topic.\n"); + return; + } + } + + /* TopicId was aquired. */ + if (client->isSleep()) + { + /* client is sleeping. save PUBLISH */ + client->setClientSleepPacket(snPacket); + WRITELOG(FORMAT_YE_NL, currentDateTime(), packet->getName(), + RIGHTARROW, client->getClientId(), "is sleeping. a message was saved."); + int type = 0; + if (pub.header.bits.qos == 1) + { + type = PUBACK; + } + else if ( pub.header.bits.qos == 2) + { + WRITELOG(" While Client is sleeping, QoS2 is not supported.\n"); + type = PUBREC; + } + replyACK(client, &pub, type); + pub.header.bits.qos = 0; + replyACK(client, &pub, PUBACK); + pub.msgId = 0; + snPacket->setPUBLISH((uint8_t) pub.header.bits.dup, (int) pub.header.bits.qos, + (uint8_t) pub.header.bits.retain, (uint16_t) pub.msgId, topicId, (uint8_t*) pub.payload, + pub.payloadlen); + client->setClientSleepPacket(snPacket); + } + else if (client->isActive()) + { + snPacket->setPUBLISH((uint8_t) pub.header.bits.dup, (int) pub.header.bits.qos, (uint8_t) pub.header.bits.retain, + (uint16_t) pub.msgId, topicId, (uint8_t*) pub.payload, pub.payloadlen); + Event* ev1 = new Event(); + ev1->setClientSendEvent(client, snPacket); + _gateway->getClientSendQue()->post(ev1); + } + +} + +void MQTTGWPublishHandler::replyACK(Client* client, Publish* pub, int type) +{ + MQTTGWPacket* pubAck = new MQTTGWPacket(); + pubAck->setAck(type, (uint16_t)pub->msgId); + Event* ev1 = new Event(); + ev1->setBrokerSendEvent(client, pubAck); + _gateway->getBrokerSendQue()->post(ev1); +} + +void MQTTGWPublishHandler::handlePuback(Client* client, MQTTGWPacket* packet) +{ + Ack ack; + packet->getAck(&ack); + uint16_t topicId = client->getWaitedPubTopicId((uint16_t)ack.msgId); + if (topicId) + { + MQTTSNPacket* mqttsnPacket = new MQTTSNPacket(); + mqttsnPacket->setPUBACK(topicId, (uint16_t)ack.msgId, 0); + + client->eraseWaitedPubTopicId((uint16_t)ack.msgId); + Event* ev1 = new Event(); + ev1->setClientSendEvent(client, mqttsnPacket); + _gateway->getClientSendQue()->post(ev1); + return; + } + WRITELOG(" PUBACK from the Broker is invalid. PacketID : %04X ClientID : %s \n", (uint16_t)ack.msgId, client->getClientId()); +} + +void MQTTGWPublishHandler::handleAck(Client* client, MQTTGWPacket* packet, int type) +{ + Ack ack; + packet->getAck(&ack); + MQTTSNPacket* mqttsnPacket = new MQTTSNPacket(); + if (type == PUBREC) + { + mqttsnPacket->setPUBREC((uint16_t) ack.msgId); + } + else if (type == PUBREL) + { + mqttsnPacket->setPUBREL((uint16_t) ack.msgId); + } + else if (type == PUBCOMP) + { + mqttsnPacket->setPUBCOMP((uint16_t) ack.msgId); + } + + Event* ev1 = new Event(); + ev1->setClientSendEvent(client, mqttsnPacket); + _gateway->getClientSendQue()->post(ev1); +} + diff --git a/MQTTSNGateway/src/MQTTGWPublishHandler.h b/MQTTSNGateway/src/MQTTGWPublishHandler.h new file mode 100644 index 0000000..72ac8b1 --- /dev/null +++ b/MQTTSNGateway/src/MQTTGWPublishHandler.h @@ -0,0 +1,45 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * 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: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ +#ifndef MQTTGWPUBLISHHANDLER_H_ +#define MQTTGWPUBLISHHANDLER_H_ + +#include "MQTTSNGWDefines.h" +#include "MQTTSNGWPacket.h" +#include "MQTTSNGateway.h" + +namespace MQTTSNGW +{ + +class MQTTGWPublishHandler +{ +public: + MQTTGWPublishHandler(Gateway* gateway); + ~MQTTGWPublishHandler(); + void handlePublish(Client* client, MQTTGWPacket* packet); + void handlePuback(Client* client, MQTTGWPacket* packet); + void handleAck(Client* client, MQTTGWPacket* packet, int type); + +private: + void replyACK(Client* client, Publish* pub, int type); + + Gateway* _gateway; +}; + +} + + + +#endif /* MQTTGWPUBLISHHANDLER_H_ */ diff --git a/MQTTSNGateway/src/MQTTGWSubscribeHandler.cpp b/MQTTSNGateway/src/MQTTGWSubscribeHandler.cpp new file mode 100644 index 0000000..b31ac7d --- /dev/null +++ b/MQTTSNGateway/src/MQTTGWSubscribeHandler.cpp @@ -0,0 +1,74 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * 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: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ + +#include "MQTTGWSubscribeHandler.h" +#include "MQTTGWPacket.h" + +using namespace std; +using namespace MQTTSNGW; + +MQTTGWSubscribeHandler::MQTTGWSubscribeHandler(Gateway* gateway) +{ + _gateway = gateway; +} + +MQTTGWSubscribeHandler::~MQTTGWSubscribeHandler() +{ + +} + +void MQTTGWSubscribeHandler::handleSuback(Client* client, MQTTGWPacket* packet) +{ + uint16_t msgId; + uint8_t rc; + uint8_t returnCode; + int qos = 0; + + packet->getSUBACK(&msgId, &rc); + uint16_t topicId = client->getWaitedSubTopicId(msgId); + + if (topicId) + { + MQTTSNPacket* snPacket = new MQTTSNPacket(); + client->eraseWaitedSubTopicId(msgId); + + if (rc == 0x80) + { + returnCode = MQTTSN_RC_REJECTED_INVALID_TOPIC_ID; + } + else + { + returnCode = MQTTSN_RC_ACCEPTED; + qos = rc; + } + snPacket->setSUBACK(qos, topicId, msgId, returnCode); + Event* ev1 = new Event(); + ev1->setClientSendEvent(client, snPacket); + _gateway->getClientSendQue()->post(ev1); + } +} + +void MQTTGWSubscribeHandler::handleUnsuback(Client* client, MQTTGWPacket* packet) +{ + Ack ack; + packet->getAck(&ack); + MQTTSNPacket* snPacket = new MQTTSNPacket(); + snPacket->setUNSUBACK(ack.msgId); + Event* ev1 = new Event(); + ev1->setClientSendEvent(client, snPacket); + _gateway->getClientSendQue()->post(ev1); +} + diff --git a/MQTTSNGateway/src/MQTTGWSubscribeHandler.h b/MQTTSNGateway/src/MQTTGWSubscribeHandler.h new file mode 100644 index 0000000..1775826 --- /dev/null +++ b/MQTTSNGateway/src/MQTTGWSubscribeHandler.h @@ -0,0 +1,41 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * 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: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ +#ifndef MQTTGWSUBSCRIBEHANDLER_H_ +#define MQTTGWSUBSCRIBEHANDLER_H_ + +#include "MQTTSNGWDefines.h" +#include "MQTTGWPacket.h" +#include "MQTTSNGateway.h" +#include "MQTTSNGWClient.h" + +namespace MQTTSNGW +{ + +class MQTTGWSubscribeHandler +{ +public: + MQTTGWSubscribeHandler(Gateway* gateway); + ~MQTTGWSubscribeHandler(); + void handleSuback(Client* clnode, MQTTGWPacket* packet); + void handleUnsuback(Client* clnode, MQTTGWPacket* packet); + +private: + Gateway* _gateway; +}; + +} + +#endif /* MQTTGWSUBSCRIBEHANDLER_H_ */ diff --git a/MQTTSNGateway/src/MQTTSNGWBrokerRecvTask.cpp b/MQTTSNGateway/src/MQTTSNGWBrokerRecvTask.cpp new file mode 100644 index 0000000..83f1c05 --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWBrokerRecvTask.cpp @@ -0,0 +1,195 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * 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: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ + +#include "MQTTSNGWBrokerRecvTask.h" +#include "MQTTSNGWClient.h" + +using namespace std; +using namespace MQTTSNGW; + +char* currentDateTime(void); + +/*===================================== + Class BrokerRecvTask + =====================================*/ +BrokerRecvTask::BrokerRecvTask(Gateway* gateway) +{ + _gateway = gateway; + _gateway->attach((Thread*)this); + _light = 0; +} + +BrokerRecvTask::~BrokerRecvTask() +{ + +} + +/** + * Initialize attributs of this class + */ +void BrokerRecvTask::initialize(int argc, char** argv) +{ + _light = _gateway->getLightIndicator(); +} + +/** + * receive a MQTT messge from the broker and post a event. + */ +void BrokerRecvTask::run(void) +{ + struct timeval timeout; + MQTTGWPacket* packet = 0; + int rc; + Event* ev = 0; + fd_set rset; + fd_set wset; + + while (true) + { + timeout.tv_sec = 0; + timeout.tv_usec = 500000; // 500 msec + FD_ZERO(&rset); + FD_ZERO(&wset); + int maxSock = 0; + int sockfd = 0; + + /* Prepare sockets list to read */ + Client* client = _gateway->getClientList()->getClient(); + _light->blueLight(false); + + while (client > 0) + { + if (client->getNetwork()->isValid()) + { + sockfd = client->getNetwork()->getSock(); + FD_SET(sockfd, &rset); + FD_SET(sockfd, &wset); + if (sockfd > maxSock) + { + maxSock = sockfd; + } + } + client = client->getNextClient(); + } + + if (maxSock > 0) + { + /* Check sockets is ready to read */ + int activity = select(maxSock + 1, &rset, 0, 0, &timeout); + + if (activity > 0) + { + client = _gateway->getClientList()->getClient(); + + while (client > 0) + { + _light->blueLight(false); + if (client->getNetwork()->isValid()) + { + int sockfd = client->getNetwork()->getSock(); + if (FD_ISSET(sockfd, &rset)) + { + packet = new MQTTGWPacket(); + rc = 0; + /* read sockets */ + _light->blueLight(true); + rc = packet->recv(client->getNetwork()); + if ( rc > 0 ) + { + log(client, packet); + + /* post a BrokerRecvEvent */ + ev = new Event(); + ev->setBrokerRecvEvent(client, packet); + _gateway->getPacketEventQue()->post(ev); + } + else + { + _light->blueLight(false); + if ( rc == 0 ) + { + delete packet; + continue; + } + else if (rc == -1) + { + WRITELOG("%s BrokerRecvTask can't receive a packet from the broker errno=%d %s%s\n", ERRMSG_HEADER, errno, client->getClientId(), ERRMSG_FOOTER); + } + else if ( rc == -2 ) + { + WRITELOG("%s BrokerRecvTask receive invalid length of packet from the broker. DISCONNECT %s %s\n", ERRMSG_HEADER, client->getClientId(),ERRMSG_FOOTER); + } + else if ( rc == -3 ) + { + WRITELOG("%s BrokerRecvTask can't create the packet %s%s\n", ERRMSG_HEADER, client->getClientId(), ERRMSG_FOOTER); + } + + /* disconnect the client */ + client->disconnected(); + client->getNetwork()->disconnect(); + rc = 0; + delete packet; + } + } + } + client = client->getNextClient(); + } + _light->blueLight(false); + } + } + else + { + _light->greenLight(false); + } + maxSock = 0; + } +} + +/** + * write message content into stdout or Ringbuffer + */ +void BrokerRecvTask::log(Client* client, MQTTGWPacket* packet) +{ + char pbuf[SIZEOF_LOG_PACKET * 3]; + char msgId[6]; + + switch (packet->getType()) + { + case CONNACK: + case DISCONNECT: + WRITELOG(FORMAT_YE_GR_MSGID, currentDateTime(), packet->getName(), packet->getMsgId(msgId), LEFTARROW, client->getClientId(), packet->print(pbuf)); + break; + case PUBLISH: + WRITELOG(FORMAT_GR_MSGID_NL, currentDateTime(), packet->getName(), packet->getMsgId(msgId), LEFTARROW, client->getClientId(), packet->print(pbuf)); + break; + case PUBACK: + case PUBREC: + case PUBREL: + case PUBCOMP: + WRITELOG(FORMAT_GR_MSGID, currentDateTime(), packet->getName(), packet->getMsgId(msgId), LEFTARROW, client->getClientId(), packet->print(pbuf)); + break; + case SUBACK: + case UNSUBACK: + WRITELOG(FORMAT_YE_GR_MSGID, currentDateTime(), packet->getName(), packet->getMsgId(msgId), LEFTARROW, client->getClientId(), packet->print(pbuf)); + break; + case PINGRESP: + WRITELOG(FORMAT_GR_NL, currentDateTime(), packet->getName(), LEFTARROW, client->getClientId(), packet->print(pbuf)); + break; + default: + WRITELOG(FORMAT_GR_NL, currentDateTime(), "UNKOWN_TYPE", LEFTARROW, client->getClientId(), packet->print(pbuf)); + break; + } +} diff --git a/MQTTSNGateway/src/MQTTSNGWBrokerRecvTask.h b/MQTTSNGateway/src/MQTTSNGWBrokerRecvTask.h new file mode 100644 index 0000000..e506e95 --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWBrokerRecvTask.h @@ -0,0 +1,49 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * 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: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ +#ifndef MQTTSNGWBROKERRECVTASK_H_ +#define MQTTSNGWBROKERRECVTASK_H_ + +#include "MQTTSNGWDefines.h" +#include "MQTTSNGateway.h" + +namespace MQTTSNGW +{ + +#define ERRNO_APL_03 13 // Task Initialize Error +/*===================================== + Class BrokerRecvTask + =====================================*/ +class BrokerRecvTask: public Thread +{ +MAGIC_WORD_FOR_THREAD; + ; +public: + BrokerRecvTask(Gateway* gateway); + ~BrokerRecvTask(); + void initialize(int argc, char** argv); + void run(void); + +private: + void log(Client*, MQTTGWPacket*); + + Gateway* _gateway; + LightIndicator* _light; +}; + +} + + +#endif /* MQTTSNGWBROKERRECVTASK_H_ */ diff --git a/MQTTSNGateway/src/MQTTSNGWBrokerSendTask.cpp b/MQTTSNGateway/src/MQTTSNGWBrokerSendTask.cpp new file mode 100644 index 0000000..b0ee3a2 --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWBrokerSendTask.cpp @@ -0,0 +1,162 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * 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: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ + +#include "MQTTSNGWBrokerSendTask.h" +#include "MQTTSNGWDefines.h" +#include "MQTTSNGateway.h" +#include "MQTTSNGWClient.h" +#include "MQTTGWPacket.h" + +using namespace std; +using namespace MQTTSNGW; + +char* currentDateTime(); +#define ERRMSG_FORMAT "\n%s \x1b[0m\x1b[31merror:\x1b[0m\x1b[37m Can't Xmit to the Broker. errno=%d\n" + +/*===================================== + Class BrokerSendTask + =====================================*/ +BrokerSendTask::BrokerSendTask(Gateway* gateway) +{ + _gateway = gateway; + _gateway->attach((Thread*)this); + _host = 0; + _service = 0; + _light = 0; +} + +BrokerSendTask::~BrokerSendTask() +{ + if (_host) + { + free(_host); + } + if (_service) + { + free(_service); + } +} + +/** + * Initialize attributs of this class + */ +void BrokerSendTask::initialize(int argc, char** argv) +{ + char param[MQTTSNGW_PARAM_MAX]; + + if (_gateway->getParam("BrokerName", param) == 0) + { + _host = strdup(param); + } + if (_gateway->getParam("BrokerPortNo", param) == 0) + { + _service = strdup(param); + } + _light = _gateway->getLightIndicator(); +} + +/** + * connect to the broker and send MQTT messges + */ +void BrokerSendTask::run() +{ + Event* ev = 0; + MQTTGWPacket* packet = 0; + Client* client = 0; + + while (true) + { + int rc = 0; + ev = _gateway->getBrokerSendQue()->wait(); + client = ev->getClient(); + packet = ev->getMQTTGWPacket(); + + if ( !client->getNetwork()->isValid() ) + { + /* connect to the broker and send a packet */ + if ( !client->getNetwork()->connect(_host, _service) ) + { + /* disconnect the broker and chage the client's status */ + WRITELOG("%s BrokerSendTask can't open the socket. errno=%d %s%s\n", + ERRMSG_HEADER, rc == -1 ? errno : 0, client->getClientId(), ERRMSG_FOOTER); + client->disconnected(); + client->getNetwork()->disconnect(); + delete ev; + continue; + } + } + + /* send a packet */ + _light->blueLight(true); + if ( (rc = packet->send(client->getNetwork())) > 0 ) + { + if ( packet->getType() == CONNECT ) + { + client->setWaitWillMsgFlg(false); + client->connectSended(); + } + log(client, packet); + } + else + { + WRITELOG("%s BrokerSendTask can't send a packet. errno=%d %s%s\n", + ERRMSG_HEADER, rc == -1 ? errno : 0, client->getClientId(), ERRMSG_FOOTER); + WRITELOG("%s BrokerSendTask can't send a packet to the broker errno=%d %s%s\n", + ERRMSG_HEADER, rc == -1 ? errno : 0, client->getClientId(), ERRMSG_FOOTER); + client->disconnected(); + client->getNetwork()->disconnect(); + } + + _light->blueLight(false); + delete ev; + } +} + + +/** + * write message content into stdout or Ringbuffer + */ +void BrokerSendTask::log(Client* client, MQTTGWPacket* packet) +{ + char pbuf[SIZEOF_LOG_PACKET * 3]; + char msgId[6]; + + switch (packet->getType()) + { + case CONNECT: + WRITELOG(FORMAT_YE_GR, currentDateTime(), packet->getName(), RIGHTARROW, client->getClientId(), packet->print(pbuf)); + break; + case PUBLISH: + WRITELOG(FORMAT_WH_GR_MSGID_NL, currentDateTime(), packet->getName(), packet->getMsgId(msgId), RIGHTARROW, client->getClientId(), packet->print(pbuf)); + break; + case SUBSCRIBE: + case UNSUBSCRIBE: + case PUBACK: + case PUBREC: + case PUBREL: + case PUBCOMP: + WRITELOG(FORMAT_WH_GR_MSGID, currentDateTime(), packet->getName(), packet->getMsgId(msgId), RIGHTARROW, client->getClientId(), packet->print(pbuf)); + break; + case PINGREQ: + WRITELOG(FORMAT_YE, currentDateTime(), packet->getName(), RIGHTARROW, client->getClientId(), packet->print(pbuf)); + break; + case DISCONNECT: + WRITELOG(FORMAT_YE, currentDateTime(), packet->getName(), RIGHTARROW, client->getClientId(), packet->print(pbuf)); + break; + default: + break; + } +} diff --git a/MQTTSNGateway/src/MQTTSNGWBrokerSendTask.h b/MQTTSNGateway/src/MQTTSNGWBrokerSendTask.h new file mode 100644 index 0000000..0d30004 --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWBrokerSendTask.h @@ -0,0 +1,47 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * 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: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ +#ifndef MQTTSNGWBROKERSENDTASK_H_ +#define MQTTSNGWBROKERSENDTASK_H_ + +#include "MQTTSNGWDefines.h" +#include "MQTTSNGateway.h" +#include "MQTTSNGWClient.h" + +namespace MQTTSNGW +{ +#define ERRNO_APL_04 14 // Task Initialize Error +/*===================================== + Class BrokerSendTask + =====================================*/ +class BrokerSendTask : public Thread +{ + MAGIC_WORD_FOR_THREAD; +public: + BrokerSendTask(Gateway* gateway); + ~BrokerSendTask(); + void initialize(int argc, char** argv); + void run(); +private: + void log(Client*, MQTTGWPacket*); + + Gateway* _gateway; + char* _host; + char* _service; + LightIndicator* _light; +}; + +} +#endif /* MQTTSNGWBROKERSENDTASK_H_ */ diff --git a/MQTTSNGateway/src/MQTTSNGWClient.cpp b/MQTTSNGateway/src/MQTTSNGWClient.cpp new file mode 100644 index 0000000..d8dd663 --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWClient.cpp @@ -0,0 +1,1142 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * 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: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ + +#include "MQTTSNGWClient.h" +#include "MQTTSNGateway.h" +#include "SensorNetwork.h" +#include "Network.h" +#include +#include +#include + +using namespace MQTTSNGW; + +/*===================================== + Class ClientList + =====================================*/ +ClientList::ClientList() +{ + _clientCnt = 0; + _authorize = false; + _firstClient = 0; + _endClient = 0; +} + +ClientList::~ClientList() +{ + _mutex.lock(); + Client* cl = _firstClient; + Client* ncl; + + while (cl != 0) + { + ncl = cl->_nextClient; + delete cl; + cl = ncl; + }; + _mutex.unlock(); +} + +/** + * Create ClientList from a client list file. + * @param File name of the client list + * @return true: Reject client connection that is not registered in the client list + * + * File format is: + * Lines bigning with # are comment line. + * ClientId, SensorNetAddress, "unstableLine", "secureConnection" + * in case of UDP, SensorNetAddress format is portNo@IPAddress. + * if the SensorNetwork is not stable, write unstableLine. + * if BrokerConnection is SSL, write secureConnection. + * + * Ex: + * #Client List + * ClientId1,11200@192.168.10.10 + * ClientID2,35000@192.168.50.200,unstableLine + * ClientID3,40000@192.168.200.50,secureConnection + * ClientID4,41000@192.168.200.51,unstableLine,secureConnection + */ +bool ClientList::authorize(const char* fileName) +{ + FILE* fp; + char buf[258]; + size_t pos;; + bool secure; + bool stable; + SensorNetAddress netAddr; + MQTTSNString clientId; + + clientId.cstring = 0; + clientId.lenstring.data = 0; + clientId.lenstring.len = 0; + + if ((fp = fopen(fileName, "r")) != 0) + { + while (fgets(buf, 256, fp) != 0) + { + if (*buf == '#') + { + continue; + } + string data = string(buf); + while ((pos = data.find_first_of("  \t\n")) != string::npos) + { + data.erase(pos, 1); + } + if (data.empty()) + { + continue; + } + pos = data.find_first_of(","); + string id = data.substr(0, pos); + + clientId.cstring = strdup(id.c_str()); + + string addr = data.substr(pos + 1); + + if (netAddr.setAddress(&addr) == 0) + { + secure = (data.find("secureConnection") != string::npos); + stable = !(data.find("unstableLine") != string::npos); + createClient(&netAddr, &clientId, stable, secure); + } + else + { + WRITELOG("Invalid address %s\n", data.c_str()); + } + } + fclose(fp); + _authorize = true; + } + return _authorize; +} + +void ClientList::erase(Client* client) +{ + _mutex.lock(); + Client* prev = client->_prevClient; + Client* next = client->_nextClient; + + if (prev) + { + prev->_nextClient = next; + } + else + { + _firstClient = next; + + } + if (next) + { + next->_prevClient = prev; + } + else + { + _endClient = prev; + } + _clientCnt--; + _authorize = false; + _mutex.unlock(); +} + +Client* ClientList::getClient(SensorNetAddress* addr) +{ + _mutex.lock(); + Client* client = _firstClient; + + while (client != 0) + { + if (client->getSensorNetAddress()->isMatch(addr) ) + { + _mutex.unlock(); + return client; + } + client = client->_nextClient; + } + _mutex.unlock(); + return 0; +} + +Client* ClientList::getClient(void) +{ + return _firstClient; +} + +Client* ClientList::createClient(SensorNetAddress* addr, MQTTSNString* clientId, bool unstableLine, bool secure) +{ + Client* client = 0; + + /* clients must be authorized */ + if ( _authorize ) + { + /* search cliene with sensorNetAddress from the list */ + return getClient(addr); + } + + /* anonimous clients */ + if ( _clientCnt > DEFAULT_MAX_CLIENTS ) + { + return 0; // full of clients + } + + client = getClient(addr); + if ( client ) + { + return client; + } + + /* creat a new client */ + client = new Client(secure); + client->setClientAddress(addr); + client->setSensorNetType(unstableLine); + if ( MQTTSNstrlen(*clientId) ) + { + client->setClientId(*clientId); + } + + /* add the list */ + if ( _firstClient == 0 ) + { + _firstClient = client; + _endClient = client; + } + else + { + _endClient->_nextClient = client; + client->_prevClient = _endClient; + _endClient = client; + } + _clientCnt++; + _mutex.unlock(); + return client; +} + +uint16_t ClientList::getClientCount() +{ + return _clientCnt; +} + +bool ClientList::isAuthorized() +{ + return _authorize; +} + +/*===================================== + Class Client + =====================================*/ +static const char* theClientStatus[] = { "Disconnected", "TryConnecting", "Connecting", "Active", "Awake", "Asleep", "Lost" }; + +Client::Client(bool secure) +{ + _packetId = 0; + _snMsgId = 0; + _status = Cstat_Disconnected; + _keepAliveMsec = 0; + _topics = new Topics(); + _clientId = 0; + _willTopic = 0; + _willMsg = 0; + _connectData.Protocol = 0; + _connectData.clientID = 0; + _connectData.flags.all = 0; + _connectData.header.byte = 0; + _connectData.keepAliveTimer = 0; + _connectData.version = 0; + _connectData.willMsg = 0; + _connectData.willTopic = 0; + _network = new Network(secure); + _secureNetwork = secure; + _sensorNetype = true; + _connAck = 0; + _waitWillMsgFlg = false; + _prevClient = 0; + _nextClient = 0; +} + +Client::~Client() +{ + if ( _topics ) + { + delete _topics; + } + + if ( _clientId ) + { + free(_clientId); + } + + if ( _willTopic ) + { + free(_willTopic); + } + + if ( _willMsg ) + { + free(_willMsg); + } + + if (_connAck) + { + delete _connAck; + } + + if (_network) + { + delete _network; + } +} + +uint16_t Client::getWaitedPubTopicId(uint16_t msgId) +{ + MQTTSN_topicTypes type; + return _waitedPubTopicIdMap.getTopicId(msgId, &type); +} + +uint16_t Client::getWaitedSubTopicId(uint16_t msgId) +{ + MQTTSN_topicTypes type; + return _waitedSubTopicIdMap.getTopicId(msgId, &type); +} + +MQTTSNPacket* Client::getClientSleepPacket() +{ + return _clientSleepPacketQue.getPacket(); +} + +Connect* Client::getConnectData(void) +{ + return &_connectData; +} + +void Client::eraseWaitedPubTopicId(uint16_t msgId) +{ + _waitedPubTopicIdMap.erase(msgId); +} + +void Client::eraseWaitedSubTopicId(uint16_t msgId) +{ + _waitedSubTopicIdMap.erase(msgId); +} + +void Client::clearWaitedPubTopicId(void) +{ + _waitedPubTopicIdMap.clear(); +} + +void Client::clearWaitedSubTopicId(void) +{ + _waitedSubTopicIdMap.clear(); +} + +void Client::setWaitedPubTopicId(uint16_t msgId, uint16_t topicId, MQTTSN_topicTypes type) +{ + _waitedPubTopicIdMap.add(msgId, topicId, type); +} +void Client::setWaitedSubTopicId(uint16_t msgId, uint16_t topicId, MQTTSN_topicTypes type) +{ + _waitedSubTopicIdMap.add(msgId, topicId, type); +} + +void Client::setClientSleepPacket(MQTTSNPacket* packet) +{ + updateStatus(packet); + _clientSleepPacketQue.post(packet); +} + +void Client::checkTimeover(void) +{ + if (_status == Cstat_Active && _keepAliveTimer.isTimeup()) + { + _status = Cstat_Lost; + _network->disconnect(); + } +} + +void Client::setKeepAlive(MQTTSNPacket* packet) +{ + MQTTSNPacket_connectData param; + if (packet->getCONNECT(¶m)) + { + _keepAliveMsec = param.duration * 1000UL; + _keepAliveTimer.start(_keepAliveMsec * 1.5); + } +} + +void Client::updateStatus(MQTTSNPacket* packet) +{ + if (((_status == Cstat_Disconnected) || (_status == Cstat_Lost)) && packet->getType() == MQTTSN_CONNECT) + { + setKeepAlive(packet); + } + else if (_status == Cstat_Active) + { + switch (packet->getType()) + { + case MQTTSN_PINGREQ: + case MQTTSN_PUBLISH: + case MQTTSN_SUBSCRIBE: + case MQTTSN_UNSUBSCRIBE: + case MQTTSN_PUBACK: + case MQTTSN_PUBCOMP: + case MQTTSN_PUBREL: + case MQTTSN_PUBREC: + _keepAliveTimer.start(_keepAliveMsec * 1.5); + break; + case MQTTSN_DISCONNECT: + { + uint16_t duration; + packet->getDISCONNECT(&duration); + if (duration) + { + _status = Cstat_Asleep; + _keepAliveMsec = duration * 1000UL; + } + else + { + disconnected(); + } + } + break; + default: + break; + } + + } + else if (_status == Cstat_Asleep) + { + if (packet->getType() == MQTTSN_CONNECT) + { + setKeepAlive(packet); + _status = Cstat_Connecting; + } + else if (packet->getType() == MQTTSN_PINGREQ) + { + if ( packet->getPINGREQ() > 0 ) + { + _status = Cstat_Awake; + } + } + } + else if (_status == Cstat_Awake) + { + switch (packet->getType()) + { + case MQTTSN_CONNECT: + _status = Cstat_Connecting; + setKeepAlive(packet); + break; + case MQTTSN_DISCONNECT: + disconnected(); + break; + case MQTTSN_PINGRESP: + _status = Cstat_Asleep; + break; + default: + break; + } + } +} + +void Client::updateStatus(ClientStatus stat) +{ + _status = stat; +} + +void Client::connectSended() +{ + _status = Cstat_Connecting; +} + +void Client::connackSended(int rc) +{ + if (rc == MQTTSN_RC_ACCEPTED) + { + _status = Cstat_Active; + } + else + { + disconnected(); + } +} + +void Client::disconnected(void) +{ + _status = Cstat_Disconnected; + _waitWillMsgFlg = false; +} + +bool Client::isConnectSendable(void) +{ + if (_status == Cstat_Lost || _status == Cstat_TryConnecting) + { + return false; + } + else + { + return true; + } +} + +uint16_t Client::getNextPacketId(void) +{ + _packetId++; + if ( _packetId == 0xffff ) + { + _packetId = 1; + } + return _packetId; +} + +uint8_t Client::getNextSnMsgId(void) +{ + _snMsgId++; + if (_snMsgId == 0) + { + _snMsgId++; + } + return _snMsgId; +} + +Topics* Client::getTopics(void) +{ + return _topics; +} + +Network* Client::getNetwork(void) +{ + return _network; +} + +void Client::setClientAddress(SensorNetAddress* sensorNetAddr) +{ + _sensorNetAddr = *sensorNetAddr; +} + +SensorNetAddress* Client::getSensorNetAddress(void) +{ + return &_sensorNetAddr; +} + +void Client::setSensorNetType(bool stable) +{ + _sensorNetype = stable; +} + +void Client::setTopics(Topics* topics) +{ + _topics = topics; +} + +void Client::setWaitWillMsgFlg(bool flg) +{ + _waitWillMsgFlg = flg; +} + +bool Client::isWaitWillMsg(void) +{ + return _waitWillMsgFlg; +} + +bool Client::isDisconnect(void) +{ + return (_status == Cstat_Disconnected); +} + +bool Client::isActive(void) +{ + return (_status == Cstat_Active); +} + +bool Client::isSleep(void) +{ + return (_status == Cstat_Asleep); +} + +bool Client::isSecureNetwork(void) +{ + return _secureNetwork; +} + +bool Client::isSensorNetStable(void) +{ + return _sensorNetype; +} + +WaitREGACKPacketList* Client::getWaitREGACKPacketList() +{ + return &_waitREGACKList; +} + +Client* Client::getNextClient(void) +{ + return _nextClient; +} + +void Client::setClientId(MQTTSNString id) +{ + if ( _clientId ) + { + free(_clientId); + } + + /* save clientId into (char*)_clientId NULL terminated */ + _clientId = (char*)calloc(MQTTSNstrlen(id) + 1, 1); + unsigned char* ptr = (unsigned char*)_clientId; + writeMQTTSNString((unsigned char**)&ptr, id); +} + +void Client::setWillTopic(MQTTSNString willTopic) +{ + if ( _willTopic ) + { + free(_willTopic); + } + + _willTopic = (char*)calloc(MQTTSNstrlen(willTopic) + 1, 1); + /* save willTopic into (char*)_willTopic with NULL termination */ + unsigned char* ptr = (unsigned char*)_willTopic; + writeMQTTSNString((unsigned char**)&ptr, willTopic); +} + +void Client::setWillMsg(MQTTSNString willMsg) +{ + if ( _willMsg) + { + free(_willMsg); + } + + _willMsg = (char*)calloc(MQTTSNstrlen(willMsg) + 1, 1); + /* save willMsg into (char*)_willMsg with NULL termination */ + unsigned char* ptr = (unsigned char*)_willMsg; + writeMQTTSNString((unsigned char**)&ptr, willMsg); +} + +char* Client::getClientId(void) +{ + return _clientId; +} + +char* Client::getWillTopic(void) +{ + return _willTopic; +} + +char* Client::getWillMsg(void) +{ + return _willMsg; +} + +const char* Client::getStatus(void) +{ + return theClientStatus[_status]; +} + +/*===================================== + Class Topic + ======================================*/ +Topic::Topic() +{ + _topicName = 0; + _topicId = 0; + _next = 0; +} + +Topic::Topic(string* topic) +{ + _topicName = topic; + _topicId = 0; + _next = 0; +} + +Topic::~Topic() +{ + if ( _topicName ) + { + delete _topicName; + } +} + +string* Topic::getTopicName(void) +{ + return _topicName; +} + +uint16_t Topic::getTopicId(void) +{ + return _topicId; +} + +int Topic::hasWildCard(unsigned int* pos) +{ + unsigned int p = _topicName->find("+", 0); + if (p != string::npos) + { + *pos = p; + return 1; + } + else + { + string::iterator it = _topicName->end(); + if (*it == '#') + { + *pos = _topicName->size() - 1; + return 2; + } + } + *pos = 0; + return 0; +} + +bool Topic::isMatch(string* topicName) +{ + unsigned int pos; + + if (topicName->size() < _topicName->size()) + { + return false; + } + + if (hasWildCard(&pos) == 1) + { + if (_topicName->compare(0, pos - 1, *topicName, 0, pos - 1) == 0) + { + if (_topicName->compare(pos + 1, 1, "/") == 0) + { + unsigned int loc = topicName->find('/', pos); + if (loc != 0) + { + if (_topicName->compare(pos + 1, _topicName->size() - pos - 1, *topicName, loc, + topicName->size() - pos - 1) == 0) + { + return true; + } + } + } + else + { + unsigned int loc = _topicName->find(pos, '/'); + if (loc != 0) + { + if (topicName->find('/', loc) != 0) + { + return false; + } + } + } + return true; + } + } + else if (hasWildCard(&pos) == 2 && (_topicName->compare(0, pos, *topicName, 0, pos) == 0)) + { + return true; + } + else if (_topicName->compare(*topicName) == 0) + { + return true; + } + return false; +} + +/*===================================== + Class Topics + ======================================*/ +Topics::Topics() +{ + _first = 0; + _nextTopicId = 0; +} + +Topics::~Topics() +{ + Topic* p = _first; + while (p) + { + Topic* q = p->_next; + delete p; + p = q; + } +} + +uint16_t Topics::getTopicId(MQTTSN_topicid* topicid) +{ + if (topicid->type != MQTTSN_TOPIC_TYPE_NORMAL) + { + return 0; + } + + Topic* p = _first; + while (p) + { + if (strncmp(p->_topicName->c_str(), topicid->data.long_.name, topicid->data.long_.len) == 0) + { + return p->_topicId; + } + p = p->_next; + } + return 0; +} + +Topic* Topics::getTopic(uint16_t id) +{ + Topic* p = _first; + while (p) + { + if (p->_topicId == id) + { + return p; + } + p = p->_next; + } + return 0; +} + +Topic* Topics::getTopic(MQTTSN_topicid* topicid) +{ + Topic* p = _first; + while (p) + { + if (p->_topicId == topicid->data.id) + { + return p; + } + p = p->_next; + } + return 0; +} + +Topic* Topics::add(MQTTSN_topicid* topicid) +{ + Topic* topic; + uint16_t id = 0; + string* topicName = 0; + + if (topicid->type != MQTTSN_TOPIC_TYPE_NORMAL) + { + return 0; + } + id = getTopicId(topicid); + + if (id) + { + topic = getTopic(id); + } + else + { + topicName = new string(topicid->data.long_.name, topicid->data.long_.len); + topic = add(topicName); + } + return topic; +} + + +Topic* Topics::add(string* topicName) +{ + Topic* topic = 0; + + Topic* tp = _first; + + topic = new Topic(); + + if (topic == 0) + { + return 0; + } + string* name = new string(*topicName); + topic->_topicName = name; + topic->_topicId = getNextTopicId(); + + if (tp == 0) + { + _first = topic; + } + + while (tp) + { + if (tp->_next == 0) + { + tp->_next = topic; + break; + } + else + { + tp = tp->_next; + } + } + return topic; +} + +uint16_t Topics::getNextTopicId() +{ + return ++_nextTopicId == 0xffff ? _nextTopicId += 2 : _nextTopicId; +} + +Topic* Topics::match(MQTTSN_topicid* topicid) +{ + if (topicid->type != MQTTSN_TOPIC_TYPE_NORMAL) + { + return 0; + } + string topicName(topicid->data.long_.name, topicid->data.long_.len); + + Topic* topic = _first; + while (topic) + { + if (topic->isMatch(&topicName)) + { + return topic; + } + topic = topic->_next; + } + return 0; +} + +/*===================================== + Class TopicIdMap + =====================================*/ +TopicIdMapelement::TopicIdMapelement(uint16_t msgId, uint16_t topicId, MQTTSN_topicTypes type) +{ + _msgId = msgId; + _topicId = topicId; + _type = type; + _next = 0; + _prev = 0; +} + +TopicIdMapelement::~TopicIdMapelement() +{ + +} + +TopicIdMap::TopicIdMap() +{ + char param[MQTTSNGW_PARAM_MAX]; + + _maxInflight = DEFAULT_INFLIGHTMESSAGE; + if ( theProcess->getParam("MaxInflightMsg", param) == 0 ) + { + _maxInflight = atoi(param); + } + + _msgIds = 0; + _first = 0; + _end = 0; + _cnt = 0; +} + +TopicIdMap::~TopicIdMap() +{ + TopicIdMapelement* p = _first; + while ( p ) + { + TopicIdMapelement* q = p->_next; + delete p; + p = q; + } +} + +uint16_t TopicIdMap::getTopicId(uint16_t msgId, MQTTSN_topicTypes* type) +{ + TopicIdMapelement* p = _first; + while ( p ) + { + if ( p->_msgId == msgId ) + { + *type = p->_type; + return p->_topicId; + } + p = p->_next; + } + return 0; +} + +int TopicIdMap::add(uint16_t msgId, uint16_t topicId, MQTTSN_topicTypes type) +{ + if ( _cnt > _maxInflight * 2 || topicId == 0) + { + return 0; + } + if ( getTopicId(msgId, &type) > 0 ) + { + erase(msgId); + } + + TopicIdMapelement* elm = new TopicIdMapelement(msgId, topicId, type); + if ( elm == 0 ) + { + return 0; + } + if ( _first == 0 ) + { + _first = elm; + _end = elm; + } + else + { + elm->_prev = _end; + _end->_next = elm; + _end = elm; + } + _cnt++; + return 1; +} + +void TopicIdMap::erase(uint16_t msgId) +{ + TopicIdMapelement* p = _first; + while ( p ) + { + if ( p->_msgId == msgId ) + { + if ( p->_prev == 0 ) + { + _first = p->_next; + } + else + { + p->_prev->_next = p->_next; + } + + if ( p->_next == 0 ) + { + _end = p->_prev; + } + else + { + p->_next->_prev = p->_prev; + } + delete p; + break; + + } + p = p->_next; + } + _cnt--; +} + +void TopicIdMap::clear(void) +{ + TopicIdMapelement* p = _first; + while ( p ) + { + TopicIdMapelement* q = p->_next; + delete p; + p = q; + } + _first = 0; + _end = 0; + _cnt = 0; +} + +/*===================================== + Class WaitREGACKPacket + =====================================*/ +waitREGACKPacket::waitREGACKPacket(MQTTSNPacket* packet, uint16_t REGACKMsgId) +{ + _packet = packet; + _msgId = REGACKMsgId; + _next = 0; + _prev = 0; +} + +waitREGACKPacket::~waitREGACKPacket() +{ + delete _packet; +} + +/*===================================== + Class WaitREGACKPacketList + =====================================*/ + +WaitREGACKPacketList::WaitREGACKPacketList() +{ + _first = 0; + _end = 0; +} + +WaitREGACKPacketList::~WaitREGACKPacketList() +{ + waitREGACKPacket* p = _first; + while (p) + { + waitREGACKPacket* q = p->_next; + delete p; + p = q; + } +} + +int WaitREGACKPacketList::setPacket(MQTTSNPacket* packet, uint16_t REGACKMsgId) +{ + waitREGACKPacket* elm = new waitREGACKPacket(packet, REGACKMsgId); + if (elm == 0) + { + return 0; + } + + if (_first == 0) + { + _first = elm; + _end = elm; + } + elm->_prev = _end; + _end->_next = elm; + return 1; +} + +MQTTSNPacket* WaitREGACKPacketList::getPacket(uint16_t REGACKMsgId) +{ + waitREGACKPacket* p = _first; + while (p) + { + if (p->_msgId == REGACKMsgId) + { + return p->_packet; + } + p = p->_next; + } + return 0; +} + +void WaitREGACKPacketList::erase(uint16_t REGACKMsgId) +{ + waitREGACKPacket* p = _first; + while (p) + { + if (p->_msgId == REGACKMsgId) + { + if (p->_prev == 0) + { + _first = p->_next; + + } + else + { + p->_prev->_next = p->_next; + } + if (p->_next == 0) + { + _end = p->_prev; + } + else + { + p->_next->_prev = p->_prev; + } + break; + // Do not delete element. Element is deleted after sending to Client. + } + p = p->_next; + } +} + diff --git a/MQTTSNGateway/src/MQTTSNGWClient.h b/MQTTSNGateway/src/MQTTSNGWClient.h new file mode 100644 index 0000000..48a7f9a --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWClient.h @@ -0,0 +1,347 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * 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: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ + +#ifndef MQTTSNGWCLIENT_H_ +#define MQTTSNGWCLIENT_H_ + +#include "MQTTSNGWProcess.h" +#include "MQTTGWPacket.h" +#include "MQTTSNGWPacket.h" +#include "MQTTSNPacket.h" +#include "Network.h" +#include "SensorNetwork.h" +#include "MQTTSNPacket.h" +#include "linux.h" // Timer class + +namespace MQTTSNGW +{ + +#define MQTTSN_TOPIC_MULTI_WILDCARD '#' +#define MQTTSN_TOPIC_SINGLE_WILDCARD '+' + +/*===================================== + Class PacketQue + =====================================*/ +template class PacketQue +{ +public: + PacketQue() + { + _que = new Que; + } + + ~PacketQue() + { + clear(); + delete _que; + } + + T* getPacket() + { + T* packet; + if (_que->size() > 0) + { + _mutex.lock(); + packet = _que->front(); + _mutex.unlock(); + return packet; + } + else + { + return 0; + } + } + + void post(T* packet) + { + _mutex.lock(); + _que->post(packet); + _mutex.unlock(); + } + + void pop() + { + if (_que->size() > 0) + { + _mutex.lock(); + _que->pop(); + _mutex.unlock(); + } + } + + void clear() + { + _mutex.lock(); + while (_que->size() > 0) + { + delete _que->front(); + _que->pop(); + } + _mutex.unlock(); + } + +private: + Que* _que; + Mutex _mutex; +}; + + +/*===================================== + Class Topic + ======================================*/ +class Topic +{ + friend class Topics; +public: + Topic(); + Topic(string* topic); + ~Topic(); + + string* getTopicName(void); + uint16_t getTopicId(void); + int hasWildCard(unsigned int* pos); + bool isMatch(string* topicName); + +private: + uint16_t _topicId; + string* _topicName; + Topic* _next; +}; + +/*===================================== + Class Topics + ======================================*/ +class Topics +{ +public: + Topics(); + ~Topics(); + Topic* add(MQTTSN_topicid* topicid); + Topic* add(string* topic); + uint16_t getTopicId(MQTTSN_topicid* topic); + uint16_t getNextTopicId(); + Topic* getTopic(uint16_t topicId); + Topic* getTopic(MQTTSN_topicid* topicid); + Topic* match(MQTTSN_topicid* topicid); + +private: + uint16_t _nextTopicId; + Topic* _first; + +}; + +/*===================================== + Class TopicIdMap + =====================================*/ +class TopicIdMapelement +{ + friend class TopicIdMap; +public: + TopicIdMapelement(uint16_t msgId, uint16_t topicId, MQTTSN_topicTypes type); + ~TopicIdMapelement(); + +private: + uint16_t _msgId; + uint16_t _topicId; + MQTTSN_topicTypes _type; + TopicIdMapelement* _next; + TopicIdMapelement* _prev; +}; + +class TopicIdMap +{ +public: + TopicIdMap(); + ~TopicIdMap(); + uint16_t getTopicId(uint16_t msgId, MQTTSN_topicTypes* type); + Topic* getTopic(MQTTSN_topicTypes type); + int add(uint16_t msgId, uint16_t topicId, MQTTSN_topicTypes type); + void erase(uint16_t msgId); + void clear(void); +private: + int find(uint16_t msgId); + uint16_t* _msgIds; + TopicIdMapelement* _first; + TopicIdMapelement* _end; + int _cnt; + int _maxInflight; +}; + +/*===================================== + Class WaitREGACKPacket + =====================================*/ +class waitREGACKPacket +{ + friend class WaitREGACKPacketList; +public: + waitREGACKPacket(MQTTSNPacket* packet, uint16_t REGACKMsgId); + ~waitREGACKPacket(); + +private: + uint16_t _msgId; + MQTTSNPacket* _packet; + waitREGACKPacket* _next; + waitREGACKPacket* _prev; +}; + +/*===================================== + Class WaitREGACKPacketList + =====================================*/ +class WaitREGACKPacketList +{ +public: + WaitREGACKPacketList(); + ~WaitREGACKPacketList(); + int setPacket(MQTTSNPacket* packet, uint16_t REGACKMsgId); + MQTTSNPacket* getPacket(uint16_t REGACKMsgId); + void erase(uint16_t REGACKMsgId); + +private: + waitREGACKPacket* _first; + waitREGACKPacket* _end; +}; + +/*===================================== + Class Client + =====================================*/ +#define MQTTSN_CLIENTID_LENGTH 23 + +typedef enum +{ + Cstat_Disconnected = 0, Cstat_TryConnecting, Cstat_Connecting, Cstat_Active, Cstat_Asleep, Cstat_Awake, Cstat_Lost +} ClientStatus; + + +class Client +{ + friend class ClientList; +public: + Client(bool secure = false); + Client(uint8_t maxInflightMessages, bool secure); + ~Client(); + + Connect* getConnectData(void); + uint16_t getWaitedPubTopicId(uint16_t msgId); + uint16_t getWaitedSubTopicId(uint16_t msgId); + MQTTSNPacket* getClientSleepPacket(); + WaitREGACKPacketList* getWaitREGACKPacketList(void); + + void eraseWaitedPubTopicId(uint16_t msgId); + void eraseWaitedSubTopicId(uint16_t msgId); + void clearWaitedPubTopicId(void); + void clearWaitedSubTopicId(void); + + void setClientSleepPacket(MQTTSNPacket*); + void setWaitedPubTopicId(uint16_t msgId, uint16_t topicId, MQTTSN_topicTypes type); + void setWaitedSubTopicId(uint16_t msgId, uint16_t topicId, MQTTSN_topicTypes type); + + void checkTimeover(void); + void updateStatus(MQTTSNPacket*); + void updateStatus(ClientStatus); + void connectSended(void); + void connackSended(int rc); + void disconnected(void); + bool isConnectSendable(void); + + uint16_t getNextPacketId(void); + uint8_t getNextSnMsgId(void); + Topics* getTopics(void); + void setTopics(Topics* topics); + void setKeepAlive(MQTTSNPacket* packet); + + SensorNetAddress* getSensorNetAddress(void); + Network* getNetwork(void); + void setClientAddress(SensorNetAddress* sensorNetAddr); + void setSensorNetType(bool stable); + + void setClientId(MQTTSNString id); + void setWillTopic(MQTTSNString willTopic); + void setWillMsg(MQTTSNString willmsg); + char* getClientId(void); + char* getWillTopic(void); + char* getWillMsg(void); + const char* getStatus(void); + void setWaitWillMsgFlg(bool); + + bool isDisconnect(void); + bool isActive(void); + bool isSleep(void); + bool isSecureNetwork(void); + bool isSensorNetStable(void); + bool isWaitWillMsg(void); + + Client* getNextClient(void); + +private: + PacketQue _clientSleepPacketQue; + WaitREGACKPacketList _waitREGACKList; + + Topics* _topics; + TopicIdMap _waitedPubTopicIdMap; + TopicIdMap _waitedSubTopicIdMap; + + Connect _connectData; + MQTTSNPacket* _connAck; + + char* _clientId; + char* _willTopic; + char* _willMsg; + + Timer _keepAliveTimer; + uint32_t _keepAliveMsec; + + ClientStatus _status; + bool _waitWillMsgFlg; + + uint16_t _packetId; + uint8_t _snMsgId; + + Network* _network; // Broker + bool _secureNetwork; // SSL + bool _sensorNetype; // false: unstable network like a G3 + SensorNetAddress _sensorNetAddr; + + Client* _nextClient; + Client* _prevClient; + +}; + +/*===================================== + Class ClientList + =====================================*/ +class ClientList +{ +public: + ClientList(); + ~ClientList(); + bool authorize(const char* fileName); + void erase(Client*); + Client* getClient(SensorNetAddress* addr); + Client* createClient(SensorNetAddress* addr, MQTTSNString* clientId, bool unstableLine, + bool secure); + uint16_t getClientCount(void); + Client* getClient(void); + bool isAuthorized(); +private: + Client* _firstClient; + Client* _endClient; + Mutex _mutex; + uint16_t _clientCnt; + bool _authorize; +}; + +} +#endif /* MQTTSNGWCLIENT_H_ */ diff --git a/MQTTSNGateway/src/MQTTSNGWClientRecvTask.cpp b/MQTTSNGateway/src/MQTTSNGWClientRecvTask.cpp new file mode 100644 index 0000000..c905459 --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWClientRecvTask.cpp @@ -0,0 +1,172 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * 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: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ + +#include "MQTTSNGWClientRecvTask.h" +#include "MQTTSNGateway.h" +char* currentDateTime(void); +/*===================================== + Class ClientRecvTask + =====================================*/ +ClientRecvTask::ClientRecvTask(Gateway* gateway) +{ + _gateway = gateway; + _gateway->attach((Thread*)this); + _sensorNetwork = _gateway->getSensorNetwork(); +} + +ClientRecvTask::~ClientRecvTask() +{ + +} + +/** + * Initialize SensorNetwork + */ +void ClientRecvTask::initialize(int argc, char** argv) +{ + if ( _sensorNetwork->initialize() < 0 ) + { + throw Exception(" Can't open the sensor network.\n"); + } +} + +/* + * Receive a packet from clients via sensor netwwork + * and generate a event to execute the packet handling procedure + * of MQTTSNPacketHandlingTask. + */ +void ClientRecvTask::run() +{ + Event* ev = 0; + Client* client = 0; + + while (true) + { + MQTTSNPacket* packet = new MQTTSNPacket(); + int packetLen = packet->recv(_sensorNetwork); + + if (packetLen < 3 ) + { + delete packet; + continue; + } + + if ( packet->getType() <= MQTTSN_ADVERTISE || packet->getType() == MQTTSN_GWINFO ) + { + delete packet; + continue; + } + + if ( packet->getType() == MQTTSN_SEARCHGW ) + { + /* write log and post Event */ + log(0, packet); + ev = new Event(); + ev->setBrodcastEvent(packet); + _gateway->getPacketEventQue()->post(ev); + continue; + } + + /* get client from the ClientList of Gateway by sensorNetAddress. */ + client = _gateway->getClientList()->getClient(_sensorNetwork->getSenderAddress()); + + if ( client ) + { + /* write log and post Event */ + log(client, packet); + ev = new Event(); + ev->setClientRecvEvent(client,packet); + _gateway->getPacketEventQue()->post(ev); + } + else + { + /* new client */ + if (packet->getType() == MQTTSN_CONNECT) + { + MQTTSNPacket_connectData data; + memset(&data, 0, sizeof(MQTTSNPacket_connectData)); + packet->getCONNECT(&data); + + /* create a client */ + client = _gateway->getClientList()->createClient(_sensorNetwork->getSenderAddress(), &data.clientID, false, false); + + if (!client) + { + WRITELOG("%s Can't create a Client. CONNECT message has been discarded.%s\n", ERRMSG_HEADER, ERRMSG_FOOTER); + delete packet; + continue; + } + + log(client, packet); + + /* set sensorNetAddress & post Event */ + client->setClientAddress(_sensorNetwork->getSenderAddress()); + ev = new Event(); + ev->setClientRecvEvent(client, packet); + _gateway->getPacketEventQue()->post(ev); + } + else + { + log(client, packet); + delete packet; + continue; + } + } + } +} + +void ClientRecvTask::log(Client* client, MQTTSNPacket* packet) +{ + char pbuf[SIZEOF_LOG_PACKET * 3]; + char msgId[6]; + const char* clientId = client ? (const char*)client->getClientId() :"Non Active Client !" ; + + switch (packet->getType()) + { + case MQTTSN_SEARCHGW: + WRITELOG(FORMAT_CY_NL, currentDateTime(), packet->getName(), LEFTARROW, CLIENT, packet->print(pbuf)); + break; + case MQTTSN_CONNECT: + WRITELOG(FORMAT_YE_WH_NL, currentDateTime(), packet->getName(), LEFTARROW, clientId, packet->print(pbuf)); + break; + case MQTTSN_WILLTOPIC: + case MQTTSN_WILLMSG: + case MQTTSN_DISCONNECT: + case MQTTSN_WILLTOPICUPD: + case MQTTSN_WILLMSGUPD: + WRITELOG(FORMAT_WHITE_NL, currentDateTime(), packet->getName(), LEFTARROW, clientId, packet->print(pbuf)); + break; + case MQTTSN_PUBLISH: + case MQTTSN_REGISTER: + case MQTTSN_SUBSCRIBE: + case MQTTSN_UNSUBSCRIBE: + WRITELOG(FORMAT_WH_MSGID_NL, currentDateTime(), packet->getName(), packet->getMsgId(msgId), LEFTARROW, clientId, packet->print(pbuf)); + break; + case MQTTSN_REGACK: + case MQTTSN_PUBACK: + case MQTTSN_PUBREC: + case MQTTSN_PUBREL: + case MQTTSN_PUBCOMP: + WRITELOG(FORMAT_WH_MSGID, currentDateTime(), packet->getName(), packet->getMsgId(msgId), LEFTARROW, clientId, packet->print(pbuf)); + break; + case MQTTSN_PINGREQ: + WRITELOG(FORMAT_WH_NL, currentDateTime(), packet->getName(), LEFTARROW, clientId, packet->print(pbuf)); + break; + default: + WRITELOG(FORMAT_WH_NL, currentDateTime(), packet->getName(), LEFTARROW, clientId, packet->print(pbuf)); + break; + } +} diff --git a/MQTTSNGateway/src/MQTTSNGWClientRecvTask.h b/MQTTSNGateway/src/MQTTSNGWClientRecvTask.h new file mode 100644 index 0000000..4af4f0d --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWClientRecvTask.h @@ -0,0 +1,46 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * 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: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ +#ifndef MQTTSNGWCLIENTRECVTASK_H_ +#define MQTTSNGWCLIENTRECVTASK_H_ + +#include "SensorNetwork.h" +#include "MQTTSNGateway.h" + +namespace MQTTSNGW +{ + +/*===================================== + Class ClientRecvTask + =====================================*/ +class ClientRecvTask:public Thread +{ + MAGIC_WORD_FOR_THREAD; +public: + ClientRecvTask(Gateway*); + ~ClientRecvTask(); + virtual void initialize(int argc, char** argv); + void run(); + +private: + void log(Client*, MQTTSNPacket*); + + Gateway* _gateway; + SensorNetwork* _sensorNetwork; +}; + +} + +#endif /* MQTTSNGWCLIENTRECVTASK_H_ */ diff --git a/MQTTSNGateway/src/MQTTSNGWClientSendTask.cpp b/MQTTSNGateway/src/MQTTSNGWClientSendTask.cpp new file mode 100644 index 0000000..ff74aae --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWClientSendTask.cpp @@ -0,0 +1,105 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * 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: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ +#include "MQTTSNGWClientSendTask.h" +#include "MQTTSNGWPacket.h" +#include "MQTTSNGWPacket.h" +#include "MQTTSNGateway.h" + +using namespace MQTTSNGW; +using namespace std; +char* currentDateTime(void); +/*===================================== + Class ClientSendTask + =====================================*/ +ClientSendTask::ClientSendTask(Gateway* gateway) +{ + _gateway = gateway; + _gateway->attach((Thread*)this); + _sensorNetwork = _gateway->getSensorNetwork(); +} + +ClientSendTask::~ClientSendTask() +{ + +} + +void ClientSendTask::run() +{ + Client* client = 0; + MQTTSNPacket* packet = 0; + + while (true) + { + Event* ev = _gateway->getClientSendQue()->wait(); + + if (ev->getEventType() == EtClientSend) + { + client = ev->getClient(); + packet = ev->getMQTTSNPacket(); + packet->unicast(_sensorNetwork, client->getSensorNetAddress()); + } + else if (ev->getEventType() == EtBroadcast) + { + packet = ev->getMQTTSNPacket(); + packet->broadcast(_sensorNetwork); + } + + log(client, packet); + delete ev; + } +} + +void ClientSendTask::log(Client* client, MQTTSNPacket* packet) +{ + char pbuf[SIZEOF_LOG_PACKET * 3]; + char msgId[6]; + + switch (packet->getType()) + { + case MQTTSN_ADVERTISE: + case MQTTSN_GWINFO: + WRITELOG(FORMAT_CY_NL, currentDateTime(), packet->getName(), RIGHTARROW, CLIENTS, packet->print(pbuf)); + break; + case MQTTSN_CONNACK: + case MQTTSN_DISCONNECT: + WRITELOG(FORMAT_YE_WH, currentDateTime(), packet->getName(), RIGHTARROW, client->getClientId(), packet->print(pbuf)); + break; + case MQTTSN_WILLTOPICREQ: + case MQTTSN_WILLMSGREQ: + case MQTTSN_WILLTOPICRESP: + case MQTTSN_WILLMSGRESP: + WRITELOG(FORMAT_GR, currentDateTime(), packet->getName(), RIGHTARROW, client->getClientId(), packet->print(pbuf)); + break; + case MQTTSN_REGISTER: + case MQTTSN_PUBLISH: + WRITELOG(FORMAT_GR_WH_MSGID_NL, currentDateTime(), packet->getName(), packet->getMsgId(msgId), RIGHTARROW, client->getClientId(), packet->print(pbuf)); + break; + case MQTTSN_REGACK: + case MQTTSN_PUBACK: + case MQTTSN_PUBREC: + case MQTTSN_PUBREL: + case MQTTSN_PUBCOMP: + case MQTTSN_SUBACK: + case MQTTSN_UNSUBACK: + WRITELOG(FORMAT_GR_WH_MSGID, currentDateTime(), packet->getName(), packet->getMsgId(msgId), RIGHTARROW, client->getClientId(), packet->print(pbuf)); + break; + case MQTTSN_PINGRESP: + WRITELOG(FORMAT_CY, currentDateTime(), packet->getName(), RIGHTARROW, client->getClientId(), packet->print(pbuf)); + break; + default: + break; + } +} diff --git a/MQTTSNGateway/src/MQTTSNGWClientSendTask.h b/MQTTSNGateway/src/MQTTSNGWClientSendTask.h new file mode 100644 index 0000000..d9e1646 --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWClientSendTask.h @@ -0,0 +1,45 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * 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: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ +#ifndef MQTTSNGWCLIENTSENDTASK_H_ +#define MQTTSNGWCLIENTSENDTASK_H_ + +#include "MQTTSNGateway.h" +#include "SensorNetwork.h" + +namespace MQTTSNGW +{ + +/*===================================== + Class ClientSendTask + =====================================*/ +class ClientSendTask: public Thread +{ + MAGIC_WORD_FOR_THREAD; +public: + ClientSendTask(Gateway* gateway); + ~ClientSendTask(); + void run(); + +private: + void log(Client*, MQTTSNPacket*); + + Gateway* _gateway; + SensorNetwork* _sensorNetwork; +}; + +} + +#endif /* MQTTSNGWCLIENTSENDTASK_H_ */ diff --git a/MQTTSNGateway/src/MQTTSNGWConnectionHandler.cpp b/MQTTSNGateway/src/MQTTSNGWConnectionHandler.cpp new file mode 100644 index 0000000..28baeae --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWConnectionHandler.cpp @@ -0,0 +1,251 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * 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: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ + +#include "MQTTSNGWConnectionHandler.h" +#include "MQTTSNGateway.h" +#include "MQTTSNGWPacket.h" +#include "MQTTGWPacket.h" + +using namespace std; +using namespace MQTTSNGW; + +/*===================================== + Class MQTTSNConnectionHandler + =====================================*/ +MQTTSNConnectionHandler::MQTTSNConnectionHandler(Gateway* gateway) +{ + _gateway = gateway; +} + +MQTTSNConnectionHandler::~MQTTSNConnectionHandler() +{ + +} + +/* + * ADVERTISE + */ +void MQTTSNConnectionHandler::sendADVERTISE() +{ + MQTTSNPacket* adv = new MQTTSNPacket(); + adv->setADVERTISE(_gateway->getGWParams()->gatewayId, _gateway->getGWParams()->keepAlive); + Event* ev1 = new Event(); + ev1->setBrodcastEvent(adv); //broadcast + _gateway->getClientSendQue()->post(ev1); +} + +/* + * SEARCHGW + */ +void MQTTSNConnectionHandler::handleSearchgw(MQTTSNPacket* packet) +{ + if (packet->getType() == MQTTSN_SEARCHGW) + { + if (_gateway->getClientList()->getClientCount() < DEFAULT_MAX_CLIENTS) + { + MQTTSNPacket* gwinfo = new MQTTSNPacket(); + gwinfo->setGWINFO(_gateway->getGWParams()->gatewayId); + Event* ev1 = new Event(); + ev1->setBrodcastEvent(gwinfo); + _gateway->getClientSendQue()->post(ev1); + } + } +} + +/* + * CONNECT + */ +void MQTTSNConnectionHandler::handleConnect(Client* client, MQTTSNPacket* packet) +{ + MQTTSNPacket_connectData data; + packet->getCONNECT(&data); + + /* clear ConnectData of Client */ + Connect* connectData = client->getConnectData(); + memset(connectData, 0, sizeof(Connect)); + client->disconnected(); + + Topics* topics = client->getTopics(); + + /* CONNECT was not sent yet. prepare Connect data */ + connectData->header.bits.type = CONNECT; + connectData->clientID = client->getClientId(); + connectData->version = _gateway->getGWParams()->mqttVersion; + connectData->keepAliveTimer = data.duration; + connectData->flags.bits.will = data.willFlag; + + if ((const char*) _gateway->getGWParams()->loginId != 0 && (const char*) _gateway->getGWParams()->password != 0) + { + connectData->flags.bits.password = 1; + connectData->flags.bits.username = 1; + } + + if (data.cleansession) + { + connectData->flags.bits.cleanstart = 1; + /* reset the table of msgNo and TopicId pare */ + client->clearWaitedPubTopicId(); + client->clearWaitedSubTopicId(); + + /* renew the TopicList */ + if (topics) + { + delete topics; + } + topics = new Topics(); + client->setTopics(topics); + } + + if (data.willFlag) + { + /* create & send WILLTOPICREQ message to the client */ + MQTTSNPacket* reqTopic = new MQTTSNPacket(); + reqTopic->setWILLTOPICREQ(); + Event* evwr = new Event(); + evwr->setClientSendEvent(client, reqTopic); + + /* Send WILLTOPICREQ to the client */ + _gateway->getClientSendQue()->post(evwr); + } + else + { + + /* CONNECT message was not qued in. + * create CONNECT message & send it to the broker */ + MQTTGWPacket* mqMsg = new MQTTGWPacket(); + mqMsg->setCONNECT(client->getConnectData(), (unsigned char*)_gateway->getGWParams()->loginId, (unsigned char*)_gateway->getGWParams()->password); + Event* ev1 = new Event(); + ev1->setBrokerSendEvent(client, mqMsg); + _gateway->getBrokerSendQue()->post(ev1); + } +} + +/* + * WILLTOPIC + */ +void MQTTSNConnectionHandler::handleWilltopic(Client* client, MQTTSNPacket* packet) +{ + int willQos; + uint8_t willRetain; + MQTTSNString willTopic; + + packet->getWILLTOPIC(&willQos, &willRetain, &willTopic); + client->setWillTopic(willTopic); + Connect* connectData = client->getConnectData(); + + /* add the connectData for MQTT CONNECT message */ + connectData->willTopic = client->getWillTopic(); + connectData->flags.bits.willQoS = willQos; + connectData->flags.bits.willRetain = willRetain; + + /* Send WILLMSGREQ to the client */ + client->setWaitWillMsgFlg(true); + MQTTSNPacket* reqMsg = new MQTTSNPacket(); + reqMsg->setWILLMSGREQ(); + Event* evt = new Event(); + evt->setClientSendEvent(client, reqMsg); + _gateway->getClientSendQue()->post(evt); +} + +/* + * WILLMSG + */ +void MQTTSNConnectionHandler::handleWillmsg(Client* client, MQTTSNPacket* packet) +{ + if ( !client->isWaitWillMsg() ) + { + DEBUGLOG(" MQTTSNConnectionHandler::handleWillmsg WaitWillMsgFlg is off.\n"); + return; + } + + MQTTSNString willmsg; + Connect* connectData = client->getConnectData(); + + if( client->isConnectSendable() ) + { + /* save WillMsg in the client */ + packet->getWILLMSG(&willmsg); + client->setWillMsg(willmsg); + + /* create CONNECT message */ + MQTTGWPacket* mqttPacket = new MQTTGWPacket(); + connectData->willMsg = client->getWillMsg(); + mqttPacket->setCONNECT(connectData, (unsigned char*)_gateway->getGWParams()->loginId, (unsigned char*)_gateway->getGWParams()->password); + + /* Send CONNECT to the broker */ + Event* ev1 = new Event(); + ev1->setBrokerSendEvent(client, mqttPacket); + _gateway->getBrokerSendQue()->post(ev1); + } +} + +/* + * DISCONNECT + */ +void MQTTSNConnectionHandler::handleDisconnect(Client* client, MQTTSNPacket* packet) +{ + MQTTGWPacket* mqMsg = new MQTTGWPacket(); + MQTTSNPacket* snMsg = new MQTTSNPacket(); + + mqMsg->setHeader(DISCONNECT); + snMsg->setDISCONNECT(0); + + Event* ev1 = new Event(); + ev1->setClientSendEvent(client, snMsg); + _gateway->getClientSendQue()->post(ev1); + + ev1 = new Event(); + ev1->setBrokerSendEvent(client, mqMsg); + _gateway->getBrokerSendQue()->post(ev1); +} + +/* + * WILLTOPICUPD + */ +void MQTTSNConnectionHandler::handleWilltopicupd(Client* client, MQTTSNPacket* packet) +{ + /* send NOT_SUPPORTED responce to the client */ + MQTTSNPacket* respMsg = new MQTTSNPacket(); + respMsg->setWILLTOPICRESP(MQTTSN_RC_NOT_SUPPORTED); + Event* evt = new Event(); + evt->setClientSendEvent(client, respMsg); + _gateway->getClientSendQue()->post(evt); +} + +/* + * WILLMSGUPD + */ +void MQTTSNConnectionHandler::handleWillmsgupd(Client* client, MQTTSNPacket* packet) +{ + /* send NOT_SUPPORTED responce to the client */ + MQTTSNPacket* respMsg = new MQTTSNPacket(); + respMsg->setWILLMSGRESP(MQTTSN_RC_NOT_SUPPORTED); + Event* evt = new Event(); + evt->setClientSendEvent(client, respMsg); + _gateway->getClientSendQue()->post(evt); +} + +/* + * PINGREQ + */ +void MQTTSNConnectionHandler::handlePingreq(Client* client, MQTTSNPacket* packet) +{ + /* send PINGREQ to the broker */ + MQTTGWPacket* pingreq = new MQTTGWPacket(); + pingreq->setHeader(PINGREQ); + Event* ev1 = new Event(); + ev1->setBrokerSendEvent(client, pingreq); +} diff --git a/MQTTSNGateway/src/MQTTSNGWConnectionHandler.h b/MQTTSNGateway/src/MQTTSNGWConnectionHandler.h new file mode 100644 index 0000000..044e766 --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWConnectionHandler.h @@ -0,0 +1,46 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * 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: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ +#ifndef MQTTSNGWCONNECTIONHANDLER_H_ +#define MQTTSNGWCONNECTIONHANDLER_H_ + +#include "MQTTSNGateway.h" +#include "MQTTSNGWPacket.h" + +namespace MQTTSNGW +{ + +class MQTTSNConnectionHandler +{ +public: + MQTTSNConnectionHandler(Gateway* gateway); + ~MQTTSNConnectionHandler(); + void sendADVERTISE(void); + void handleSearchgw(MQTTSNPacket* packet); + void handleConnect(Client* client, MQTTSNPacket* packet); + void handleWilltopic(Client* client, MQTTSNPacket* packet); + void handleWillmsg(Client* client, MQTTSNPacket* packet); + void handleDisconnect(Client* client, MQTTSNPacket* packet); + void handleWilltopicupd(Client* client, MQTTSNPacket* packet); + void handleWillmsgupd(Client* client, MQTTSNPacket* packet); + void handlePingreq(Client* client, MQTTSNPacket* packet); +private: + char _pbuf[MQTTSNGW_MAX_PACKET_SIZE * 3]; + Gateway* _gateway; +}; + +} + +#endif /* MQTTSNGWCONNECTIONHANDLER_H_ */ diff --git a/MQTTSNGateway/src/MQTTSNGWDefines.h b/MQTTSNGateway/src/MQTTSNGWDefines.h new file mode 100644 index 0000000..541dad5 --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWDefines.h @@ -0,0 +1,62 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * 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: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ + +#ifndef MQTTSNGWDEFINES_H_ +#define MQTTSNGWDEFINES_H_ + +namespace MQTTSNGW +{ + +/*================================= + * Starting prompt + ==================================*/ +#define GATEWAY_VERSION "(Ver 0.1.1)" + +/*================================= + * Log controls + ==================================*/ +//#define DEBUG // print out log for debug +//#define RINGBUFFER // print out Packets log into shared memory + +/*================================= + * Parametrs + ==================================*/ +#define MQTTSNGW_MAX_PACKET_SIZE (1024) // Max Packet size +#define SIZEOF_LOG_PACKET (128) // Length of the packet log in bytes + +#define MQTTSNGW_CONFIG_FILE "/usr/local/etc/mqttsnGateway/config/param.conf" +#define MQTTSNGW_CLIENT_LIST "/usr/local/etc/mqttsnGateway/config/clientList.conf" +#define MQTTSNGW_TLS_CA_DIR "/etc/ssl/certs" + +/*================================= + * Data Type + ==================================*/ +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; + +/*================================= + * Macros + ==================================*/ +#ifdef DEBUG +#define DEBUGLOG(...) printf(__VA_ARGS__) +#undef RINGBUFFER +#else +#define DEBUGLOG(...) +#endif + +} +#endif /* MQTTSNGWDEFINES_H_ */ diff --git a/MQTTSNGateway/src/MQTTSNGWLogmonitor.cpp b/MQTTSNGateway/src/MQTTSNGWLogmonitor.cpp new file mode 100644 index 0000000..50dc482 --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWLogmonitor.cpp @@ -0,0 +1,50 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * 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: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ +#define RINGBUFFER + +#include "MQTTSNGWProcess.h" +#include "MQTTSNGWLogmonitor.h" +#include + +using namespace std; +using namespace MQTTSNGW; + +Logmonitor::Logmonitor() +{ + theProcess = this; +} + +Logmonitor::~Logmonitor() +{ + +} + +void Logmonitor::run() +{ + while (true) + { + const char* data = getLog(); + if ( *data == 0 ) + { + break; + } + else + { + printf("%s", data); + } + } +} + diff --git a/MQTTSNGateway/src/MQTTSNGWLogmonitor.h b/MQTTSNGateway/src/MQTTSNGWLogmonitor.h new file mode 100644 index 0000000..cb5838a --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWLogmonitor.h @@ -0,0 +1,33 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * 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: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ +#ifndef MQTTSNGWLOGMONITOR_H_ +#define MQTTSNGWLOGMONITOR_H_ + +#include "MQTTSNGWProcess.h" + +namespace MQTTSNGW +{ +class Logmonitor: public Process +{ +public: + Logmonitor(); + ~Logmonitor(); + void run(); +}; + +} + +#endif /* MQTTSNGWLOGMONITOR_H_ */ diff --git a/MQTTSNGateway/src/MQTTSNGWPacket.cpp b/MQTTSNGateway/src/MQTTSNGWPacket.cpp new file mode 100644 index 0000000..b52116a --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWPacket.cpp @@ -0,0 +1,410 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * 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: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ + +#include "MQTTSNGateway.h" +#include "MQTTSNGWPacket.h" +#include "MQTTSNPacket.h" +#include "SensorNetwork.h" +#include +#include +#include + +using namespace std; +using namespace MQTTSNGW; + +MQTTSNPacket::MQTTSNPacket() +{ + _buf = 0; + _bufLen = 0; +} +MQTTSNPacket::~MQTTSNPacket() +{ + if (_buf) + { + free(_buf); + } +} + +int MQTTSNPacket::unicast(SensorNetwork* network, SensorNetAddress* sendTo) +{ + return network->unicast(_buf, _bufLen, sendTo); +} + +int MQTTSNPacket::broadcast(SensorNetwork* network) +{ + return network->broadcast(_buf, _bufLen); +} + +int MQTTSNPacket::serialize(uint8_t* buf) +{ + buf = _buf; + return _bufLen; +} + +int MQTTSNPacket::desirialize(unsigned char* buf, unsigned short len) +{ + if ( _buf ) + { + free(_buf); + } + + _buf = (unsigned char*)calloc(len, sizeof(unsigned char)); + if ( _buf ) + { + memcpy(_buf, buf, len); + _bufLen = len; + } + else + { + _bufLen = 0; + } + return _bufLen; +} + +int MQTTSNPacket::recv(SensorNetwork* network) +{ + uint8_t buf[MQTTSNGW_MAX_PACKET_SIZE]; + int len = network->read((uint8_t*) buf, MQTTSNGW_MAX_PACKET_SIZE); + if (len >= 3) + { + len = desirialize(buf, len); + } + else + { + len = 0; + } + return len; + +} + +int MQTTSNPacket::getType(void) +{ + if ( _bufLen == 0 ) + { + return 0; + } + int value = 0; + int p = MQTTSNPacket_decode(_buf, _bufLen, &value); + return _buf[p]; +} + +unsigned char* MQTTSNPacket::getPacketData(void) +{ + return _buf; +} + +int MQTTSNPacket::getPacketLength(void) +{ + return _bufLen; +} + +const char* MQTTSNPacket::getName() +{ + return MQTTSNPacket_name(getType()); +} + +int MQTTSNPacket::setADVERTISE(uint8_t gatewayid, uint16_t duration) +{ + unsigned char buf[5]; + int len = MQTTSNSerialize_advertise(buf, 5, (unsigned char) gatewayid, + (unsigned short) duration); + return desirialize(buf, len); +} + +int MQTTSNPacket::setGWINFO(uint8_t gatewayId) +{ + unsigned char buf[3]; + int len = MQTTSNSerialize_gwinfo(buf, 3, (unsigned char) gatewayId, 0, 0); + return desirialize(buf, len); +} + +int MQTTSNPacket::setConnect(void) +{ + unsigned char buf[40]; + MQTTSNPacket_connectData data; + data.clientID.cstring = (char*)"client01"; + int len = MQTTSNSerialize_connect(buf, 40, &data); + return desirialize(buf, len); +} + +int MQTTSNPacket::setCONNACK(uint8_t returnCode) +{ + unsigned char buf[3]; + int len = MQTTSNSerialize_connack(buf, 3, (int) returnCode); + return desirialize(buf, len); +} + +int MQTTSNPacket::setWILLTOPICREQ(void) +{ + unsigned char buf[2]; + int len = MQTTSNSerialize_willtopicreq(buf, 2); + return desirialize(buf, len); +} + +int MQTTSNPacket::setWILLMSGREQ(void) +{ + unsigned char buf[2]; + int len = MQTTSNSerialize_willmsgreq(buf, 2); + return desirialize(buf, len); +} + +int MQTTSNPacket::setREGISTER(uint16_t topicId, uint16_t msgId, MQTTSNString* topicName) +{ + unsigned char buf[MQTTSNGW_MAX_PACKET_SIZE]; + int len = MQTTSNSerialize_register(buf, MQTTSNGW_MAX_PACKET_SIZE, (unsigned short) topicId, (unsigned short) msgId, + topicName); + return desirialize(buf, len); +} + +int MQTTSNPacket::setREGACK(uint16_t topicId, uint16_t msgId, uint8_t returnCode) +{ + unsigned char buf[7]; + int len = MQTTSNSerialize_regack(buf, 7, (unsigned short) topicId, (unsigned short) msgId, + (unsigned char) returnCode); + return desirialize(buf, len); +} + +int MQTTSNPacket::setPUBLISH(uint8_t dup, int qos, uint8_t retained, uint16_t msgId, MQTTSN_topicid topic, + uint8_t* payload, uint16_t payloadlen) +{ + unsigned char buf[MQTTSNGW_MAX_PACKET_SIZE]; + int len = MQTTSNSerialize_publish(buf, MQTTSNGW_MAX_PACKET_SIZE, (unsigned char) dup, qos, (unsigned char) retained, + (unsigned short) msgId, topic, (unsigned char*) payload, (int) payloadlen); + return desirialize(buf, len); +} + +int MQTTSNPacket::setPUBACK(uint16_t topicId, uint16_t msgId, uint8_t returnCode) +{ + unsigned char buf[7]; + int len = MQTTSNSerialize_puback(buf, 7, (unsigned short) topicId, (unsigned short) msgId, + (unsigned char) returnCode); + return desirialize(buf, len); +} + +int MQTTSNPacket::setPUBREC(uint16_t msgId) +{ + unsigned char buf[4]; + int len = MQTTSNSerialize_pubrec(buf, 4, (unsigned short) msgId); + return desirialize(buf, len); +} + +int MQTTSNPacket::setPUBREL(uint16_t msgId) +{ + unsigned char buf[4]; + int len = MQTTSNSerialize_pubrel(buf, 4, (unsigned short) msgId); + return desirialize(buf, len); +} + +int MQTTSNPacket::setPUBCOMP(uint16_t msgId) +{ + unsigned char buf[4]; + int len = MQTTSNSerialize_pubcomp(buf, 4, (unsigned short) msgId); + return desirialize(buf, len); +} + +int MQTTSNPacket::setSUBACK(int qos, uint16_t topicId, uint16_t msgId, uint8_t returnCode) +{ + unsigned char buf[8]; + int len = MQTTSNSerialize_suback(buf, 8, qos, (unsigned short) topicId, + (unsigned short) msgId, (unsigned char) returnCode); + return desirialize(buf, len); +} + +int MQTTSNPacket::setUNSUBACK(uint16_t msgId) +{ + unsigned char buf[4]; + int len = MQTTSNSerialize_unsuback(buf, 4, (unsigned short) msgId); + return desirialize(buf, len); +} + +int MQTTSNPacket::setPINGRESP(void) +{ + unsigned char buf[32]; + int len = MQTTSNSerialize_pingresp(buf, 32); + return desirialize(buf, len); +} + +int MQTTSNPacket::setDISCONNECT(uint16_t duration) +{ + unsigned char buf[4]; + int len = MQTTSNSerialize_disconnect(buf, 4, (int) duration); + return desirialize(buf, len); +} + +int MQTTSNPacket::setWILLTOPICRESP(uint8_t returnCode) +{ + unsigned char buf[MQTTSNGW_MAX_PACKET_SIZE]; + int len = MQTTSNSerialize_willtopicresp(buf, MQTTSNGW_MAX_PACKET_SIZE, (int) returnCode); + return desirialize(buf, len); +} + +int MQTTSNPacket::setWILLMSGRESP(uint8_t returnCode) +{ + unsigned char buf[MQTTSNGW_MAX_PACKET_SIZE]; + int len = MQTTSNSerialize_willmsgresp(buf, MQTTSNGW_MAX_PACKET_SIZE, (int) returnCode); + return desirialize(buf, len); +} + +int MQTTSNPacket::getSERCHGW(uint8_t* radius) +{ + return MQTTSNDeserialize_searchgw((unsigned char*) radius, (unsigned char*) _buf, _bufLen); +} + +int MQTTSNPacket::getCONNECT(MQTTSNPacket_connectData* data) +{ + return MQTTSNDeserialize_connect(data, _buf, _bufLen); +} + +int MQTTSNPacket::getCONNACK(uint8_t* returnCode) +{ + return MQTTSNSerialize_connack(_buf, _bufLen, (int) *returnCode); +} + +int MQTTSNPacket::getWILLTOPIC(int* willQoS, uint8_t* willRetain, MQTTSNString* willTopic) +{ + return MQTTSNDeserialize_willtopic((int*) willQoS, (unsigned char*) willRetain, willTopic, _buf, _bufLen); +} + +int MQTTSNPacket::getWILLMSG(MQTTSNString* willmsg) +{ + return MQTTSNDeserialize_willmsg(willmsg, _buf, _bufLen); +} + +int MQTTSNPacket::getREGISTER(uint16_t* topicId, uint16_t* msgId, MQTTSNString* topicName) +{ + return MQTTSNDeserialize_register((unsigned short*) topicId, (unsigned short*) msgId, topicName, + _buf, _bufLen); +} + +int MQTTSNPacket::getREGACK(uint16_t* topicId, uint16_t* msgId, uint8_t* returnCode) +{ + return MQTTSNDeserialize_regack((unsigned short*) topicId, (unsigned short*) msgId, (unsigned char*) returnCode, _buf, _bufLen); +} + +int MQTTSNPacket::getPUBLISH(uint8_t* dup, int* qos, uint8_t* retained, uint16_t* msgId, MQTTSN_topicid* topic, + uint8_t** payload, int* payloadlen) +{ + return MQTTSNDeserialize_publish((unsigned char*) dup, qos, (unsigned char*) retained, (unsigned short*) msgId, + topic, (unsigned char**) payload, (int*) payloadlen, _buf, _bufLen); +} + +int MQTTSNPacket::getPUBACK(uint16_t* topicId, uint16_t* msgId, uint8_t* returnCode) +{ + return MQTTSNDeserialize_puback((unsigned short*) topicId, (unsigned short*) msgId, (unsigned char*) returnCode, + _buf, _bufLen); +} + +int MQTTSNPacket::getACK(uint16_t* msgId) +{ + unsigned char type; + return MQTTSNDeserialize_ack(&type, (unsigned short*) msgId, _buf, _bufLen); +} + +int MQTTSNPacket::getSUBSCRIBE(uint8_t* dup, int* qos, uint16_t* msgId, MQTTSN_topicid* topicFilter) +{ + return MQTTSNDeserialize_subscribe((unsigned char*) dup, qos, (unsigned short*) msgId, topicFilter, _buf, _bufLen); +} + +int MQTTSNPacket::getUNSUBSCRIBE(uint16_t* msgId, MQTTSN_topicid* topicFilter) +{ + return MQTTSNDeserialize_unsubscribe((unsigned short*) msgId, topicFilter, _buf, _bufLen); +} + +int MQTTSNPacket::getPINGREQ(void) +{ + if (getType() == MQTTSN_PINGRESP && _bufLen > 2 ) + { + return _bufLen - 2; + } + return 0; +} + +int MQTTSNPacket::getDISCONNECT(uint16_t* duration) +{ + return MQTTSNDeserialize_disconnect((int*) duration, _buf, _bufLen); +} + +int MQTTSNPacket::getWILLTOPICUPD(uint8_t* willQoS, uint8_t* willRetain, MQTTSNString* willTopic) +{ + return MQTTSNDeserialize_willtopicupd((int*) willQoS, (unsigned char*) willRetain, willTopic, _buf, _bufLen); +} + +int MQTTSNPacket::getWILLMSGUPD(MQTTSNString* willMsg) +{ + return MQTTSNDeserialize_willmsgupd(willMsg, _buf, _bufLen); +} + +char* MQTTSNPacket::print(char* pbuf) +{ + char* ptr = pbuf; + char** pptr = &pbuf; + int value = 0; + + int i = MQTTSNPacket_decode(_buf, _bufLen, &value); + int size = _bufLen > SIZEOF_LOG_PACKET ? SIZEOF_LOG_PACKET : _bufLen; + + for (; i < size; i++) + { + sprintf(*pptr, " %02X", *(_buf + i)); + *pptr += 3; + } + **pptr = 0; + return ptr; +} + +char* MQTTSNPacket::getMsgId(char* pbuf) +{ + int value = 0; + int p = 0; + + switch ( getType() ) + { + case MQTTSN_PUBLISH: + p = MQTTSNPacket_decode(_buf, _bufLen, &value); + if ( _buf[p + 1] & 0x80 ) + { + sprintf(pbuf, "+%02X%02X", _buf[p + 4], _buf[p + 5]); + } + else + { + sprintf(pbuf, " %02X%02X", _buf[p + 4], _buf[p + 5]); + } + break; + case MQTTSN_PUBACK: + case MQTTSN_REGISTER: + case MQTTSN_REGACK: + sprintf(pbuf, " %02X%02X", _buf[4], _buf[5]); + break; + case MQTTSN_PUBREC: + case MQTTSN_PUBREL: + case MQTTSN_PUBCOMP: + sprintf(pbuf, " %02X%02X", _buf[2], _buf[3]); + break; + case MQTTSN_SUBSCRIBE: + case MQTTSN_UNSUBSCRIBE: + p = MQTTSNPacket_decode(_buf, _bufLen, &value); + sprintf(pbuf, " %02X%02X", _buf[p + 2], _buf[p + 3]); + break; + case MQTTSN_SUBACK: + case MQTTSN_UNSUBACK: + sprintf(pbuf, " %02X%02X", _buf[5], _buf[6]); + break; + default: + sprintf(pbuf, " "); + break; + } + return pbuf; +} diff --git a/MQTTSNGateway/src/MQTTSNGWPacket.h b/MQTTSNGateway/src/MQTTSNGWPacket.h new file mode 100644 index 0000000..5cd7578 --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWPacket.h @@ -0,0 +1,88 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * 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: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ +#ifndef MQTTSNGWPACKET_H_ +#define MQTTSNGWPACKET_H_ + +#include "MQTTSNGWDefines.h" +#include "MQTTSNPacket.h" +#include "SensorNetwork.h" + +namespace MQTTSNGW +{ + +class MQTTSNPacket +{ +public: + MQTTSNPacket(); + ~MQTTSNPacket(); + int unicast(SensorNetwork* network, SensorNetAddress* sendTo); + int broadcast(SensorNetwork* network); + int recv(SensorNetwork* network); + int serialize(uint8_t* buf); + int desirialize(unsigned char* buf, unsigned short len); + int getType(void); + unsigned char* getPacketData(void); + int getPacketLength(void); + const char* getName(); + + int setConnect(void); // Debug + int setADVERTISE(uint8_t gatewayid, uint16_t duration); + int setGWINFO(uint8_t gatewayId); + int setCONNACK(uint8_t returnCode); + int setWILLTOPICREQ(void); + int setWILLMSGREQ(void); + int setREGISTER(uint16_t topicId, uint16_t msgId, MQTTSNString* TopicName); + int setREGACK(uint16_t topicId, uint16_t msgId, uint8_t returnCode); + int setPUBLISH(uint8_t dup, int qos, uint8_t retained, uint16_t msgId, + MQTTSN_topicid topic, uint8_t* payload, uint16_t payloadlen); + int setPUBACK(uint16_t topicId, uint16_t msgId, uint8_t returnCode); + int setPUBREC(uint16_t msgId); + int setPUBREL(uint16_t msgId); + int setPUBCOMP(uint16_t msgId); + int setSUBACK(int qos, uint16_t topicId, uint16_t msgId, uint8_t returnCode); + int setUNSUBACK(uint16_t msgId); + int setPINGRESP(void); + int setDISCONNECT(uint16_t duration); + int setWILLTOPICRESP(uint8_t returnCode); + int setWILLMSGRESP(uint8_t returnCode); + + int getSERCHGW(uint8_t* radius); + int getCONNECT(MQTTSNPacket_connectData* option); + int getCONNACK(uint8_t* returnCode); + int getWILLTOPIC(int* willQoS, uint8_t* willRetain, MQTTSNString* willTopic); + int getWILLMSG(MQTTSNString* willmsg); + int getREGISTER(uint16_t* topicId, uint16_t* msgId, MQTTSNString* topicName); + int getREGACK(uint16_t* topicId, uint16_t* msgId, uint8_t* returnCode); + int getPUBLISH(uint8_t* dup, int* qos, uint8_t* retained, uint16_t* msgId, + MQTTSN_topicid* topic, unsigned char** payload, int* payloadlen); + int getPUBACK(uint16_t* topicId, uint16_t* msgId, uint8_t* returnCode); + int getACK(uint16_t* msgId); + int getSUBSCRIBE(uint8_t* dup, int* qos, uint16_t* msgId, MQTTSN_topicid* topicFilter); + int getUNSUBSCRIBE(uint16_t* msgId, MQTTSN_topicid* topicFilter); + int getPINGREQ(void); + int getDISCONNECT(uint16_t* duration); + int getWILLTOPICUPD(uint8_t* willQoS, uint8_t* willRetain, MQTTSNString* willTopic); + int getWILLMSGUPD(MQTTSNString* willMsg); + char* getMsgId(char* buf); + char* print(char* buf); + +private: + unsigned char* _buf; // Ptr to a packet data + int _bufLen; // length of the packet data +}; + +} +#endif /* MQTTSNGWPACKET_H_ */ diff --git a/MQTTSNGateway/src/MQTTSNGWPacketHandleTask.cpp b/MQTTSNGateway/src/MQTTSNGWPacketHandleTask.cpp new file mode 100644 index 0000000..c24659b --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWPacketHandleTask.cpp @@ -0,0 +1,236 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * 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: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ + +#include "MQTTSNGWDefines.h" +#include "MQTTSNGWPacketHandleTask.h" +#include "MQTTSNGWProcess.h" +#include "MQTTGWPacket.h" +#include "MQTTSNGWClient.h" +#include "MQTTSNGWProcess.h" +#include "MQTTGWConnectionHandler.h" +#include "MQTTGWPublishHandler.h" +#include "MQTTGWSubscribeHandler.h" +#include "MQTTSNGWConnectionHandler.h" +#include "MQTTSNGWPublishHandler.h" +#include "MQTTSNGWSubscribeHandler.h" + +using namespace std; +using namespace MQTTSNGW; + +#define EVENT_QUE_TIME_OUT 2000 // 2000 msecs + +/*===================================== + Class PacketHandleTask + =====================================*/ + +PacketHandleTask::PacketHandleTask(Gateway* gateway) +{ + _gateway = gateway; + _gateway->attach((Thread*)this); + _mqttConnection = new MQTTGWConnectionHandler(_gateway); + _mqttPublish = new MQTTGWPublishHandler(_gateway); + _mqttSubscribe = new MQTTGWSubscribeHandler(_gateway); + _mqttsnConnection = new MQTTSNConnectionHandler(_gateway); + _mqttsnPublish = new MQTTSNPublishHandler(_gateway); + _mqttsnSubscribe = new MQTTSNSubscribeHandler(_gateway); +} + +/** + * Destructor is called by Gateway's destructor indirectly. + */ +PacketHandleTask::~PacketHandleTask() +{ + if ( _mqttConnection ) + { + delete _mqttConnection; + } + if ( _mqttPublish ) + { + delete _mqttPublish; + } + if ( _mqttSubscribe ) + { + delete _mqttSubscribe; + } + if ( _mqttsnConnection ) + { + delete _mqttsnConnection; + } + if ( _mqttsnPublish ) + { + delete _mqttsnPublish; + } + if ( _mqttsnSubscribe ) + { + delete _mqttsnSubscribe; + } +} + +void PacketHandleTask::run() +{ + Event* ev = 0; + EventQue* eventQue = _gateway->getPacketEventQue(); + ClientList* clist = _gateway->getClientList(); + Client* client = 0; + MQTTSNPacket* snPacket = 0; + MQTTGWPacket* brPacket = 0; + char msgId[6]; + memset(msgId, 0, 6); + + _advertiseTimer.start(_gateway->getGWParams()->keepAlive * 1000UL); + + while (true) + { + if (theProcess->checkSignal() == SIGINT) + { + throw Exception("Terminated by CTL-C"); + } + + /* wait Event */ + ev = eventQue->timedwait(EVENT_QUE_TIME_OUT); + + if (ev->getEventType() == EtTimeout) + { + /*------ Is Client Lost ? ---------*/ + client = clist->getClient(); + while (client > 0) + { + client->checkTimeover(); + client = client->getNextClient(); + } + /*------ Check Keep Alive Timer & send Advertise ------*/ + if (_advertiseTimer.isTimeup()) + { + _mqttsnConnection->sendADVERTISE(); + _advertiseTimer.start(_gateway->getGWParams()->keepAlive * 1000UL); + } + } + + /*------ Handle SEARCHGW Message ---------*/ + else if (ev->getEventType() == EtBroadcast) + { + snPacket = ev->getMQTTSNPacket(); + _mqttsnConnection->handleSearchgw(snPacket); + } + + /*------ Handle Messages form Clients ---------*/ + else if (ev->getEventType() == EtClientRecv) + { + client = ev->getClient(); + snPacket = ev->getMQTTSNPacket(); + + DEBUGLOG(" PacketHandleTask gets %s %s from the client.\n", snPacket->getName(), snPacket->getMsgId(msgId)); + + switch (snPacket->getType()) + { + case MQTTSN_CONNECT: + _mqttsnConnection->handleConnect(client, snPacket); + break; + case MQTTSN_WILLTOPIC: + _mqttsnConnection->handleWilltopic(client, snPacket); + break; + case MQTTSN_WILLMSG: + _mqttsnConnection->handleWillmsg(client, snPacket); + break; + case MQTTSN_DISCONNECT: + _mqttsnConnection->handleDisconnect(client, snPacket); + break; + case MQTTSN_WILLMSGUPD: + _mqttsnConnection->handleWillmsgupd(client, snPacket); + break; + case MQTTSN_PINGREQ: + _mqttsnConnection->handlePingreq(client, snPacket); + break; + case MQTTSN_PUBLISH: + _mqttsnPublish->handlePublish(client, snPacket); + break; + case MQTTSN_PUBACK: + _mqttsnPublish->handlePuback(client, snPacket); + break; + _mqttsnPublish->handleAck(client, snPacket, PUBREC); + break; + case MQTTSN_PUBREL: + _mqttsnPublish->handleAck(client, snPacket, PUBREL); + break; + case MQTTSN_PUBCOMP: + _mqttsnPublish->handleAck(client, snPacket, PUBCOMP); + break; + case MQTTSN_REGISTER: + _mqttsnPublish->handleRegister(client, snPacket); + break; + case MQTTSN_REGACK: + // NOP + break; + case MQTTSN_SUBSCRIBE: + _mqttsnSubscribe->handleSubscribe(client, snPacket); + break; + case MQTTSN_UNSUBSCRIBE: + _mqttsnSubscribe->handleUnsubscribe(client, snPacket); + break; + default: + break; + } + + /* Reset the Timer for PINGREQ. */ + client->updateStatus(snPacket); + } + + /*------ Handle Messages form Broker ---------*/ + else if (ev->getEventType() == EtBrokerRecv) + { + client = ev->getClient(); + brPacket = ev->getMQTTGWPacket(); + DEBUGLOG(" PacketHandleTask gets %s %s from the broker.\n", brPacket->getName(), brPacket->getMsgId(msgId)); + switch (brPacket->getType()) + { + case CONNACK: + _mqttConnection->handleConnack(client, brPacket); + break; + case DISCONNECT: + _mqttConnection->handleDisconnect(client, brPacket); + break; + case PINGRESP: + _mqttConnection->handlePingresp(client, brPacket); + break; + case PUBLISH: + _mqttPublish->handlePublish(client, brPacket); + break; + case PUBACK: + _mqttPublish->handlePuback(client, brPacket); + break; + case PUBREC: + _mqttPublish->handleAck(client, brPacket, PUBREC); + break; + case PUBREL: + _mqttPublish->handleAck(client, brPacket, PUBREL); + break; + case PUBCOMP: + _mqttPublish->handleAck(client, brPacket, PUBCOMP); + break; + case SUBACK: + _mqttSubscribe->handleSuback(client, brPacket); + break; + case UNSUBACK: + _mqttSubscribe->handleUnsuback(client, brPacket); + break; + default: + break; + } + } + delete ev; + } +} + diff --git a/MQTTSNGateway/src/MQTTSNGWPacketHandleTask.h b/MQTTSNGateway/src/MQTTSNGWPacketHandleTask.h new file mode 100644 index 0000000..c91e690 --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWPacketHandleTask.h @@ -0,0 +1,65 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * 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: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ + +#ifndef MQTTSNGWPACKETHANDLETASK_H_ +#define MQTTSNGWPACKETHANDLETASK_H_ + +#include "MQTTSNGWDefines.h" +#include "MQTTSNGateway.h" +#include "MQTTSNGWClient.h" +#include "MQTTSNGWPacket.h" +#include "MQTTGWPacket.h" +#include "MQTTGWConnectionHandler.h" +#include "MQTTGWPublishHandler.h" +#include "MQTTGWSubscribeHandler.h" +#include "MQTTSNGWConnectionHandler.h" +#include "MQTTSNGWPublishHandler.h" +#include "MQTTSNGWSubscribeHandler.h" +#include "SensorNetwork.h" +#include "linux.h" + + +namespace MQTTSNGW +{ + +#define ERRNO_APL_01 11 // Task Initialize Error + +/*===================================== + Class PacketHandleTask + =====================================*/ +class PacketHandleTask : public Thread +{ + MAGIC_WORD_FOR_THREAD; +public: + PacketHandleTask(Gateway* gateway); + ~PacketHandleTask(); + void run(); +private: + Gateway* _gateway; + Timer _advertiseTimer; + Timer _sendUnixTimer; + MQTTGWConnectionHandler* _mqttConnection; + MQTTGWPublishHandler* _mqttPublish; + MQTTGWSubscribeHandler* _mqttSubscribe; + MQTTSNConnectionHandler* _mqttsnConnection; + MQTTSNPublishHandler* _mqttsnPublish; + MQTTSNSubscribeHandler* _mqttsnSubscribe; +}; + + +} + +#endif /* MQTTSNGWPACKETHANDLETASK_H_ */ diff --git a/MQTTSNGateway/src/MQTTSNGWProcess.cpp b/MQTTSNGateway/src/MQTTSNGWProcess.cpp new file mode 100644 index 0000000..c8e07fb --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWProcess.cpp @@ -0,0 +1,333 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * 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: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include "MQTTSNGWProcess.h" +#include "Threading.h" +#include "linux.h" + +using namespace std; +using namespace MQTTSNGW; + +char* currentDateTime(void); + +/*===================================== + Global Variables & Functions + ======================================*/ +Process* MQTTSNGW::theProcess = 0; +MultiTaskProcess* MQTTSNGW::theMultiTaskProcess = 0; + +/* + * Save the type of signal + */ +volatile int theSignaled = 0; + +static void signalHandler(int sig) +{ + theSignaled = sig; +} + +/*===================================== + Class Process + ====================================*/ +Process::Process() +{ + _argc = 0; + _argv = 0; + _rbsem = new Semaphore(MQTTSNGW_RB_SEMAPHOR_NAME, 0); + _rb = new RingBuffer(); +} + +Process::~Process() +{ + delete _rb; + delete _rbsem; +} + +void Process::run() +{ + +} + +void Process::initialize(int argc, char** argv) +{ + _argc = argc; + _argv = argv; + signal(SIGINT, signalHandler); + signal(SIGTERM, signalHandler); + signal(SIGHUP, signalHandler); +} + +int Process::getArgc() +{ + return _argc; +} + +char** Process::getArgv() +{ + return _argv; +} + +int Process::getParam(const char* parameter, char* value) +{ + char str[MQTTSNGW_PARAM_MAX]; + char param[MQTTSNGW_PARAM_MAX]; + FILE *fp; + int i = 0, j = 0; + + if ((fp = fopen(MQTTSNGW_CONFIG_FILE, "r")) == NULL) + { + WRITELOG("No config file:[%s]\n", MQTTSNGW_CONFIG_FILE); + return -1; + } + + while (true) + { + if (fgets(str, MQTTSNGW_PARAM_MAX - 1, fp) == NULL) + { + fclose(fp); + return -3; + } + if (!strncmp(str, parameter, strlen(parameter))) + { + while (str[i++] != '=') + { + ; + } + while (str[i] != '\n') + { + param[j++] = str[i++]; + } + param[j] = '\0'; + + for (i = strlen(param) - 1; i >= 0 && isspace(param[i]); i--) + ; + param[i + 1] = '\0'; + for (i = 0; isspace(param[i]); i++) + ; + if (i > 0) + { + j = 0; + while (param[i]) + param[j++] = param[i++]; + param[j] = '\0'; + } + strcpy(value, param); + fclose(fp); + return 0; + } + } + fclose(fp); + return -2; +} + +void Process::putLog(const char* format, ...) +{ + _mt.lock(); + va_list arg; + va_start(arg, format); + vsprintf(_rbdata, format, arg); + va_end(arg); + if (strlen(_rbdata)) + { + _rb->put(_rbdata); + _rbsem->post(); + } + _mt.unlock(); +} + +const char* Process::getLog() +{ + int len = 0; + _mt.lock(); + while ((len = _rb->get(_rbdata, PROCESS_LOG_BUFFER_SIZE)) == 0) + { + _rbsem->timedwait(1000); + if ( checkSignal() == SIGINT) + { + break; + } + } + *(_rbdata + len) = 0; + _mt.unlock(); + return _rbdata; +} + +void Process::resetRingBuffer() +{ + _rb->reset(); +} + +int Process::checkSignal(void) +{ + return theSignaled; +} + +/*===================================== + Class MultiTaskProcess + ====================================*/ +MultiTaskProcess::MultiTaskProcess() +{ + theMultiTaskProcess = this; + _threadCount = 0; +} + +MultiTaskProcess::~MultiTaskProcess() +{ + for (int i = 0; i < _threadCount; i++) + { + if ( _threadList[i] ) + { + delete _threadList[i]; + } + } +} + +void MultiTaskProcess::initialize(int argc, char** argv) +{ + Process::initialize(argc, argv); + for (int i = 0; i < _threadCount; i++) + { + _threadList[i]->initialize(argc, argv); + } + +} + +void MultiTaskProcess::run(void) +{ + for (int i = 0; i < _threadCount; i++) + { + _threadList[i]->start(); + } + + try + { + _stopProcessEvent.wait(); + } + catch (Exception* ex) + { + ex->writeMessage(); + } +} + +Semaphore* MultiTaskProcess::getStopProcessEvent(void) +{ + return &_stopProcessEvent; +} + +void MultiTaskProcess::attach(Thread* thread) +{ + if (_threadCount < MQTTSNGW_MAX_TASK) + { + _threadList[_threadCount] = thread; + _threadCount++; + } + else + { + throw Exception("Full of Threads"); + } +} + +int MultiTaskProcess::getParam(const char* parameter, char* value) +{ + _mutex.lock(); + int rc = Process::getParam(parameter, value); + _mutex.unlock(); + if (rc == -1) + { + throw Exception("No config file."); + } + return rc; +} + +/*===================================== + Class Exception + ======================================*/ +Exception::Exception(const string& message) +{ + _message = message; + _exNo = 0; + _fileName = 0; + _functionName = 0; + _line = 0; +} + +Exception::Exception(const int exNo, const string& message) +{ + _message = message; + _exNo = exNo; + _fileName = 0; + _functionName = 0; + _line = 0; +} + +Exception::Exception(const int exNo, const string& message, const char* file, + const char* function, const int line) +{ + _message = message; + _exNo = exNo; + _fileName = file; + _functionName = function; + _line = line; +} + +Exception::~Exception() throw () +{ + +} + +const char* Exception::what() const throw () +{ + return _message.c_str(); +} + +const char* Exception::getFileName() +{ + return _fileName; +} + +const char* Exception::getFunctionName() +{ + return _functionName; +} + +const int Exception::getLineNo() +{ + return _line; +} + +const int Exception::getExceptionNo() +{ + return _exNo; +} + +void Exception::writeMessage() +{ + if (getExceptionNo() == 0 ) + { + WRITELOG("%s : %s\n", currentDateTime(), what()); + } + else + { + WRITELOG("%s:%-6d %s line %-4d %s() : %s\n", currentDateTime(), getExceptionNo(), + getFileName(), getLineNo(), getFunctionName(), what()); + } +} diff --git a/MQTTSNGateway/src/MQTTSNGWProcess.h b/MQTTSNGateway/src/MQTTSNGWProcess.h new file mode 100644 index 0000000..55b223b --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWProcess.h @@ -0,0 +1,248 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * 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: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ + +#ifndef MQTTSNGWPROCESS_H_ +#define MQTTSNGWPROCESS_H_ + +#include +#include +#include "MQTTSNGWDefines.h" +#include "Threading.h" + +using namespace std; + +namespace MQTTSNGW +{ + +/*================================= + * Parameters + ==================================*/ +#define MQTTSNGW_MAX_TASK 10 // number of Tasks +#define PROCESS_LOG_BUFFER_SIZE 16384 // Ring buffer size for Logs +#define MQTTSNGW_PARAM_MAX 128 // Max length of config records. + +/*================================= + * Macros + ==================================*/ +#ifdef RINGBUFFER +#define WRITELOG theProcess->putLog +#else +#define WRITELOG printf +#endif + + +/*================================= + Class Process + ==================================*/ +class Process +{ +public: + Process(); + virtual ~Process(); + virtual void initialize(int argc, char** argv); + virtual void run(void); + void putLog(const char* format, ...); + void resetRingBuffer(void); + int getArgc(void); + char** getArgv(void); + int getParam(const char* parameter, char* value); + const char* getLog(void); + int checkSignal(void); + +private: + int _argc; + char** _argv; + RingBuffer* _rb; + Semaphore* _rbsem; + Mutex _mt; + char _rbdata[PROCESS_LOG_BUFFER_SIZE + 1]; +}; + +/*===================================== + Class MultiTaskProcess + ====================================*/ +class MultiTaskProcess: public Process +{ +public: + MultiTaskProcess(void); + ~MultiTaskProcess(); + virtual void initialize(int argc, char** argv); + virtual int getParam(const char* parameter, char* value); + void run(void); + void attach(Thread* thread); + Semaphore* getStopProcessEvent(void); + +private: + Thread* _threadList[MQTTSNGW_MAX_TASK]; + Semaphore _stopProcessEvent; + Mutex _mutex; + int _threadCount; +}; + +/*===================================== + Class Exception + =====================================*/ +class Exception: public exception +{ +public: + Exception(const string& message); + Exception(const int exNo, const string& message); + Exception(const int exNo, const string& message, + const char* file, const char* func, const int line); + virtual ~Exception() throw (); + const char* getFileName(); + const char* getFunctionName(); + const int getLineNo(); + const int getExceptionNo(); + virtual const char* what() const throw (); + void writeMessage(); + +private: + int _exNo; + string _message; + const char* _fileName; + const char* _functionName; + int _line; +}; + + +/*===================================== + Class QueElement + ====================================*/ +template +class QueElement +{ + template friend class Que; +public: + QueElement(T* t) + { + _element = t; + _next = 0; + _prev = 0; + } + + ~QueElement() + { + } + +private: + T* _element; + QueElement* _next; + QueElement* _prev; +}; + +/*===================================== + Class Que + ====================================*/ +template +class Que +{ +public: + Que() + { + _head = 0; + _tail = 0; + _cnt = 0; + } + + ~Que() + { + QueElement* elm = _head; + while (elm) + { + QueElement* next = elm->_next; + delete elm->_element; + delete elm; + elm = next; + } + } + + void pop(void) + { + if ( _head ) + { + QueElement* head = _head; + if ( _head == _tail ) + { + _head = _tail = 0; + } + else + { + _head = head->_next; + head->_prev = 0; + } + delete head; + _cnt--; + } + } + + T* front(void) + { + { + if ( _head ) + { + return _head->_element; + } + else + { + return 0; + } + } + } + + int post(T* t) + { + QueElement* elm = new QueElement(t); + if ( _head ) + { + if ( _tail == _head ) + { + elm->_prev = _tail; + _tail = elm; + } + else + { + _tail->_next = elm; + elm->_prev = _tail; + _tail = elm; + } + } + else + { + _head = elm; + _tail = elm; + } + _cnt++; + return _cnt; + } + + int size(void) + { + return _cnt; + } + +private: + int _cnt; + QueElement* _head; + QueElement* _tail; +}; + + +extern Process* theProcess; +extern MultiTaskProcess* theMultiTaskProcess; + +} +#endif /* MQTTSNGWPROCESS_H_ */ diff --git a/MQTTSNGateway/src/MQTTSNGWPublishHandler.cpp b/MQTTSNGateway/src/MQTTSNGWPublishHandler.cpp new file mode 100644 index 0000000..7029fce --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWPublishHandler.cpp @@ -0,0 +1,195 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * 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: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ + +#include "MQTTSNGWPublishHandler.h" +#include "MQTTSNGWPacket.h" +#include "MQTTGWPacket.h" +#include "MQTTSNGateway.h" +#include "MQTTSNGWClient.h" + +using namespace std; +using namespace MQTTSNGW; + +MQTTSNPublishHandler::MQTTSNPublishHandler(Gateway* gateway) +{ + _gateway = gateway; +} + +MQTTSNPublishHandler::~MQTTSNPublishHandler() +{ + +} + +void MQTTSNPublishHandler::handlePublish(Client* client, MQTTSNPacket* packet) +{ + uint8_t dup; + int qos; + uint8_t retained; + uint16_t msgId; + MQTTSN_topicid topicid; + uint8_t* payload; + int payloadlen; + Publish pub; + char shortTopic[2]; + + if ( !client->isActive() ) + { + /* Reply DISCONNECT to the client */ + WRITELOG(" The client is not active. status = %s\n", client->getStatus()); + Event* ev = new Event(); + MQTTSNPacket* disconnect = new MQTTSNPacket(); + disconnect->setDISCONNECT(0); + ev->setClientSendEvent(client, disconnect); + _gateway->getClientSendQue()->post(ev); + return; + } + + packet->getPUBLISH(&dup, &qos, &retained, &msgId, &topicid, &payload, &payloadlen); + pub.msgId = msgId; + pub.header.bits.dup = dup; + pub.header.bits.qos = qos; + pub.header.bits.retain = retained; + + Topic* topic = 0; + + if ( topicid.type == MQTTSN_TOPIC_TYPE_PREDEFINED) + { + /* + * ToDo: PUBLISH predefined Topic procedures. + */ + + if(msgId) + { + /* Reply PubAck to the client */ + MQTTSNPacket* pubAck = new MQTTSNPacket(); + pubAck->setPUBACK( topicid.data.id, msgId, MQTTSN_RC_ACCEPTED); + Event* ev1 = new Event(); + ev1->setClientSendEvent(client, pubAck); + _gateway->getClientSendQue()->post(ev1); + } + return; + } + + if( topicid.type == MQTTSN_TOPIC_TYPE_SHORT ) + { + shortTopic[0] = topicid.data.short_name[0]; + shortTopic[1] = topicid.data.short_name[1]; + pub.topic = shortTopic; + pub.topiclen = 2; + } + + if ( topicid.type == MQTTSN_TOPIC_TYPE_NORMAL ) + { + topic = client->getTopics()->getTopic(topicid.data.id); + if( !topic && msgId && qos > 0 ) + { + /* Reply PubAck of INVALID_TOPIC_ID to the client */ + MQTTSNPacket* pubAck = new MQTTSNPacket(); + pubAck->setPUBACK( topicid.data.id, msgId, MQTTSN_RC_REJECTED_INVALID_TOPIC_ID); + Event* ev1 = new Event(); + ev1->setClientSendEvent(client, pubAck); + _gateway->getClientSendQue()->post(ev1); + return; + } + if ( topic ) + { + pub.topic = (char*)topic->getTopicName()->data(); + pub.topiclen = topic->getTopicName()->length(); + } + } + /* Save a msgId & a TopicId pare for PUBACK */ + if( msgId && qos > 0 ) + { + client->setWaitedPubTopicId(msgId, topicid.data.id, topicid.type); + } + + pub.payload = (char*)payload; + pub.payloadlen = payloadlen; + + MQTTGWPacket* pulish = new MQTTGWPacket(); + pulish->setPUBLISH(&pub); + Event* ev1 = new Event(); + ev1->setBrokerSendEvent(client, pulish); + _gateway->getBrokerSendQue()->post(ev1); +} + +void MQTTSNPublishHandler::handlePuback(Client* client, MQTTSNPacket* packet) +{ + uint16_t topicId; + uint16_t msgId; + uint8_t rc; + + if ( !client->isActive() ) + { + return; + } + MQTTGWPacket* pubAck = new MQTTGWPacket(); + packet->getPUBACK(&topicId, &msgId, &rc); + if ( rc == MQTTSN_RC_ACCEPTED) + { + pubAck->setAck(PUBACK, msgId); + Event* ev1 = new Event(); + ev1->setBrokerSendEvent(client, pubAck); + _gateway->getBrokerSendQue()->post(ev1); + } + else if ( rc == MQTTSN_RC_REJECTED_INVALID_TOPIC_ID) + { + WRITELOG(" PUBACK %d : Invalid Topic ID\n", msgId); + } +} + +void MQTTSNPublishHandler::handleAck(Client* client, MQTTSNPacket* packet, uint8_t packetType) +{ + uint16_t msgId; + + if ( !client->isActive() ) + { + return; + } + packet->getACK(&msgId); + MQTTGWPacket* ackPacket = new MQTTGWPacket(); + ackPacket->setAck(packetType, msgId); + Event* ev1 = new Event(); + ev1->setBrokerSendEvent(client, ackPacket); + _gateway->getBrokerSendQue()->post(ev1); +} + +void MQTTSNPublishHandler::handleRegister(Client* client, MQTTSNPacket* packet) +{ + uint16_t id; + uint16_t msgId; + MQTTSNString topicName; + MQTTSN_topicid topicid; + + + if ( !client->isActive() ) + { + return; + } + MQTTSNPacket* regAck = new MQTTSNPacket(); + packet->getREGISTER(&id, &msgId, &topicName); + + topicid.type = MQTTSN_TOPIC_TYPE_NORMAL; + topicid.data.long_.len = topicName.lenstring.len; + topicid.data.long_.name = topicName.lenstring.data; + + id = client->getTopics()->add(&topicid)->getTopicId(); + regAck->setREGACK(id, msgId, MQTTSN_RC_ACCEPTED); + Event* ev = new Event(); + ev->setClientSendEvent(client, regAck); + _gateway->getClientSendQue()->post(ev); + +} diff --git a/MQTTSNGateway/src/MQTTSNGWPublishHandler.h b/MQTTSNGateway/src/MQTTSNGWPublishHandler.h new file mode 100644 index 0000000..7213cbe --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWPublishHandler.h @@ -0,0 +1,40 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * 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: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ +#ifndef MQTTSNGWPUCLISHHANDLER_H_ +#define MQTTSNGWPUCLISHHANDLER_H_ + +#include "MQTTGWPacket.h" +#include "MQTTSNGateway.h" +#include "MQTTSNGWClient.h" + +namespace MQTTSNGW +{ +class MQTTSNPublishHandler +{ +public: + MQTTSNPublishHandler(Gateway* gateway); + ~MQTTSNPublishHandler(); + void handlePublish(Client* client, MQTTSNPacket* packet); + void handlePuback(Client* client, MQTTSNPacket* packet); + void handleAck(Client* client, MQTTSNPacket* packet, uint8_t packetType); + void handleRegister(Client* client, MQTTSNPacket* packet); + +private: + Gateway* _gateway; +}; + +} +#endif /* MQTTSNGWPUCLISHHANDLER_H_ */ diff --git a/MQTTSNGateway/src/MQTTSNGWSubscribeHandler.cpp b/MQTTSNGateway/src/MQTTSNGWSubscribeHandler.cpp new file mode 100644 index 0000000..6dcfb98 --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWSubscribeHandler.cpp @@ -0,0 +1,192 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * 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: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ + +#include "MQTTSNGWSubscribeHandler.h" +#include "MQTTSNGWPacket.h" +#include "MQTTGWPacket.h" +#include "MQTTSNGateway.h" +#include "MQTTSNGWClient.h" + +using namespace std; +using namespace MQTTSNGW; + +MQTTSNSubscribeHandler::MQTTSNSubscribeHandler(Gateway* gateway) +{ + _gateway = gateway; +} + +MQTTSNSubscribeHandler::~MQTTSNSubscribeHandler() +{ + +} + +void MQTTSNSubscribeHandler::handleSubscribe(Client* client, MQTTSNPacket* packet) +{ + MQTTGWPacket* subscribe = new MQTTGWPacket(); + uint8_t dup; + int qos; + uint16_t msgId; + MQTTSN_topicid topicFilter; + Topic* topic = 0; + + packet->getSUBSCRIBE(&dup, &qos, &msgId, &topicFilter); + + if (topicFilter.type <= MQTTSN_TOPIC_TYPE_SHORT) + { + if (topicFilter.type == MQTTSN_TOPIC_TYPE_PREDEFINED) + { + /*----- Predefined TopicId ------*/ + MQTTSNPacket* sSuback = new MQTTSNPacket(); + + if (msgId) + { + int rc = MQTTSN_RC_ACCEPTED; + + switch (topicFilter.data.id) + { + case 1: // check topicIds are defined. + case 2: + break; + default: + rc = MQTTSN_RC_REJECTED_INVALID_TOPIC_ID; + } + sSuback->setSUBACK(qos, topicFilter.data.id, msgId, rc); + Event* evsuback = new Event(); + evsuback->setClientSendEvent(client, sSuback); + _gateway->getClientSendQue()->post(evsuback); + } + switch (topicFilter.data.id) + { + case 1: + /* + * ToDo: write here Predefined Topic 01 Procedures. + */ + break; + case 2: + /* + * ToDo: write here Predefined Topic 02 Procedures. so on + */ + break; + default: + break; + } + } + else + { + topic = client->getTopics()->getTopic(&topicFilter); + if (topic == 0) + { + if (topicFilter.type == MQTTSN_TOPIC_TYPE_NORMAL) + { + topic = client->getTopics()->add(&topicFilter); + subscribe->setSUBSCRIBE((char*)topic->getTopicName()->c_str(), (uint8_t)qos, (uint16_t)msgId); + } + else if (topicFilter.type == MQTTSN_TOPIC_TYPE_SHORT) + { + char topic[3]; + topic[0] = topicFilter.data.short_name[0]; + topic[1] = topicFilter.data.short_name[1]; + topic[2] = 0; + subscribe->setSUBSCRIBE(topic, (uint8_t)qos, (uint16_t)msgId); + } + } + else + { + subscribe->setSUBSCRIBE((char*)topic->getTopicName()->c_str(), (uint8_t)qos, (uint16_t)msgId); + } + + if ( msgId > 0 ) + { + client->setWaitedSubTopicId(msgId, topic->getTopicId(), topicFilter.type); + } + + Event* ev1 = new Event(); + ev1->setBrokerSendEvent(client, subscribe); + _gateway->getBrokerSendQue()->post(ev1); + return; + } + } + else + { + /*-- Invalid TopicIdType --*/ + if (msgId) + { + MQTTSNPacket* sSuback = new MQTTSNPacket(); + sSuback->setSUBACK(qos, topicFilter.data.id, msgId, MQTTSN_RC_REJECTED_INVALID_TOPIC_ID); + Event* evsuback = new Event(); + evsuback->setClientSendEvent(client, sSuback); + _gateway->getClientSendQue()->post(evsuback); + } + } +} +void MQTTSNSubscribeHandler::handleUnsubscribe(Client* client, MQTTSNPacket* packet) +{ + uint16_t msgId; + MQTTSN_topicid topicFilter; + + packet->getUNSUBSCRIBE(&msgId, &topicFilter); + + if ( topicFilter.type == MQTTSN_TOPIC_TYPE_PREDEFINED ) + { + /* + * ToDo: procedures for Predefined Topic + */ + return; + } + + Topic* topic = client->getTopics()->getTopic(&topicFilter); + MQTTGWPacket* unsubscribe = new MQTTGWPacket(); + + if (topicFilter.type == MQTTSN_TOPIC_TYPE_NORMAL) + { + if ( topic == 0 ) + { + if (msgId) + { + MQTTSNPacket* sUnsuback = new MQTTSNPacket(); + sUnsuback->setUNSUBACK(msgId); + Event* evsuback = new Event(); + evsuback->setClientSendEvent(client, sUnsuback); + _gateway->getClientSendQue()->post(evsuback); + delete unsubscribe; + return; + } + } + else + { + unsubscribe->setUNSUBSCRIBE(topic->getTopicName()->c_str(), msgId); + } + } + else if (topicFilter.type == MQTTSN_TOPIC_TYPE_SHORT) + { + MQTTGWPacket* unsubscribe = new MQTTGWPacket(); + char shortTopic[3]; + shortTopic[0] = topicFilter.data.short_name[0]; + shortTopic[1] = topicFilter.data.short_name[1]; + shortTopic[2] = 0; + unsubscribe->setUNSUBSCRIBE(shortTopic, msgId); + } + else + { + delete unsubscribe; + return; + } + + Event* ev1 = new Event(); + ev1->setBrokerSendEvent(client, unsubscribe); + _gateway->getBrokerSendQue()->post(ev1); +} + diff --git a/MQTTSNGateway/src/MQTTSNGWSubscribeHandler.h b/MQTTSNGateway/src/MQTTSNGWSubscribeHandler.h new file mode 100644 index 0000000..21410d5 --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGWSubscribeHandler.h @@ -0,0 +1,44 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * 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: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ +#ifndef MQTTSNGWSUBSCRIBEHANDLER_H_ +#define MQTTSNGWSUBSCRIBEHANDLER_H_ + +#include "MQTTSNGWDefines.h" +#include "MQTTSNGateway.h" +#include "MQTTSNGWPacket.h" +#include "MQTTSNGWClient.h" + +namespace MQTTSNGW +{ +/*===================================== + Class MQTTSNSubscribeHandler + =====================================*/ +class MQTTSNSubscribeHandler +{ +public: + MQTTSNSubscribeHandler(Gateway* gateway); + ~MQTTSNSubscribeHandler(); + void handleSubscribe(Client* client, MQTTSNPacket* packet); + void handleUnsubscribe(Client* client, MQTTSNPacket* packet); + +private: + Gateway* _gateway; +}; + +} + + +#endif /* MQTTSNGWSUBSCRIBEHANDLER_H_ */ diff --git a/MQTTSNGateway/src/MQTTSNGateway.cpp b/MQTTSNGateway/src/MQTTSNGateway.cpp new file mode 100644 index 0000000..9dd10ee --- /dev/null +++ b/MQTTSNGateway/src/MQTTSNGateway.cpp @@ -0,0 +1,328 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * 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: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ + +#include "MQTTSNGateway.h" +#include "MQTTSNGWProcess.h" +#include "SensorNetwork.h" + +using namespace MQTTSNGW; + +char* currentDateTime(void); + +/*===================================== + Class Gateway + =====================================*/ +Gateway::Gateway() : + MultiTaskProcess() +{ + theMultiTaskProcess = this; + theProcess = this; + resetRingBuffer(); + _params.loginId = 0; + _params.password = 0; +} + +Gateway::~Gateway() +{ + if ( _params.loginId ) + { + free(_params.loginId); + } + if ( _params.password ) + { + free(_params.password); + } +} + +void Gateway::initialize(int argc, char** argv) +{ + char param[MQTTSNGW_PARAM_MAX]; + + _params.gatewayId = 0; + if (getParam("GatewayID", param) == 0) + { + _params.gatewayId = atoi(param); + } + + if (_params.gatewayId == 0 || _params.gatewayId > 255) + { + throw Exception( "Gateway::initialize: invalid Gateway Id"); + } + + _params.mqttVersion = DEFAULT_MQTT_VERSION; + if (getParam("MQTTVersion", param) == 0) + { + _params.mqttVersion = atoi(param); + } + + _params.maxInflightMsgs = DEFAULT_MQTT_VERSION; + if (getParam("MaxInflightMsgs", param) == 0) + { + _params.maxInflightMsgs = atoi(param); + } + + _params.keepAlive = DEFAULT_KEEP_ALIVE_TIME; + + if (getParam("KeepAlive", param) == 0) + { + _params.keepAlive = atoi(param); + } + + if (_params.keepAlive > 65536) + { + throw Exception("Gateway::initialize: KeepAliveTime is grater than 65536 Secs"); + } + + if (getParam("LoginID", param) == 0) + { + _params.loginId = (uint8_t*) strdup(param); + } + + if (getParam("Password", param) == 0) + { + _params.password = (uint8_t*) strdup(param); + } + + if (getParam("ClientAuthorization", param) == 0) + { + if (!strcasecmp(param, "YES")) + { + if (!_clientList.authorize(MQTTSNGW_CLIENT_LIST)) + { + throw Exception("Gateway::initialize: can't authorize clients."); + } + } + } + + MultiTaskProcess::initialize(argc, argv); +} + +void Gateway::run(void) +{ + _lightIndicator.redLight(true); + WRITELOG("%s MQTT-SN Gateway has been started. %s %s\n", currentDateTime(), _sensorNetwork.getType(), GATEWAY_VERSION); + if ( getClientList()->isAuthorized() ) + { + WRITELOG("\n Client authentication is required by the configuration settings.\n"); + } + + /* execute threads & wait StopProcessEvent MQTTSNGWPacketHandleTask posts by CTL-C */ + MultiTaskProcess::run(); + WRITELOG("%s MQTT-SN Gateway stoped\n", currentDateTime()); + _lightIndicator.allLightOff(); +} + +EventQue* Gateway::getPacketEventQue() +{ + return &_packetEventQue; +} + +EventQue* Gateway::getClientSendQue() +{ + return &_clientSendQue; +} + +EventQue* Gateway::getBrokerSendQue() +{ + return &_brokerSendQue; +} + +ClientList* Gateway::getClientList() +{ + return &_clientList; +} + +SensorNetwork* Gateway::getSensorNetwork() +{ + return &_sensorNetwork; +} + +LightIndicator* Gateway::getLightIndicator() +{ + return &_lightIndicator; +} + +GatewayParams* Gateway::getGWParams(void) +{ + return &_params; +} + +/*===================================== + Class EventQue + =====================================*/ +EventQue::EventQue() +{ + +} + +EventQue::~EventQue() +{ + +} + +Event* EventQue::wait(void) +{ + Event* ev; + while ( true ) + { + _sem.wait(); + _mutex.lock(); + ev = _que.front(); + _que.pop(); + _mutex.unlock(); + if ( ev ) + { + return ev; + } + } +} + +Event* EventQue::timedwait(uint16_t millsec) +{ + Event* ev; + _sem.timedwait(millsec); + _mutex.lock(); + + if (_que.size() == 0) + { + ev = new Event(); + ev->setTimeout(); + } + else + { + ev = _que.front(); + _que.pop(); + if ( !ev ) + { + ev = new Event(); + ev->setTimeout(); + } + } + _mutex.unlock(); + return ev; +} + +int EventQue::post(Event* ev) +{ + if ( ev ) + { + _mutex.lock(); + _que.post(ev); + _sem.post(); + _mutex.unlock(); + } + return 0; +} + +int EventQue::size() +{ + _mutex.lock(); + int sz = _que.size(); + _mutex.unlock(); + return sz; +} + + +/*===================================== + Class Event + =====================================*/ +Event::Event() +{ + _eventType = Et_NA; + _client = 0; + _mqttSNPacket = 0; + _mqttGWPacket = 0; +} + +Event::Event(EventType type) +{ + _eventType = type; + _client = 0; + _mqttSNPacket = 0; + _mqttGWPacket = 0; +} + +Event::~Event() +{ + if (_mqttSNPacket) + { + delete _mqttSNPacket; + } + + if (_mqttGWPacket) + { + delete _mqttGWPacket; + } +} + +EventType Event::getEventType() +{ + return _eventType; +} + +void Event::setClientSendEvent(Client* client, MQTTSNPacket* packet) +{ + _client = client; + _eventType = EtClientSend; + _mqttSNPacket = packet; +} + +void Event::setBrokerSendEvent(Client* client, MQTTGWPacket* packet) +{ + _client = client; + _eventType = EtBrokerSend; + _mqttGWPacket = packet; +} + +void Event::setClientRecvEvent(Client* client, MQTTSNPacket* packet) +{ + _client = client; + _eventType = EtClientRecv; + _mqttSNPacket = packet; +} + +void Event::setBrokerRecvEvent(Client* client, MQTTGWPacket* packet) +{ + _client = client; + _eventType = EtBrokerRecv; + _mqttGWPacket = packet; +} + +void Event::setTimeout(void) +{ + _eventType = EtTimeout; +} + +void Event::setBrodcastEvent(MQTTSNPacket* msg) +{ + _mqttSNPacket = msg; + _eventType = EtBroadcast; +} + +Client* Event::getClient(void) +{ + return _client; +} + +MQTTSNPacket* Event::getMQTTSNPacket() +{ + return _mqttSNPacket; +} + +MQTTGWPacket* Event::getMQTTGWPacket(void) +{ + return _mqttGWPacket; +} diff --git a/MQTTSNGateway/src/MQTTSNGateway.h b/MQTTSNGateway/src/MQTTSNGateway.h index 94ffa59..8115a65 100644 --- a/MQTTSNGateway/src/MQTTSNGateway.h +++ b/MQTTSNGateway/src/MQTTSNGateway.h @@ -1,5 +1,5 @@ -/******************************************************************************* - * Copyright (c) 2014, 2016 IBM Corp. +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 @@ -11,286 +11,168 @@ * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: - * Ian Craggs - initial API and implementation and/or initial documentation - *******************************************************************************/ - -#if !defined(MQTTSNGATEWAY_H) -#define MQTTSNGATEWAY_H + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ +#ifndef MQTTSNGATEWAY_H_ +#define MQTTSNGATEWAY_H_ +#include "MQTTSNGWProcess.h" +#include "MQTTSNGWClient.h" #include "MQTTSNPacket.h" -#include "MQTTConnection.h" +#include "MQTTGWPacket.h" -namespace MQTTSN +namespace MQTTSNGW { - -#define MAX_PACKET_SIZE 256 -#define MQTTSNGATEWAY_QOS2 0 -#define MQTTSNGATEWAY_QOS1 0 -#define MAX_MQTT_CONNECTIONS 5 - -struct Parms -{ - char* hostname; - int port; - char* username; - char* password; -}; -enum QoS { QOS0, QOS1, QOS2 }; +/*========================================================== + * Log Formats + ===========================================================*/ +#define BROKER "Broker" +#define GATEWAY "Gateway" +#define CLIENT "Client" +#define CLIENTS "Clients" +#define LEFTARROW "<---" +#define RIGHTARROW "--->" -enum Modes { AGGREGATING, TRANSPARENT }; -// all failure return codes must be negative -enum returnCode { MAX_SUBSCRIPTIONS_EXCEEDED = -3, BUFFER_OVERFLOW = -2, FAILURE = -1, SUCCESS = 0 }; +#define FORMAT_WHITE_NL "%s %-18s%-6s%-26s%s\n" +#define FORMAT_WH_NL "\n%s %-18s%-6s%-26s%s\n" +#define FORMAT_WH_MSGID "%s %-11s%-5s %-6s%-26s%s\n" +#define FORMAT_WH_MSGID_NL "\n%s %-11s%-5s %-6s%-26s%s\n" +#define FORMAT_WH_GR "%s %-18s%-6s\x1b[0m\x1b[32m%-26s\x1b[0m\x1b[37m%s\n" +#define FORMAT_WH_GR_MSGID "%s %-11s%-5s %-6s\x1b[0m\x1b[32m%-26s\x1b[0m\x1b[37m%s\n" +#define FORMAT_WH_GR_MSGID_NL "\n%s %-11s%-5s %-6s\x1b[0m\x1b[32m%-26s\x1b[0m\x1b[37m%s\n" -struct Message -{ - enum QoS qos; - bool retained; - bool dup; - unsigned short id; - void *payload; - size_t payloadlen; +#define FORMAT_GR "%s \x1b[0m\x1b[32m%-18s%-6s\x1b[0m\x1b[37m%-26s%s\n" +#define FORMAT_GR_NL "\n%s \x1b[0m\x1b[32m%-18s%-6s\x1b[0m\x1b[37m%-26s%s\n" +#define FORMAT_GR_MSGID "%s \x1b[0m\x1b[32m%-11s%-5s %-6s%-26s\x1b[0m\x1b[37m%s\n" +#define FORMAT_GR_MSGID_NL "\n%s \x1b[0m\x1b[32m%-11s%-5s %-6s%-26s\x1b[0m\x1b[37m%s\n" +#define FORMAT_GR_WH_MSGID "%s \x1b[0m\x1b[32m%-11s%-5s %-6s\x1b[0m\x1b[37m%-26s%s\n" +#define FORMAT_GR_WH_MSGID_NL "\n%s \x1b[0m\x1b[32m%-11s%-5s %-6s\x1b[0m\x1b[37m%-26s%s\n" + +#define FORMAT_YE "%s \x1b[0m\x1b[33m%-18s%-6s%-44s\x1b[0m\x1b[37m%s\n" +#define FORMAT_YE_NL "\n%s \x1b[0m\x1b[33m%-18s%-6s%-26s\x1b[0m\x1b[37m%s\n" +#define FORMAT_YE_WH "%s \x1b[0m\x1b[33m%-18s%-6s\x1b[0m\x1b[37m%-26s\x1b[0m\x1b[37m%s\n" +#define FORMAT_YE_WH_NL "\n%s \x1b[0m\x1b[33m%-18s%-6s\x1b[0m\x1b[37m%-26s\x1b[0m\x1b[37m%s\n" +#define FORMAT_YE_GR "%s \x1b[0m\x1b[33m%-18s%-6s\x1b[0m\x1b[32m%-26s\x1b[0m\x1b[37m%s\n" +#define FORMAT_YE_GR_MSGID "%s \x1b[0m\x1b[33m%-11s%-5s %-6s\x1b[0m\x1b[32m%-26s\x1b[0m\x1b[37m%s\n" + +#define FORMAT_CY_ANY "%s \x1b[0m\x1b[36m%-18s%-6s%-44s\x1b[0m\x1b[37m%s\n" +#define FORMAT_CY "%s \x1b[0m\x1b[36m%-18s%-6s%-26s\x1b[0m\x1b[37m%s\n" +#define FORMAT_CY_NL "\n%s \x1b[0m\x1b[36m%-18s%-6s%-26s\x1b[0m\x1b[37m%s\n" + +#define FORMAT_BL_NL "\n%s \x1b[0m\x1b[34m%-18s%-6s%-26s\x1b[0m\x1b[37m%s\n" +#define FORMAT_RED "%s \x1b[0m\x1b[31m%-18s%-6s%-44s\x1b[0m\x1b[37m%s\n" +#define FORMAT_RED_NL "\n%s \x1b[0m\x1b[31m%-18s%-6s%-26s\x1b[0m\x1b[37m%s\n" +#define ERRMSG_HEADER "\x1b[0m\x1b[31mError:" +#define ERRMSG_FOOTER "\x1b[0m\x1b[37m" + +/*========================================================== + * Gateway default parameters + ===========================================================*/ +#define DEFAULT_KEEP_ALIVE_TIME (900) // 900 secs = 15 mins +#define DEFAULT_MAX_CLIENTS (100) // Number of Clients can be handled. +#define DEFAULT_MQTT_VERSION (4) // Defualt MQTT version +#define DEFAULT_INFLIGHTMESSAGE (10) // Number of inflight messages + +/*===================================== + Class Event + ====================================*/ +enum EventType{ + Et_NA = 0, + EtTimeout, + EtBrokerRecv, + EtBrokerSend, + EtClientRecv, + EtClientSend, + EtBroadcast, + EtSocketAlive }; -template -class Gateway +class Event{ +public: + Event(); + Event(EventType); + ~Event(); + EventType getEventType(void); + void setClientRecvEvent(Client*, MQTTSNPacket*); + void setClientSendEvent(Client*, MQTTSNPacket*); + void setBrokerRecvEvent(Client*, MQTTGWPacket*); + void setBrokerSendEvent(Client*, MQTTGWPacket*); + void setBrodcastEvent(MQTTSNPacket*); // ADVERTISE and GWINFO + void setTimeout(void); // Required by EventQue.timedwait() + Client* getClient(void); + MQTTSNPacket* getMQTTSNPacket(void); + MQTTGWPacket* getMQTTGWPacket(void); + +private: + EventType _eventType; + Client* _client; + MQTTSNPacket* _mqttSNPacket; + MQTTGWPacket* _mqttGWPacket; +}; + +/*===================================== + Class EventQue + ====================================*/ +class EventQue { public: + EventQue(); + ~EventQue(); + Event* wait(void); + Event* timedwait(uint16_t millsec); + int post(Event*); + int size(); - Gateway(UDPNetwork&, unsigned int command_timeout_ms = 30000); - - void run(void const* arg); - private: - - int cycle(Timer& timer); - int sendPacket(int length, Timer& timer); - int readPacket(Timer& timer); - - UDPNetwork& udpstack; // Not restricted to UDP - for want of a better name - - unsigned char sendbuf[MAX_PACKET_SIZE]; - unsigned char readbuf[MAX_PACKET_SIZE]; - - enum Modes mode; - - MQTT::Connection connections[MAX_MQTT_CONNECTIONS]; - + Que _que; + Mutex _mutex; + Semaphore _sem; }; - -} // end namespace - - -template -MQTTSN::Gateway::Gateway(UDPNetwork& network, unsigned int command_timeout_ms) : udpstack(network) -{ - mode = AGGREGATING; -} - - -template -void MQTTSN::Gateway::run(void const* arg) -{ - Timer timer; - - printf("Gateway run 0\n"); - if (mode == AGGREGATING) - { - // set up connection 0 information - Thread mythread(&connections[0].run); - } - - printf("Gateway run 1\n"); - while (true) - { - printf("Gateway cycle\n"); - cycle(timer); - } - -} - - -template -int MQTTSN::Gateway::cycle(Timer& timer) -{ - /* get one piece of work off the wire and one pass through */ - - // read the socket, see what work is due - unsigned short packet_type = readPacket(timer); - - printf("read packet\n"); - - int len = 0; - int rc = SUCCESS; - - switch (packet_type) - { - case MQTTSN_CONNECT: - { - MQTTSNPacket_connectData data = MQTTSNPacket_connectData_initializer; - MQTTSNDeserialize_connect(&data, readbuf, MAX_PACKET_SIZE); - - if (mode == TRANSPARENT) - { - //start a new MQTT connection at this point - } - - len = MQTTSNSerialize_connack(sendbuf, MAX_PACKET_SIZE, 0); - if (len <= 0) - rc = FAILURE; - else - rc = sendPacket(len, timer); - break; - } - - case MQTTSN_REGISTER: - { - unsigned short topicid, packetid; - MQTTSNString topicName; - unsigned char reg_rc = MQTTSN_RC_ACCEPTED; - if (MQTTSNDeserialize_register(&topicid, &packetid, &topicName, readbuf, MAX_PACKET_SIZE) != 1) - goto exit; - - // store topic registration info - - len = MQTTSNSerialize_regack(connections[0].sendbuf, MAX_PACKET_SIZE, topicid, packetid, reg_rc); - if (len <= 0) - rc = FAILURE; - else - rc = connections[0].sendPacket(len, timer); - - break; - } - - case MQTTSN_SUBSCRIBE: - { - unsigned char dup; - int qos; - unsigned short packetid; - MQTTSN_topicid topicFilter; - MQTTSNDeserialize_subscribe(&dup, &qos, &packetid, &topicFilter, readbuf, MAX_PACKET_SIZE); - MQTTString topic = MQTTString_initializer; - - if (topicFilter.type == MQTTSN_TOPIC_TYPE_NORMAL) - { - topic.lenstring.len = topicFilter.data.long_.len; - topic.lenstring.data = topicFilter.data.long_.name; - } - - len = MQTTSerialize_subscribe(connections[0].sendbuf, MAX_PACKET_SIZE, 0, packetid, 1, &topic, (int*)&qos); - if (len <= 0) - goto exit; - if ((rc = connections[0].sendPacket(len, timer)) != SUCCESS) // send the subscribe packet - goto exit; // there was a problem - - break; - } - - case MQTTSN_PUBLISH: - { - MQTTString topic = MQTTString_initializer; - MQTTSN_topicid topicid; - Message msg; - - if (MQTTSNDeserialize_publish((unsigned char*)&msg.dup, (int*)&msg.qos, (unsigned char*)&msg.retained, &msg.id, &topicid, - (unsigned char**)&msg.payload, (int*)&msg.payloadlen, readbuf, MAX_PACKET_SIZE) != 1) - goto exit; - - - len = MQTTSerialize_publish(connections[0].sendbuf, MAX_PACKET_SIZE, msg.dup, msg.qos, msg.retained, msg.id, topic, - (unsigned char*)msg.payload, msg.payloadlen); - if (len <= 0) - goto exit; - if ((rc = connections[0].sendPacket(len, timer)) != SUCCESS) // send the subscribe packet - goto exit; // there was a problem - - } - - - case MQTTSN_PINGRESP: - //ping_outstanding = false; - break; - } - //keepalive(); -exit: - if (rc == SUCCESS) - rc = packet_type; - return rc; -} - - - -template -int MQTTSN::Gateway::sendPacket(int length, Timer& timer) -{ - int rc = FAILURE, - sent = 0; - - do - { - sent = udpstack.write(sendbuf, length, timer.left_ms()); - printf("sendPacket, rc %d from write of %d bytes\n", sent, length); - if (sent < 0) // there was an error writing the data - break; - } - while (sent != length && !timer.expired()); - - if (sent == length) - { - //if (this->duration > 0) - // last_sent.countdown(this->duration); // record the fact that we have successfully sent the packet - rc = SUCCESS; - } - else - rc = FAILURE; - -#if defined(MQTT_DEBUG) - char printbuf[50]; - DEBUG("Rc %d from sending packet %s\n", rc, MQTTSNPacket_toString(printbuf, sizeof(printbuf), sendbuf, length)); -#endif - return rc; -} - - -/** - * If any read fails in this method, then we should disconnect from the network, as on reconnect - * the packets can be retried. - * @param timeout the max time to wait for the packet read to complete, in milliseconds - * @return the MQTT packet type, or -1 if none +/* + * GatewayParams */ -template -int MQTTSN::Gateway::readPacket(Timer& timer) +typedef struct { - int rc = FAILURE; - int len = 0; // the length of the whole packet including length field - int lenlen = 0; - int datalen = 0; + uint8_t* loginId; + uint8_t* password; + uint16_t keepAlive; + uint8_t gatewayId; + uint8_t mqttVersion; + uint16_t maxInflightMsgs; +}GatewayParams; + +/*===================================== + Class Gateway + =====================================*/ +class Gateway: public MultiTaskProcess{ +public: + Gateway(); + ~Gateway(); + virtual void initialize(int argc, char** argv); + void run(void); + + EventQue* getPacketEventQue(void); + EventQue* getClientSendQue(void); + EventQue* getBrokerSendQue(void); + ClientList* getClientList(void); + SensorNetwork* getSensorNetwork(void); + LightIndicator* getLightIndicator(void); + GatewayParams* getGWParams(void); + +private: + ClientList _clientList; + EventQue _packetEventQue; + EventQue _brokerSendQue; + EventQue _clientSendQue; + LightIndicator _lightIndicator; + GatewayParams _params; + SensorNetwork _sensorNetwork; +}; - #define MQTTSN_MIN_PACKET_LENGTH 3 - // 1. read the packet, datagram style - if ((len = udpstack.read(readbuf, MAX_PACKET_SIZE, timer.left_ms())) < MQTTSN_MIN_PACKET_LENGTH) - goto exit; - - // 2. read the length. This is variable in itself - lenlen = MQTTSNPacket_decode(readbuf, len, &datalen); - if (datalen != len) - goto exit; // there was an error - - rc = readbuf[lenlen]; - //if (this->duration > 0) - // last_received.countdown(this->duration); // record the fact that we have successfully received a packet -exit: - -#if defined(MQTT_DEBUG) - char printbuf[50]; - DEBUG("Rc %d from receiving packet %s\n", rc, MQTTSNPacket_toString(printbuf, sizeof(printbuf), readbuf, len)); -#endif - return rc; } - - -#endif +#endif /* MQTTSNGATEWAY_H_ */ diff --git a/MQTTSNGateway/src/linux/Network.cpp b/MQTTSNGateway/src/linux/Network.cpp new file mode 100644 index 0000000..cde1d02 --- /dev/null +++ b/MQTTSNGateway/src/linux/Network.cpp @@ -0,0 +1,563 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * 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: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Network.h" +#include "MQTTSNGWDefines.h" +#include "MQTTSNGWProcess.h" + +using namespace std; +using namespace MQTTSNGW; + +#define SOCKET_MAXCONNECTIONS 5 +char* currentDateTime(); + +/*======================================== + Class TCPStack + =======================================*/ +TCPStack::TCPStack() +{ + _addrinfo = 0; + _sockfd = 0; +} + +TCPStack::~TCPStack() +{ + if (_addrinfo) + { + freeaddrinfo(_addrinfo); + } +} + +bool TCPStack::isValid() +{ + return (_sockfd > 0); +} + +void TCPStack::close() +{ + if (_sockfd > 0) + { + ::close(_sockfd); + _sockfd = 0; + if (_addrinfo) + { + freeaddrinfo(_addrinfo); + _addrinfo = 0; + } + } + +} + +bool TCPStack::bind(const char* service) +{ + if (isValid()) + { + return false; + } + addrinfo hints; + memset(&hints, 0, sizeof(addrinfo)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; + + if (_addrinfo) + { + freeaddrinfo(_addrinfo); + } + int err = getaddrinfo(0, service, &hints, &_addrinfo); + if (err) + { + WRITELOG("\n%s \x1b[0m\x1b[31merror:\x1b[0m\x1b[37mgetaddrinfo(): %s\n", currentDateTime(), + gai_strerror(err)); + return false; + } + + _sockfd = socket(_addrinfo->ai_family, _addrinfo->ai_socktype, _addrinfo->ai_protocol); + if (_sockfd < 0) + { + return false; + } + int on = 1; + if (setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*) &on, sizeof(on)) == -1) + { + return false; + } + + if (::bind(_sockfd, _addrinfo->ai_addr, _addrinfo->ai_addrlen) < 0) + { + return false; + } + return true; +} + +bool TCPStack::listen() +{ + if (!isValid()) + { + return false; + } + int listen_return = ::listen(_sockfd, SOCKET_MAXCONNECTIONS); + if (listen_return == -1) + { + return false; + } + return true; +} + +bool TCPStack::accept(TCPStack& new_socket) +{ + sockaddr_storage sa; + socklen_t len = sizeof(sa); + new_socket._sockfd = ::accept(_sockfd, (struct sockaddr*) &sa, &len); + if (new_socket._sockfd <= 0) + { + return false; + } + else + { + return true; + } +} + +int TCPStack::send(const uint8_t* buf, int length) +{ + return ::send(_sockfd, buf, length, MSG_NOSIGNAL); +} + +int TCPStack::recv(uint8_t* buf, int len) +{ + return ::recv(_sockfd, buf, len, 0); +} + +bool TCPStack::connect(const char* host, const char* service) +{ + if (isValid()) + { + return false; + } + addrinfo hints; + memset(&hints, 0, sizeof(addrinfo)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + if (_addrinfo) + { + freeaddrinfo(_addrinfo); + } + + int err = getaddrinfo(host, service, &hints, &_addrinfo); + if (err) + { + WRITELOG("\n%s \x1b[0m\x1b[31merror:\x1b[0m\x1b[37mgetaddrinfo(): %s\n", currentDateTime(), + gai_strerror(err)); + return false; + } + + int sockfd = socket(_addrinfo->ai_family, _addrinfo->ai_socktype, _addrinfo->ai_protocol); + + if (sockfd < 0) + { + return false; + } + int on = 1; + + if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*) &on, sizeof(on)) == -1) + { + return false; + } + + if (::connect(sockfd, _addrinfo->ai_addr, _addrinfo->ai_addrlen) < 0) + { + //perror("TCPStack connect"); + ::close(sockfd); + return false; + } + + _sockfd = sockfd; + return true; +} + +void TCPStack::setNonBlocking(const bool b) +{ + int opts; + + opts = fcntl(_sockfd, F_GETFL); + + if (opts < 0) + { + return; + } + + if (b) + { + opts = (opts | O_NONBLOCK); + } + else + { + opts = (opts & ~O_NONBLOCK); + } + fcntl(_sockfd, F_SETFL, opts); +} + +int TCPStack::getSock() +{ + return _sockfd; +} + +/*======================================== + Class Network + =======================================*/ +int Network::_numOfInstance = 0; +SSL_CTX* Network::_ctx = 0; +SSL_SESSION* Network::_session = 0; + +Network::Network(bool secure) : + TCPStack() +{ + char error[256]; + if (secure) + { + _numOfInstance++; + if (_ctx == 0) + { + SSL_load_error_strings(); + SSL_library_init(); + _ctx = SSL_CTX_new(TLSv1_2_client_method()); + if (_ctx == 0) + { + ERR_error_string_n(ERR_get_error(), error, sizeof(error)); + WRITELOG("SSL_CTX_new() %s\n", error); + throw Exception( ERR_get_error(), "Network can't create SSL context."); + } + if (!SSL_CTX_load_verify_locations(_ctx, 0, MQTTSNGW_TLS_CA_DIR)) + { + ERR_error_string_n(ERR_get_error(), error, sizeof(error)); + WRITELOG("SSL_CTX_load_verify_locations() %s\n", error); + throw Exception( ERR_get_error(), "Network can't load CA_LIST."); + } + } + } + _ssl = 0; + _disconReq = false; + _secureFlg = secure; + _busy = false; +} + +Network::~Network() +{ + if (_secureFlg) + { + _numOfInstance--; + } + if (_ssl) + { + SSL_free(_ssl); + } + if (_session && _numOfInstance == 0) + { + SSL_SESSION_free(_session); + _session = 0; + } + if (_ctx && _numOfInstance == 0) + { + SSL_CTX_free(_ctx); + _ctx = 0; + ERR_free_strings(); + } +} + +bool Network::connect(const char* host, const char* service) +{ + char errmsg[256]; + int rc = 0; + char peer_CN[256]; + SSL_SESSION* sess = 0; + X509* peer; + + if (isValid()) + { + return false; + } + if (!TCPStack::connect(host, service)) + { + return false; + } + if (!_secureFlg) + { + return true; + } + + SSL* ssl = SSL_new(_ctx); + if (ssl == 0) + { + ERR_error_string_n(ERR_get_error(), errmsg, sizeof(errmsg)); + WRITELOG("SSL_new() %s\n", errmsg); + return false; + } + + rc = SSL_set_fd(ssl, TCPStack::getSock()); + if (rc == 0) + { + SSL_free(ssl); + return false; + } + + if (_session) + { + rc = SSL_set_session(ssl, sess); + } + else + { + rc = SSL_connect(ssl); + } + if (rc != 1) + { + ERR_error_string_n(ERR_get_error(), errmsg, sizeof(errmsg)); + WRITELOG("SSL_connect() %s\n", errmsg); + SSL_free(ssl); + return false; + } + + if (SSL_get_verify_result(ssl) != X509_V_OK) + { + WRITELOG("SSL_get_verify_result() error: Certificate doesn't verify.\n"); + SSL_free(ssl); + return false; + } + + peer = SSL_get_peer_certificate(ssl); + X509_NAME_get_text_by_NID(X509_get_subject_name(peer), NID_commonName, peer_CN, 256); + if (strcasecmp(peer_CN, host)) + { + WRITELOG("SSL_get_peer_certificate() error: Broker dosen't much host name.\n"); + SSL_free(ssl); + return false; + } + if (_session == 0) + { + _session = sess; + } + _ssl = ssl; + return true; +} + +int Network::send(const uint8_t* buf, uint16_t length) +{ + char errmsg[256]; + fd_set rset; + fd_set wset; + bool writeBlockedOnRead = false; + int bpos = 0; + + if (_secureFlg) + { + _mutex.lock(); + _busy = true; + + while (true) + { + FD_ZERO(&rset); + FD_ZERO(&wset); + FD_SET(getSock(), &rset); + FD_SET(getSock(), &wset); + + int activity = select(getSock() + 1, &rset, &wset, 0, 0); + if (activity > 0) + { + if (FD_ISSET(getSock(), &wset) || (writeBlockedOnRead && FD_ISSET(getSock(), &rset))) + { + + writeBlockedOnRead = false; + int r = SSL_write(_ssl, buf + bpos, length); + + switch (SSL_get_error(_ssl, r)) + { + case SSL_ERROR_NONE: + length -= r; + bpos += r; + if (length == 0) + { + _busy = false; + _mutex.unlock(); + return bpos; + } + break; + case SSL_ERROR_WANT_WRITE: + break; + case SSL_ERROR_WANT_READ: + writeBlockedOnRead = true; + break; + default: + ERR_error_string_n(ERR_get_error(), errmsg, sizeof(errmsg)); + WRITELOG("TLSStack::send() default %s\n", errmsg); + _busy = false; + _mutex.unlock(); + return -1; + } + } + } + } + } + else + { + return TCPStack::send(buf, length); + } +} + +int Network::recv(uint8_t* buf, uint16_t len) +{ + char errmsg[256]; + bool writeBlockedOnRead = false; + bool readBlockedOnWrite = false; + bool readBlocked = false; + int rlen = 0; + int bpos = 0; + fd_set rset; + fd_set wset; + + if (_secureFlg) + { + if (_busy) + { + return 0; + } + _mutex.lock(); + _busy = true; + + loop: do + { + readBlockedOnWrite = false; + readBlocked = false; + + rlen = SSL_read(_ssl, buf + bpos, len - bpos); + + switch (SSL_get_error(_ssl, rlen)) + { + case SSL_ERROR_NONE: + _busy = false; + _mutex.unlock(); + return rlen + bpos; + break; + case SSL_ERROR_ZERO_RETURN: + SSL_shutdown(_ssl); + _ssl = 0; + TCPStack::close(); + _busy = false; + _mutex.unlock(); + return -1; + break; + case SSL_ERROR_WANT_READ: + readBlocked = true; + break; + case SSL_ERROR_WANT_WRITE: + readBlockedOnWrite = true; + break; + default: + ERR_error_string_n(ERR_get_error(), errmsg, sizeof(errmsg)); + WRITELOG("TLSStack::recv() default %s\n", errmsg); + _busy = false; + _mutex.unlock(); + return -1; + } + } while (SSL_pending(_ssl) && !readBlocked); + + bpos += rlen; + while (true) + { + FD_ZERO(&rset); + FD_ZERO(&wset); + FD_SET(getSock(), &rset); + FD_SET(getSock(), &wset); + + int activity = select(getSock() + 1, &rset, &wset, 0, 0); + if (activity > 0) + { + if ((FD_ISSET(getSock(),&rset) && !writeBlockedOnRead) + || (readBlockedOnWrite && FD_ISSET(getSock(), &wset))) + { + goto loop; + } + } + else + { + ERR_error_string_n(ERR_get_error(), errmsg, sizeof(errmsg)); + WRITELOG("TLSStack::recv() select %s\n", errmsg); + _busy = false; + _mutex.unlock(); + return -1; + } + } + } + return TCPStack::recv(buf, len); +} + +bool Network::isValid() +{ + if (!_secureFlg) + { + return TCPStack::isValid(); + } + if (_ssl) + { + return true; + } + return false; +} + +void Network::disconnect() +{ + if (_ssl) + { + SSL_shutdown(_ssl); + _ssl = 0; + TCPStack::close(); + } + else + { + TCPStack::close(); + } +} + +int Network::getSock() +{ + return TCPStack::getSock(); +} + +SSL* Network::getSSL() +{ + if (_secureFlg) + { + return _ssl; + } + else + { + return 0; + } +} + +bool Network::isSecure() +{ + return _secureFlg; +} + diff --git a/MQTTSNGateway/src/linux/Network.h b/MQTTSNGateway/src/linux/Network.h new file mode 100644 index 0000000..5c6b79f --- /dev/null +++ b/MQTTSNGateway/src/linux/Network.h @@ -0,0 +1,95 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * 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: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ + +#ifndef NETWORK_H_ +#define NETWORK_H_ +#include +#include +#include +#include +#include +#include +#include + +#include "Threading.h" +#include "MQTTSNGWDefines.h" + +using namespace std; +using namespace MQTTSNGW; + +/*======================================== + Class TCPStack + =======================================*/ +class TCPStack +{ +public: + TCPStack(); + virtual ~TCPStack(); + + // Server initialization + bool bind(const char* service); + bool listen(); + bool accept(TCPStack&); + + // Client initialization + bool connect(const char* host, const char* service); + + int send(const uint8_t* buf, int length); + int recv(uint8_t* buf, int len); + void close(); + + void setNonBlocking(const bool); + + bool isValid(); + int getSock(); + +private: + int _sockfd; + addrinfo* _addrinfo; +}; + +/*======================================== + Class Network + =======================================*/ +class Network: public TCPStack +{ +public: + Network(bool secure); + virtual ~Network(); + + bool connect(const char* host, const char* service); + void disconnect(); + int send(const uint8_t* buf, uint16_t length); + int recv(uint8_t* buf, uint16_t len); + + bool isValid(); + bool isSecure(); + int getSock(); + SSL* getSSL(); + +private: + static SSL_CTX* _ctx; + static int _numOfInstance; + static SSL_SESSION* _session; + + SSL* _ssl; + bool _secureFlg; + bool _disconReq; + Mutex _mutex; + bool _busy; +}; + +#endif /* NETWORK_H_ */ diff --git a/MQTTSNGateway/src/linux/Threading.cpp b/MQTTSNGateway/src/linux/Threading.cpp new file mode 100644 index 0000000..4dee15a --- /dev/null +++ b/MQTTSNGateway/src/linux/Threading.cpp @@ -0,0 +1,502 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * 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: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ + +#include "MQTTSNGWProcess.h" +#include "Threading.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace MQTTSNGW; + +/*===================================== + Class Mutex + =====================================*/ + +Mutex::Mutex(void) +{ + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutex_init(&_mutex, &attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK); + _shmid = 0; + _pmutex = 0; +} + +Mutex::Mutex(const char* fileName) +{ + pthread_mutexattr_t attr; + + key_t key = ftok(fileName, 1); + + if ((_shmid = shmget(key, sizeof(pthread_mutex_t), IPC_CREAT | 0666)) < 0) + { + throw Exception( -1, "Mutex can't create a shared memory."); + } + _pmutex = (pthread_mutex_t*) shmat(_shmid, NULL, 0); + if (_pmutex < 0) + { + throw Exception( -1, "Mutex can't attach shared memory."); + } + + pthread_mutexattr_init(&attr); + + if (pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED) != 0) + { + throw Exception( -1, "Mutex can't set the process-shared flag"); + } + if (pthread_mutex_init(_pmutex, &attr) != 0) + { + throw Exception( -1, "Mutex can't initialize."); + } +} + +Mutex::~Mutex(void) +{ + if (_pmutex) + { + pthread_mutex_lock(_pmutex); + pthread_mutex_unlock(_pmutex); + pthread_mutex_destroy(_pmutex); + } + else + { + pthread_mutex_lock(&_mutex); + pthread_mutex_unlock(&_mutex); + pthread_mutex_destroy(&_mutex); + } + if (_shmid) + { + shmctl(_shmid, IPC_RMID, NULL); + } +} + +void Mutex::lock(void) +{ + if (_pmutex) + { + pthread_mutex_lock(_pmutex); + } + else + { + try + { + if (pthread_mutex_lock(&_mutex)) + { + throw; + } + } catch (char* errmsg) + { + throw Exception( -1, "The same thread can't aquire a mutex twice."); + } + } +} + +void Mutex::unlock(void) +{ + + if (_pmutex) + { + pthread_mutex_unlock(_pmutex); + } + else + { + try + { + if (pthread_mutex_unlock(&_mutex)) + { + throw; + } + } catch (char* errmsg) + { + throw Exception( -1, "Mutex can't unlock."); + } + } +} + +/*===================================== + Class Semaphore + =====================================*/ + +Semaphore::Semaphore() +{ + sem_init(&_sem, 0, 0); + _name = 0; + _psem = 0; +} + +Semaphore::Semaphore(unsigned int val) +{ + sem_init(&_sem, 0, val); + _name = 0; + _psem = 0; +} + +Semaphore::Semaphore(const char* name, unsigned int val) +{ + _psem = sem_open(name, O_CREAT, 0666, val); + if (_psem == SEM_FAILED) + { + throw Exception( -1, "Semaphore can't be created."); + } + _name = (char*) calloc(strlen(name + 1), 1); + if (_name == NULL) + { + throw Exception( -1, "Semaphore can't allocate memories."); + } + _name = strdup(name); +} + +Semaphore::~Semaphore() +{ + if (_name) + { + sem_close(_psem); + sem_unlink(_name); + free((void*) _name); + } + else + { + sem_destroy(&_sem); + } +} + +void Semaphore::post(void) +{ + int val = 0; + if (_psem) + { + sem_getvalue(_psem, &val); + if (val <= 0) + { + sem_post(_psem); + } + } + else + { + sem_getvalue(&_sem, &val); + if (val <= 0) + { + sem_post(&_sem); + } + } +} + +void Semaphore::wait(void) +{ + if (_psem) + { + sem_wait(_psem); + } + else + { + sem_wait(&_sem); + } +} + +void Semaphore::timedwait(uint16_t millsec) +{ + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + ts.tv_sec += millsec / 1000; + ts.tv_nsec = (millsec % 1000) * 1000000; + if (_psem) + { + sem_timedwait(_psem, &ts); + } + else + { + sem_timedwait(&_sem, &ts); + } +} + +/*========================================= + Class RingBuffer + =========================================*/ +RingBuffer::RingBuffer() +{ + key_t key = ftok(MQTTSNGW_RINGBUFFER_KEY, 1); + + if ((_shmid = shmget(key, PROCESS_LOG_BUFFER_SIZE, + IPC_CREAT | IPC_EXCL | 0666)) >= 0) + { + if ((_shmaddr = (uint16_t*) shmat(_shmid, NULL, 0)) > 0) + { + _length = (uint16_t*) _shmaddr; + _start = (uint16_t*) _length + sizeof(uint16_t*); + _end = (uint16_t*) _start + sizeof(uint16_t*); + _buffer = (char*) _end + sizeof(uint16_t*); + _createFlg = true; + + *_length = PROCESS_LOG_BUFFER_SIZE - sizeof(uint16_t*) * 3 - 16; + *_start = *_end = 0; + } + else + { + throw Exception(-1, "RingBuffer can't attach shared memory."); + } + } + else if ((_shmid = shmget(key, PROCESS_LOG_BUFFER_SIZE, IPC_CREAT | 0666)) >= 0) + { + if ((_shmaddr = (uint16_t*) shmat(_shmid, NULL, 0)) > 0) + { + _length = (uint16_t*) _shmaddr; + _start = (uint16_t*) _length + sizeof(uint16_t*); + _end = (uint16_t*) _start + sizeof(uint16_t*); + _buffer = (char*) _end + sizeof(uint16_t*); + _createFlg = false; + } + else + { + throw Exception(-1, "RingBuffer can't create a shared memory."); + } + } + else + { + throw Exception(-1, "RingBuffer can't create a shared memory."); + } + + _pmx = new Mutex(MQTTSNGW_RB_MUTEX_KEY); +} + +RingBuffer::~RingBuffer() +{ + if (_createFlg) + { + if (_shmid > 0) + { + shmctl(_shmid, IPC_RMID, NULL); + } + if (_pmx > 0) + { + delete _pmx; + } + } + else + { + if (_shmid > 0) + { + shmdt(_shmaddr); + } + } +} + +void RingBuffer::put(char* data) +{ + _pmx->lock(); + + uint16_t dlen = strlen(data); + uint16_t blen = *_length - *_end; + + if (*_end > *_start) + { + if (dlen < blen) + { + strncpy(_buffer + *_end, data, dlen); + if (*_end - *_start == 1) + { // Buffer is empty. + *_start = *_end; + } + *_end += dlen; + } + else + { + strncpy(_buffer + *_end, data, blen); + strncpy(_buffer, data + blen, dlen - blen); + if (*_end - *_start == 1) + { // Buffer is empty. + *_start = *_end; + *_end = dlen - blen; + } + else + { + *_end = dlen - blen; + *_start = *_end + 1; + } + } + } + else if (*_end == *_start) + { + if (dlen < blen) + { + strncpy(_buffer + *_end, data, dlen); + *_end += dlen; + } + else + { + const char* errmsg = "RingBuffer Error: data is too long"; + strcpy(_buffer + *_end, errmsg); + *_end += strlen(errmsg); + } + } + else + { // *_end < *_start + if (dlen < *_start - *_end) + { + strncpy(_buffer + *_end, data, dlen); + *_end += dlen; + *_start = *_end + 1; + } + else + { + if (dlen < blen) + { + strncpy(_buffer + *_end, data, dlen); + *_end += dlen; + *_start = *_end + 1; + } + else + { + strncpy(_buffer + *_end, data, blen); + strncpy(_buffer, data + blen, dlen - blen); + *_start = *_end; + *_end = dlen - blen; + } + } + } + _pmx->unlock(); +} + +int RingBuffer::get(char* buf, int length) +{ + int len = 0; + _pmx->lock(); + + if (*_end > *_start) + { + if (length > *_end - *_start) + { + len = *_end - *_start; + if (len == 1) + { + len = 0; + } + strncpy(buf, _buffer + *_start, len); + *_start = *_end - 1; + } + else + { + len = length; + strncpy(buf, _buffer + *_start, len); + *_start = *_start + len; + } + } + else if (*_end < *_start) + { + int blen = *_length - *_start; + if (length > blen) + { + strncpy(buf, _buffer + *_start, blen); + *_start = 0; + if (length - (blen + *_end) > 0) + { + strncpy(buf + blen, _buffer, *_end); + len = blen + *_end; + if (*_end > 0) + { + *_start = *_end - 1; + } + } + else + { + strncpy(buf + blen, _buffer, length - blen); + len = length; + *_start = length - blen; + } + } + else + { + strncpy(buf, _buffer + *_start, length); + *_start += length; + len = length; + } + } + _pmx->unlock(); + return len; +} + +void RingBuffer::reset() +{ + _pmx->lock(); + if ( _start && _end ) + { + *_start = *_end = 0; + } + else + { + throw Exception(-1, "RingBuffer can't reset. need to clear shared memory."); + } + _pmx->unlock(); +} + +/*===================================== + Class Thread + =====================================*/ +Thread::Thread() +{ + _stopProcessEvent = theMultiTaskProcess->getStopProcessEvent(); + _threadID = 0; +} + +Thread::~Thread() +{ + pthread_cancel(_threadID); + pthread_join(_threadID, 0); +} + +void* Thread::_run(void* runnable) +{ + static_cast(runnable)->EXECRUN(); + return 0; +} + +void Thread::initialize(int argc, char** argv) +{ + +} + +pthread_t Thread::getID() +{ + return pthread_self(); +} + +bool Thread::equals(pthread_t *t1, pthread_t *t2) +{ + return (pthread_equal(*t1, *t2) ? false : true); +} + +int Thread::start(void) +{ + Runnable *runnable = this; + return pthread_create(&_threadID, 0, _run, runnable); +} + +void Thread::stopProcess(void) +{ + _stopProcessEvent->post(); +} + +void Thread::testThreadCancel(void) +{ + pthread_testcancel(); +} diff --git a/MQTTSNGateway/src/linux/Threading.h b/MQTTSNGateway/src/linux/Threading.h index 016421e..3c86d31 100644 --- a/MQTTSNGateway/src/linux/Threading.h +++ b/MQTTSNGateway/src/linux/Threading.h @@ -1,36 +1,145 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * 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: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ +#ifndef THREADING_H_ +#define THREADING_H_ -extern "C" -{ - #include "Thread.h" -} +#include +#include +#include "MQTTSNGWDefines.h" - -class Thread +namespace MQTTSNGW { -/* -Thread(void (*fn)(void const *argument), void *argument) -{ - Thread_start(fn, arg); -}*/ - -public: - - Thread(void (*fn)(void const *argument)) - { - const void* arg = NULL; - - //Thread_start((void (*)(void *))fn, arg); - } - -}; - - - +#define MQTTSNGW_RINGBUFFER_KEY "/usr/local/etc/mqttsnGateway/config/ringbuffer.key" +#define MQTTSNGW_RB_MUTEX_KEY "/usr/local/etc/mqttsnGateway/config/rbmutex.key" +#define MQTTSNGW_RB_SEMAPHOR_NAME "/rbsemaphor" +/*===================================== + Class Mutex + ====================================*/ class Mutex { +public: + Mutex(); + Mutex(const char* name); + ~Mutex(); + void lock(void); + void unlock(void); +private: + pthread_mutex_t _mutex; + pthread_mutex_t* _pmutex; + int _shmid; +}; + +/*===================================== + Class Semaphore + ====================================*/ +class Semaphore +{ +public: + Semaphore(); + Semaphore(unsigned int val); + Semaphore(const char* name, unsigned int val); + ~Semaphore(); + void post(void); + void wait(void); + void timedwait(uint16_t millsec); + +private: + sem_t* _psem; + sem_t _sem; + char* _name; +}; + +/*===================================== + Class RingBuffer + =====================================*/ +class RingBuffer +{ +public: + RingBuffer(); + ~RingBuffer(); + void put(char* buffer); + int get(char* buffer, int bufferLength); + void reset(); +private: + void* _shmaddr; + uint16_t* _length; + uint16_t* _start; + uint16_t* _end; + char* _buffer; + int _shmid; + Mutex* _pmx; + bool _createFlg; +}; + + +/*===================================== + Class Runnable + ====================================*/ +class Runnable +{ +public: + Runnable(){} + virtual ~Runnable(){} + virtual void EXECRUN(){} +}; + + +#define MAGIC_WORD_FOR_THREAD \ +public: void EXECRUN() \ +{ \ + try \ + { \ + run(); \ + } \ + catch(Exception& ex) \ + { \ + ex.writeMessage();\ + stopProcess(); \ + } \ + catch(...) \ + { \ + throw; \ + } \ +} + +/*===================================== + Class Thread + ====================================*/ +class Thread : virtual public Runnable{ +public: + Thread(); + ~Thread(); + int start(void); + static pthread_t getID(); + static bool equals(pthread_t*, pthread_t*); + virtual void initialize(int argc, char** argv); + void stopProcess(void); + void testThreadCancel(void); +private: + pthread_t _threadID; + Semaphore* _stopProcessEvent; + + static void* _run(void*); }; + +} + +#endif /* THREADING_H_ */ diff --git a/MQTTSNGateway/src/linux/build.sh b/MQTTSNGateway/src/linux/build.sh deleted file mode 100644 index e597508..0000000 --- a/MQTTSNGateway/src/linux/build.sh +++ /dev/null @@ -1,2 +0,0 @@ -g++ main.cpp -I MQTTPacket/src -I MQTTSNPacket/src -I linux MQTTPacket/src/*.c MQTTSNPacket/src/*.c - diff --git a/MQTTSNGateway/src/linux/linux.cpp b/MQTTSNGateway/src/linux/linux.cpp index 222acda..1b948c6 100644 --- a/MQTTSNGateway/src/linux/linux.cpp +++ b/MQTTSNGateway/src/linux/linux.cpp @@ -1,5 +1,5 @@ -/******************************************************************************* - * Copyright (c) 2014, 2015 IBM Corp. +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 @@ -11,214 +11,214 @@ * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: - * Ian Craggs - initial API and implementation and/or initial documentation - *******************************************************************************/ + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ -#include -#include -#include -#include -#include -#include -#include -#include -#include #include +#include +#include #include -#include #include - -#include #include -#include +#include "MQTTSNGWDefines.h" +#include "linux.h" -class IPStack +using namespace std; + +using namespace MQTTSNGW; + +/*===================================== + Print Current Date & Time + =====================================*/ +char theCurrentTime[32]; + +char* currentDateTime() { -public: - IPStack() - { + struct timeval now; + struct tm tstruct; + gettimeofday(&now, 0); + tstruct = *localtime(&now.tv_sec); + strftime(theCurrentTime, sizeof(theCurrentTime), "%Y%m%d %H%M%S", &tstruct); + sprintf(theCurrentTime + 15, " %03d", (int)now.tv_usec / 1000 ); + return theCurrentTime; +} - } - - int Socket_error(const char* aString) - { - int rc = 0; - //if (errno != EINTR && errno != EAGAIN && errno != EINPROGRESS && errno != EWOULDBLOCK) - //{ - if (strcmp(aString, "shutdown") != 0 || (errno != ENOTCONN && errno != ECONNRESET)) - { - if (errno != EINTR && errno != EAGAIN && errno != EINPROGRESS && errno != EWOULDBLOCK) - printf("Socket error %s in %s for socket %d\n", strerror(errno), aString, mysock); - rc = errno; - } - //} - return errno; - } - - int connect(const char* hostname, int port) - { - int type = SOCK_STREAM; - struct sockaddr_in address; - int rc = -1; - sa_family_t family = AF_INET; - struct addrinfo *result = NULL; - struct addrinfo hints = {0, AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP, 0, NULL, NULL, NULL}; - - if ((rc = getaddrinfo(hostname, NULL, &hints, &result)) == 0) - { - struct addrinfo* res = result; - - /* prefer ip4 addresses */ - while (res) - { - if (res->ai_family == AF_INET) - { - result = res; - break; - } - res = res->ai_next; - } - - if (result->ai_family == AF_INET) - { - address.sin_port = htons(port); - address.sin_family = family = AF_INET; - address.sin_addr = ((struct sockaddr_in*)(result->ai_addr))->sin_addr; - } - else - rc = -1; - - freeaddrinfo(result); - } - - if (rc == 0) - { - mysock = socket(family, type, 0); - if (mysock != -1) - { - int opt = 1; - - //if (setsockopt(mysock, SOL_SOCKET, SO_NOSIGPIPE, (void*)&opt, sizeof(opt)) != 0) - // printf("Could not set SO_NOSIGPIPE for socket %d", mysock); - - rc = ::connect(mysock, (struct sockaddr*)&address, sizeof(address)); - } - } - - return rc; - } - - int read(unsigned char* buffer, int len, int timeout_ms) - { - struct timeval interval = {timeout_ms / 1000, (timeout_ms % 1000) * 1000}; - if (interval.tv_sec < 0 || (interval.tv_sec == 0 && interval.tv_usec <= 0)) - { - interval.tv_sec = 0; - interval.tv_usec = 100; - } - - setsockopt(mysock, SOL_SOCKET, SO_RCVTIMEO, (char *)&interval, sizeof(struct timeval)); - - int bytes = 0; - while (bytes < len) - { - int rc = ::recv(mysock, &buffer[bytes], (size_t)(len - bytes), 0); - if (rc == -1) - { - if (Socket_error("read") != 0) - { - bytes = -1; - break; - } - } - else - bytes += rc; - } - return bytes; - } - - int write(unsigned char* buffer, int len, int timeout) - { - struct timeval tv; - - tv.tv_sec = 0; /* 30 Secs Timeout */ - tv.tv_usec = timeout * 1000; // Not init'ing this can cause strange errors - - setsockopt(mysock, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv,sizeof(struct timeval)); - int rc = ::write(mysock, buffer, len); - //printf("write rc %d\n", rc); - return rc; - } - - int disconnect() - { - return ::close(mysock); - } - -private: - - int mysock; - -}; - - -class Countdown +/*============================================ + Timer + ============================================*/ +Timer::Timer(void) { -public: - Countdown() - { - - } + stop(); +} - Countdown(int ms) - { - countdown_ms(ms); - } - +Timer::~Timer(void) +{ - bool expired() - { - struct timeval now, res; - gettimeofday(&now, NULL); - timersub(&end_time, &now, &res); - //printf("left %d ms\n", (res.tv_sec < 0) ? 0 : res.tv_sec * 1000 + res.tv_usec / 1000); - //if (res.tv_sec > 0 || res.tv_usec > 0) - // printf("expired %d %d\n", res.tv_sec, res.tv_usec); - return res.tv_sec < 0 || (res.tv_sec == 0 && res.tv_usec <= 0); - } - +} - void countdown_ms(int ms) - { - struct timeval now; - gettimeofday(&now, NULL); - struct timeval interval = {ms / 1000, (ms % 1000) * 1000}; - //printf("interval %d %d\n", interval.tv_sec, interval.tv_usec); - timeradd(&now, &interval, &end_time); - } +void Timer::start(uint32_t msecs) +{ + gettimeofday(&_startTime, 0); + _millis = msecs; +} - - void countdown(int seconds) - { - struct timeval now; - gettimeofday(&now, NULL); - struct timeval interval = {seconds, 0}; - timeradd(&now, &interval, &end_time); - } +bool Timer::isTimeup(void) +{ + return isTimeup(_millis); +} - - int left_ms() - { - struct timeval now, res; - gettimeofday(&now, NULL); - timersub(&end_time, &now, &res); - //printf("left %d ms\n", (res.tv_sec < 0) ? 0 : res.tv_sec * 1000 + res.tv_usec / 1000); - return (res.tv_sec < 0) ? 0 : res.tv_sec * 1000 + res.tv_usec / 1000; - } - -private: +bool Timer::isTimeup(uint32_t msecs) +{ + struct timeval curTime; + long secs, usecs; + if (_startTime.tv_sec == 0) + { + return false; + } + else + { + gettimeofday(&curTime, 0); + secs = (curTime.tv_sec - _startTime.tv_sec) * 1000; + usecs = (curTime.tv_usec - _startTime.tv_usec) / 1000.0; + return ((secs + usecs) > (long) msecs); + } +} + +void Timer::stop() +{ + _startTime.tv_sec = 0; + _millis = 0; +} + +/*===================================== +Class LightIndicator +=====================================*/ + +LightIndicator::LightIndicator() +{ + _greenStatus = false; + for ( int i = 0; i <= MAX_GPIO; i++) + { + _gpio[i] = 0; + } + init(); +} + +LightIndicator::~LightIndicator() +{ + for ( int i = 0; i <= MAX_GPIO; i++) + { + if ( _gpio[i] ) + { + close( _gpio[i]); + } + } +} + +void LightIndicator::greenLight(bool on) +{ + if (on) + { + if (!_greenStatus) + { + _greenStatus = true; + //Turn Green on & turn Red off + lit(LIGHT_INDICATOR_GREEN, "1"); + lit(LIGHT_INDICATOR_RED, "0"); + } + } + else + { + if (_greenStatus) + { + _greenStatus = false; + //Turn Green off & turn Red on + lit(LIGHT_INDICATOR_GREEN, "0"); + lit(LIGHT_INDICATOR_RED, "1"); + } + } +} +void LightIndicator::blueLight(bool on) +{ + if (on) + { + lit(LIGHT_INDICATOR_BLUE, "1"); + if ( !_greenStatus ) + { + greenLight(true); + } + } + else + { + lit(LIGHT_INDICATOR_BLUE, "0"); + } +} + +void LightIndicator::redLight(bool on) +{ + if (on) + { + lit(LIGHT_INDICATOR_RED, "1"); + } + else + { + lit(LIGHT_INDICATOR_RED, "0"); + } +} + +void LightIndicator::allLightOff(void) +{ + lit(LIGHT_INDICATOR_RED, "0"); + lit(LIGHT_INDICATOR_BLUE, "0"); + lit(LIGHT_INDICATOR_GREEN, "0"); + _greenStatus = false; +} + +void LightIndicator::init() +{ + pinMode(LIGHT_INDICATOR_GREEN); + pinMode(LIGHT_INDICATOR_RED); + pinMode(LIGHT_INDICATOR_BLUE); +} + +void LightIndicator::lit(int gpioNo, const char* onoff) +{ + if( _gpio[gpioNo] ) + { + write(_gpio[gpioNo], onoff, 1); + } +} + +void LightIndicator::pinMode(int gpioNo) +{ + int fd = open("/sys/class/gpio/export", O_WRONLY); + if ( fd < 0 ) + { + return; + } + char no[4]; + sprintf(no,"%d", gpioNo); + write(fd, no, strlen(no)); + close(fd); + + char fileName[64]; + sprintf( fileName, "/sys/class/gpio/gpio%d/direction", gpioNo); + + fd = open(fileName, O_WRONLY); + if ( fd < 0 ) + { + return; + } + write(fd,"out", 3); + close(fd); + + sprintf( fileName, "/sys/class/gpio/gpio%d/value", gpioNo); + fd = open(fileName, O_WRONLY); + if ( fd > 0 ) + { + _gpio[gpioNo] = fd; + } +} - struct timeval end_time; -}; diff --git a/MQTTSNGateway/src/linux/linux.h b/MQTTSNGateway/src/linux/linux.h new file mode 100644 index 0000000..c73e544 --- /dev/null +++ b/MQTTSNGateway/src/linux/linux.h @@ -0,0 +1,70 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * 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: + * Tomoaki Yamaguchi - initial API and implementation + **************************************************************************************/ +#ifndef MQTTSNGATEWAY_SRC_LINUX_LINUX_H_ +#define MQTTSNGATEWAY_SRC_LINUX_LINUX_H_ + +namespace MQTTSNGW +{ +/*========================================================== + * Light Indicators + ===========================================================*/ +#define MAX_GPIO 27 // GPIO02 - GPIO27 +#define LIGHT_INDICATOR_GREEN 23 // RPi connector 16 +#define LIGHT_INDICATOR_RED 24 // RPi connector 18 +#define LIGHT_INDICATOR_BLUE 25 // RPi connector 22 + +/*============================================ + Timer + ============================================*/ +class Timer +{ +public: + Timer(void); + ~Timer(void); + void start(uint32_t msecs = 0); + bool isTimeup(void); + bool isTimeup(uint32_t msecs); + void stop(); + +private: + struct timeval _startTime; + uint32_t _millis; +}; + +/*===================================== + Class LightIndicator + =====================================*/ +class LightIndicator +{ +public: + LightIndicator(); + ~LightIndicator(); + void greenLight(bool on); + void blueLight(bool on); + void redLight(bool on); + void allLightOff(void); + +private: + void init(); + void lit(int gpioNo, const char* onoff); + void pinMode(int gpioNo); + bool _greenStatus; + int _gpio[MAX_GPIO + 1]; +}; + +} + +#endif /* MQTTSNGATEWAY_SRC_LINUX_LINUX_H_ */ diff --git a/MQTTSNGateway/src/linux/main.cpp b/MQTTSNGateway/src/linux/main.cpp deleted file mode 100644 index 3505782..0000000 --- a/MQTTSNGateway/src/linux/main.cpp +++ /dev/null @@ -1,24 +0,0 @@ - -#include "Threading.h" - -//#include "MQTTSNUDP.h" -//#include "MQTTEthernet.h" -#include "linux.cpp" -#include "MQTTSNGateway.h" - -MQTTSN::Parms parms(); - - -int main() -{ - // set up MQTT-SN network listening - UDPStack net; - net.listen(1884); - - MQTTSN::Gateway gateway = - MQTTSN::Gateway(net); - - gateway.run(NULL); - - return 0; -} diff --git a/MQTTSNGateway/src/linux/udp/SensorNetwork.cpp b/MQTTSNGateway/src/linux/udp/SensorNetwork.cpp new file mode 100644 index 0000000..0144e79 --- /dev/null +++ b/MQTTSNGateway/src/linux/udp/SensorNetwork.cpp @@ -0,0 +1,336 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * 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: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "SensorNetwork.h" +#include "MQTTSNGWProcess.h" + +using namespace std; +using namespace MQTTSNGW; + +/*=========================================== + Class SensorNetAddreess + ============================================*/ +SensorNetAddress::SensorNetAddress() +{ + _portNo = 0; + _IpAddr = 0; +} + +SensorNetAddress::~SensorNetAddress() +{ + +} + +void SensorNetAddress::setAddress(uint32_t IpAddr, uint16_t port) +{ + _IpAddr = IpAddr; + _portNo = port; +} + +/** + * convert Text data to SensorNetAddress + * @param buf is pointer of PortNo@IP_Address format text + * @return success = 0, Invalid format = -1 + */ +int SensorNetAddress::setAddress(string* data) +{ + size_t pos = data->find_first_of("@"); + + if ( pos == string::npos ) + { + _portNo = 0; + _IpAddr = INADDR_NONE; + return -1; + } + + string port = data->substr(0, pos); + string ip = data->substr(pos + 1); + int portNo = 0; + + if ((portNo = atoi(port.c_str())) == 0 || (_IpAddr = inet_addr(ip.c_str())) == INADDR_NONE) + { + return -1; + } + _portNo = htons(portNo); + return 0; +} + +bool SensorNetAddress::isMatch(SensorNetAddress* addr) +{ + return ((this->_portNo == addr->_portNo) && (this->_IpAddr == addr->_IpAddr)); +} + +SensorNetAddress& SensorNetAddress::operator =(SensorNetAddress& addr) +{ + this->_portNo = addr._portNo; + this->_IpAddr = addr._IpAddr; + return *this; +} + +/*=========================================== + Class SensorNetwork + ============================================*/ +SensorNetwork::SensorNetwork() +{ + +} + +SensorNetwork::~SensorNetwork() +{ + +} + +int SensorNetwork::unicast(const uint8_t* payload, uint16_t payloadLength, SensorNetAddress* sendToAddr) +{ + return UDPPort::unicast(payload, payloadLength, sendToAddr); +} + +int SensorNetwork::broadcast(const uint8_t* payload, uint16_t payloadLength) +{ + return UDPPort::broadcast(payload, payloadLength); +} + +int SensorNetwork::read(uint8_t* buf, uint16_t bufLen) +{ + return UDPPort::recv(buf, bufLen, &_clientAddr); +} + +int SensorNetwork::initialize(void) +{ + char param[MQTTSNGW_PARAM_MAX]; + uint16_t multicastPortNo = 0; + uint16_t unicastPortNo = 0; + + if (theProcess->getParam("MulticastPortNo", param) == 0) + { + multicastPortNo = atoi(param); + } + if (theProcess->getParam("GatewayPortNo", param) == 0) + { + unicastPortNo = atoi(param); + } + + theProcess->getParam("MulticastIP", param); + return UDPPort::open(param, multicastPortNo, unicastPortNo); +} + +const char* SensorNetwork::getType(void) +{ + return "UDP"; +} + +/*========================================= + Class udpStack + =========================================*/ + +UDPPort::UDPPort() +{ + _disconReq = false; + _sockfdUnicast = -1; + _sockfdMulticast = -1; +} + +UDPPort::~UDPPort() +{ + close(); +} + +void UDPPort::close(void) +{ + if (_sockfdUnicast > 0) + { + ::close(_sockfdUnicast); + _sockfdUnicast = -1; + } + if (_sockfdMulticast > 0) + { + ::close(_sockfdMulticast); + _sockfdMulticast = -1; + } +} + +int UDPPort::open(char* ipAddress, uint16_t multiPortNo, uint16_t uniPortNo) +{ + char loopch = 0; + const int reuse = 1; + + if (uniPortNo == 0 || multiPortNo == 0) + { + D_NWSTACK("error portNo undefined in UDPPort::open\n"); + return -1; + } + + uint32_t ip = inet_addr(ipAddress); + _grpAddr.setAddress(ip, htons(multiPortNo)); + _clientAddr.setAddress(ip, htons(uniPortNo)); + + /*------ Create unicast socket --------*/ + _sockfdUnicast = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (_sockfdUnicast < 0) + { + D_NWSTACK("error can't create unicast socket in UDPPort::open\n"); + return -1; + } + + setsockopt(_sockfdUnicast, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); + + sockaddr_in addru; + addru.sin_family = AF_INET; + addru.sin_port = htons(uniPortNo); + addru.sin_addr.s_addr = INADDR_ANY; + + if (::bind(_sockfdUnicast, (sockaddr*) &addru, sizeof(addru)) < 0) + { + D_NWSTACK("error can't bind unicast socket in UDPPort::open\n"); + return -1; + } + if (setsockopt(_sockfdUnicast, IPPROTO_IP, IP_MULTICAST_LOOP, (char*) &loopch, sizeof(loopch)) < 0) + { + D_NWSTACK("error IP_MULTICAST_LOOP in UDPPort::open\n"); + close(); + return -1; + } + + /*------ Create Multicast socket --------*/ + _sockfdMulticast = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (_sockfdMulticast < 0) + { + D_NWSTACK("error can't create multicast socket in UDPPort::open\n"); + close(); + return -1; + } + + setsockopt(_sockfdMulticast, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); + + sockaddr_in addrm; + addrm.sin_family = AF_INET; + addrm.sin_port = _grpAddr.getPortNo(); + addrm.sin_addr.s_addr = INADDR_ANY; + + if (::bind(_sockfdMulticast, (sockaddr*) &addrm, sizeof(addrm)) < 0) + { + D_NWSTACK("error can't bind multicast socket in UDPPort::open\n"); + return -1; + } + if (setsockopt(_sockfdMulticast, IPPROTO_IP, IP_MULTICAST_LOOP, (char*) &loopch, sizeof(loopch)) < 0) + { + D_NWSTACK("error IP_MULTICAST_LOOP in UDPPort::open\n"); + close(); + return -1; + } + + ip_mreq mreq; + mreq.imr_interface.s_addr = INADDR_ANY; + mreq.imr_multiaddr.s_addr = _grpAddr.getIpAddress(); + + if (setsockopt(_sockfdMulticast, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) + { + D_NWSTACK("error Multicast IP_ADD_MEMBERSHIP in UDPPort::open\n"); + close(); + return -1; + } + + if (setsockopt(_sockfdUnicast, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) + { + D_NWSTACK("error Unicast IP_ADD_MEMBERSHIP in UDPPort::open\n"); + close(); + return -1; + } + return 0; +} + +int UDPPort::unicast(const uint8_t* buf, uint32_t length, SensorNetAddress* addr) +{ + sockaddr_in dest; + dest.sin_family = AF_INET; + dest.sin_port = addr->getPortNo(); + dest.sin_addr.s_addr = addr->getIpAddress(); + ; + + int status = ::sendto(_sockfdUnicast, buf, length, 0, (const sockaddr*) &dest, sizeof(dest)); + if (status < 0) + { + D_NWSTACK("errno == %d in UDPPort::sendto\n", errno); + } + D_NWSTACK("sendto %s:%u length = %d\n", inet_ntoa(dest.sin_addr), htons(addr->getPortNo()), status); + return status; +} + +int UDPPort::broadcast(const uint8_t* buf, uint32_t length) +{ + return unicast(buf, length, &_grpAddr); +} + +int UDPPort::recv(uint8_t* buf, uint16_t len, SensorNetAddress* addr) +{ + fd_set recvfds; + int maxSock = 0; + + FD_ZERO(&recvfds); + FD_SET(_sockfdUnicast, &recvfds); + FD_SET(_sockfdMulticast, &recvfds); + + if (_sockfdMulticast > _sockfdUnicast) + { + maxSock = _sockfdMulticast; + } + else + { + maxSock = _sockfdUnicast; + } + + select(maxSock + 1, &recvfds, 0, 0, 0); + + if (FD_ISSET(_sockfdUnicast, &recvfds)) + { + return recvfrom(_sockfdUnicast, buf, len, 0, addr); + } + else if (FD_ISSET(_sockfdMulticast, &recvfds)) + { + return recvfrom(_sockfdMulticast, buf, len, 0, &_grpAddr); + } + return 0; +} + +int UDPPort::recvfrom(int sockfd, uint8_t* buf, uint16_t len, uint8_t flags, SensorNetAddress* addr) +{ + sockaddr_in sender; + socklen_t addrlen = sizeof(sender); + memset(&sender, 0, addrlen); + + int status = ::recvfrom(sockfd, buf, len, flags, (sockaddr*) &sender, &addrlen); + + if (status < 0 && errno != EAGAIN) + { + D_NWSTACK("errno == %d in UDPPort::recvfrom\n", errno); + return -1; + } + addr->setAddress(sender.sin_addr.s_addr, sender.sin_port); + D_NWSTACK("recved from %s:%d length = %d\n", inet_ntoa(sender.sin_addr), htons(addr->getPortNo()), status); + return status; +} + diff --git a/MQTTSNGateway/src/linux/udp/SensorNetwork.h b/MQTTSNGateway/src/linux/udp/SensorNetwork.h new file mode 100644 index 0000000..b474f16 --- /dev/null +++ b/MQTTSNGateway/src/linux/udp/SensorNetwork.h @@ -0,0 +1,114 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * 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: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ + +#ifndef SENSORNETWORK_H_ +#define SENSORNETWORK_H_ + +#include "MQTTSNGWDefines.h" +#include + +using namespace std; + +namespace MQTTSNGW +{ + +#ifdef DEBUG_NWSTACK + #define D_NWSTACK(...) printf(__VA_ARGS__) +#else + #define D_NWSTACK(...) +#endif + +#define SENSORNETWORK_TYPE "UDP" +/*=========================================== + Class SensorNetAddreess + ============================================*/ +class SensorNetAddress +{ +public: + SensorNetAddress(); + ~SensorNetAddress(); + void setAddress(uint32_t IpAddr, uint16_t port); + int setAddress(string* data); + uint32_t getIpAddress(void) + { + return _IpAddr; + } + uint16_t getPortNo(void) + { + return _portNo; + } + bool isMatch(SensorNetAddress* addr); + SensorNetAddress& operator =(SensorNetAddress& addr); + +private: + uint16_t _portNo; + uint32_t _IpAddr; +}; + +/*======================================== + Class UpdPort + =======================================*/ +class UDPPort +{ +public: + UDPPort(); + virtual ~UDPPort(); + + int open(char* ipAddress, uint16_t multiPortNo, uint16_t uniPortNo); + void close(void); + int unicast(const uint8_t* buf, uint32_t length, SensorNetAddress* sendToAddr); + int broadcast(const uint8_t* buf, uint32_t length); + int recv(uint8_t* buf, uint16_t len, SensorNetAddress* addr); + +private: + void setNonBlocking(const bool); + int recvfrom(int sockfd, uint8_t* buf, uint16_t len, uint8_t flags, SensorNetAddress* addr); + + int _sockfdUnicast; + int _sockfdMulticast; + + SensorNetAddress _grpAddr; + SensorNetAddress _clientAddr; + bool _disconReq; + +}; + +/*=========================================== + Class SensorNetwork + ============================================*/ +class SensorNetwork: public UDPPort +{ +public: + SensorNetwork(); + ~SensorNetwork(); + + int unicast(const uint8_t* payload, uint16_t payloadLength, SensorNetAddress* sendto); + int broadcast(const uint8_t* payload, uint16_t payloadLength); + int read(uint8_t* buf, uint16_t bufLen); + int initialize(void); + const char* getType(void); + SensorNetAddress* getSenderAddress(void) + { + return &_clientAddr; + } + + +private: + SensorNetAddress _clientAddr; // Sender's address. not gateway's one. +}; + +} +#endif /* SENSORNETWORK_H_ */ diff --git a/MQTTSNGateway/src/linux/xbee/SensorNetwork.cpp b/MQTTSNGateway/src/linux/xbee/SensorNetwork.cpp new file mode 100644 index 0000000..5996401 --- /dev/null +++ b/MQTTSNGateway/src/linux/xbee/SensorNetwork.cpp @@ -0,0 +1,434 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * 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: + * Tomoaki Yamaguchi - initial API and implementation + **************************************************************************************/ + +#include "SensorNetwork.h" +#include "MQTTSNGWProcess.h" +#include "Threading.h" +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace MQTTSNGW; + +/*=========================================== + Class SensorNetAddreess + ============================================*/ +SensorNetAddress::SensorNetAddress() +{ + memset(_address64, 0, sizeof(_address64)); + memset(_address16, 0, sizeof(_address16)); +} + +SensorNetAddress::~SensorNetAddress() +{ + +} + +void SensorNetAddress::setAddress(uint8_t* address64, uint8_t* address16) +{ + memcpy(_address64, address64, 8); + memcpy(_address16, address16, 2); +} + + +int SensorNetAddress::setAddress(string* address64) +{ + memcpy(_address64, address64->c_str(), 8); + memset(_address16, 0, sizeof(_address16)); + return 0; +} + +void SensorNetAddress::setBroadcastAddress(void) +{ + memset(_address64, 0, 6); + _address64[6] = 0xff; + _address64[7] = 0xff; + _address16[0] = 0xff; + _address16[1] = 0xfe; +} + +bool SensorNetAddress::isMatch(SensorNetAddress* addr) +{ + + return (memcmp(this->_address64, addr->_address64, 8 ) == 0 && memcmp(this->_address16, addr->_address16, 2) == 0); +} + +SensorNetAddress& SensorNetAddress::operator =(SensorNetAddress& addr) +{ + memcpy(_address64, addr._address64, 8); + memcpy(_address16, addr._address16, 2); + return *this; +} + +/*=========================================== + Class SensorNetwork + ============================================*/ +SensorNetwork::SensorNetwork() +{ + +} + +SensorNetwork::~SensorNetwork() +{ + +} + +int SensorNetwork::unicast(const uint8_t* payload, uint16_t payloadLength, SensorNetAddress* sendToAddr) +{ + return XBee::unicast(payload, payloadLength, sendToAddr); +} + +int SensorNetwork::broadcast(const uint8_t* payload, uint16_t payloadLength) +{ + return XBee::broadcast(payload, payloadLength); +} + +int SensorNetwork::read(uint8_t* buf, uint16_t bufLen) +{ + return XBee::recv(buf, bufLen, &_clientAddr); +} + +int SensorNetwork::initialize(void) +{ + char param[MQTTSNGW_PARAM_MAX]; + uint16_t baudrate = 9600; + + if (theProcess->getParam("Baudrate", param) == 0) + { + baudrate = (uint16_t)atoi(param); + } + + theProcess->getParam("SerialDevice", param); + + return XBee::open(param, baudrate); +} + +const char* SensorNetwork::getType(void) +{ + return "XBee"; +} + +/*=========================================== + Class XBee + ============================================*/ +XBee::XBee(){ + _serialPort = new SerialPort(); + _respCd = 0; + _dataLen = 0; + _frameId = 0; +} + +XBee::~XBee(){ + if ( _serialPort ) + { + delete _serialPort; + } +} + +int XBee::open(char* device, int baudrate) +{ + int rate = B9600; + + switch (baudrate) + { + case 9600: + rate = B9600; + break; + case 19200: + rate = B19200; + break; + case 38400: + rate = B38400; + break; + case 57600: + rate = B57600; + break; + case 115200: + rate = B115200; + break; + default: + return -1; + } + + return _serialPort->open(device, rate, false, 1, O_RDWR | O_NOCTTY); +} + +int XBee::broadcast(const uint8_t* payload, uint16_t payloadLen){ + SensorNetAddress addr; + addr.setBroadcastAddress(); + send(payload, (uint8_t) payloadLen, &addr); + return 1; +} + +int XBee:: unicast(const uint8_t* payload, uint16_t payloadLen, SensorNetAddress* addr){ + send(payload, (uint8_t) payloadLen, addr); + return 1; +} + +int XBee::recv(uint8_t* buf, uint16_t bufLen, SensorNetAddress* clientAddr) +{ + uint8_t data[128]; + int len; + + while ( true ) + { + + if ( (len = readApiFrame(data)) > 0 ) + { + + if ( data[0] == API_RESPONSE ) + { + memcpy(clientAddr->_address64, data + 1, 8); + memcpy(clientAddr->_address16, data + 9, 2); + len -= 12; + memcpy( buf, data + 12, len); + return len; + } + else if ( data[0] == API_XMITSTATUS ) + { + _respCd = data[5]; + _respId = data[1]; + _sem.post(); + } + } + } +} + +int XBee::readApiFrame(uint8_t* recvData){ + uint8_t buf; + uint8_t pos = 0; + uint8_t checksum = 0; + uint8_t len = 0; + + while ( _serialPort->recv(&buf) ) + { + if ( buf == START_BYTE) + { + pos = 1; + D_NWSTACK("\r\n===> Recv: "); + break; + } + } + + if ( pos == 0 ) + { + goto errexit; + } + + if ( recv(&buf) < 0 ) // MSB length + { + goto errexit; + } + + if ( recv(&buf) < 0 ) // LSB length + { + goto errexit; + } + else + { + len = buf; + } + + pos = 0; + while ( len-- ) + { + if ( recv(&buf) < 0 ) + { + goto errexit; + } + recvData[pos++] = buf; + checksum += buf; + } + + recv(&buf); // checksum + if ( (0xff - checksum ) == buf ){ + D_NWSTACK(" checksum ok\r\n"); + return pos; + } + else + { + D_NWSTACK(" checksum error %02x\r\n", 0xff - checksum); + goto errexit; + } +errexit: + _serialPort->flush(); + return -1; +} + +int XBee::send(const uint8_t* payload, uint8_t pLen, SensorNetAddress* addr){ + D_NWSTACK("\r\n===> Send: "); + uint8_t checksum = 0; + _respCd = -1; + + _serialPort->send(START_BYTE); + send(0x00); // Message Length + send(14 + pLen); // Message Length + + _serialPort->send(API_XMITREQUEST); // Transmit Request API + checksum += API_XMITREQUEST; + + if (_frameId++ == 0x00 ) // Frame ID + { + _frameId = 1; + } + send(_frameId); + checksum += _frameId; + + for ( int i = 0; i < 8; i++) // Address64 + { + send(addr->_address64[i]); + checksum += addr->_address64[i]; + } + for ( int i = 0; i < 2; i++) // Address16 + { + send(addr->_address16[i]); + checksum += addr->_address16[i]; + } + + send(0x00); // Broadcast Radius + checksum += 0x00; + + send(0x00); // Option: Use the extended transmission timeout 0x40 + checksum += 0x00; + + D_NWSTACK("\r\n Payload: "); + + for ( uint8_t i = 0; i < pLen; i++ ){ + send(payload[i]); // Payload + checksum += payload[i]; + } + + checksum = 0xff - checksum; + D_NWSTACK(" checksum "); + send(checksum); + D_NWSTACK("\r\n"); + + /* wait Txim Status 0x8B */ + _sem.timedwait(5000); // 5sec + + if ( _respCd || _frameId != _respId ) + { + D_NWSTACK(" frameId = %02x Not Acknowleged\r\n", _frameId); + return -1; + } + return 0; +} + +void XBee::send(uint8_t c) +{ + if(c == START_BYTE || c == ESCAPE || c == XON || c == XOFF){ + _serialPort->send(ESCAPE); + _serialPort->send(c ^ 0x20); + }else{ + _serialPort->send(c); + } +} + +int XBee::recv(uint8_t* buf) +{ + if (_serialPort->recv(buf) ) + { + if ( *buf == ESCAPE) + { + _serialPort->recv(buf); + *buf = 0x20 ^ *buf; + } + return 0; + } + return -1; +} + +/*========================================= + Class SerialPort + =========================================*/ +SerialPort::SerialPort() +{ + _tio.c_iflag = IGNBRK | IGNPAR; + _tio.c_cflag = CS8 | CLOCAL | CRTSCTS; + _tio.c_cc[VINTR] = 0; + _tio.c_cc[VTIME] = 0; + _tio.c_cc[VMIN] = 1; + _fd = 0; +} + +SerialPort::~SerialPort() +{ + if (_fd) + { + ::close(_fd); + } +} + +int SerialPort::open(char* devName, unsigned int baudrate, bool parity, + unsigned int stopbit, unsigned int flg) +{ + _fd = ::open(devName, flg); + if (_fd < 0) + { + return _fd; + } + + if (parity) + { + _tio.c_cflag = _tio.c_cflag | PARENB; + } + if (stopbit == 2) + { + _tio.c_cflag = _tio.c_cflag | CSTOPB; + } + + if (cfsetspeed(&_tio, baudrate) < 0) + { + return errno; + } + return tcsetattr(_fd, TCSANOW, &_tio); +} + +bool SerialPort::send(unsigned char b) +{ + if (write(_fd, &b, 1) < 0) + { + return false; + } + else + { + D_NWSTACK( " %02x", b); + return true; + } +} + +bool SerialPort::recv(unsigned char* buf) +{ + if (read(_fd, buf, 1) == 0) + { + return false; + } + else + { + D_NWSTACK( " %02x",buf[0] ); + return true; + } +} + +void SerialPort::flush(void) +{ + tcsetattr(_fd, TCSAFLUSH, &_tio); +} + diff --git a/MQTTSNGateway/src/linux/xbee/SensorNetwork.h b/MQTTSNGateway/src/linux/xbee/SensorNetwork.h new file mode 100644 index 0000000..f4a18ee --- /dev/null +++ b/MQTTSNGateway/src/linux/xbee/SensorNetwork.h @@ -0,0 +1,141 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * 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: + * Tomoaki Yamaguchi - initial API and implementation + **************************************************************************************/ +#ifndef SENSORNETWORKX_H_ +#define SENSORNETWORKX_H_ + +#include "MQTTSNGWDefines.h" +#include "MQTTSNGWProcess.h" +#include +#include + +using namespace std; + +namespace MQTTSNGW +{ +//#define DEBUG_NWSTACK + +#ifdef DEBUG_NWSTACK + #define D_NWSTACK(...) printf(__VA_ARGS__) +#else + #define D_NWSTACK(...) +#endif + +#define API_XMITREQUEST 0x10 +#define API_RESPONSE 0x90 +#define API_MODEMSTATUS 0x8A +#define API_XMITSTATUS 0x8B + +#define START_BYTE 0x7e +#define ESCAPE 0x7d +#define XON 0x11 +#define XOFF 0x13 + +/*=========================================== + Class SerialPort + ============================================*/ +class SerialPort{ +public: + SerialPort(); + ~SerialPort(); + int open(char* devName, unsigned int baudrate, bool parity, unsigned int stopbit, unsigned int flg); + bool send(unsigned char b); + bool recv(unsigned char* b); + void flush(); + +private: + int _fd; // file descriptor + struct termios _tio; +}; + +/*=========================================== + Class SensorNetAddreess + ============================================*/ +class SensorNetAddress +{ + friend class XBee; +public: + SensorNetAddress(); + ~SensorNetAddress(); + void setAddress(uint8_t* address64, uint8_t* address16); + int setAddress(string* data); + void setBroadcastAddress(void); + bool isMatch(SensorNetAddress* addr); + SensorNetAddress& operator =(SensorNetAddress& addr); + +private: + uint8_t _address16[2]; + uint8_t _address64[8]; +}; + +/*======================================== + Class XBee + =======================================*/ +class XBee +{ +public: + XBee(); + ~XBee(); + + int open(char* device, int boudrate); + void close(void); + int unicast(const uint8_t* buf, uint16_t length, SensorNetAddress* sendToAddr); + int broadcast(const uint8_t* buf, uint16_t length); + int recv(uint8_t* buf, uint16_t len, SensorNetAddress* addr); + +private: + int readApiFrame(uint8_t* recvData); + int recv(uint8_t* buf); + int send(const uint8_t* payload, uint8_t pLen, SensorNetAddress* addr); + void send(uint8_t b); + + Semaphore _sem; + Mutex _meutex; + SerialPort* _serialPort; + uint8_t _frameId; + + uint8_t _respCd; + uint8_t _respId; + uint8_t _dataLen; +}; + +/*=========================================== + Class SensorNetwork + ============================================*/ +class SensorNetwork: public XBee +{ +public: + SensorNetwork(); + ~SensorNetwork(); + + int unicast(const uint8_t* payload, uint16_t payloadLength, SensorNetAddress* sendto); + int broadcast(const uint8_t* payload, uint16_t payloadLength); + int read(uint8_t* buf, uint16_t bufLen); + int initialize(void); + const char* getType(void); + + SensorNetAddress* getSenderAddress(void) + { + return &_clientAddr; + } + + +private: + SensorNetAddress _clientAddr; // Sender's address. not gateway's one. +}; + +} + +#endif /* SENSORNETWORKX_H_ */ diff --git a/MQTTSNGateway/src/main.cpp b/MQTTSNGateway/src/main.cpp deleted file mode 100644 index ae9c9fb..0000000 --- a/MQTTSNGateway/src/main.cpp +++ /dev/null @@ -1,33 +0,0 @@ -/* - * main.cpp - * - * Created on: Jan 5, 2016 - * Author: icraggs - */ - - -void read_config() -{ - -} - - - -int main(int argc, char* argv[]) -{ - - - // 1. listen for MQTT-SN connections - - // 2. connect to MQTT broker - - // 3. listen for MQTT-SN packets, and act on them - // 4. listen for MQTT packets too, and act on them - - - - - - return 0; -} - diff --git a/MQTTSNGateway/src/mainGateway.cpp b/MQTTSNGateway/src/mainGateway.cpp new file mode 100644 index 0000000..e33d915 --- /dev/null +++ b/MQTTSNGateway/src/mainGateway.cpp @@ -0,0 +1,46 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * 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: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ +#include "MQTTSNGateway.h" +#include "MQTTSNGWBrokerRecvTask.h" +#include "MQTTSNGWBrokerSendTask.h" +#include "MQTTSNGWClientRecvTask.h" +#include "MQTTSNGWClientSendTask.h" +#include "MQTTSNGWPacketHandleTask.h" + +using namespace MQTTSNGW; + +/* + * Gateway Process + * + * Configure file "/usr/local/etc/mqttsnGateway/config/param.conf" + * Clientlist file "/usr/local/etc/mqttsnGateway/config/clientList.conf" + * Certificate file "/etc/ssl/certs" + * These are defined in MQTTSNGWDefines.h + */ +Gateway* gateway = new Gateway(); +PacketHandleTask* t0 = new PacketHandleTask(gateway); +ClientRecvTask* t1 = new ClientRecvTask(gateway); +ClientSendTask* t2 = new ClientSendTask(gateway); +BrokerRecvTask* t3 = new BrokerRecvTask(gateway); +BrokerSendTask* t4 = new BrokerSendTask(gateway); + +int main(int argc, char** argv) +{ + theProcess->initialize(argc, argv); + theProcess->run(); + return 0; +} + diff --git a/MQTTSNGateway/src/mainLogmonitor.cpp b/MQTTSNGateway/src/mainLogmonitor.cpp new file mode 100644 index 0000000..f73a2a9 --- /dev/null +++ b/MQTTSNGateway/src/mainLogmonitor.cpp @@ -0,0 +1,33 @@ +/************************************************************************************** + * Copyright (c) 2016, Tomoaki Yamaguchi + * + * 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: + * Tomoaki Yamaguchi - initial API and implementation and/or initial documentation + **************************************************************************************/ + +#include "MQTTSNGWProcess.h" +#include "MQTTSNGWLogmonitor.h" + +using namespace MQTTSNGW; + + +/* + * Logmonitor process + */ +int main(int argc, char** argv) +{ + Logmonitor monitor = Logmonitor(); + monitor.initialize(argc, argv); + monitor.run(); + return 0; +} + diff --git a/MQTTSNPacket/src/MQTTSNConnectClient.c b/MQTTSNPacket/src/MQTTSNConnectClient.c index 5a1179f..d62d3c1 100644 --- a/MQTTSNPacket/src/MQTTSNConnectClient.c +++ b/MQTTSNPacket/src/MQTTSNConnectClient.c @@ -20,6 +20,7 @@ #include + /** * Determines the length of the MQTT connect packet that would be produced using the supplied connect options. * @param options the options to be used to build the connect packet @@ -116,7 +117,7 @@ int MQTTSNSerialize_disconnectLength(int duration) int len = 0; FUNC_ENTRY; - len = (duration >= 0) ? 3 : 1; + len = (duration > 0) ? 3 : 1; FUNC_EXIT_RC(len); return len; } @@ -144,7 +145,7 @@ int MQTTSNSerialize_disconnect(unsigned char* buf, int buflen, int duration) ptr += MQTTSNPacket_encode(ptr, len); /* write length */ writeChar(&ptr, MQTTSN_DISCONNECT); /* write message type */ - if (duration >= 0) + if (duration > 0) writeInt(&ptr, duration); rc = ptr - buf; diff --git a/MQTTSNPacket/src/MQTTSNDeserializePublish.c b/MQTTSNPacket/src/MQTTSNDeserializePublish.c index 9aae227..8dd0f7e 100644 --- a/MQTTSNPacket/src/MQTTSNDeserializePublish.c +++ b/MQTTSNPacket/src/MQTTSNDeserializePublish.c @@ -56,7 +56,7 @@ int MQTTSNDeserialize_publish(unsigned char* dup, int* qos, unsigned char* retai *qos = flags.bits.QoS; *retained = flags.bits.retain; - topic->type = flags.bits.topicIdType; + topic->type = (MQTTSN_topicTypes)flags.bits.topicIdType; if (topic->type == MQTTSN_TOPIC_TYPE_NORMAL && *qos == 3) { /* special arrangement for long topic names in QoS -1 publishes. The length of the topic is in the topicid field */ diff --git a/MQTTSNPacket/src/MQTTSNPacket.c b/MQTTSNPacket/src/MQTTSNPacket.c index 4f98b6a..80f55f4 100644 --- a/MQTTSNPacket/src/MQTTSNPacket.c +++ b/MQTTSNPacket/src/MQTTSNPacket.c @@ -19,7 +19,7 @@ #include -static char* packet_names[] = +static const char* packet_names[] = { "ADVERTISE", "SEARCHGW", "GWINFO", "RESERVED", "CONNECT", "CONNACK", "WILLTOPICREQ", "WILLTOPIC", "WILLMSGREQ", "WILLMSG", "REGISTER", "REGACK", @@ -35,7 +35,7 @@ static char* packet_names[] = * @param code MsgType code * @return the corresponding packet name */ -char* MQTTSNPacket_name(int code) +const char* MQTTSNPacket_name(int code) { return (code >= 0 && code <= MQTTSN_WILLMSGRESP) ? packet_names[code] : "UNKNOWN"; } @@ -51,7 +51,6 @@ int MQTTSNPacket_len(int length) return (length > 255) ? length + 3 : length + 1; } - /** * Encodes the MQTT-SN message length * @param buf the buffer into which the encoded data is written @@ -95,7 +94,7 @@ int MQTTSNPacket_decode(unsigned char* buf, int buflen, int* value) if (buf[0] == 1) { unsigned char* bufptr = &buf[1]; - if (buflen < 3) + if (buflen < MAX_NO_OF_LENGTH_BYTES) goto exit; *value = readInt(&bufptr); len = 3; @@ -188,7 +187,7 @@ void writeMQTTSNString(unsigned char** pptr, MQTTSNString MQTTSNString) { if (MQTTSNString.lenstring.len > 0) { - memcpy(*pptr, MQTTSNString.lenstring.data, MQTTSNString.lenstring.len); + memcpy(*pptr, (const unsigned char*)MQTTSNString.lenstring.data, MQTTSNString.lenstring.len); *pptr += MQTTSNString.lenstring.len; } else if (MQTTSNString.cstring) diff --git a/MQTTSNPacket/src/MQTTSNPacket.h b/MQTTSNPacket/src/MQTTSNPacket.h index 974619f..a3466c6 100644 --- a/MQTTSNPacket/src/MQTTSNPacket.h +++ b/MQTTSNPacket/src/MQTTSNPacket.h @@ -12,6 +12,7 @@ * * Contributors: * Ian Craggs - initial API and implementation and/or initial documentation + * TomoakiiYamaguchi - modify for C++ *******************************************************************************/ #ifndef MQTTSNPACKET_H_ @@ -35,15 +36,15 @@ enum MQTTSN_connackCodes MQTTSN_RC_ACCEPTED, MQTTSN_RC_REJECTED_CONGESTED, MQTTSN_RC_REJECTED_INVALID_TOPIC_ID, + MQTTSN_RC_NOT_SUPPORTED }; -enum MQTTSN_topicTypes +typedef enum { MQTTSN_TOPIC_TYPE_NORMAL, /* topic id in publish, topic name in subscribe */ MQTTSN_TOPIC_TYPE_PREDEFINED, MQTTSN_TOPIC_TYPE_SHORT, -}; - +}MQTTSN_topicTypes; enum MQTTSN_msgTypes { @@ -60,7 +61,7 @@ enum MQTTSN_msgTypes typedef struct { - enum MQTTSN_topicTypes type; + MQTTSN_topicTypes type; union { unsigned short id; @@ -73,7 +74,6 @@ typedef struct } data; } MQTTSN_topicid; - /** * Bitfields for the MQTT-SN flags byte. */ @@ -126,7 +126,7 @@ int MQTTSNstrlen(MQTTSNString mqttsnstring); #include "MQTTSNUnsubscribe.h" #include "MQTTSNSearch.h" -char* MQTTSNPacket_name(int ptype); +const char* MQTTSNPacket_name(int ptype); int MQTTSNPacket_len(int length); int MQTTSNPacket_encode(unsigned char* buf, int length); diff --git a/MQTTSNPacket/src/MQTTSNPublish.h b/MQTTSNPacket/src/MQTTSNPublish.h index 937817b..a020f96 100644 --- a/MQTTSNPacket/src/MQTTSNPublish.h +++ b/MQTTSNPacket/src/MQTTSNPublish.h @@ -41,5 +41,6 @@ int MQTTSNSerialize_regack(unsigned char* buf, int buflen, unsigned short topici unsigned char return_code); int MQTTSNDeserialize_regack(unsigned short* topicid, unsigned short* packetid, unsigned char* return_code, unsigned char* buf, int buflen); +int MQTTSNSerialize_pubrel(unsigned char* buf, int buflen, unsigned short packetid); #endif /* MQTTSNPUBLISH_H_ */ diff --git a/MQTTSNPacket/src/MQTTSNSubscribeServer.c b/MQTTSNPacket/src/MQTTSNSubscribeServer.c index d16f80a..8e48919 100644 --- a/MQTTSNPacket/src/MQTTSNSubscribeServer.c +++ b/MQTTSNPacket/src/MQTTSNSubscribeServer.c @@ -53,7 +53,7 @@ int MQTTSNDeserialize_subscribe(unsigned char* dup, int* qos, unsigned short* pa *packetid = readInt(&curdata); - topicFilter->type = flags.bits.topicIdType; + topicFilter->type = (MQTTSN_topicTypes)flags.bits.topicIdType; if (topicFilter->type == MQTTSN_TOPIC_TYPE_NORMAL) { diff --git a/MQTTSNPacket/src/MQTTSNUnsubscribeServer.c b/MQTTSNPacket/src/MQTTSNUnsubscribeServer.c index 4dff54f..c2205d9 100644 --- a/MQTTSNPacket/src/MQTTSNUnsubscribeServer.c +++ b/MQTTSNPacket/src/MQTTSNUnsubscribeServer.c @@ -38,7 +38,7 @@ int MQTTSNDeserialize_unsubscribe(unsigned short* packetid, MQTTSN_topicid* topi flags.all = readChar(&curdata); *packetid = readInt(&curdata); - topicFilter->type = flags.bits.topicIdType; + topicFilter->type = (MQTTSN_topicTypes)flags.bits.topicIdType; if (topicFilter->type == MQTTSN_TOPIC_TYPE_NORMAL) { topicFilter->data.long_.len = enddata - curdata; diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..8c8ec4c --- /dev/null +++ b/Makefile @@ -0,0 +1,106 @@ +PROGNAME := MQTT-SNGateway +APPL := mainGateway + +LPROGNAME := MQTT-SNLogmonitor +LAPPL := mainLogmonitor + +SRCDIR := MQTTSNGateway/src +SUBDIR := MQTTSNPacket/src + +OS := linux +SENSORNET := udp + +CPPSRCS := \ +$(SRCDIR)/MQTTGWConnectionHandler.cpp \ +$(SRCDIR)/MQTTGWPacket.cpp \ +$(SRCDIR)/MQTTGWPublishHandler.cpp \ +$(SRCDIR)/MQTTGWSubscribeHandler.cpp \ +$(SRCDIR)/MQTTSNGateway.cpp \ +$(SRCDIR)/MQTTSNGWBrokerRecvTask.cpp \ +$(SRCDIR)/MQTTSNGWBrokerSendTask.cpp \ +$(SRCDIR)/MQTTSNGWClient.cpp \ +$(SRCDIR)/MQTTSNGWClientRecvTask.cpp \ +$(SRCDIR)/MQTTSNGWClientSendTask.cpp \ +$(SRCDIR)/MQTTSNGWConnectionHandler.cpp \ +$(SRCDIR)/MQTTSNGWLogmonitor.cpp \ +$(SRCDIR)/MQTTSNGWPacket.cpp \ +$(SRCDIR)/MQTTSNGWPacketHandleTask.cpp \ +$(SRCDIR)/MQTTSNGWProcess.cpp \ +$(SRCDIR)/MQTTSNGWPublishHandler.cpp \ +$(SRCDIR)/MQTTSNGWSubscribeHandler.cpp \ +$(SRCDIR)/$(OS)/$(SENSORNET)/SensorNetwork.cpp \ +$(SRCDIR)/$(OS)/$(OS).cpp \ +$(SRCDIR)/$(OS)/Network.cpp \ +$(SRCDIR)/$(OS)/Threading.cpp + +CSRCS := $(SUBDIR)/MQTTSNConnectClient.c \ +$(SUBDIR)/MQTTSNConnectServer.c \ +$(SUBDIR)/MQTTSNDeserializePublish.c \ +$(SUBDIR)/MQTTSNPacket.c \ +$(SUBDIR)/MQTTSNSearchClient.c \ +$(SUBDIR)/MQTTSNSearchServer.c \ +$(SUBDIR)/MQTTSNSerializePublish.c \ +$(SUBDIR)/MQTTSNSubscribeClient.c \ +$(SUBDIR)/MQTTSNSubscribeServer.c \ +$(SUBDIR)/MQTTSNUnsubscribeClient.c \ +$(SUBDIR)/MQTTSNUnsubscribeServer.c + +CXX := g++ +CPPFLAGS += + +INCLUDES += -IMQTTSNGateway/src \ +-IMQTTSNGateway/src/$(OS) \ +-IMQTTSNGateway/src/$(OS)/$(SENSORNET) \ +-IMQTTSNPacket/src + +DEFS := +LIBS += +LDFLAGS := +CXXFLAGS := -Wall -O3 -std=c++11 +LDADD := -lpthread -lssl -lcrypto +OUTDIR := Build + +PROG := $(OUTDIR)/$(PROGNAME) +LPROG := $(OUTDIR)/$(LPROGNAME) +OBJS := $(CPPSRCS:%.cpp=$(OUTDIR)/%.o) +OBJS += $(CSRCS:%.c=$(OUTDIR)/%.o) +DEPS := $(CPPSRCS:%.cpp=$(OUTDIR)/%.d) +DEPS += $(CSRCS:%.c=$(OUTDIR)/%.d) + +.PHONY: install clean + +all: $(PROG) + +monitor: $(LPROG) + +-include $(DEPS) + +$(PROG): $(OBJS) $(OUTDIR)/$(SRCDIR)/$(APPL).o + $(CXX) $(LDFLAGS) -o $@ $^ $(LIBS) $(LDADD) + +$(LPROG): $(OBJS) $(OUTDIR)/$(SRCDIR)/$(LAPPL).o + $(CXX) $(LDFLAGS) -o $@ $^ $(LIBS) $(LDADD) + +$(OUTDIR)/$(SRCDIR)/%.o:$(SRCDIR)/%.cpp + @if [ ! -e `dirname $@` ]; then mkdir -p `dirname $@`; fi + $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(INCLUDES) $(DEFS) -o $@ -c -MMD -MP -MF $(@:%.o=%.d) $< + +$(OUTDIR)/$(SRCDIR)/$(APPL).o:$(SRCDIR)/$(APPL).cpp + @if [ ! -e `dirname $@` ]; then mkdir -p `dirname $@`; fi + $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(INCLUDES) $(DEFS) -o $@ -c -MMD -MP -MF $(@:%.o=%.d) $< + +$(OUTDIR)/$(SRCDIR)/$(LAPPL).o:$(SRCDIR)/$(LAPPL).cpp + @if [ ! -e `dirname $@` ]; then mkdir -p `dirname $@`; fi + $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(INCLUDES) $(DEFS) -o $@ -c -MMD -MP -MF $(@:%.o=%.d) $< + +$(OUTDIR)/$(SUBDIR)/%.o:$(SUBDIR)/%.c + @if [ ! -e `dirname $@` ]; then mkdir -p `dirname $@`; fi + $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(INCLUDES) $(DEFS) -o $@ -c -MMD -MP -MF $(@:%.o=%.d) $< + +clean: + rm -rf $(OUTDIR) + +install: + cp -pf $(PROG) ../ + + \ No newline at end of file diff --git a/README.md b/README.md index bc7a72d..9396809 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Eclipse Paho MQTT-SN Embedded C client +# Eclipse Paho MQTT-SN Embedded C ## Project description: