From db463cb987cccee09efa469d1c34eb4eea259a94 Mon Sep 17 00:00:00 2001 From: Espressif Systems Date: Mon, 8 Jan 2018 10:25:01 +0800 Subject: [PATCH] feat(mqtt): Add paho mqtt to third_party internal: 0d398ac6 --- Makefile | 1 + include/mqtt/MQTTClient.h | 238 ++++++ include/mqtt/MQTTConnect.h | 148 ++++ include/mqtt/MQTTFormat.h | 37 + include/mqtt/MQTTFreeRTOS.h | 70 ++ include/mqtt/MQTTPacket.h | 133 ++++ include/mqtt/MQTTPublish.h | 38 + include/mqtt/MQTTSubscribe.h | 39 + include/mqtt/MQTTUnsubscribe.h | 38 + include/mqtt/StackTrace.h | 78 ++ lib/libmqtt.a | Bin 0 -> 77208 bytes third_party/mqtt/Makefile | 47 ++ third_party/mqtt/library/MQTTClient.c | 696 ++++++++++++++++++ third_party/mqtt/library/MQTTConnectClient.c | 214 ++++++ third_party/mqtt/library/MQTTConnectServer.c | 148 ++++ .../mqtt/library/MQTTDeserializePublish.c | 107 +++ third_party/mqtt/library/MQTTFormat.c | 262 +++++++ third_party/mqtt/library/MQTTPacket.c | 412 +++++++++++ .../mqtt/library/MQTTSerializePublish.c | 169 +++++ .../mqtt/library/MQTTSubscribeClient.c | 137 ++++ .../mqtt/library/MQTTSubscribeServer.c | 112 +++ .../mqtt/library/MQTTUnsubscribeClient.c | 106 +++ .../mqtt/library/MQTTUnsubscribeServer.c | 102 +++ third_party/mqtt/library/Makefile | 46 ++ third_party/mqtt/platform/MQTTFreeRTOS.c | 262 +++++++ third_party/mqtt/platform/Makefile | 46 ++ 26 files changed, 3686 insertions(+) create mode 100755 include/mqtt/MQTTClient.h create mode 100644 include/mqtt/MQTTConnect.h create mode 100644 include/mqtt/MQTTFormat.h create mode 100755 include/mqtt/MQTTFreeRTOS.h create mode 100644 include/mqtt/MQTTPacket.h create mode 100644 include/mqtt/MQTTPublish.h create mode 100644 include/mqtt/MQTTSubscribe.h create mode 100644 include/mqtt/MQTTUnsubscribe.h create mode 100644 include/mqtt/StackTrace.h create mode 100644 lib/libmqtt.a create mode 100644 third_party/mqtt/Makefile create mode 100755 third_party/mqtt/library/MQTTClient.c create mode 100644 third_party/mqtt/library/MQTTConnectClient.c create mode 100644 third_party/mqtt/library/MQTTConnectServer.c create mode 100644 third_party/mqtt/library/MQTTDeserializePublish.c create mode 100644 third_party/mqtt/library/MQTTFormat.c create mode 100644 third_party/mqtt/library/MQTTPacket.c create mode 100644 third_party/mqtt/library/MQTTSerializePublish.c create mode 100644 third_party/mqtt/library/MQTTSubscribeClient.c create mode 100644 third_party/mqtt/library/MQTTSubscribeServer.c create mode 100644 third_party/mqtt/library/MQTTUnsubscribeClient.c create mode 100644 third_party/mqtt/library/MQTTUnsubscribeServer.c create mode 100644 third_party/mqtt/library/Makefile create mode 100755 third_party/mqtt/platform/MQTTFreeRTOS.c create mode 100644 third_party/mqtt/platform/Makefile diff --git a/Makefile b/Makefile index 3dd71b67..1e0d22e8 100644 --- a/Makefile +++ b/Makefile @@ -425,3 +425,4 @@ INCLUDES += -I $(SDK_PATH)/include/spiffs INCLUDES += -I $(SDK_PATH)/include/ssl INCLUDES += -I $(SDK_PATH)/include/json INCLUDES += -I $(SDK_PATH)/include/openssl +INCLUDES += -I $(SDK_PATH)/include/mqtt diff --git a/include/mqtt/MQTTClient.h b/include/mqtt/MQTTClient.h new file mode 100755 index 00000000..5bd187b9 --- /dev/null +++ b/include/mqtt/MQTTClient.h @@ -0,0 +1,238 @@ +/******************************************************************************* + * Copyright (c) 2014, 2017 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: + * Allan Stockdill-Mander/Ian Craggs - initial API and implementation and/or initial documentation + * Ian Craggs - documentation and platform specific header + * Ian Craggs - add setMessageHandler function + *******************************************************************************/ + +#if !defined(__MQTT_CLIENT_C_) +#define __MQTT_CLIENT_C_ + +#if defined(__cplusplus) + extern "C" { +#endif + +#if defined(WIN32_DLL) || defined(WIN64_DLL) + #define DLLImport __declspec(dllimport) + #define DLLExport __declspec(dllexport) +#elif defined(LINUX_SO) + #define DLLImport extern + #define DLLExport __attribute__ ((visibility ("default"))) +#else + #define DLLImport + #define DLLExport +#endif + +#include "MQTTPacket.h" +#include "stdio.h" +#include "MQTTFreeRTOS.h" + +#if defined(MQTTCLIENT_PLATFORM_HEADER) +/* The following sequence of macros converts the MQTTCLIENT_PLATFORM_HEADER value + * into a string constant suitable for use with include. + */ +#define xstr(s) str(s) +#define str(s) #s +#include xstr(MQTTCLIENT_PLATFORM_HEADER) +#endif + +#define MAX_PACKET_ID 65535 /* according to the MQTT specification - do not change! */ + +#if !defined(MAX_MESSAGE_HANDLERS) +#define MAX_MESSAGE_HANDLERS 5 /* redefinable - how many subscriptions do you want? */ +#endif + +enum QoS { QOS0, QOS1, QOS2, SUBFAIL=0x80 }; + +/* all failure return codes must be negative */ +enum returnCode { BUFFER_OVERFLOW = -2, FAILURE = -1, SUCCESS = 0 }; + +/* The Platform specific header must define the Network and Timer structures and functions + * which operate on them. + * +typedef struct Network +{ + int (*mqttread)(Network*, unsigned char* read_buffer, int, int); + int (*mqttwrite)(Network*, unsigned char* send_buffer, int, int); +} Network;*/ + +/* The Timer structure must be defined in the platform specific header, + * and have the following functions to operate on it. */ +extern void TimerInit(Timer*); +extern char TimerIsExpired(Timer*); +extern void TimerCountdownMS(Timer*, unsigned int); +extern void TimerCountdown(Timer*, unsigned int); +extern int TimerLeftMS(Timer*); + +typedef struct MQTTMessage +{ + enum QoS qos; + unsigned char retained; + unsigned char dup; + unsigned short id; + void *payload; + size_t payloadlen; +} MQTTMessage; + +typedef struct MessageData +{ + MQTTMessage* message; + MQTTString* topicName; +} MessageData; + +typedef struct MQTTConnackData +{ + unsigned char rc; + unsigned char sessionPresent; +} MQTTConnackData; + +typedef struct MQTTSubackData +{ + enum QoS grantedQoS; +} MQTTSubackData; + +typedef void (*messageHandler)(MessageData*); + +typedef struct MQTTClient +{ + unsigned int next_packetid, + command_timeout_ms; + size_t buf_size, + readbuf_size; + unsigned char *buf, + *readbuf; + unsigned int keepAliveInterval; + char ping_outstanding; + int isconnected; + int cleansession; + + struct MessageHandlers + { + const char* topicFilter; + void (*fp) (MessageData*); + } messageHandlers[MAX_MESSAGE_HANDLERS]; /* Message handlers are indexed by subscription topic */ + + void (*defaultMessageHandler) (MessageData*); + + Network* ipstack; + Timer last_sent, last_received; +#if defined(MQTT_TASK) + Mutex mutex; + Thread thread; +#endif +} MQTTClient; + +#define DefaultClient {0, 0, 0, 0, NULL, NULL, 0, 0, 0} + + +/** + * Create an MQTT client object + * @param client + * @param network + * @param command_timeout_ms + * @param + */ +DLLExport void MQTTClientInit(MQTTClient* client, Network* network, unsigned int command_timeout_ms, + unsigned char* sendbuf, size_t sendbuf_size, unsigned char* readbuf, size_t readbuf_size); + +/** MQTT Connect - send an MQTT connect packet down the network and wait for a Connack + * The nework object must be connected to the network endpoint before calling this + * @param options - connect options + * @return success code + */ +DLLExport int MQTTConnectWithResults(MQTTClient* client, MQTTPacket_connectData* options, + MQTTConnackData* data); + +/** MQTT Connect - send an MQTT connect packet down the network and wait for a Connack + * The nework object must be connected to the network endpoint before calling this + * @param options - connect options + * @return success code + */ +DLLExport int MQTTConnect(MQTTClient* client, MQTTPacket_connectData* options); + +/** MQTT Publish - send an MQTT publish packet and wait for all acks to complete for all QoSs + * @param client - the client object to use + * @param topic - the topic to publish to + * @param message - the message to send + * @return success code + */ +DLLExport int MQTTPublish(MQTTClient* client, const char*, MQTTMessage*); + +/** MQTT SetMessageHandler - set or remove a per topic message handler + * @param client - the client object to use + * @param topicFilter - the topic filter set the message handler for + * @param messageHandler - pointer to the message handler function or NULL to remove + * @return success code + */ +DLLExport int MQTTSetMessageHandler(MQTTClient* c, const char* topicFilter, messageHandler messageHandler); + +/** MQTT Subscribe - send an MQTT subscribe packet and wait for suback before returning. + * @param client - the client object to use + * @param topicFilter - the topic filter to subscribe to + * @param message - the message to send + * @return success code + */ +DLLExport int MQTTSubscribe(MQTTClient* client, const char* topicFilter, enum QoS, messageHandler); + +/** MQTT Subscribe - send an MQTT subscribe packet and wait for suback before returning. + * @param client - the client object to use + * @param topicFilter - the topic filter to subscribe to + * @param message - the message to send + * @param data - suback granted QoS returned + * @return success code + */ +DLLExport int MQTTSubscribeWithResults(MQTTClient* client, const char* topicFilter, enum QoS, messageHandler, MQTTSubackData* data); + +/** MQTT Subscribe - send an MQTT unsubscribe packet and wait for unsuback before returning. + * @param client - the client object to use + * @param topicFilter - the topic filter to unsubscribe from + * @return success code + */ +DLLExport int MQTTUnsubscribe(MQTTClient* client, const char* topicFilter); + +/** MQTT Disconnect - send an MQTT disconnect packet and close the connection + * @param client - the client object to use + * @return success code + */ +DLLExport int MQTTDisconnect(MQTTClient* client); + +/** MQTT Yield - MQTT background + * @param client - the client object to use + * @param time - the time, in milliseconds, to yield for + * @return success code + */ +DLLExport int MQTTYield(MQTTClient* client, int time); + +/** MQTT isConnected + * @param client - the client object to use + * @return truth value indicating whether the client is connected to the server + */ +static inline DLLExport int MQTTIsConnected(MQTTClient* client) +{ + return client->isconnected; +} + +#if defined(MQTT_TASK) +/** MQTT start background thread for a client. After this, MQTTYield should not be called. +* @param client - the client object to use +* @return success code +*/ +DLLExport int MQTTStartTask(MQTTClient* client); +#endif + +#if defined(__cplusplus) + } +#endif + +#endif diff --git a/include/mqtt/MQTTConnect.h b/include/mqtt/MQTTConnect.h new file mode 100644 index 00000000..98c2c164 --- /dev/null +++ b/include/mqtt/MQTTConnect.h @@ -0,0 +1,148 @@ +/******************************************************************************* + * Copyright (c) 2014, 2017 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 - add connack return code definitions + * Xiang Rong - 442039 Add makefile to Embedded C client + * Ian Craggs - fix for issue #64, bit order in connack response + *******************************************************************************/ + +#ifndef MQTTCONNECT_H_ +#define MQTTCONNECT_H_ + +enum connack_return_codes +{ + MQTT_CONNECTION_ACCEPTED = 0, + MQTT_UNNACCEPTABLE_PROTOCOL = 1, + MQTT_CLIENTID_REJECTED = 2, + MQTT_SERVER_UNAVAILABLE = 3, + MQTT_BAD_USERNAME_OR_PASSWORD = 4, + MQTT_NOT_AUTHORIZED = 5, +}; + +#if !defined(DLLImport) + #define DLLImport +#endif +#if !defined(DLLExport) + #define DLLExport +#endif + + +typedef union +{ + unsigned char all; /**< all connect flags */ +#if defined(REVERSED) + struct + { + unsigned int username : 1; /**< 3.1 user name */ + unsigned int password : 1; /**< 3.1 password */ + unsigned int willRetain : 1; /**< will retain setting */ + unsigned int willQoS : 2; /**< will QoS value */ + unsigned int will : 1; /**< will flag */ + unsigned int cleansession : 1; /**< clean session flag */ + unsigned int : 1; /**< unused */ + } bits; +#else + struct + { + unsigned int : 1; /**< unused */ + unsigned int cleansession : 1; /**< cleansession flag */ + unsigned int will : 1; /**< will flag */ + unsigned int willQoS : 2; /**< will QoS value */ + unsigned int willRetain : 1; /**< will retain setting */ + unsigned int password : 1; /**< 3.1 password */ + unsigned int username : 1; /**< 3.1 user name */ + } bits; +#endif +} MQTTConnectFlags; /**< connect flags byte */ + + + +/** + * Defines the MQTT "Last Will and Testament" (LWT) settings for + * the connect packet. + */ +typedef struct +{ + /** The eyecatcher for this structure. must be MQTW. */ + char struct_id[4]; + /** The version number of this structure. Must be 0 */ + int struct_version; + /** The LWT topic to which the LWT message will be published. */ + MQTTString topicName; + /** The LWT payload. */ + MQTTString message; + /** + * The retained flag for the LWT message (see MQTTAsync_message.retained). + */ + unsigned char retained; + /** + * The quality of service setting for the LWT message (see + * MQTTAsync_message.qos and @ref qos). + */ + char qos; +} MQTTPacket_willOptions; + + +#define MQTTPacket_willOptions_initializer { {'M', 'Q', 'T', 'W'}, 0, {NULL, {0, NULL}}, {NULL, {0, NULL}}, 0, 0 } + + +typedef struct +{ + /** The eyecatcher for this structure. must be MQTC. */ + char struct_id[4]; + /** The version number of this structure. Must be 0 */ + int struct_version; + /** Version of MQTT to be used. 3 = 3.1 4 = 3.1.1 + */ + unsigned char MQTTVersion; + MQTTString clientID; + unsigned short keepAliveInterval; + unsigned char cleansession; + unsigned char willFlag; + MQTTPacket_willOptions will; + MQTTString username; + MQTTString password; +} MQTTPacket_connectData; + +typedef union +{ + unsigned char all; /**< all connack flags */ +#if defined(REVERSED) + struct + { + unsigned int reserved : 7; /**< unused */ + unsigned int sessionpresent : 1; /**< session present flag */ + } bits; +#else + struct + { + unsigned int sessionpresent : 1; /**< session present flag */ + unsigned int reserved: 7; /**< unused */ + } bits; +#endif +} MQTTConnackFlags; /**< connack flags byte */ + +#define MQTTPacket_connectData_initializer { {'M', 'Q', 'T', 'C'}, 0, 4, {NULL, {0, NULL}}, 60, 1, 0, \ + MQTTPacket_willOptions_initializer, {NULL, {0, NULL}}, {NULL, {0, NULL}} } + +DLLExport int MQTTSerialize_connect(unsigned char* buf, int buflen, MQTTPacket_connectData* options); +DLLExport int MQTTDeserialize_connect(MQTTPacket_connectData* data, unsigned char* buf, int len); + +DLLExport int MQTTSerialize_connack(unsigned char* buf, int buflen, unsigned char connack_rc, unsigned char sessionPresent); +DLLExport int MQTTDeserialize_connack(unsigned char* sessionPresent, unsigned char* connack_rc, unsigned char* buf, int buflen); + +DLLExport int MQTTSerialize_disconnect(unsigned char* buf, int buflen); +DLLExport int MQTTSerialize_pingreq(unsigned char* buf, int buflen); + +#endif /* MQTTCONNECT_H_ */ diff --git a/include/mqtt/MQTTFormat.h b/include/mqtt/MQTTFormat.h new file mode 100644 index 00000000..47b0c414 --- /dev/null +++ b/include/mqtt/MQTTFormat.h @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 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 + *******************************************************************************/ + +#if !defined(MQTTFORMAT_H) +#define MQTTFORMAT_H + +#include "StackTrace.h" +#include "MQTTPacket.h" + +const char* MQTTPacket_getName(unsigned short packetid); +int MQTTStringFormat_connect(char* strbuf, int strbuflen, MQTTPacket_connectData* data); +int MQTTStringFormat_connack(char* strbuf, int strbuflen, unsigned char connack_rc, unsigned char sessionPresent); +int MQTTStringFormat_publish(char* strbuf, int strbuflen, unsigned char dup, int qos, unsigned char retained, + unsigned short packetid, MQTTString topicName, unsigned char* payload, int payloadlen); +int MQTTStringFormat_ack(char* strbuf, int strbuflen, unsigned char packettype, unsigned char dup, unsigned short packetid); +int MQTTStringFormat_subscribe(char* strbuf, int strbuflen, unsigned char dup, unsigned short packetid, int count, + MQTTString topicFilters[], int requestedQoSs[]); +int MQTTStringFormat_suback(char* strbuf, int strbuflen, unsigned short packetid, int count, int* grantedQoSs); +int MQTTStringFormat_unsubscribe(char* strbuf, int strbuflen, unsigned char dup, unsigned short packetid, + int count, MQTTString topicFilters[]); +char* MQTTFormat_toClientString(char* strbuf, int strbuflen, unsigned char* buf, int buflen); +char* MQTTFormat_toServerString(char* strbuf, int strbuflen, unsigned char* buf, int buflen); + +#endif diff --git a/include/mqtt/MQTTFreeRTOS.h b/include/mqtt/MQTTFreeRTOS.h new file mode 100755 index 00000000..3ad67092 --- /dev/null +++ b/include/mqtt/MQTTFreeRTOS.h @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (c) 2014, 2015 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: + * Allan Stockdill-Mander - initial API and implementation and/or initial documentation + *******************************************************************************/ + +#if !defined(MQTTFreeRTOS_H) +#define MQTTFreeRTOS_H + +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "freertos/task.h" + +typedef struct Timer +{ + portTickType xTicksToWait; + xTimeOutType xTimeOut; +} Timer; + +typedef struct Network Network; + +struct Network +{ + int my_socket; + int (*mqttread) (Network*, unsigned char*, int, int); + int (*mqttwrite) (Network*, unsigned char*, int, int); + void (*disconnect) (Network*); +}; + +void TimerInit(Timer*); +char TimerIsExpired(Timer*); +void TimerCountdownMS(Timer*, unsigned int); +void TimerCountdown(Timer*, unsigned int); +int TimerLeftMS(Timer*); + +typedef struct Mutex +{ + xSemaphoreHandle sem; +} Mutex; + +void MutexInit(Mutex*); +int MutexLock(Mutex*); +int MutexUnlock(Mutex*); + +typedef struct Thread +{ + xTaskHandle task; +} Thread; + +int ThreadStart(Thread*, void (*fn)(void*), void* arg); + +int esp_read(Network*, unsigned char*, int, int); +int esp_write(Network*, unsigned char*, int, int); +void esp_disconnect(Network*); + +void NetworkInit(Network*); +int NetworkConnect(Network*, char*, int); +/*int NetworkConnectTLS(Network*, char*, int, SlSockSecureFiles_t*, unsigned char, unsigned int, char);*/ + +#endif diff --git a/include/mqtt/MQTTPacket.h b/include/mqtt/MQTTPacket.h new file mode 100644 index 00000000..0ee46231 --- /dev/null +++ b/include/mqtt/MQTTPacket.h @@ -0,0 +1,133 @@ +/******************************************************************************* + * Copyright (c) 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 + * Xiang Rong - 442039 Add makefile to Embedded C client + *******************************************************************************/ + +#ifndef MQTTPACKET_H_ +#define MQTTPACKET_H_ + +#if defined(__cplusplus) /* If this is a C++ compiler, use C linkage */ +extern "C" { +#endif + +#if defined(WIN32_DLL) || defined(WIN64_DLL) + #define DLLImport __declspec(dllimport) + #define DLLExport __declspec(dllexport) +#elif defined(LINUX_SO) + #define DLLImport extern + #define DLLExport __attribute__ ((visibility ("default"))) +#else + #define DLLImport + #define DLLExport +#endif + +enum errors +{ + MQTTPACKET_BUFFER_TOO_SHORT = -2, + MQTTPACKET_READ_ERROR = -1, + MQTTPACKET_READ_COMPLETE +}; + +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 */ + unsigned int dup : 1; /**< DUP flag bit */ + unsigned int qos : 2; /**< QoS value, 0, 1 or 2 */ + unsigned int retain : 1; /**< retained flag bit */ + } bits; +#else + struct + { + unsigned int retain : 1; /**< retained flag bit */ + unsigned int qos : 2; /**< QoS value, 0, 1 or 2 */ + unsigned int dup : 1; /**< DUP flag bit */ + unsigned int type : 4; /**< message type nibble */ + } bits; +#endif +} MQTTHeader; + +typedef struct +{ + int len; + char* data; +} MQTTLenString; + +typedef struct +{ + char* cstring; + MQTTLenString lenstring; +} MQTTString; + +#define MQTTString_initializer {NULL, {0, NULL}} + +int MQTTstrlen(MQTTString mqttstring); + +#include "MQTTConnect.h" +#include "MQTTPublish.h" +#include "MQTTSubscribe.h" +#include "MQTTUnsubscribe.h" +#include "MQTTFormat.h" + +DLLExport int MQTTSerialize_ack(unsigned char* buf, int buflen, unsigned char type, unsigned char dup, unsigned short packetid); +DLLExport int MQTTDeserialize_ack(unsigned char* packettype, unsigned char* dup, unsigned short* packetid, unsigned char* buf, int buflen); + +int MQTTPacket_len(int rem_len); +DLLExport int MQTTPacket_equals(MQTTString* a, char* b); + +DLLExport int MQTTPacket_encode(unsigned char* buf, int length); +int MQTTPacket_decode(int (*getcharfn)(unsigned char*, int), int* value); +int MQTTPacket_decodeBuf(unsigned char* buf, int* value); + +int readInt(unsigned char** pptr); +char readChar(unsigned char** pptr); +void writeChar(unsigned char** pptr, char c); +void writeInt(unsigned char** pptr, int anInt); +int readMQTTLenString(MQTTString* mqttstring, unsigned char** pptr, unsigned char* enddata); +void writeCString(unsigned char** pptr, const char* string); +void writeMQTTString(unsigned char** pptr, MQTTString mqttstring); + +DLLExport int MQTTPacket_read(unsigned char* buf, int buflen, int (*getfn)(unsigned char*, int)); + +typedef struct { + int (*getfn)(void *, unsigned char*, int); /* must return -1 for error, 0 for call again, or the number of bytes read */ + void *sck; /* pointer to whatever the system may use to identify the transport */ + int multiplier; + int rem_len; + int len; + char state; +}MQTTTransport; + +int MQTTPacket_readnb(unsigned char* buf, int buflen, MQTTTransport *trp); + +#ifdef __cplusplus /* If this is a C++ compiler, use C linkage */ +} +#endif + + +#endif /* MQTTPACKET_H_ */ diff --git a/include/mqtt/MQTTPublish.h b/include/mqtt/MQTTPublish.h new file mode 100644 index 00000000..ebe479dd --- /dev/null +++ b/include/mqtt/MQTTPublish.h @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 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 + * Xiang Rong - 442039 Add makefile to Embedded C client + *******************************************************************************/ + +#ifndef MQTTPUBLISH_H_ +#define MQTTPUBLISH_H_ + +#if !defined(DLLImport) + #define DLLImport +#endif +#if !defined(DLLExport) + #define DLLExport +#endif + +DLLExport int MQTTSerialize_publish(unsigned char* buf, int buflen, unsigned char dup, int qos, unsigned char retained, unsigned short packetid, + MQTTString topicName, unsigned char* payload, int payloadlen); + +DLLExport int MQTTDeserialize_publish(unsigned char* dup, int* qos, unsigned char* retained, unsigned short* packetid, MQTTString* topicName, + unsigned char** payload, int* payloadlen, unsigned char* buf, int len); + +DLLExport int MQTTSerialize_puback(unsigned char* buf, int buflen, unsigned short packetid); +DLLExport int MQTTSerialize_pubrel(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid); +DLLExport int MQTTSerialize_pubcomp(unsigned char* buf, int buflen, unsigned short packetid); + +#endif /* MQTTPUBLISH_H_ */ diff --git a/include/mqtt/MQTTSubscribe.h b/include/mqtt/MQTTSubscribe.h new file mode 100644 index 00000000..aa918268 --- /dev/null +++ b/include/mqtt/MQTTSubscribe.h @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 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 + * Xiang Rong - 442039 Add makefile to Embedded C client + *******************************************************************************/ + +#ifndef MQTTSUBSCRIBE_H_ +#define MQTTSUBSCRIBE_H_ + +#if !defined(DLLImport) + #define DLLImport +#endif +#if !defined(DLLExport) + #define DLLExport +#endif + +DLLExport int MQTTSerialize_subscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid, + int count, MQTTString topicFilters[], int requestedQoSs[]); + +DLLExport int MQTTDeserialize_subscribe(unsigned char* dup, unsigned short* packetid, + int maxcount, int* count, MQTTString topicFilters[], int requestedQoSs[], unsigned char* buf, int len); + +DLLExport int MQTTSerialize_suback(unsigned char* buf, int buflen, unsigned short packetid, int count, int* grantedQoSs); + +DLLExport int MQTTDeserialize_suback(unsigned short* packetid, int maxcount, int* count, int grantedQoSs[], unsigned char* buf, int len); + + +#endif /* MQTTSUBSCRIBE_H_ */ diff --git a/include/mqtt/MQTTUnsubscribe.h b/include/mqtt/MQTTUnsubscribe.h new file mode 100644 index 00000000..355ca9a4 --- /dev/null +++ b/include/mqtt/MQTTUnsubscribe.h @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 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 + * Xiang Rong - 442039 Add makefile to Embedded C client + *******************************************************************************/ + +#ifndef MQTTUNSUBSCRIBE_H_ +#define MQTTUNSUBSCRIBE_H_ + +#if !defined(DLLImport) + #define DLLImport +#endif +#if !defined(DLLExport) + #define DLLExport +#endif + +DLLExport int MQTTSerialize_unsubscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid, + int count, MQTTString topicFilters[]); + +DLLExport int MQTTDeserialize_unsubscribe(unsigned char* dup, unsigned short* packetid, int max_count, int* count, MQTTString topicFilters[], + unsigned char* buf, int len); + +DLLExport int MQTTSerialize_unsuback(unsigned char* buf, int buflen, unsigned short packetid); + +DLLExport int MQTTDeserialize_unsuback(unsigned short* packetid, unsigned char* buf, int len); + +#endif /* MQTTUNSUBSCRIBE_H_ */ diff --git a/include/mqtt/StackTrace.h b/include/mqtt/StackTrace.h new file mode 100644 index 00000000..2808a0d1 --- /dev/null +++ b/include/mqtt/StackTrace.h @@ -0,0 +1,78 @@ +/******************************************************************************* + * Copyright (c) 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 - fix for bug #434081 + *******************************************************************************/ + +#ifndef STACKTRACE_H_ +#define STACKTRACE_H_ + +#include +#define NOSTACKTRACE 1 + +#if defined(NOSTACKTRACE) +#define FUNC_ENTRY +#define FUNC_ENTRY_NOLOG +#define FUNC_ENTRY_MED +#define FUNC_ENTRY_MAX +#define FUNC_EXIT +#define FUNC_EXIT_NOLOG +#define FUNC_EXIT_MED +#define FUNC_EXIT_MAX +#define FUNC_EXIT_RC(x) +#define FUNC_EXIT_MED_RC(x) +#define FUNC_EXIT_MAX_RC(x) + +#else + +#if defined(WIN32) +#define inline __inline +#define FUNC_ENTRY StackTrace_entry(__FUNCTION__, __LINE__, TRACE_MINIMUM) +#define FUNC_ENTRY_NOLOG StackTrace_entry(__FUNCTION__, __LINE__, -1) +#define FUNC_ENTRY_MED StackTrace_entry(__FUNCTION__, __LINE__, TRACE_MEDIUM) +#define FUNC_ENTRY_MAX StackTrace_entry(__FUNCTION__, __LINE__, TRACE_MAXIMUM) +#define FUNC_EXIT StackTrace_exit(__FUNCTION__, __LINE__, NULL, TRACE_MINIMUM) +#define FUNC_EXIT_NOLOG StackTrace_exit(__FUNCTION__, __LINE__, -1) +#define FUNC_EXIT_MED StackTrace_exit(__FUNCTION__, __LINE__, NULL, TRACE_MEDIUM) +#define FUNC_EXIT_MAX StackTrace_exit(__FUNCTION__, __LINE__, NULL, TRACE_MAXIMUM) +#define FUNC_EXIT_RC(x) StackTrace_exit(__FUNCTION__, __LINE__, &x, TRACE_MINIMUM) +#define FUNC_EXIT_MED_RC(x) StackTrace_exit(__FUNCTION__, __LINE__, &x, TRACE_MEDIUM) +#define FUNC_EXIT_MAX_RC(x) StackTrace_exit(__FUNCTION__, __LINE__, &x, TRACE_MAXIMUM) +#else +#define FUNC_ENTRY StackTrace_entry(__func__, __LINE__, TRACE_MINIMUM) +#define FUNC_ENTRY_NOLOG StackTrace_entry(__func__, __LINE__, -1) +#define FUNC_ENTRY_MED StackTrace_entry(__func__, __LINE__, TRACE_MEDIUM) +#define FUNC_ENTRY_MAX StackTrace_entry(__func__, __LINE__, TRACE_MAXIMUM) +#define FUNC_EXIT StackTrace_exit(__func__, __LINE__, NULL, TRACE_MINIMUM) +#define FUNC_EXIT_NOLOG StackTrace_exit(__func__, __LINE__, NULL, -1) +#define FUNC_EXIT_MED StackTrace_exit(__func__, __LINE__, NULL, TRACE_MEDIUM) +#define FUNC_EXIT_MAX StackTrace_exit(__func__, __LINE__, NULL, TRACE_MAXIMUM) +#define FUNC_EXIT_RC(x) StackTrace_exit(__func__, __LINE__, &x, TRACE_MINIMUM) +#define FUNC_EXIT_MED_RC(x) StackTrace_exit(__func__, __LINE__, &x, TRACE_MEDIUM) +#define FUNC_EXIT_MAX_RC(x) StackTrace_exit(__func__, __LINE__, &x, TRACE_MAXIMUM) + +void StackTrace_entry(const char* name, int line, int trace); +void StackTrace_exit(const char* name, int line, void* return_value, int trace); + +void StackTrace_printStack(FILE* dest); +char* StackTrace_get(unsigned long); + +#endif + +#endif + + + + +#endif /* STACKTRACE_H_ */ diff --git a/lib/libmqtt.a b/lib/libmqtt.a new file mode 100644 index 0000000000000000000000000000000000000000..20f27ae687e7d9a741ad2e55ee52e5da5ac87d62 GIT binary patch literal 77208 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 OnEQx6|6(g +#include + +static void NewMessageData(MessageData* md, MQTTString* aTopicName, MQTTMessage* aMessage) { + md->topicName = aTopicName; + md->message = aMessage; +} + + +static int getNextPacketId(MQTTClient *c) { + return c->next_packetid = (c->next_packetid == MAX_PACKET_ID) ? 1 : c->next_packetid + 1; +} + + +static int sendPacket(MQTTClient* c, int length, Timer* timer) +{ + int rc = FAILURE, + sent = 0; + + while (sent < length && !TimerIsExpired(timer)) + { + rc = c->ipstack->mqttwrite(c->ipstack, &c->buf[sent], length, TimerLeftMS(timer)); + if (rc < 0) // there was an error writing the data + break; + sent += rc; + } + if (sent == length) + { + TimerCountdown(&c->last_sent, c->keepAliveInterval); // record the fact that we have successfully sent the packet + rc = SUCCESS; + } + else + rc = FAILURE; + return rc; +} + + +void MQTTClientInit(MQTTClient* c, Network* network, unsigned int command_timeout_ms, + unsigned char* sendbuf, size_t sendbuf_size, unsigned char* readbuf, size_t readbuf_size) +{ + int i; + c->ipstack = network; + + for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i) + c->messageHandlers[i].topicFilter = 0; + c->command_timeout_ms = command_timeout_ms; + c->buf = sendbuf; + c->buf_size = sendbuf_size; + c->readbuf = readbuf; + c->readbuf_size = readbuf_size; + c->isconnected = 0; + c->cleansession = 0; + c->ping_outstanding = 0; + c->defaultMessageHandler = NULL; + c->next_packetid = 1; + TimerInit(&c->last_sent); + TimerInit(&c->last_received); +#if defined(MQTT_TASK) + MutexInit(&c->mutex); +#endif +} + + +static int decodePacket(MQTTClient* c, int* value, int timeout) +{ + unsigned char i; + 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 = c->ipstack->mqttread(c->ipstack, &i, 1, timeout); + if (rc != 1) + goto exit; + *value += (i & 127) * multiplier; + multiplier *= 128; + } while ((i & 128) != 0); +exit: + return len; +} + + +static int readPacket(MQTTClient* c, Timer* timer) +{ + MQTTHeader header = {0}; + int len = 0; + int rem_len = 0; + + /* 1. read the header byte. This has the packet type in it */ + int rc = c->ipstack->mqttread(c->ipstack, c->readbuf, 1, TimerLeftMS(timer)); + if (rc != 1) + goto exit; + + len = 1; + /* 2. read the remaining length. This is variable in itself */ + decodePacket(c, &rem_len, TimerLeftMS(timer)); + len += MQTTPacket_encode(c->readbuf + 1, rem_len); /* put the original remaining length back into the buffer */ + + if (rem_len > (c->readbuf_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 && (rc = c->ipstack->mqttread(c->ipstack, c->readbuf + len, rem_len, TimerLeftMS(timer)) != rem_len)) { + rc = 0; + goto exit; + } + + header.byte = c->readbuf[0]; + rc = header.bits.type; + if (c->keepAliveInterval > 0) + TimerCountdown(&c->last_received, c->keepAliveInterval); // record the fact that we have successfully received a packet +exit: + return rc; +} + + +// assume topic filter and name is in correct format +// # can only be at end +// + and # can only be next to separator +static char isTopicMatched(char* topicFilter, MQTTString* topicName) +{ + char* curf = topicFilter; + char* curn = topicName->lenstring.data; + char* curn_end = curn + topicName->lenstring.len; + + while (*curf && curn < curn_end) + { + if (*curn == '/' && *curf != '/') + break; + if (*curf != '+' && *curf != '#' && *curf != *curn) + break; + if (*curf == '+') + { // skip until we meet the next separator, or end of string + char* nextpos = curn + 1; + while (nextpos < curn_end && *nextpos != '/') + nextpos = ++curn + 1; + } + else if (*curf == '#') + curn = curn_end - 1; // skip until end of string + curf++; + curn++; + }; + + return (curn == curn_end) && (*curf == '\0'); +} + + +int deliverMessage(MQTTClient* c, MQTTString* topicName, MQTTMessage* message) +{ + int i; + int rc = FAILURE; + + // we have to find the right message handler - indexed by topic + for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i) + { + if (c->messageHandlers[i].topicFilter != 0 && (MQTTPacket_equals(topicName, (char*)c->messageHandlers[i].topicFilter) || + isTopicMatched((char*)c->messageHandlers[i].topicFilter, topicName))) + { + if (c->messageHandlers[i].fp != NULL) + { + MessageData md; + NewMessageData(&md, topicName, message); + c->messageHandlers[i].fp(&md); + rc = SUCCESS; + } + } + } + + if (rc == FAILURE && c->defaultMessageHandler != NULL) + { + MessageData md; + NewMessageData(&md, topicName, message); + c->defaultMessageHandler(&md); + rc = SUCCESS; + } + + return rc; +} + + +int keepalive(MQTTClient* c) +{ + int rc = SUCCESS; + + if (c->keepAliveInterval == 0) + goto exit; + + if (TimerIsExpired(&c->last_sent) || TimerIsExpired(&c->last_received)) + { + if (c->ping_outstanding) + rc = FAILURE; /* PINGRESP not received in keepalive interval */ + else + { + Timer timer; + TimerInit(&timer); + TimerCountdownMS(&timer, 1000); + int len = MQTTSerialize_pingreq(c->buf, c->buf_size); + if (len > 0 && (rc = sendPacket(c, len, &timer)) == SUCCESS) // send the ping packet + c->ping_outstanding = 1; + } + } + +exit: + return rc; +} + + +void MQTTCleanSession(MQTTClient* c) +{ + int i = 0; + + for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i) + c->messageHandlers[i].topicFilter = NULL; +} + + +void MQTTCloseSession(MQTTClient* c) +{ + c->ping_outstanding = 0; + c->isconnected = 0; + if (c->cleansession) + MQTTCleanSession(c); +} + + +int cycle(MQTTClient* c, Timer* timer) +{ + int len = 0, + rc = SUCCESS; + + int packet_type = readPacket(c, timer); /* read the socket, see what work is due */ + + switch (packet_type) + { + default: + /* no more data to read, unrecoverable. Or read packet fails due to unexpected network error */ + rc = packet_type; + goto exit; + case 0: /* timed out reading packet */ + break; + case CONNACK: + case PUBACK: + case SUBACK: + case UNSUBACK: + break; + case PUBLISH: + { + MQTTString topicName; + MQTTMessage msg; + int intQoS; + msg.payloadlen = 0; /* this is a size_t, but deserialize publish sets this as int */ + if (MQTTDeserialize_publish(&msg.dup, &intQoS, &msg.retained, &msg.id, &topicName, + (unsigned char**)&msg.payload, (int*)&msg.payloadlen, c->readbuf, c->readbuf_size) != 1) + goto exit; + msg.qos = (enum QoS)intQoS; + deliverMessage(c, &topicName, &msg); + if (msg.qos != QOS0) + { + if (msg.qos == QOS1) + len = MQTTSerialize_ack(c->buf, c->buf_size, PUBACK, 0, msg.id); + else if (msg.qos == QOS2) + len = MQTTSerialize_ack(c->buf, c->buf_size, PUBREC, 0, msg.id); + if (len <= 0) + rc = FAILURE; + else + rc = sendPacket(c, len, timer); + if (rc == FAILURE) + goto exit; // there was a problem + } + break; + } + case PUBREC: + case PUBREL: + { + unsigned short mypacketid; + unsigned char dup, type; + if (MQTTDeserialize_ack(&type, &dup, &mypacketid, c->readbuf, c->readbuf_size) != 1) + rc = FAILURE; + else if ((len = MQTTSerialize_ack(c->buf, c->buf_size, + (packet_type == PUBREC) ? PUBREL : PUBCOMP, 0, mypacketid)) <= 0) + rc = FAILURE; + else if ((rc = sendPacket(c, len, timer)) != SUCCESS) // send the PUBREL packet + rc = FAILURE; // there was a problem + if (rc == FAILURE) + goto exit; // there was a problem + break; + } + + case PUBCOMP: + break; + case PINGRESP: + c->ping_outstanding = 0; + break; + } + + if (keepalive(c) != SUCCESS) { + //check only keepalive FAILURE status so that previous FAILURE status can be considered as FAULT + rc = FAILURE; + } + +exit: + if (rc == SUCCESS) + rc = packet_type; + else if (c->isconnected) + MQTTCloseSession(c); + return rc; +} + + +int MQTTYield(MQTTClient* c, int timeout_ms) +{ + int rc = SUCCESS; + Timer timer; + + TimerInit(&timer); + TimerCountdownMS(&timer, timeout_ms); + + do + { + if (cycle(c, &timer) < 0) + { + rc = FAILURE; + break; + } + } while (!TimerIsExpired(&timer)); + + return rc; +} + + +void MQTTRun(void* parm) +{ + Timer timer; + MQTTClient* c = (MQTTClient*)parm; + + TimerInit(&timer); + + while (1) + { +#if defined(MQTT_TASK) + MutexLock(&c->mutex); +#endif + TimerCountdownMS(&timer, 500); /* Don't wait too long if no traffic is incoming */ + cycle(c, &timer); +#if defined(MQTT_TASK) + MutexUnlock(&c->mutex); +#endif + } +} + + +#if defined(MQTT_TASK) +int MQTTStartTask(MQTTClient* client) +{ + return ThreadStart(&client->thread, &MQTTRun, client); +} +#endif + + +int waitfor(MQTTClient* c, int packet_type, Timer* timer) +{ + int rc = FAILURE; + + do + { + if (TimerIsExpired(timer)) + break; // we timed out + rc = cycle(c, timer); + } + while (rc != packet_type && rc >= 0); + + return rc; +} + + + + +int MQTTConnectWithResults(MQTTClient* c, MQTTPacket_connectData* options, MQTTConnackData* data) +{ + Timer connect_timer; + int rc = FAILURE; + MQTTPacket_connectData default_options = MQTTPacket_connectData_initializer; + int len = 0; + +#if defined(MQTT_TASK) + MutexLock(&c->mutex); +#endif + if (c->isconnected) /* don't send connect packet again if we are already connected */ + goto exit; + + TimerInit(&connect_timer); + TimerCountdownMS(&connect_timer, c->command_timeout_ms); + + if (options == 0) + options = &default_options; /* set default options if none were supplied */ + + c->keepAliveInterval = options->keepAliveInterval; + c->cleansession = options->cleansession; + TimerCountdown(&c->last_received, c->keepAliveInterval); + if ((len = MQTTSerialize_connect(c->buf, c->buf_size, options)) <= 0) + goto exit; + if ((rc = sendPacket(c, len, &connect_timer)) != SUCCESS) // send the connect packet + goto exit; // there was a problem + + // this will be a blocking call, wait for the connack + if (waitfor(c, CONNACK, &connect_timer) == CONNACK) + { + data->rc = 0; + data->sessionPresent = 0; + if (MQTTDeserialize_connack(&data->sessionPresent, &data->rc, c->readbuf, c->readbuf_size) == 1) + rc = data->rc; + else + rc = FAILURE; + } + else + rc = FAILURE; + +exit: + if (rc == SUCCESS) + { + c->isconnected = 1; + c->ping_outstanding = 0; + } + +#if defined(MQTT_TASK) + MutexUnlock(&c->mutex); +#endif + + return rc; +} + + +int MQTTConnect(MQTTClient* c, MQTTPacket_connectData* options) +{ + MQTTConnackData data; + return MQTTConnectWithResults(c, options, &data); +} + + +int MQTTSetMessageHandler(MQTTClient* c, const char* topicFilter, messageHandler messageHandler) +{ + int rc = FAILURE; + int i = -1; + + /* first check for an existing matching slot */ + for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i) + { + if (c->messageHandlers[i].topicFilter != NULL && strcmp(c->messageHandlers[i].topicFilter, topicFilter) == 0) + { + if (messageHandler == NULL) /* remove existing */ + { + c->messageHandlers[i].topicFilter = NULL; + c->messageHandlers[i].fp = NULL; + } + rc = SUCCESS; /* return i when adding new subscription */ + break; + } + } + /* if no existing, look for empty slot (unless we are removing) */ + if (messageHandler != NULL) { + if (rc == FAILURE) + { + for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i) + { + if (c->messageHandlers[i].topicFilter == NULL) + { + rc = SUCCESS; + break; + } + } + } + if (i < MAX_MESSAGE_HANDLERS) + { + c->messageHandlers[i].topicFilter = topicFilter; + c->messageHandlers[i].fp = messageHandler; + } + } + return rc; +} + + +int MQTTSubscribeWithResults(MQTTClient* c, const char* topicFilter, enum QoS qos, + messageHandler messageHandler, MQTTSubackData* data) +{ + int rc = FAILURE; + Timer timer; + int len = 0; + MQTTString topic = MQTTString_initializer; + topic.cstring = (char *)topicFilter; + +#if defined(MQTT_TASK) + MutexLock(&c->mutex); +#endif + if (!c->isconnected) + goto exit; + + TimerInit(&timer); + TimerCountdownMS(&timer, c->command_timeout_ms); + + len = MQTTSerialize_subscribe(c->buf, c->buf_size, 0, getNextPacketId(c), 1, &topic, (int*)&qos); + if (len <= 0) + goto exit; + if ((rc = sendPacket(c, len, &timer)) != SUCCESS) // send the subscribe packet + goto exit; // there was a problem + + if (waitfor(c, SUBACK, &timer) == SUBACK) // wait for suback + { + int count = 0; + unsigned short mypacketid; + data->grantedQoS = QOS0; + if (MQTTDeserialize_suback(&mypacketid, 1, &count, (int*)&data->grantedQoS, c->readbuf, c->readbuf_size) == 1) + { + if (data->grantedQoS != 0x80) + rc = MQTTSetMessageHandler(c, topicFilter, messageHandler); + } + } + else + rc = FAILURE; + +exit: + if (rc == FAILURE) + MQTTCloseSession(c); +#if defined(MQTT_TASK) + MutexUnlock(&c->mutex); +#endif + return rc; +} + + +int MQTTSubscribe(MQTTClient* c, const char* topicFilter, enum QoS qos, + messageHandler messageHandler) +{ + MQTTSubackData data; + return MQTTSubscribeWithResults(c, topicFilter, qos, messageHandler, &data); +} + + +int MQTTUnsubscribe(MQTTClient* c, const char* topicFilter) +{ + int rc = FAILURE; + Timer timer; + MQTTString topic = MQTTString_initializer; + topic.cstring = (char *)topicFilter; + int len = 0; + +#if defined(MQTT_TASK) + MutexLock(&c->mutex); +#endif + if (!c->isconnected) + goto exit; + + TimerInit(&timer); + TimerCountdownMS(&timer, c->command_timeout_ms); + + if ((len = MQTTSerialize_unsubscribe(c->buf, c->buf_size, 0, getNextPacketId(c), 1, &topic)) <= 0) + goto exit; + if ((rc = sendPacket(c, len, &timer)) != SUCCESS) // send the subscribe packet + goto exit; // there was a problem + + if (waitfor(c, UNSUBACK, &timer) == UNSUBACK) + { + unsigned short mypacketid; // should be the same as the packetid above + if (MQTTDeserialize_unsuback(&mypacketid, c->readbuf, c->readbuf_size) == 1) + { + /* remove the subscription message handler associated with this topic, if there is one */ + MQTTSetMessageHandler(c, topicFilter, NULL); + } + } + else + rc = FAILURE; + +exit: + if (rc == FAILURE) + MQTTCloseSession(c); +#if defined(MQTT_TASK) + MutexUnlock(&c->mutex); +#endif + return rc; +} + + +int MQTTPublish(MQTTClient* c, const char* topicName, MQTTMessage* message) +{ + int rc = FAILURE; + Timer timer; + MQTTString topic = MQTTString_initializer; + topic.cstring = (char *)topicName; + int len = 0; + +#if defined(MQTT_TASK) + MutexLock(&c->mutex); +#endif + if (!c->isconnected) + goto exit; + + TimerInit(&timer); + TimerCountdownMS(&timer, c->command_timeout_ms); + + if (message->qos == QOS1 || message->qos == QOS2) + message->id = getNextPacketId(c); + + len = MQTTSerialize_publish(c->buf, c->buf_size, 0, message->qos, message->retained, message->id, + topic, (unsigned char*)message->payload, message->payloadlen); + if (len <= 0) + goto exit; + if ((rc = sendPacket(c, len, &timer)) != SUCCESS) // send the subscribe packet + goto exit; // there was a problem + + if (message->qos == QOS1) + { + if (waitfor(c, PUBACK, &timer) == PUBACK) + { + unsigned short mypacketid; + unsigned char dup, type; + if (MQTTDeserialize_ack(&type, &dup, &mypacketid, c->readbuf, c->readbuf_size) != 1) + rc = FAILURE; + } + else + rc = FAILURE; + } + else if (message->qos == QOS2) + { + if (waitfor(c, PUBCOMP, &timer) == PUBCOMP) + { + unsigned short mypacketid; + unsigned char dup, type; + if (MQTTDeserialize_ack(&type, &dup, &mypacketid, c->readbuf, c->readbuf_size) != 1) + rc = FAILURE; + } + else + rc = FAILURE; + } + +exit: + if (rc == FAILURE) + MQTTCloseSession(c); +#if defined(MQTT_TASK) + MutexUnlock(&c->mutex); +#endif + return rc; +} + + +int MQTTDisconnect(MQTTClient* c) +{ + int rc = FAILURE; + Timer timer; // we might wait for incomplete incoming publishes to complete + int len = 0; + +#if defined(MQTT_TASK) + MutexLock(&c->mutex); +#endif + TimerInit(&timer); + TimerCountdownMS(&timer, c->command_timeout_ms); + + len = MQTTSerialize_disconnect(c->buf, c->buf_size); + if (len > 0) + rc = sendPacket(c, len, &timer); // send the disconnect packet + MQTTCloseSession(c); + +#if defined(MQTT_TASK) + MutexUnlock(&c->mutex); +#endif + return rc; +} diff --git a/third_party/mqtt/library/MQTTConnectClient.c b/third_party/mqtt/library/MQTTConnectClient.c new file mode 100644 index 00000000..5f3cc296 --- /dev/null +++ b/third_party/mqtt/library/MQTTConnectClient.c @@ -0,0 +1,214 @@ +/******************************************************************************* + * Copyright (c) 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 + *******************************************************************************/ + +#include "MQTTPacket.h" +#include "StackTrace.h" + +#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 + * @return the length of buffer needed to contain the serialized version of the packet + */ +int MQTTSerialize_connectLength(MQTTPacket_connectData* options) +{ + int len = 0; + + FUNC_ENTRY; + + if (options->MQTTVersion == 3) + len = 12; /* variable depending on MQTT or MQIsdp */ + else if (options->MQTTVersion == 4) + len = 10; + + len += MQTTstrlen(options->clientID)+2; + if (options->willFlag) + len += MQTTstrlen(options->will.topicName)+2 + MQTTstrlen(options->will.message)+2; + if (options->username.cstring || options->username.lenstring.data) + len += MQTTstrlen(options->username)+2; + if (options->password.cstring || options->password.lenstring.data) + len += MQTTstrlen(options->password)+2; + + FUNC_EXIT_RC(len); + return len; +} + + +/** + * Serializes the connect options into the buffer. + * @param buf the buffer into which the packet will be serialized + * @param len the length in bytes of the supplied buffer + * @param options the options to be used to build the connect packet + * @return serialized length, or error if 0 + */ +int MQTTSerialize_connect(unsigned char* buf, int buflen, MQTTPacket_connectData* options) +{ + unsigned char *ptr = buf; + MQTTHeader header = {0}; + MQTTConnectFlags flags = {0}; + int len = 0; + int rc = -1; + + FUNC_ENTRY; + if (MQTTPacket_len(len = MQTTSerialize_connectLength(options)) > buflen) + { + rc = MQTTPACKET_BUFFER_TOO_SHORT; + goto exit; + } + + header.byte = 0; + header.bits.type = CONNECT; + writeChar(&ptr, header.byte); /* write header */ + + ptr += MQTTPacket_encode(ptr, len); /* write remaining length */ + + if (options->MQTTVersion == 4) + { + writeCString(&ptr, "MQTT"); + writeChar(&ptr, (char) 4); + } + else + { + writeCString(&ptr, "MQIsdp"); + writeChar(&ptr, (char) 3); + } + + flags.all = 0; + flags.bits.cleansession = options->cleansession; + flags.bits.will = (options->willFlag) ? 1 : 0; + if (flags.bits.will) + { + flags.bits.willQoS = options->will.qos; + flags.bits.willRetain = options->will.retained; + } + + if (options->username.cstring || options->username.lenstring.data) + flags.bits.username = 1; + if (options->password.cstring || options->password.lenstring.data) + flags.bits.password = 1; + + writeChar(&ptr, flags.all); + writeInt(&ptr, options->keepAliveInterval); + writeMQTTString(&ptr, options->clientID); + if (options->willFlag) + { + writeMQTTString(&ptr, options->will.topicName); + writeMQTTString(&ptr, options->will.message); + } + if (flags.bits.username) + writeMQTTString(&ptr, options->username); + if (flags.bits.password) + writeMQTTString(&ptr, options->password); + + rc = ptr - buf; + + exit: FUNC_EXIT_RC(rc); + return rc; +} + + +/** + * Deserializes the supplied (wire) buffer into connack data - return code + * @param sessionPresent the session present flag returned (only for MQTT 3.1.1) + * @param connack_rc returned integer value of the connack return code + * @param buf the raw buffer data, of the correct length determined by the remaining length field + * @param len the length in bytes of the data in the supplied buffer + * @return error code. 1 is success, 0 is failure + */ +int MQTTDeserialize_connack(unsigned char* sessionPresent, unsigned char* connack_rc, unsigned char* buf, int buflen) +{ + MQTTHeader header = {0}; + unsigned char* curdata = buf; + unsigned char* enddata = NULL; + int rc = 0; + int mylen; + MQTTConnackFlags flags = {0}; + + FUNC_ENTRY; + header.byte = readChar(&curdata); + if (header.bits.type != CONNACK) + goto exit; + + curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */ + enddata = curdata + mylen; + if (enddata - curdata < 2) + goto exit; + + flags.all = readChar(&curdata); + *sessionPresent = flags.bits.sessionpresent; + *connack_rc = readChar(&curdata); + + rc = 1; +exit: + FUNC_EXIT_RC(rc); + return rc; +} + + +/** + * Serializes a 0-length packet into the supplied buffer, ready for writing to a socket + * @param buf the buffer into which the packet will be serialized + * @param buflen the length in bytes of the supplied buffer, to avoid overruns + * @param packettype the message type + * @return serialized length, or error if 0 + */ +int MQTTSerialize_zero(unsigned char* buf, int buflen, unsigned char packettype) +{ + MQTTHeader header = {0}; + int rc = -1; + unsigned char *ptr = buf; + + FUNC_ENTRY; + if (buflen < 2) + { + rc = MQTTPACKET_BUFFER_TOO_SHORT; + goto exit; + } + header.byte = 0; + header.bits.type = packettype; + writeChar(&ptr, header.byte); /* write header */ + + ptr += MQTTPacket_encode(ptr, 0); /* write remaining length */ + rc = ptr - buf; +exit: + FUNC_EXIT_RC(rc); + return rc; +} + + +/** + * Serializes a disconnect packet into the supplied buffer, ready for writing to a socket + * @param buf the buffer into which the packet will be serialized + * @param buflen the length in bytes of the supplied buffer, to avoid overruns + * @return serialized length, or error if 0 + */ +int MQTTSerialize_disconnect(unsigned char* buf, int buflen) +{ + return MQTTSerialize_zero(buf, buflen, DISCONNECT); +} + + +/** + * Serializes a disconnect packet into the supplied buffer, ready for writing to a socket + * @param buf the buffer into which the packet will be serialized + * @param buflen the length in bytes of the supplied buffer, to avoid overruns + * @return serialized length, or error if 0 + */ +int MQTTSerialize_pingreq(unsigned char* buf, int buflen) +{ + return MQTTSerialize_zero(buf, buflen, PINGREQ); +} diff --git a/third_party/mqtt/library/MQTTConnectServer.c b/third_party/mqtt/library/MQTTConnectServer.c new file mode 100644 index 00000000..07c7cb53 --- /dev/null +++ b/third_party/mqtt/library/MQTTConnectServer.c @@ -0,0 +1,148 @@ +/******************************************************************************* + * Copyright (c) 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 + *******************************************************************************/ + +#include "StackTrace.h" +#include "MQTTPacket.h" +#include + +#define min(a, b) ((a < b) ? a : b) + + +/** + * Validates MQTT protocol name and version combinations + * @param protocol the MQTT protocol name as an MQTTString + * @param version the MQTT protocol version number, as in the connect packet + * @return correct MQTT combination? 1 is true, 0 is false + */ +int MQTTPacket_checkVersion(MQTTString* protocol, int version) +{ + int rc = 0; + + if (version == 3 && memcmp(protocol->lenstring.data, "MQIsdp", + min(6, protocol->lenstring.len)) == 0) + rc = 1; + else if (version == 4 && memcmp(protocol->lenstring.data, "MQTT", + min(4, protocol->lenstring.len)) == 0) + rc = 1; + return rc; +} + + +/** + * Deserializes the supplied (wire) buffer into connect data structure + * @param data the connect data structure to be filled out + * @param buf the raw buffer data, of the correct length determined by the remaining length field + * @param len the length in bytes of the data in the supplied buffer + * @return error code. 1 is success, 0 is failure + */ +int MQTTDeserialize_connect(MQTTPacket_connectData* data, unsigned char* buf, int len) +{ + MQTTHeader header = {0}; + MQTTConnectFlags flags = {0}; + unsigned char* curdata = buf; + unsigned char* enddata = &buf[len]; + int rc = 0; + MQTTString Protocol; + int version; + int mylen = 0; + + FUNC_ENTRY; + header.byte = readChar(&curdata); + if (header.bits.type != CONNECT) + goto exit; + + curdata += MQTTPacket_decodeBuf(curdata, &mylen); /* read remaining length */ + + if (!readMQTTLenString(&Protocol, &curdata, enddata) || + enddata - curdata < 0) /* do we have enough data to read the protocol version byte? */ + goto exit; + + version = (int)readChar(&curdata); /* Protocol version */ + /* If we don't recognize the protocol version, we don't parse the connect packet on the + * basis that we don't know what the format will be. + */ + if (MQTTPacket_checkVersion(&Protocol, version)) + { + flags.all = readChar(&curdata); + data->cleansession = flags.bits.cleansession; + data->keepAliveInterval = readInt(&curdata); + if (!readMQTTLenString(&data->clientID, &curdata, enddata)) + goto exit; + data->willFlag = flags.bits.will; + if (flags.bits.will) + { + data->will.qos = flags.bits.willQoS; + data->will.retained = flags.bits.willRetain; + if (!readMQTTLenString(&data->will.topicName, &curdata, enddata) || + !readMQTTLenString(&data->will.message, &curdata, enddata)) + goto exit; + } + if (flags.bits.username) + { + if (enddata - curdata < 3 || !readMQTTLenString(&data->username, &curdata, enddata)) + goto exit; /* username flag set, but no username supplied - invalid */ + if (flags.bits.password && + (enddata - curdata < 3 || !readMQTTLenString(&data->password, &curdata, enddata))) + goto exit; /* password flag set, but no password supplied - invalid */ + } + else if (flags.bits.password) + goto exit; /* password flag set without username - invalid */ + rc = 1; + } +exit: + FUNC_EXIT_RC(rc); + return rc; +} + + +/** + * Serializes the connack packet into the supplied buffer. + * @param buf the buffer into which the packet will be serialized + * @param buflen the length in bytes of the supplied buffer + * @param connack_rc the integer connack return code to be used + * @param sessionPresent the MQTT 3.1.1 sessionPresent flag + * @return serialized length, or error if 0 + */ +int MQTTSerialize_connack(unsigned char* buf, int buflen, unsigned char connack_rc, unsigned char sessionPresent) +{ + MQTTHeader header = {0}; + int rc = 0; + unsigned char *ptr = buf; + MQTTConnackFlags flags = {0}; + + FUNC_ENTRY; + if (buflen < 2) + { + rc = MQTTPACKET_BUFFER_TOO_SHORT; + goto exit; + } + header.byte = 0; + header.bits.type = CONNACK; + writeChar(&ptr, header.byte); /* write header */ + + ptr += MQTTPacket_encode(ptr, 2); /* write remaining length */ + + flags.all = 0; + flags.bits.sessionpresent = sessionPresent; + writeChar(&ptr, flags.all); + writeChar(&ptr, connack_rc); + + rc = ptr - buf; +exit: + FUNC_EXIT_RC(rc); + return rc; +} + diff --git a/third_party/mqtt/library/MQTTDeserializePublish.c b/third_party/mqtt/library/MQTTDeserializePublish.c new file mode 100644 index 00000000..dafb6a32 --- /dev/null +++ b/third_party/mqtt/library/MQTTDeserializePublish.c @@ -0,0 +1,107 @@ +/******************************************************************************* + * Copyright (c) 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 + *******************************************************************************/ + +#include "StackTrace.h" +#include "MQTTPacket.h" +#include + +#define min(a, b) ((a < b) ? 1 : 0) + +/** + * Deserializes the supplied (wire) buffer into publish data + * @param dup returned integer - the MQTT dup flag + * @param qos returned integer - the MQTT QoS value + * @param retained returned integer - the MQTT retained flag + * @param packetid returned integer - the MQTT packet identifier + * @param topicName returned MQTTString - the MQTT topic in the publish + * @param payload returned byte buffer - the MQTT publish payload + * @param payloadlen returned integer - the length of the MQTT payload + * @param buf the raw buffer data, of the correct length determined by the remaining length field + * @param buflen the length in bytes of the data in the supplied buffer + * @return error code. 1 is success + */ +int MQTTDeserialize_publish(unsigned char* dup, int* qos, unsigned char* retained, unsigned short* packetid, MQTTString* topicName, + unsigned char** payload, int* payloadlen, unsigned char* buf, int buflen) +{ + MQTTHeader header = {0}; + unsigned char* curdata = buf; + unsigned char* enddata = NULL; + int rc = 0; + int mylen = 0; + + FUNC_ENTRY; + header.byte = readChar(&curdata); + if (header.bits.type != PUBLISH) + goto exit; + *dup = header.bits.dup; + *qos = header.bits.qos; + *retained = header.bits.retain; + + curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */ + enddata = curdata + mylen; + + if (!readMQTTLenString(topicName, &curdata, enddata) || + enddata - curdata < 0) /* do we have enough data to read the protocol version byte? */ + goto exit; + + if (*qos > 0) + *packetid = readInt(&curdata); + + *payloadlen = enddata - curdata; + *payload = curdata; + rc = 1; +exit: + FUNC_EXIT_RC(rc); + return rc; +} + + + +/** + * Deserializes the supplied (wire) buffer into an ack + * @param packettype returned integer - the MQTT packet type + * @param dup returned integer - the MQTT dup flag + * @param packetid returned integer - the MQTT packet identifier + * @param buf the raw buffer data, of the correct length determined by the remaining length field + * @param buflen the length in bytes of the data in the supplied buffer + * @return error code. 1 is success, 0 is failure + */ +int MQTTDeserialize_ack(unsigned char* packettype, unsigned char* dup, unsigned short* packetid, unsigned char* buf, int buflen) +{ + MQTTHeader header = {0}; + unsigned char* curdata = buf; + unsigned char* enddata = NULL; + int rc = 0; + int mylen; + + FUNC_ENTRY; + header.byte = readChar(&curdata); + *dup = header.bits.dup; + *packettype = header.bits.type; + + curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */ + enddata = curdata + mylen; + + if (enddata - curdata < 2) + goto exit; + *packetid = readInt(&curdata); + + rc = 1; +exit: + FUNC_EXIT_RC(rc); + return rc; +} + diff --git a/third_party/mqtt/library/MQTTFormat.c b/third_party/mqtt/library/MQTTFormat.c new file mode 100644 index 00000000..2eff31f8 --- /dev/null +++ b/third_party/mqtt/library/MQTTFormat.c @@ -0,0 +1,262 @@ +/******************************************************************************* + * Copyright (c) 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 + *******************************************************************************/ + +#include "StackTrace.h" +#include "MQTTPacket.h" + +#include + + +const char* MQTTPacket_names[] = +{ + "RESERVED", "CONNECT", "CONNACK", "PUBLISH", "PUBACK", "PUBREC", "PUBREL", + "PUBCOMP", "SUBSCRIBE", "SUBACK", "UNSUBSCRIBE", "UNSUBACK", + "PINGREQ", "PINGRESP", "DISCONNECT" +}; + + +const char* MQTTPacket_getName(unsigned short packetid) +{ + return MQTTPacket_names[packetid]; +} + + +int MQTTStringFormat_connect(char* strbuf, int strbuflen, MQTTPacket_connectData* data) +{ + int strindex = 0; + + strindex = snprintf(strbuf, strbuflen, + "CONNECT MQTT version %d, client id %.*s, clean session %d, keep alive %d", + (int)data->MQTTVersion, data->clientID.lenstring.len, data->clientID.lenstring.data, + (int)data->cleansession, data->keepAliveInterval); + if (data->willFlag) + strindex += snprintf(&strbuf[strindex], strbuflen - strindex, + ", will QoS %d, will retain %d, will topic %.*s, will message %.*s", + data->will.qos, data->will.retained, + data->will.topicName.lenstring.len, data->will.topicName.lenstring.data, + data->will.message.lenstring.len, data->will.message.lenstring.data); + if (data->username.lenstring.data && data->username.lenstring.len > 0) + strindex += snprintf(&strbuf[strindex], strbuflen - strindex, + ", user name %.*s", data->username.lenstring.len, data->username.lenstring.data); + if (data->password.lenstring.data && data->password.lenstring.len > 0) + strindex += snprintf(&strbuf[strindex], strbuflen - strindex, + ", password %.*s", data->password.lenstring.len, data->password.lenstring.data); + return strindex; +} + + +int MQTTStringFormat_connack(char* strbuf, int strbuflen, unsigned char connack_rc, unsigned char sessionPresent) +{ + int strindex = snprintf(strbuf, strbuflen, "CONNACK session present %d, rc %d", sessionPresent, connack_rc); + return strindex; +} + + +int MQTTStringFormat_publish(char* strbuf, int strbuflen, unsigned char dup, int qos, unsigned char retained, + unsigned short packetid, MQTTString topicName, unsigned char* payload, int payloadlen) +{ + int strindex = snprintf(strbuf, strbuflen, + "PUBLISH dup %d, QoS %d, retained %d, packet id %d, topic %.*s, payload length %d, payload %.*s", + dup, qos, retained, packetid, + (topicName.lenstring.len < 20) ? topicName.lenstring.len : 20, topicName.lenstring.data, + payloadlen, (payloadlen < 20) ? payloadlen : 20, payload); + return strindex; +} + + +int MQTTStringFormat_ack(char* strbuf, int strbuflen, unsigned char packettype, unsigned char dup, unsigned short packetid) +{ + int strindex = snprintf(strbuf, strbuflen, "%s, packet id %d", MQTTPacket_names[packettype], packetid); + if (dup) + strindex += snprintf(strbuf + strindex, strbuflen - strindex, ", dup %d", dup); + return strindex; +} + + +int MQTTStringFormat_subscribe(char* strbuf, int strbuflen, unsigned char dup, unsigned short packetid, int count, + MQTTString topicFilters[], int requestedQoSs[]) +{ + return snprintf(strbuf, strbuflen, + "SUBSCRIBE dup %d, packet id %d count %d topic %.*s qos %d", + dup, packetid, count, + topicFilters[0].lenstring.len, topicFilters[0].lenstring.data, + requestedQoSs[0]); +} + + +int MQTTStringFormat_suback(char* strbuf, int strbuflen, unsigned short packetid, int count, int* grantedQoSs) +{ + return snprintf(strbuf, strbuflen, + "SUBACK packet id %d count %d granted qos %d", packetid, count, grantedQoSs[0]); +} + + +int MQTTStringFormat_unsubscribe(char* strbuf, int strbuflen, unsigned char dup, unsigned short packetid, + int count, MQTTString topicFilters[]) +{ + return snprintf(strbuf, strbuflen, + "UNSUBSCRIBE dup %d, packet id %d count %d topic %.*s", + dup, packetid, count, + topicFilters[0].lenstring.len, topicFilters[0].lenstring.data); +} + + +#if defined(MQTT_CLIENT) +char* MQTTFormat_toClientString(char* strbuf, int strbuflen, unsigned char* buf, int buflen) +{ + int index = 0; + int rem_length = 0; + MQTTHeader header = {0}; + int strindex = 0; + + header.byte = buf[index++]; + index += MQTTPacket_decodeBuf(&buf[index], &rem_length); + + switch (header.bits.type) + { + + case CONNACK: + { + unsigned char sessionPresent, connack_rc; + if (MQTTDeserialize_connack(&sessionPresent, &connack_rc, buf, buflen) == 1) + strindex = MQTTStringFormat_connack(strbuf, strbuflen, connack_rc, sessionPresent); + } + break; + case PUBLISH: + { + unsigned char dup, retained, *payload; + unsigned short packetid; + int qos, payloadlen; + MQTTString topicName = MQTTString_initializer; + if (MQTTDeserialize_publish(&dup, &qos, &retained, &packetid, &topicName, + &payload, &payloadlen, buf, buflen) == 1) + strindex = MQTTStringFormat_publish(strbuf, strbuflen, dup, qos, retained, packetid, + topicName, payload, payloadlen); + } + break; + case PUBACK: + case PUBREC: + case PUBREL: + case PUBCOMP: + { + unsigned char packettype, dup; + unsigned short packetid; + if (MQTTDeserialize_ack(&packettype, &dup, &packetid, buf, buflen) == 1) + strindex = MQTTStringFormat_ack(strbuf, strbuflen, packettype, dup, packetid); + } + break; + case SUBACK: + { + unsigned short packetid; + int maxcount = 1, count = 0; + int grantedQoSs[1]; + if (MQTTDeserialize_suback(&packetid, maxcount, &count, grantedQoSs, buf, buflen) == 1) + strindex = MQTTStringFormat_suback(strbuf, strbuflen, packetid, count, grantedQoSs); + } + break; + case UNSUBACK: + { + unsigned short packetid; + if (MQTTDeserialize_unsuback(&packetid, buf, buflen) == 1) + strindex = MQTTStringFormat_ack(strbuf, strbuflen, UNSUBACK, 0, packetid); + } + break; + case PINGREQ: + case PINGRESP: + case DISCONNECT: + strindex = snprintf(strbuf, strbuflen, "%s", MQTTPacket_names[header.bits.type]); + break; + } + return strbuf; +} +#endif + +#if defined(MQTT_SERVER) +char* MQTTFormat_toServerString(char* strbuf, int strbuflen, unsigned char* buf, int buflen) +{ + int index = 0; + int rem_length = 0; + MQTTHeader header = {0}; + int strindex = 0; + + header.byte = buf[index++]; + index += MQTTPacket_decodeBuf(&buf[index], &rem_length); + + switch (header.bits.type) + { + case CONNECT: + { + MQTTPacket_connectData data; + int rc; + if ((rc = MQTTDeserialize_connect(&data, buf, buflen)) == 1) + strindex = MQTTStringFormat_connect(strbuf, strbuflen, &data); + } + break; + case PUBLISH: + { + unsigned char dup, retained, *payload; + unsigned short packetid; + int qos, payloadlen; + MQTTString topicName = MQTTString_initializer; + if (MQTTDeserialize_publish(&dup, &qos, &retained, &packetid, &topicName, + &payload, &payloadlen, buf, buflen) == 1) + strindex = MQTTStringFormat_publish(strbuf, strbuflen, dup, qos, retained, packetid, + topicName, payload, payloadlen); + } + break; + case PUBACK: + case PUBREC: + case PUBREL: + case PUBCOMP: + { + unsigned char packettype, dup; + unsigned short packetid; + if (MQTTDeserialize_ack(&packettype, &dup, &packetid, buf, buflen) == 1) + strindex = MQTTStringFormat_ack(strbuf, strbuflen, packettype, dup, packetid); + } + break; + case SUBSCRIBE: + { + unsigned char dup; + unsigned short packetid; + int maxcount = 1, count = 0; + MQTTString topicFilters[1]; + int requestedQoSs[1]; + if (MQTTDeserialize_subscribe(&dup, &packetid, maxcount, &count, + topicFilters, requestedQoSs, buf, buflen) == 1) + strindex = MQTTStringFormat_subscribe(strbuf, strbuflen, dup, packetid, count, topicFilters, requestedQoSs);; + } + break; + case UNSUBSCRIBE: + { + unsigned char dup; + unsigned short packetid; + int maxcount = 1, count = 0; + MQTTString topicFilters[1]; + if (MQTTDeserialize_unsubscribe(&dup, &packetid, maxcount, &count, topicFilters, buf, buflen) == 1) + strindex = MQTTStringFormat_unsubscribe(strbuf, strbuflen, dup, packetid, count, topicFilters); + } + break; + case PINGREQ: + case PINGRESP: + case DISCONNECT: + strindex = snprintf(strbuf, strbuflen, "%s", MQTTPacket_names[header.bits.type]); + break; + } + strbuf[strbuflen] = '\0'; + return strbuf; +} +#endif diff --git a/third_party/mqtt/library/MQTTPacket.c b/third_party/mqtt/library/MQTTPacket.c new file mode 100644 index 00000000..4f1f95a7 --- /dev/null +++ b/third_party/mqtt/library/MQTTPacket.c @@ -0,0 +1,412 @@ +/******************************************************************************* + * Copyright (c) 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 + * Sergio R. Caprile - non-blocking packet read functions for stream transport + *******************************************************************************/ + +#include "StackTrace.h" +#include "MQTTPacket.h" + +#include + +/** + * 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(unsigned char* buf, int length) +{ + int rc = 0; + + FUNC_ENTRY; + 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); + FUNC_EXIT_RC(rc); + return rc; +} + + +/** + * Decodes the message length according to the MQTT algorithm + * @param getcharfn pointer to function to read the next character from the data source + * @param value the decoded length returned + * @return the number of bytes read from the socket + */ +int MQTTPacket_decode(int (*getcharfn)(unsigned char*, int), int* value) +{ + unsigned char c; + int multiplier = 1; + int len = 0; +#define MAX_NO_OF_REMAINING_LENGTH_BYTES 4 + + FUNC_ENTRY; + *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 = (*getcharfn)(&c, 1); + if (rc != 1) + goto exit; + *value += (c & 127) * multiplier; + multiplier *= 128; + } while ((c & 128) != 0); +exit: + FUNC_EXIT_RC(len); + return len; +} + + +int MQTTPacket_len(int rem_len) +{ + rem_len += 1; /* header byte */ + + /* now remaining_length field */ + if (rem_len < 128) + rem_len += 1; + else if (rem_len < 16384) + rem_len += 2; + else if (rem_len < 2097151) + rem_len += 3; + else + rem_len += 4; + return rem_len; +} + + +static unsigned char* bufptr; + +int bufchar(unsigned char* c, int count) +{ + int i; + + for (i = 0; i < count; ++i) + *c = *bufptr++; + return count; +} + + +int MQTTPacket_decodeBuf(unsigned char* buf, int* value) +{ + bufptr = buf; + return MQTTPacket_decode(bufchar, value); +} + + +/** + * 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(unsigned char** pptr) +{ + unsigned char* ptr = *pptr; + int len = 256*(*ptr) + (*(ptr+1)); + *pptr += 2; + return 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 + */ +char readChar(unsigned char** pptr) +{ + 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 writeCString(unsigned char** pptr, const char* string) +{ + int len = strlen(string); + writeInt(pptr, len); + memcpy(*pptr, string, len); + *pptr += len; +} + + +int getLenStringLen(char* ptr) +{ + int len = 256*((unsigned char)(*ptr)) + (unsigned char)(*(ptr+1)); + return len; +} + + +void writeMQTTString(unsigned char** pptr, MQTTString mqttstring) +{ + if (mqttstring.lenstring.len > 0) + { + writeInt(pptr, mqttstring.lenstring.len); + memcpy(*pptr, mqttstring.lenstring.data, mqttstring.lenstring.len); + *pptr += mqttstring.lenstring.len; + } + else if (mqttstring.cstring) + writeCString(pptr, mqttstring.cstring); + else + writeInt(pptr, 0); +} + + +/** + * @param mqttstring the MQTTString structure into which the data is to be read + * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned + * @param enddata pointer to the end of the data: do not read beyond + * @return 1 if successful, 0 if not + */ +int readMQTTLenString(MQTTString* mqttstring, unsigned char** pptr, unsigned char* enddata) +{ + int rc = 0; + + FUNC_ENTRY; + /* the first two bytes are the length of the string */ + if (enddata - (*pptr) > 1) /* enough length to read the integer? */ + { + mqttstring->lenstring.len = readInt(pptr); /* increments pptr to point past length */ + if (&(*pptr)[mqttstring->lenstring.len] <= enddata) + { + mqttstring->lenstring.data = (char*)*pptr; + *pptr += mqttstring->lenstring.len; + rc = 1; + } + } + mqttstring->cstring = NULL; + FUNC_EXIT_RC(rc); + return rc; +} + + +/** + * Return the length of the MQTTstring - C string if there is one, otherwise the length delimited string + * @param mqttstring the string to return the length of + * @return the length of the string + */ +int MQTTstrlen(MQTTString mqttstring) +{ + int rc = 0; + + if (mqttstring.cstring) + rc = strlen(mqttstring.cstring); + else + rc = mqttstring.lenstring.len; + return rc; +} + + +/** + * Compares an MQTTString to a C string + * @param a the MQTTString to compare + * @param bptr the C string to compare + * @return boolean - equal or not + */ +int MQTTPacket_equals(MQTTString* a, char* bptr) +{ + int alen = 0, + blen = 0; + char *aptr; + + if (a->cstring) + { + aptr = a->cstring; + alen = strlen(a->cstring); + } + else + { + aptr = a->lenstring.data; + alen = a->lenstring.len; + } + blen = strlen(bptr); + + return (alen == blen) && (strncmp(aptr, bptr, alen) == 0); +} + + +/** + * Helper function to read packet data from some source into a buffer + * @param buf the buffer into which the packet will be serialized + * @param buflen the length in bytes of the supplied buffer + * @param getfn pointer to a function which will read any number of bytes from the needed source + * @return integer MQTT packet type, or -1 on error + * @note the whole message must fit into the caller's buffer + */ +int MQTTPacket_read(unsigned char* buf, int buflen, int (*getfn)(unsigned char*, int)) +{ + int rc = -1; + MQTTHeader header = {0}; + int len = 0; + int rem_len = 0; + + /* 1. read the header byte. This has the packet type in it */ + if ((*getfn)(buf, 1) != 1) + goto exit; + + len = 1; + /* 2. read the remaining length. This is variable in itself */ + MQTTPacket_decode(getfn, &rem_len); + len += MQTTPacket_encode(buf + 1, rem_len); /* put the original remaining length back into the buffer */ + + /* 3. read the rest of the buffer using a callback to supply the rest of the data */ + if((rem_len + len) > buflen) + goto exit; + if (rem_len && ((*getfn)(buf + len, rem_len) != rem_len)) + goto exit; + + header.byte = buf[0]; + rc = header.bits.type; +exit: + return rc; +} + +/** + * Decodes the message length according to the MQTT algorithm, non-blocking + * @param trp pointer to a transport structure holding what is needed to solve getting data from it + * @param value the decoded length returned + * @return integer the number of bytes read from the socket, 0 for call again, or -1 on error + */ +static int MQTTPacket_decodenb(MQTTTransport *trp) +{ + unsigned char c; + int rc = MQTTPACKET_READ_ERROR; + + FUNC_ENTRY; + if(trp->len == 0){ /* initialize on first call */ + trp->multiplier = 1; + trp->rem_len = 0; + } + do { + int frc; + if (trp->len >= MAX_NO_OF_REMAINING_LENGTH_BYTES) + goto exit; + if ((frc=(*trp->getfn)(trp->sck, &c, 1)) == -1) + goto exit; + if (frc == 0){ + rc = 0; + goto exit; + } + ++(trp->len); + trp->rem_len += (c & 127) * trp->multiplier; + trp->multiplier *= 128; + } while ((c & 128) != 0); + rc = trp->len; +exit: + FUNC_EXIT_RC(rc); + return rc; +} + +/** + * Helper function to read packet data from some source into a buffer, non-blocking + * @param buf the buffer into which the packet will be serialized + * @param buflen the length in bytes of the supplied buffer + * @param trp pointer to a transport structure holding what is needed to solve getting data from it + * @return integer MQTT packet type, 0 for call again, or -1 on error + * @note the whole message must fit into the caller's buffer + */ +int MQTTPacket_readnb(unsigned char* buf, int buflen, MQTTTransport *trp) +{ + int rc = -1, frc; + MQTTHeader header = {0}; + + switch(trp->state){ + default: + trp->state = 0; + /*FALLTHROUGH*/ + case 0: + /* read the header byte. This has the packet type in it */ + if ((frc=(*trp->getfn)(trp->sck, buf, 1)) == -1) + goto exit; + if (frc == 0) + return 0; + trp->len = 0; + ++trp->state; + /*FALLTHROUGH*/ + /* read the remaining length. This is variable in itself */ + case 1: + if((frc=MQTTPacket_decodenb(trp)) == MQTTPACKET_READ_ERROR) + goto exit; + if(frc == 0) + return 0; + trp->len = 1 + MQTTPacket_encode(buf + 1, trp->rem_len); /* put the original remaining length back into the buffer */ + if((trp->rem_len + trp->len) > buflen) + goto exit; + ++trp->state; + /*FALLTHROUGH*/ + case 2: + if(trp->rem_len){ + /* read the rest of the buffer using a callback to supply the rest of the data */ + if ((frc=(*trp->getfn)(trp->sck, buf + trp->len, trp->rem_len)) == -1) + goto exit; + if (frc == 0) + return 0; + trp->rem_len -= frc; + trp->len += frc; + if(trp->rem_len) + return 0; + } + header.byte = buf[0]; + rc = header.bits.type; + break; + } + +exit: + trp->state = 0; + return rc; +} + diff --git a/third_party/mqtt/library/MQTTSerializePublish.c b/third_party/mqtt/library/MQTTSerializePublish.c new file mode 100644 index 00000000..77a58b54 --- /dev/null +++ b/third_party/mqtt/library/MQTTSerializePublish.c @@ -0,0 +1,169 @@ +/******************************************************************************* + * Copyright (c) 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 - fix for https://bugs.eclipse.org/bugs/show_bug.cgi?id=453144 + *******************************************************************************/ + +#include "MQTTPacket.h" +#include "StackTrace.h" + +#include + + +/** + * Determines the length of the MQTT publish packet that would be produced using the supplied parameters + * @param qos the MQTT QoS of the publish (packetid is omitted for QoS 0) + * @param topicName the topic name to be used in the publish + * @param payloadlen the length of the payload to be sent + * @return the length of buffer needed to contain the serialized version of the packet + */ +int MQTTSerialize_publishLength(int qos, MQTTString topicName, int payloadlen) +{ + int len = 0; + + len += 2 + MQTTstrlen(topicName) + payloadlen; + if (qos > 0) + len += 2; /* packetid */ + return len; +} + + +/** + * Serializes the supplied publish data into the supplied buffer, ready for sending + * @param buf the buffer into which the packet will be serialized + * @param buflen the length in bytes of the supplied buffer + * @param dup integer - the MQTT dup flag + * @param qos integer - the MQTT QoS value + * @param retained integer - the MQTT retained flag + * @param packetid integer - the MQTT packet identifier + * @param topicName MQTTString - the MQTT topic in the publish + * @param payload byte buffer - the MQTT publish payload + * @param payloadlen integer - the length of the MQTT payload + * @return the length of the serialized data. <= 0 indicates error + */ +int MQTTSerialize_publish(unsigned char* buf, int buflen, unsigned char dup, int qos, unsigned char retained, unsigned short packetid, + MQTTString topicName, unsigned char* payload, int payloadlen) +{ + unsigned char *ptr = buf; + MQTTHeader header = {0}; + int rem_len = 0; + int rc = 0; + + FUNC_ENTRY; + if (MQTTPacket_len(rem_len = MQTTSerialize_publishLength(qos, topicName, payloadlen)) > buflen) + { + rc = MQTTPACKET_BUFFER_TOO_SHORT; + goto exit; + } + + header.bits.type = PUBLISH; + header.bits.dup = dup; + header.bits.qos = qos; + header.bits.retain = retained; + writeChar(&ptr, header.byte); /* write header */ + + ptr += MQTTPacket_encode(ptr, rem_len); /* write remaining length */; + + writeMQTTString(&ptr, topicName); + + if (qos > 0) + writeInt(&ptr, packetid); + + memcpy(ptr, payload, payloadlen); + ptr += payloadlen; + + rc = ptr - buf; + +exit: + FUNC_EXIT_RC(rc); + return rc; +} + + + +/** + * Serializes the ack packet into the supplied buffer. + * @param buf the buffer into which the packet will be serialized + * @param buflen the length in bytes of the supplied buffer + * @param type the MQTT packet type + * @param dup the MQTT dup flag + * @param packetid the MQTT packet identifier + * @return serialized length, or error if 0 + */ +int MQTTSerialize_ack(unsigned char* buf, int buflen, unsigned char packettype, unsigned char dup, unsigned short packetid) +{ + MQTTHeader header = {0}; + int rc = 0; + unsigned char *ptr = buf; + + FUNC_ENTRY; + if (buflen < 4) + { + rc = MQTTPACKET_BUFFER_TOO_SHORT; + goto exit; + } + header.bits.type = packettype; + header.bits.dup = dup; + header.bits.qos = (packettype == PUBREL) ? 1 : 0; + writeChar(&ptr, header.byte); /* write header */ + + ptr += MQTTPacket_encode(ptr, 2); /* write remaining length */ + writeInt(&ptr, packetid); + rc = ptr - buf; +exit: + FUNC_EXIT_RC(rc); + return rc; +} + + +/** + * Serializes a puback packet into the supplied buffer. + * @param buf the buffer into which the packet will be serialized + * @param buflen the length in bytes of the supplied buffer + * @param packetid integer - the MQTT packet identifier + * @return serialized length, or error if 0 + */ +int MQTTSerialize_puback(unsigned char* buf, int buflen, unsigned short packetid) +{ + return MQTTSerialize_ack(buf, buflen, PUBACK, 0, packetid); +} + + +/** + * Serializes a pubrel packet into the supplied buffer. + * @param buf the buffer into which the packet will be serialized + * @param buflen the length in bytes of the supplied buffer + * @param dup integer - the MQTT dup flag + * @param packetid integer - the MQTT packet identifier + * @return serialized length, or error if 0 + */ +int MQTTSerialize_pubrel(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid) +{ + return MQTTSerialize_ack(buf, buflen, PUBREL, dup, packetid); +} + + +/** + * Serializes a pubrel packet into the supplied buffer. + * @param buf the buffer into which the packet will be serialized + * @param buflen the length in bytes of the supplied buffer + * @param packetid integer - the MQTT packet identifier + * @return serialized length, or error if 0 + */ +int MQTTSerialize_pubcomp(unsigned char* buf, int buflen, unsigned short packetid) +{ + return MQTTSerialize_ack(buf, buflen, PUBCOMP, 0, packetid); +} + + diff --git a/third_party/mqtt/library/MQTTSubscribeClient.c b/third_party/mqtt/library/MQTTSubscribeClient.c new file mode 100644 index 00000000..57a06138 --- /dev/null +++ b/third_party/mqtt/library/MQTTSubscribeClient.c @@ -0,0 +1,137 @@ +/******************************************************************************* + * Copyright (c) 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 + *******************************************************************************/ + +#include "MQTTPacket.h" +#include "StackTrace.h" + +#include + +/** + * Determines the length of the MQTT subscribe packet that would be produced using the supplied parameters + * @param count the number of topic filter strings in topicFilters + * @param topicFilters the array of topic filter strings to be used in the publish + * @return the length of buffer needed to contain the serialized version of the packet + */ +int MQTTSerialize_subscribeLength(int count, MQTTString topicFilters[]) +{ + int i; + int len = 2; /* packetid */ + + for (i = 0; i < count; ++i) + len += 2 + MQTTstrlen(topicFilters[i]) + 1; /* length + topic + req_qos */ + return len; +} + + +/** + * Serializes the supplied subscribe data into the supplied buffer, ready for sending + * @param buf the buffer into which the packet will be serialized + * @param buflen the length in bytes of the supplied bufferr + * @param dup integer - the MQTT dup flag + * @param packetid integer - the MQTT packet identifier + * @param count - number of members in the topicFilters and reqQos arrays + * @param topicFilters - array of topic filter names + * @param requestedQoSs - array of requested QoS + * @return the length of the serialized data. <= 0 indicates error + */ +int MQTTSerialize_subscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid, int count, + MQTTString topicFilters[], int requestedQoSs[]) +{ + unsigned char *ptr = buf; + MQTTHeader header = {0}; + int rem_len = 0; + int rc = 0; + int i = 0; + + FUNC_ENTRY; + if (MQTTPacket_len(rem_len = MQTTSerialize_subscribeLength(count, topicFilters)) > buflen) + { + rc = MQTTPACKET_BUFFER_TOO_SHORT; + goto exit; + } + + header.byte = 0; + header.bits.type = SUBSCRIBE; + header.bits.dup = dup; + header.bits.qos = 1; + writeChar(&ptr, header.byte); /* write header */ + + ptr += MQTTPacket_encode(ptr, rem_len); /* write remaining length */; + + writeInt(&ptr, packetid); + + for (i = 0; i < count; ++i) + { + writeMQTTString(&ptr, topicFilters[i]); + writeChar(&ptr, requestedQoSs[i]); + } + + rc = ptr - buf; +exit: + FUNC_EXIT_RC(rc); + return rc; +} + + + +/** + * Deserializes the supplied (wire) buffer into suback data + * @param packetid returned integer - the MQTT packet identifier + * @param maxcount - the maximum number of members allowed in the grantedQoSs array + * @param count returned integer - number of members in the grantedQoSs array + * @param grantedQoSs returned array of integers - the granted qualities of service + * @param buf the raw buffer data, of the correct length determined by the remaining length field + * @param buflen the length in bytes of the data in the supplied buffer + * @return error code. 1 is success, 0 is failure + */ +int MQTTDeserialize_suback(unsigned short* packetid, int maxcount, int* count, int grantedQoSs[], unsigned char* buf, int buflen) +{ + MQTTHeader header = {0}; + unsigned char* curdata = buf; + unsigned char* enddata = NULL; + int rc = 0; + int mylen; + + FUNC_ENTRY; + header.byte = readChar(&curdata); + if (header.bits.type != SUBACK) + goto exit; + + curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */ + enddata = curdata + mylen; + if (enddata - curdata < 2) + goto exit; + + *packetid = readInt(&curdata); + + *count = 0; + while (curdata < enddata) + { + if (*count > maxcount) + { + rc = -1; + goto exit; + } + grantedQoSs[(*count)++] = readChar(&curdata); + } + + rc = 1; +exit: + FUNC_EXIT_RC(rc); + return rc; +} + + diff --git a/third_party/mqtt/library/MQTTSubscribeServer.c b/third_party/mqtt/library/MQTTSubscribeServer.c new file mode 100644 index 00000000..5579645f --- /dev/null +++ b/third_party/mqtt/library/MQTTSubscribeServer.c @@ -0,0 +1,112 @@ +/******************************************************************************* + * Copyright (c) 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 + *******************************************************************************/ + +#include "MQTTPacket.h" +#include "StackTrace.h" + +#include + + +/** + * Deserializes the supplied (wire) buffer into subscribe data + * @param dup integer returned - the MQTT dup flag + * @param packetid integer returned - the MQTT packet identifier + * @param maxcount - the maximum number of members allowed in the topicFilters and requestedQoSs arrays + * @param count - number of members in the topicFilters and requestedQoSs arrays + * @param topicFilters - array of topic filter names + * @param requestedQoSs - array of requested QoS + * @param buf the raw buffer data, of the correct length determined by the remaining length field + * @param buflen the length in bytes of the data in the supplied buffer + * @return the length of the serialized data. <= 0 indicates error + */ +int MQTTDeserialize_subscribe(unsigned char* dup, unsigned short* packetid, int maxcount, int* count, MQTTString topicFilters[], + int requestedQoSs[], unsigned char* buf, int buflen) +{ + MQTTHeader header = {0}; + unsigned char* curdata = buf; + unsigned char* enddata = NULL; + int rc = -1; + int mylen = 0; + + FUNC_ENTRY; + header.byte = readChar(&curdata); + if (header.bits.type != SUBSCRIBE) + goto exit; + *dup = header.bits.dup; + + curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */ + enddata = curdata + mylen; + + *packetid = readInt(&curdata); + + *count = 0; + while (curdata < enddata) + { + if (!readMQTTLenString(&topicFilters[*count], &curdata, enddata)) + goto exit; + if (curdata >= enddata) /* do we have enough data to read the req_qos version byte? */ + goto exit; + requestedQoSs[*count] = readChar(&curdata); + (*count)++; + } + + rc = 1; +exit: + FUNC_EXIT_RC(rc); + return rc; +} + + +/** + * Serializes the supplied suback data into the supplied buffer, ready for sending + * @param buf the buffer into which the packet will be serialized + * @param buflen the length in bytes of the supplied buffer + * @param packetid integer - the MQTT packet identifier + * @param count - number of members in the grantedQoSs array + * @param grantedQoSs - array of granted QoS + * @return the length of the serialized data. <= 0 indicates error + */ +int MQTTSerialize_suback(unsigned char* buf, int buflen, unsigned short packetid, int count, int* grantedQoSs) +{ + MQTTHeader header = {0}; + int rc = -1; + unsigned char *ptr = buf; + int i; + + FUNC_ENTRY; + if (buflen < 2 + count) + { + rc = MQTTPACKET_BUFFER_TOO_SHORT; + goto exit; + } + header.byte = 0; + header.bits.type = SUBACK; + writeChar(&ptr, header.byte); /* write header */ + + ptr += MQTTPacket_encode(ptr, 2 + count); /* write remaining length */ + + writeInt(&ptr, packetid); + + for (i = 0; i < count; ++i) + writeChar(&ptr, grantedQoSs[i]); + + rc = ptr - buf; +exit: + FUNC_EXIT_RC(rc); + return rc; +} + + diff --git a/third_party/mqtt/library/MQTTUnsubscribeClient.c b/third_party/mqtt/library/MQTTUnsubscribeClient.c new file mode 100644 index 00000000..e7ec5302 --- /dev/null +++ b/third_party/mqtt/library/MQTTUnsubscribeClient.c @@ -0,0 +1,106 @@ +/******************************************************************************* + * Copyright (c) 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 + *******************************************************************************/ + +#include "MQTTPacket.h" +#include "StackTrace.h" + +#include + +/** + * Determines the length of the MQTT unsubscribe packet that would be produced using the supplied parameters + * @param count the number of topic filter strings in topicFilters + * @param topicFilters the array of topic filter strings to be used in the publish + * @return the length of buffer needed to contain the serialized version of the packet + */ +int MQTTSerialize_unsubscribeLength(int count, MQTTString topicFilters[]) +{ + int i; + int len = 2; /* packetid */ + + for (i = 0; i < count; ++i) + len += 2 + MQTTstrlen(topicFilters[i]); /* length + topic*/ + return len; +} + + +/** + * Serializes the supplied unsubscribe data into the supplied buffer, ready for sending + * @param buf the raw buffer data, of the correct length determined by the remaining length field + * @param buflen the length in bytes of the data in the supplied buffer + * @param dup integer - the MQTT dup flag + * @param packetid integer - the MQTT packet identifier + * @param count - number of members in the topicFilters array + * @param topicFilters - array of topic filter names + * @return the length of the serialized data. <= 0 indicates error + */ +int MQTTSerialize_unsubscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid, + int count, MQTTString topicFilters[]) +{ + unsigned char *ptr = buf; + MQTTHeader header = {0}; + int rem_len = 0; + int rc = -1; + int i = 0; + + FUNC_ENTRY; + if (MQTTPacket_len(rem_len = MQTTSerialize_unsubscribeLength(count, topicFilters)) > buflen) + { + rc = MQTTPACKET_BUFFER_TOO_SHORT; + goto exit; + } + + header.byte = 0; + header.bits.type = UNSUBSCRIBE; + header.bits.dup = dup; + header.bits.qos = 1; + writeChar(&ptr, header.byte); /* write header */ + + ptr += MQTTPacket_encode(ptr, rem_len); /* write remaining length */; + + writeInt(&ptr, packetid); + + for (i = 0; i < count; ++i) + writeMQTTString(&ptr, topicFilters[i]); + + rc = ptr - buf; +exit: + FUNC_EXIT_RC(rc); + return rc; +} + + +/** + * Deserializes the supplied (wire) buffer into unsuback data + * @param packetid returned integer - the MQTT packet identifier + * @param buf the raw buffer data, of the correct length determined by the remaining length field + * @param buflen the length in bytes of the data in the supplied buffer + * @return error code. 1 is success, 0 is failure + */ +int MQTTDeserialize_unsuback(unsigned short* packetid, unsigned char* buf, int buflen) +{ + unsigned char type = 0; + unsigned char dup = 0; + int rc = 0; + + FUNC_ENTRY; + rc = MQTTDeserialize_ack(&type, &dup, packetid, buf, buflen); + if (type == UNSUBACK) + rc = 1; + FUNC_EXIT_RC(rc); + return rc; +} + + diff --git a/third_party/mqtt/library/MQTTUnsubscribeServer.c b/third_party/mqtt/library/MQTTUnsubscribeServer.c new file mode 100644 index 00000000..42b6102a --- /dev/null +++ b/third_party/mqtt/library/MQTTUnsubscribeServer.c @@ -0,0 +1,102 @@ +/******************************************************************************* + * Copyright (c) 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 + *******************************************************************************/ + +#include "MQTTPacket.h" +#include "StackTrace.h" + +#include + + +/** + * Deserializes the supplied (wire) buffer into unsubscribe data + * @param dup integer returned - the MQTT dup flag + * @param packetid integer returned - the MQTT packet identifier + * @param maxcount - the maximum number of members allowed in the topicFilters and requestedQoSs arrays + * @param count - number of members in the topicFilters and requestedQoSs arrays + * @param topicFilters - array of topic filter names + * @param buf the raw buffer data, of the correct length determined by the remaining length field + * @param buflen the length in bytes of the data in the supplied buffer + * @return the length of the serialized data. <= 0 indicates error + */ +int MQTTDeserialize_unsubscribe(unsigned char* dup, unsigned short* packetid, int maxcount, int* count, MQTTString topicFilters[], + unsigned char* buf, int len) +{ + MQTTHeader header = {0}; + unsigned char* curdata = buf; + unsigned char* enddata = NULL; + int rc = 0; + int mylen = 0; + + FUNC_ENTRY; + header.byte = readChar(&curdata); + if (header.bits.type != UNSUBSCRIBE) + goto exit; + *dup = header.bits.dup; + + curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */ + enddata = curdata + mylen; + + *packetid = readInt(&curdata); + + *count = 0; + while (curdata < enddata) + { + if (!readMQTTLenString(&topicFilters[*count], &curdata, enddata)) + goto exit; + (*count)++; + } + + rc = 1; +exit: + FUNC_EXIT_RC(rc); + return rc; +} + + +/** + * Serializes the supplied unsuback data into the supplied buffer, ready for sending + * @param buf the buffer into which the packet will be serialized + * @param buflen the length in bytes of the supplied buffer + * @param packetid integer - the MQTT packet identifier + * @return the length of the serialized data. <= 0 indicates error + */ +int MQTTSerialize_unsuback(unsigned char* buf, int buflen, unsigned short packetid) +{ + MQTTHeader header = {0}; + int rc = 0; + unsigned char *ptr = buf; + + FUNC_ENTRY; + if (buflen < 2) + { + rc = MQTTPACKET_BUFFER_TOO_SHORT; + goto exit; + } + header.byte = 0; + header.bits.type = UNSUBACK; + writeChar(&ptr, header.byte); /* write header */ + + ptr += MQTTPacket_encode(ptr, 2); /* write remaining length */ + + writeInt(&ptr, packetid); + + rc = ptr - buf; +exit: + FUNC_EXIT_RC(rc); + return rc; +} + + diff --git a/third_party/mqtt/library/Makefile b/third_party/mqtt/library/Makefile new file mode 100644 index 00000000..10f4067c --- /dev/null +++ b/third_party/mqtt/library/Makefile @@ -0,0 +1,46 @@ + +############################################################# +# Required variables for each makefile +# Discard this section from all parent makefiles +# Expected variables (with automatic defaults): +# CSRCS (all "C" files in the dir) +# SUBDIRS (all subdirs with a Makefile) +# GEN_LIBS - list of libs to be generated () +# GEN_IMAGES - list of images to be generated () +# COMPONENTS_xxx - a list of libs/objs in the form +# subdir/lib to be extracted and rolled up into +# a generated lib/image xxx.a () +# +ifndef PDIR + +GEN_LIBS = liblibrary.a + +endif + + +############################################################# +# Configuration i.e. compile options etc. +# Target specific stuff (defines etc.) goes in here! +# Generally values applying to a tree are captured in the +# makefile at its root level - these are then overridden +# for a subtree within the makefile rooted therein +# +#DEFINES += + +############################################################# +# Recursion Magic - Don't touch this!! +# +# Each subtree potentially has an include directory +# corresponding to the common APIs applicable to modules +# rooted at that subtree. Accordingly, the INCLUDE PATH +# of a module can only contain the include directories up +# its parent path, and not its siblings +# +# Required for each makefile to inherit from the parent +# + +INCLUDES := $(INCLUDES) -I $(PDIR)include +INCLUDES += -I ./ +PDIR := ../$(PDIR) +sinclude $(PDIR)Makefile + diff --git a/third_party/mqtt/platform/MQTTFreeRTOS.c b/third_party/mqtt/platform/MQTTFreeRTOS.c new file mode 100755 index 00000000..03f1aedf --- /dev/null +++ b/third_party/mqtt/platform/MQTTFreeRTOS.c @@ -0,0 +1,262 @@ +/******************************************************************************* + * Copyright (c) 2014, 2015 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: + * Allan Stockdill-Mander - initial API and implementation and/or initial documentation + * Ian Craggs - convert to FreeRTOS + *******************************************************************************/ + +#include "MQTTFreeRTOS.h" +#include "lwip/netdb.h" + +int ThreadStart(Thread* thread, void (*fn)(void*), void* arg) +{ + int rc = 0; + uint16_t usTaskStackSize = (configMINIMAL_STACK_SIZE * 5); + unsigned portBASE_TYPE uxTaskPriority = uxTaskPriorityGet(NULL); /* set the priority as the same as the calling task*/ + + rc = xTaskCreate(fn, /* The function that implements the task. */ + "MQTTTask", /* Just a text name for the task to aid debugging. */ + usTaskStackSize, /* The stack size is defined in FreeRTOSIPConfig.h. */ + arg, /* The task parameter, not used in this case. */ + uxTaskPriority, /* The priority assigned to the task is defined in FreeRTOSConfig.h. */ + &thread->task); /* The task handle is not used. */ + + return rc; +} + + +void MutexInit(Mutex* mutex) +{ + mutex->sem = xSemaphoreCreateMutex(); +} + +int MutexLock(Mutex* mutex) +{ + return xSemaphoreTake(mutex->sem, portMAX_DELAY); +} + +int MutexUnlock(Mutex* mutex) +{ + return xSemaphoreGive(mutex->sem); +} + + +void TimerCountdownMS(Timer* timer, unsigned int timeout_ms) +{ + timer->xTicksToWait = timeout_ms / portTICK_RATE_MS; /* convert milliseconds to ticks */ + vTaskSetTimeOutState(&timer->xTimeOut); /* Record the time at which this function was entered. */ +} + + +void TimerCountdown(Timer* timer, unsigned int timeout) +{ + TimerCountdownMS(timer, timeout * 1000); +} + + +int TimerLeftMS(Timer* timer) +{ + xTaskCheckForTimeOut(&timer->xTimeOut, &timer->xTicksToWait); /* updates xTicksToWait to the number left */ + return (timer->xTicksToWait < 0) ? 0 : (timer->xTicksToWait * portTICK_RATE_MS); +} + + +char TimerIsExpired(Timer* timer) +{ + return xTaskCheckForTimeOut(&timer->xTimeOut, &timer->xTicksToWait) == pdTRUE; +} + + +void TimerInit(Timer* timer) +{ + timer->xTicksToWait = 0; + memset(&timer->xTimeOut, '\0', sizeof(timer->xTimeOut)); +} + + +int esp_read(Network* n, unsigned char* buffer, int len, int timeout_ms) +{ + portTickType xTicksToWait = timeout_ms / portTICK_RATE_MS; /* convert milliseconds to ticks */ + xTimeOutType xTimeOut; + int recvLen = 0; + int recv_timeout = timeout_ms; + int rc = 0; + + struct timeval timeout; + fd_set fdset; + + FD_ZERO(&fdset); + FD_SET(n->my_socket, &fdset); + + timeout.tv_sec = 0; + timeout.tv_usec = timeout_ms * 1000; + + vTaskSetTimeOutState(&xTimeOut); /* Record the time at which this function was entered. */ + if (select(n->my_socket + 1, &fdset, NULL, NULL, &timeout) > 0) { + if (FD_ISSET(n->my_socket, &fdset)) { + do { + rc = recv(n->my_socket, buffer + recvLen, len - recvLen, MSG_DONTWAIT); + if (rc > 0) { + recvLen += rc; + } else if (rc < 0) { + recvLen = rc; + break; + } + } while (recvLen < len && xTaskCheckForTimeOut(&xTimeOut, &xTicksToWait) == pdFALSE); + } + } + return recvLen; +} + + +int esp_write(Network* n, unsigned char* buffer, int len, int timeout_ms) +{ + portTickType xTicksToWait = timeout_ms / portTICK_RATE_MS; /* convert milliseconds to ticks */ + xTimeOutType xTimeOut; + int sentLen = 0; + int send_timeout = timeout_ms; + int rc = 0; + int readysock; + + struct timeval timeout; + fd_set fdset; + + FD_ZERO(&fdset); + FD_SET(n->my_socket, &fdset); + + timeout.tv_sec = 0; + timeout.tv_usec = timeout_ms * 1000; + + vTaskSetTimeOutState(&xTimeOut); /* Record the time at which this function was entered. */ + do { + readysock = select(n->my_socket + 1, NULL, &fdset, NULL, &timeout); + } while (readysock <= 0); + if (FD_ISSET(n->my_socket, &fdset)) { + do { + rc = send(n->my_socket, buffer + sentLen, len - sentLen, MSG_DONTWAIT); + if (rc > 0) { + sentLen += rc; + } else if (rc < 0) { + sentLen = rc; + break; + } + } while (sentLen < len && xTaskCheckForTimeOut(&xTimeOut, &xTicksToWait) == pdFALSE); + } + + return sentLen; +} + + +void esp_disconnect(Network* n) +{ + close(n->my_socket); +} + + +void NetworkInit(Network* n) +{ + n->my_socket = 0; + n->mqttread = esp_read; + n->mqttwrite = esp_write; + n->disconnect = esp_disconnect; +} + + +int NetworkConnect(Network* n, char* addr, int port) +{ + struct sockaddr_in sAddr; + int retVal = -1; + struct hostent *ipAddress; + + if ((ipAddress = gethostbyname(addr)) == 0) + goto exit; + + sAddr.sin_family = AF_INET; + sAddr.sin_addr.s_addr = ((struct in_addr*)(ipAddress->h_addr))->s_addr; + sAddr.sin_port = htons(port); + + if ((n->my_socket = socket(AF_INET, SOCK_STREAM, 0)) < 0) + goto exit; + + if ((retVal = connect(n->my_socket, (struct sockaddr*)&sAddr, sizeof(sAddr))) < 0) + { + close(n->my_socket); + goto exit; + } + +exit: + return retVal; +} + + +#if 0 +int NetworkConnectTLS(Network *n, char* addr, int port, SlSockSecureFiles_t* certificates, unsigned char sec_method, unsigned int cipher, char server_verify) +{ + SlSockAddrIn_t sAddr; + int addrSize; + int retVal; + unsigned long ipAddress; + + retVal = sl_NetAppDnsGetHostByName(addr, strlen(addr), &ipAddress, AF_INET); + if (retVal < 0) { + return -1; + } + + sAddr.sin_family = AF_INET; + sAddr.sin_port = sl_Htons((unsigned short)port); + sAddr.sin_addr.s_addr = sl_Htonl(ipAddress); + + addrSize = sizeof(SlSockAddrIn_t); + + n->my_socket = sl_Socket(SL_AF_INET, SL_SOCK_STREAM, SL_SEC_SOCKET); + if (n->my_socket < 0) { + return -1; + } + + SlSockSecureMethod method; + method.secureMethod = sec_method; + retVal = sl_SetSockOpt(n->my_socket, SL_SOL_SOCKET, SL_SO_SECMETHOD, &method, sizeof(method)); + if (retVal < 0) { + return retVal; + } + + SlSockSecureMask mask; + mask.secureMask = cipher; + retVal = sl_SetSockOpt(n->my_socket, SL_SOL_SOCKET, SL_SO_SECURE_MASK, &mask, sizeof(mask)); + if (retVal < 0) { + return retVal; + } + + if (certificates != NULL) { + retVal = sl_SetSockOpt(n->my_socket, SL_SOL_SOCKET, SL_SO_SECURE_FILES, certificates->secureFiles, sizeof(SlSockSecureFiles_t)); + if (retVal < 0) + { + return retVal; + } + } + + retVal = sl_Connect(n->my_socket, (SlSockAddr_t *)&sAddr, addrSize); + if (retVal < 0) { + if (server_verify || retVal != -453) { + sl_Close(n->my_socket); + return retVal; + } + } + + SysTickIntRegister(SysTickIntHandler); + SysTickPeriodSet(80000); + SysTickEnable(); + + return retVal; +} +#endif diff --git a/third_party/mqtt/platform/Makefile b/third_party/mqtt/platform/Makefile new file mode 100644 index 00000000..749b4787 --- /dev/null +++ b/third_party/mqtt/platform/Makefile @@ -0,0 +1,46 @@ + +############################################################# +# Required variables for each makefile +# Discard this section from all parent makefiles +# Expected variables (with automatic defaults): +# CSRCS (all "C" files in the dir) +# SUBDIRS (all subdirs with a Makefile) +# GEN_LIBS - list of libs to be generated () +# GEN_IMAGES - list of images to be generated () +# COMPONENTS_xxx - a list of libs/objs in the form +# subdir/lib to be extracted and rolled up into +# a generated lib/image xxx.a () +# +ifndef PDIR + +GEN_LIBS = libplatform.a + +endif + + +############################################################# +# Configuration i.e. compile options etc. +# Target specific stuff (defines etc.) goes in here! +# Generally values applying to a tree are captured in the +# makefile at its root level - these are then overridden +# for a subtree within the makefile rooted therein +# +#DEFINES += + +############################################################# +# Recursion Magic - Don't touch this!! +# +# Each subtree potentially has an include directory +# corresponding to the common APIs applicable to modules +# rooted at that subtree. Accordingly, the INCLUDE PATH +# of a module can only contain the include directories up +# its parent path, and not its siblings +# +# Required for each makefile to inherit from the parent +# + +INCLUDES := $(INCLUDES) -I $(PDIR)include +INCLUDES += -I ./ +PDIR := ../$(PDIR) +sinclude $(PDIR)Makefile +