diff --git a/.cproject b/.cproject new file mode 100644 index 0000000..439b340 --- /dev/null +++ b/.cproject @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.project b/.project new file mode 100644 index 0000000..09b1b50 --- /dev/null +++ b/.project @@ -0,0 +1,26 @@ + + + MQTTSN-embedded-C + + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + + diff --git a/src/MQTTSNConnect.h b/src/MQTTSNConnect.h new file mode 100644 index 0000000..c84a17d --- /dev/null +++ b/src/MQTTSNConnect.h @@ -0,0 +1,43 @@ +/******************************************************************************* + * 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 + *******************************************************************************/ + +#ifndef MQTTSNCONNECT_H_ +#define MQTTSNCONNECT_H_ + +typedef struct +{ + /** The eyecatcher for this structure. must be MQSC. */ + char struct_id[4]; + /** The version number of this structure. Must be 0. + */ + int struct_version; + MQTTString clientID; + int duration; + int cleansession; + int willFlag; +} MQTTSNPacket_connectData; + +#define MQTTSNPacket_connectData_initializer { {'M', 'Q', 'S', 'C'}, 0, {NULL, {0, NULL}}, 0, 0, 0 } + +int MQTTSNSerialize_connect(unsigned char* buf, int buflen, MQTTSNPacket_connectData* options); +int MQTTSNDeserialize_connect(MQTTSNPacket_connectData* data, unsigned char* buf, int len); + +int MQTTSNSerialize_connack(unsigned char* buf, int buflen, int connack_rc); +int MQTTSNDeserialize_connack(int* connack_rc, unsigned char* buf, int buflen); + +int MQTTSNSerialize_disconnect(unsigned char* buf, int buflen, int duration); + +#endif /* MQTTSNCONNECT_H_ */ diff --git a/src/MQTTSNConnectClient.c b/src/MQTTSNConnectClient.c new file mode 100644 index 0000000..7f182ed --- /dev/null +++ b/src/MQTTSNConnectClient.c @@ -0,0 +1,153 @@ +/******************************************************************************* + * 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 "MQTTSNPacket.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 MQTTSNSerialize_connectLength(MQTTSNPacket_connectData* options) +{ + int len = 0; + + FUNC_ENTRY; + len = 5 + MQTTstrlen(options->clientID); + 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 MQTTSNSerialize_connect(unsigned char* buf, int buflen, MQTTSNPacket_connectData* options) +{ + unsigned char *ptr = buf; + MQTTSNFlags flags; + int len = 0; + int rc = -1; + + FUNC_ENTRY; + if ((len = MQTTSNPacket_len(MQTTSNSerialize_connectLength(options))) > buflen) + { + rc = MQTTSNPACKET_BUFFER_TOO_SHORT; + goto exit; + } + ptr += MQTTSNPacket_encode(ptr, len); /* write length */ + writeChar(&ptr, MQTTSN_CONNECT); /* write message type */ + + flags.all = 0; + flags.bits.cleanSession = options->cleansession; + flags.bits.will = options->willFlag; + writeChar(&ptr, flags.all); + writeChar(&ptr, 0x01); /* protocol ID */ + writeInt(&ptr, options->duration); + writeMQTTSNString(&ptr, options->clientID); + + rc = ptr - buf; + + exit: FUNC_EXIT_RC(rc); + return rc; +} + + +/** + * Deserializes the supplied (wire) buffer into connack data - return code + * @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 MQTTSNDeserialize_connack(int* connack_rc, unsigned char* buf, int buflen) +{ + unsigned char* curdata = buf; + unsigned char* enddata = NULL; + int rc = 0; + int mylen; + + FUNC_ENTRY; + curdata += (rc = MQTTSNPacket_decodeBuf(curdata, &mylen)); /* read length */ + enddata = buf + mylen; + if (enddata - curdata < 2) + goto exit; + + if (readChar(&curdata) != MQTTSN_CONNACK) + goto exit; + + *connack_rc = readInt(&curdata); + + rc = 1; +exit: + FUNC_EXIT_RC(rc); + return rc; +} + + +/** + * Determines the length of the MQTT disconnect packet (without length field) + * @param duration the parameter used for the disconnect + * @return the length of buffer needed to contain the serialized version of the packet + */ +int MQTTSNSerialize_disconnectLength(int duration) +{ + int len = 0; + + FUNC_ENTRY; + len = (duration >= 0) ? 3 : 1; + FUNC_EXIT_RC(len); + return len; +} + + +/** + * 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 + * @param duration optional duration, not added to packet if < 0 + * @return serialized length, or error if 0 + */ +int MQTTSNSerialize_disconnect(unsigned char* buf, int buflen, int duration) +{ + int rc = -1; + unsigned char *ptr = buf; + int len = 0; + + FUNC_ENTRY; + if ((len = MQTTSNPacket_len(MQTTSNSerialize_disconnectLength(duration))) > buflen) + { + rc = MQTTSNPACKET_BUFFER_TOO_SHORT; + goto exit; + } + ptr += MQTTSNPacket_encode(ptr, len); /* write length */ + writeChar(&ptr, MQTTSN_DISCONNECT); /* write message type */ + + if (duration >= 0) + writeInt(&ptr, duration); + + rc = ptr - buf; +exit: + FUNC_EXIT_RC(rc); + return rc; +} diff --git a/src/MQTTSNConnectServer.c b/src/MQTTSNConnectServer.c new file mode 100644 index 0000000..7ebf8d1 --- /dev/null +++ b/src/MQTTSNConnectServer.c @@ -0,0 +1,96 @@ +/******************************************************************************* + * 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 "MQTTSNPacket.h" +#include + +#define min(a, b) ((a < b) ? 1 : 0) + + +/** + * 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 MQTTSNDeserialize_connect(MQTTSNPacket_connectData* data, unsigned char* buf, int len) +{ + MQTTSNFlags flags; + unsigned char* curdata = buf; + unsigned char* enddata = &buf[len]; + int rc = 0; + int version; + int mylen = 0; + + FUNC_ENTRY; + curdata += (rc = MQTTSNPacket_decodeBuf(curdata, &mylen)); /* read length */ + enddata = buf + mylen; + if (enddata - curdata < 2) + goto exit; + + if (readChar(&curdata) != MQTTSN_CONNECT) + goto exit; + + flags.all = readChar(&curdata); + data->cleansession = flags.bits.cleanSession; + data->willFlag = flags.bits.will; + + if ((version = (int)readChar(&curdata)) != 1) /* Protocol version */ + goto exit; + + data->duration = readInt(&curdata); + + if (!readMQTTSNString(&data->clientID, &curdata, enddata)) + goto exit; + + 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 + * @return serialized length, or error if 0 + */ +int MQTTSNSerialize_connack(unsigned char* buf, int buflen, int connack_rc) +{ + int rc = 0; + unsigned char *ptr = buf; + + FUNC_ENTRY; + if (buflen < 4) + { + rc = MQTTSNPACKET_BUFFER_TOO_SHORT; + goto exit; + } + + ptr += MQTTSNPacket_encode(ptr, 4); /* write length */ + writeChar(&ptr, MQTTSN_CONNACK); + writeInt(&ptr, connack_rc); + + rc = ptr - buf; +exit: + FUNC_EXIT_RC(rc); + return rc; +} + diff --git a/src/MQTTSNDeserializePublish.c b/src/MQTTSNDeserializePublish.c new file mode 100644 index 0000000..7371189 --- /dev/null +++ b/src/MQTTSNDeserializePublish.c @@ -0,0 +1,114 @@ +/******************************************************************************* + * 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 "MQTTSNPacket.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 MQTTSNDeserialize_publish(int* dup, int* qos, int* retained, int* packetid, MQTTSN_topicid* topic, + unsigned char** payload, int* payloadlen, unsigned char* buf, int buflen) +{ + MQTTSNFlags flags; + unsigned char* curdata = buf; + unsigned char* enddata = NULL; + int rc = 0; + int mylen = 0; + + FUNC_ENTRY; + curdata += (rc = MQTTSNPacket_decodeBuf(curdata, &mylen)); /* read length */ + enddata = buf + mylen; + if (enddata - curdata > buflen) + goto exit; + + if (readChar(&curdata) != MQTTSN_PUBLISH) + goto exit; + + flags.all = readChar(&curdata); + *dup = flags.bits.dup; + *qos = flags.bits.QoS; + *retained = flags.bits.retain; + + topic->type = flags.bits.topicIdType; + if (topic->type == MQTTSN_TOPIC_TYPE_NORMAL || topic->type == MQTTSN_TOPIC_TYPE_PREDEFINED) + topic->data.id = readInt(&curdata); + else + { + topic->data.name[0] = readChar(&curdata); + topic->data.name[1] = readChar(&curdata); + } + *packetid = readInt(&curdata); + + *payloadlen = enddata - curdata; + *payload = curdata; + rc = 1; +exit: + FUNC_EXIT_RC(rc); + return rc; +} + +#if 0 + +/** + * Deserializes the supplied (wire) buffer into an ack + * @param type 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(int* type, int* dup, int* packetid, char* buf, int buflen) +{ + MQTTHeader header; + char* curdata = buf; + char* enddata = NULL; + int rc = 0; + int mylen; + + FUNC_ENTRY; + header.byte = readChar(&curdata); + *dup = header.bits.dup; + *type = 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; +} + +#endif diff --git a/src/MQTTSNPacket.c b/src/MQTTSNPacket.c new file mode 100644 index 0000000..748a6e8 --- /dev/null +++ b/src/MQTTSNPacket.c @@ -0,0 +1,291 @@ +/******************************************************************************* + * 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 "MQTTSNPacket.h" + +#include + +static char* packet_names[] = +{ + "ADVERTISE", "SEARCHGW", "GWINFO", "RESERVED", "CONNECT", "CONNACK", + "WILLTOPICREQ", "WILLTOPIC", "WILLMSGREQ", "WILLMSG", "REGISTER", "REGACK", + "PUBLISH", "PUBACK", "PUBCOMP", "PUBREC", "PUBREL", "RESERVED", + "SUBSCRIBE", "SUBACK", "UNSUBSCRIBE", "UNSUBACK", "PINGREQ", "PINGRESP", + "DISCONNECT", "RESERVED", "WILLTOPICUPD", "WILLTOPICRESP", "WILLMSGUPD", + "WILLMSGRESP" +}; + + +/** + * Returns a character string representing the packet name given a MsgType code + * @param code MsgType code + * @return the corresponding packet name + */ +char* MQTTSNPacket_name(int code) +{ + return (code >= 0 && code <= MQTTSN_WILLMSGRESP) ? packet_names[code] : "UNKNOWN"; +} + + +/** + * Calculates the full packet length including length field + * @param length the length of the MQTT-SN packet without the length field + * @return the total length of the MQTT-SN packet including the length field + */ +int MQTTSNPacket_len(int length) +{ + return (length > 255) ? length + 3 : length + 1; +} + + +/** + * Encodes the MQTT-SN message length + * @param buf the buffer into which the encoded data is written + * @param length the length to be encoded + * @return the number of bytes written to the buffer + */ +int MQTTSNPacket_encode(unsigned char* buf, int length) +{ + int rc = 0; + + FUNC_ENTRY; + if (length > 255) + { + buf[rc++] = 0x01; + writeInt(&buf, length); + rc += 2; + } + else + buf[rc++] = length; + + FUNC_EXIT_RC(rc); + return rc; +} + + +/** + * Obtains the MQTT-SN packet length from received data + * @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 MQTTSNPacket_decode(int (*getcharfn)(unsigned char*, int), int* value) +{ + unsigned char c; + int rc = MQTTSNPACKET_READ_ERROR; + int len = MQTTSNPACKET_READ_ERROR; +#define MAX_NO_OF_LENGTH_BYTES 3 + + FUNC_ENTRY; + rc = (*getcharfn)(&c, 1); + if (rc != 1) + goto exit; + + if (c == 1) + { + unsigned char buf[2]; + unsigned char* ptr = buf; + + rc = (*getcharfn)(buf, 2); + if (rc != 2) + goto exit; + *value = readInt(&ptr); + len = 3; + } + else + { + *value = c; + len = 1; + } +exit: + FUNC_EXIT_RC(len); + return 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 MQTTSNPacket_decodeBuf(unsigned char* buf, int* value) +{ + bufptr = buf; + return MQTTSNPacket_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*((unsigned char)(*ptr)) + (unsigned char)(*(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 = (unsigned char)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: 0 to 65535 + */ +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, char* string) +{ + int len = strlen(string); + memcpy(*pptr, string, len); + *pptr += len; +} + + +int getLenStringLen(char* ptr) +{ + int len = 256*((unsigned char)(*ptr)) + (unsigned char)(*(ptr+1)); + return len; +} + + +void writeMQTTSNString(unsigned char** pptr, MQTTString mqttstring) +{ + if (mqttstring.lenstring.len > 0) + { + memcpy(*pptr, mqttstring.lenstring.data, mqttstring.lenstring.len); + *pptr += mqttstring.lenstring.len; + } + else if (mqttstring.cstring) + writeCString(pptr, mqttstring.cstring); +} + + +/** + * @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 readMQTTSNString(MQTTString* mqttstring, unsigned char** pptr, unsigned char* enddata) +{ + int rc = 0; + + FUNC_ENTRY; + mqttstring->lenstring.len = enddata - *pptr; + mqttstring->lenstring.data = (char*)*pptr; + *pptr += mqttstring->lenstring.len; + rc = 1; + 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; +} + + +/** + * 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 MQTTSNPACKET_READ_ERROR on error + */ +int MQTTSNPacket_read(unsigned char* buf, int buflen, int (*getfn)(unsigned char*, int)) +{ + int rc = MQTTSNPACKET_READ_ERROR; + int len = 0; /* the length of the whole packet including length field */ + int lenlen = 0; /* the length of the length field: 1 or 3 */ + + /* 1. read the length. This is variable in itself */ + lenlen = MQTTSNPacket_decode(getfn, &len); + if (lenlen <= 0) + goto exit; /* there was an error */ + + if (MQTTSNPacket_encode(buf, len) != lenlen) /* put the original remaining length back into the buffer */ + goto exit; + + /* 2. read the rest of the data using a callback */ + if ((*getfn)(buf + lenlen, len - lenlen) != len - lenlen) + goto exit; + + rc = buf[lenlen]; /* return the packet type */ +exit: + return rc; +} + + diff --git a/src/MQTTSNPacket.h b/src/MQTTSNPacket.h new file mode 100644 index 0000000..05a7782 --- /dev/null +++ b/src/MQTTSNPacket.h @@ -0,0 +1,146 @@ +/******************************************************************************* + * 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 + *******************************************************************************/ + +#ifndef MQTTSNPACKET_H_ +#define MQTTSNPACKET_H_ + +#if defined(__cplusplus) /* If this is a C++ compiler, use C linkage */ +extern "C" { +#endif + +enum errors +{ + MQTTSNPACKET_BUFFER_TOO_SHORT = -2, + MQTTSNPACKET_READ_ERROR = -1, + MQTTSNPACKET_READ_COMPLETE, +}; + +#define MQTTSN_PROTOCOL_VERSION 0x01 + +enum MQTTSN_connackCodes +{ + MQTTSN_RC_ACCEPTED, + MQTTSN_RC_REJECTED_CONGESTED, + MQTTSN_RC_REJECTED_INVALID_TOPIC_ID, +}; + +enum MQTTSN_topicTypes +{ + MQTTSN_TOPIC_TYPE_NORMAL, /* topic id in publish, topic name in subscribe */ + MQTTSN_TOPIC_TYPE_PREDEFINED, + MQTTSN_TOPIC_TYPE_SHORT, +}; + + +enum MQTTSN_msgTypes +{ + MQTTSN_ADVERTISE, MQTTSN_SEARCHGW, MQTTSN_GWINFO, MQTTSN_RESERVED1, + MQTTSN_CONNECT, MQTTSN_CONNACK, + MQTTSN_WILLTOPICREQ, MQTTSN_WILLTOPIC, MQTTSN_WILLMSGREQ, MQTTSN_WILLMSG, + MQTTSN_REGISTER, MQTTSN_REGACK, + MQTTSN_PUBLISH, MQTTSN_PUBACK, MQTTSN_PUBCOMP, MQTTSN_PUBREC, MQTTSN_PUBREL, MQTTSN_RESERVED2, + MQTTSN_SUBSCRIBE, MQTTSN_SUBACK, MQTTSN_UNSUBSCRIBE, MQTTSN_UNSUBACK, + MQTTSN_PINGREQ, MQTTSN_PINGRESP, + MQTTSN_DISCONNECT, MQTTSN_RESERVED3, + MQTTSN_WILLTOPICUPD, MQTTSN_WILLTOPICRESP, MQTTSN_WILLMSGUPD, MQTTSN_WILLMSGRESP, +}; + +typedef struct +{ + enum MQTTSN_topicTypes type; + union + { + unsigned short id; + char name[2]; + } data; +} MQTTSN_topicid; + + +/** + * Bitfields for the MQTT-SN flags byte. + */ +typedef union +{ + unsigned char all; +#if defined(REVERSED) + struct + { + int dup: 1; + unsigned int QoS : 2; + unsigned int retain : 1; + unsigned int will : 1; + unsigned int cleanSession : 1; + unsigned int topicIdType : 2; + } bits; +#else + struct + { + unsigned int topicIdType : 2; + unsigned int cleanSession : 1; + unsigned int will : 1; + unsigned int retain : 1; + unsigned int QoS : 2; + int dup: 1; + } bits; +#endif +} MQTTSNFlags; + + +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 "MQTTSNConnect.h" +#include "MQTTSNPublish.h" +/*#include "MQTTSNSubscribe.h" +#include "MQTTSNUnsubscribe.h" +*/ + +char* MQTTSNPacket_name(int ptype); +int MQTTSNPacket_len(int length); + +int MQTTSNPacket_encode(unsigned char* buf, int length); +int MQTTSNPacket_decode(int (*getcharfn)(unsigned char*, int), int* value); +int MQTTSNPacket_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 readMQTTSNString(MQTTString* mqttstring, unsigned char** pptr, unsigned char* enddata); +void writeCString(unsigned char** pptr, char* string); +void writeMQTTSNString(unsigned char** pptr, MQTTString mqttstring); + +int MQTTDeserialize_ack(int* type, int* dup, int* packetid, char* buf, int buflen); + +#ifdef __cplusplus /* If this is a C++ compiler, use C linkage */ +} +#endif + + +#endif /* MQTTSNPACKET_H_ */ diff --git a/src/MQTTSNPublish.h b/src/MQTTSNPublish.h new file mode 100644 index 0000000..5c690ce --- /dev/null +++ b/src/MQTTSNPublish.h @@ -0,0 +1,30 @@ +/******************************************************************************* + * 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 + *******************************************************************************/ + +#ifndef MQTTSNPUBLISH_H_ +#define MQTTSNPUBLISH_H_ + +int MQTTSNSerialize_publish(unsigned char* buf, int buflen, int dup, int qos, int retained, int packetid, + MQTTSN_topicid topic, unsigned char* payload, int payloadlen); + +int MQTTSNDeserialize_publish(int* dup, int* qos, int* retained, int* packetid, + MQTTSN_topicid* topic, unsigned char** payload, int* payloadlen, unsigned char* buf, int len); + +int MQTTSerialize_puback(char* buf, int buflen, unsigned short packetid, unsigned short topicid, unsigned char returncode); +int MQTTSerialize_pubrel(char* buf, int buflen, int packetid); +int MQTTSerialize_pubcomp(char* buf, int buflen, int packetid); + +#endif /* MQTTSNPUBLISH_H_ */ diff --git a/src/MQTTSNSerializePublish.c b/src/MQTTSNSerializePublish.c new file mode 100644 index 0000000..c56250f --- /dev/null +++ b/src/MQTTSNSerializePublish.c @@ -0,0 +1,167 @@ +/******************************************************************************* + * 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 "MQTTSNPacket.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 MQTTSNSerialize_publishLength(int payloadlen) +{ + return payloadlen + 6; +} + + +/** + * 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 MQTTSNSerialize_publish(unsigned char* buf, int buflen, int dup, int qos, int retained, int packetid, + MQTTSN_topicid topic, unsigned char* payload, int payloadlen) +{ + unsigned char *ptr = buf; + MQTTSNFlags flags; + int len = 0; + int rc = 0; + + FUNC_ENTRY; + if ((len = MQTTSNPacket_len(MQTTSNSerialize_publishLength(payloadlen))) > buflen) + { + rc = MQTTSNPACKET_BUFFER_TOO_SHORT; + goto exit; + } + ptr += MQTTSNPacket_encode(ptr, len); /* write length */ + writeChar(&ptr, MQTTSN_PUBLISH); /* write message type */ + + flags.all = 0; + flags.bits.dup = dup; + flags.bits.QoS = qos; + flags.bits.retain = retained; + flags.bits.topicIdType = topic.type; + writeChar(&ptr, flags.all); + + if (topic.type == MQTTSN_TOPIC_TYPE_NORMAL || topic.type == MQTTSN_TOPIC_TYPE_PREDEFINED) + writeInt(&ptr, topic.data.id); + else + { + writeChar(&ptr, topic.data.name[0]); + writeChar(&ptr, topic.data.name[1]); + } + writeInt(&ptr, packetid); + memcpy(ptr, payload, payloadlen); + ptr += payloadlen; + + rc = ptr - buf; +exit: + FUNC_EXIT_RC(rc); + return rc; +} + + +#if 0 +/** + * 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 integer - the MQTT packet type + * @param dup integer - the MQTT dup flag + * @param packetid integer - the MQTT packet identifier + * @return serialized length, or error if 0 + */ +int MQTTSerialize_ack(char* buf, int buflen, int type, int dup, int packetid) +{ + MQTTHeader header; + int rc = 0; + char *ptr = buf; + + FUNC_ENTRY; + if (buflen < 4) + { + rc = MQTTPACKET_BUFFER_TOO_SHORT; + goto exit; + } + header.bits.type = type; + header.bits.dup = dup; + header.bits.qos = 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(char* buf, int buflen, int packetid) +{ + return MQTTSerialize_ack(buf, buflen, PUBACK, packetid, 0); +} + + +/** + * 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(char* buf, int buflen, int dup, int packetid) +{ + return MQTTSerialize_ack(buf, buflen, PUBREL, packetid, dup); +} + + +/** + * 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(char* buf, int buflen, int packetid) +{ + return MQTTSerialize_ack(buf, buflen, PUBCOMP, packetid, 0); +} + + +#endif diff --git a/test/build_test b/test/build_test new file mode 100644 index 0000000..9ef86cd --- /dev/null +++ b/test/build_test @@ -0,0 +1 @@ +gcc -Wall test1.c -I../src ../src/MQTTSNConnectClient.c ../src/MQTTSNConnectServer.c ../src/MQTTSNPacket.c ../src/MQTTSNSerializePublish.c ../src/MQTTSNDeserializePublish.c diff --git a/test/test1.c b/test/test1.c new file mode 100644 index 0000000..5659820 --- /dev/null +++ b/test/test1.c @@ -0,0 +1,606 @@ +/******************************************************************************* + * 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 "MQTTSNPacket.h" +#include +#include +#include + +#if !defined(_WINDOWS) + #include + #include + #include + #include +#else +#include +#include +#define MAXHOSTNAMELEN 256 +#define EAGAIN WSAEWOULDBLOCK +#define EINTR WSAEINTR +#define EINPROGRESS WSAEINPROGRESS +#define EWOULDBLOCK WSAEWOULDBLOCK +#define ENOTCONN WSAENOTCONN +#define ECONNRESET WSAECONNRESET +#endif + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) + +struct Options +{ + char* connection; /**< connection to system under test. */ + char** haconnections; + int hacount; + int verbose; + int test_no; +} options = +{ + "tcp://m2m.eclipse.org:1883", + NULL, + 0, + 0, + 0, +}; + +void usage() +{ + +} + +void getopts(int argc, char** argv) +{ + int count = 1; + + while (count < argc) + { + if (strcmp(argv[count], "--test_no") == 0) + { + if (++count < argc) + options.test_no = atoi(argv[count]); + else + usage(); + } + else if (strcmp(argv[count], "--connection") == 0) + { + if (++count < argc) + { + options.connection = argv[count]; + printf("\nSetting connection to %s\n", options.connection); + } + else + usage(); + } + else if (strcmp(argv[count], "--haconnections") == 0) + { + if (++count < argc) + { + char* tok = strtok(argv[count], " "); + options.hacount = 0; + options.haconnections = malloc(sizeof(char*) * 5); + while (tok) + { + options.haconnections[options.hacount] = malloc(strlen(tok) + 1); + strcpy(options.haconnections[options.hacount], tok); + options.hacount++; + tok = strtok(NULL, " "); + } + } + else + usage(); + } + else if (strcmp(argv[count], "--verbose") == 0) + { + options.verbose = 1; + printf("\nSetting verbose on\n"); + } + count++; + } +} + + +#define LOGA_DEBUG 0 +#define LOGA_INFO 1 +#include +#include +#include +void MyLog(int LOGA_level, char* format, ...) +{ + static char msg_buf[256]; + va_list args; + struct timeb ts; + + struct tm *timeinfo; + + if (LOGA_level == LOGA_DEBUG && options.verbose == 0) + return; + + ftime(&ts); + timeinfo = localtime(&ts.time); + strftime(msg_buf, 80, "%Y%m%d %H%M%S", timeinfo); + + sprintf(&msg_buf[strlen(msg_buf)], ".%.3hu ", ts.millitm); + + va_start(args, format); + vsnprintf(&msg_buf[strlen(msg_buf)], sizeof(msg_buf) - strlen(msg_buf), format, args); + va_end(args); + + printf("%s\n", msg_buf); + fflush(stdout); +} + + +#if defined(WIN32) || defined(_WINDOWS) +#define mqsleep(A) Sleep(1000*A) +#define START_TIME_TYPE DWORD +static DWORD start_time = 0; +START_TIME_TYPE start_clock(void) +{ + return GetTickCount(); +} +#elif defined(AIX) +#define mqsleep sleep +#define START_TIME_TYPE struct timespec +START_TIME_TYPE start_clock(void) +{ + static struct timespec start; + clock_gettime(CLOCK_REALTIME, &start); + return start; +} +#else +#define mqsleep sleep +#define START_TIME_TYPE struct timeval +/* TODO - unused - remove? static struct timeval start_time; */ +START_TIME_TYPE start_clock(void) +{ + struct timeval start_time; + gettimeofday(&start_time, NULL); + return start_time; +} +#endif + + +#if defined(WIN32) +long elapsed(START_TIME_TYPE start_time) +{ + return GetTickCount() - start_time; +} +#elif defined(AIX) +#define assert(a) +long elapsed(struct timespec start) +{ + struct timespec now, res; + + clock_gettime(CLOCK_REALTIME, &now); + ntimersub(now, start, res); + return (res.tv_sec)*1000L + (res.tv_nsec)/1000000L; +} +#else +long elapsed(START_TIME_TYPE start_time) +{ + struct timeval now, res; + + gettimeofday(&now, NULL); + timersub(&now, &start_time, &res); + return (res.tv_sec)*1000 + (res.tv_usec)/1000; +} +#endif + + +#define assert(a, b, c, d) myassert(__FILE__, __LINE__, a, b, c, d) +#define assert1(a, b, c, d, e) myassert(__FILE__, __LINE__, a, b, c, d, e) + +int tests = 0; +int failures = 0; +FILE* xml; +START_TIME_TYPE global_start_time; +char output[3000]; +char* cur_output = output; + + +void write_test_result() +{ + long duration = elapsed(global_start_time); + + fprintf(xml, " time=\"%ld.%.3ld\" >\n", duration / 1000, duration % 1000); + if (cur_output != output) + { + fprintf(xml, "%s", output); + cur_output = output; + } + fprintf(xml, "\n"); +} + + +void myassert(char* filename, int lineno, char* description, int value, char* format, ...) +{ + ++tests; + if (!value) + { + va_list args; + + ++failures; + printf("Assertion failed, file %s, line %d, description: %s\n", filename, lineno, description); + + va_start(args, format); + vprintf(format, args); + va_end(args); + + cur_output += sprintf(cur_output, "file %s, line %d \n", + description, filename, lineno); + } + else + MyLog(LOGA_DEBUG, "Assertion succeeded, file %s, line %d, description: %s", filename, lineno, description); +} + +#define min(a, b) ((a < b) ? a : b) + +int checkMQTTStrings(MQTTString a, MQTTString b) +{ + if (!a.lenstring.data) + { + a.lenstring.data = a.cstring; + if (a.cstring) + a.lenstring.len = strlen(a.cstring); + } + if (!b.lenstring.data) + { + b.lenstring.data = b.cstring; + if (b.cstring) + b.lenstring.len = strlen(b.cstring); + } + return memcmp(a.lenstring.data, b.lenstring.data, min(a.lenstring.len, b.lenstring.len)) == 0; +} + +int checkMQTTSNTopics(MQTTSN_topicid a, MQTTSN_topicid b) +{ + return a.type == b.type && a.data.id == b.data.id; +} + + +int checkConnectPackets(MQTTSNPacket_connectData* before, MQTTSNPacket_connectData* after) +{ + int rc = 0; + int start_failures = failures; + + assert("struct_ids should be the same", + memcmp(before->struct_id, after->struct_id, 4) == 0, "struct_ids were different %.4s\n", after->struct_id); + + assert("struct_versions should be the same", + before->struct_version == after->struct_version, "struct_versions were different\n", rc); + + assert("ClientIDs should be the same", + checkMQTTStrings(before->clientID, after->clientID), "ClientIDs were different\n", rc); + + assert("durations should be the same", + before->duration == after->duration, "durations were different\n", rc); + + assert("cleansessions should be the same", + before->cleansession == after->cleansession, "cleansessions were different\n", rc); + + assert("willFlags should be the same", + before->willFlag == after->willFlag, "willFlags were different\n", rc); + + return failures == start_failures; +} + +int test1(struct Options options) +{ + MQTTSNPacket_connectData data = MQTTSNPacket_connectData_initializer; + MQTTSNPacket_connectData data_after = MQTTSNPacket_connectData_initializer; + int rc = 0; + unsigned char buf[100]; + int buflen = sizeof(buf); + + fprintf(xml, " 0, "rc was %d\n", rc); + + rc = MQTTSNDeserialize_connect(&data_after, buf, buflen); + assert("good rc from deserialize connect", rc == 1, "rc was %d\n", rc); + + /* data after should be the same as data before */ + rc = checkConnectPackets(&data, &data_after); + assert("packets should be the same", rc == 1, "packets were different\n", rc); + +/* exit: */ + MyLog(LOGA_INFO, "TEST1: test %s. %d tests run, %d failures.", + (failures == 0) ? "passed" : "failed", tests, failures); + write_test_result(); + return failures; +} + + +int test2(struct Options options) +{ + int rc = 0; + unsigned char buf[100]; + int buflen = sizeof(buf); + + int dup = 0; + int qos = 2; + int retained = 0; + int msgid = 23; + MQTTSN_topicid topic; + unsigned char *payload = (unsigned char*)"kkhkhkjkj jkjjk jk jk "; + int payloadlen = strlen((char*)payload); + + int dup2 = 1; + int qos2 = 1; + int retained2 = 1; + int msgid2 = 3243; + MQTTSN_topicid topic2; + unsigned char *payload2 = NULL; + int payloadlen2 = 0; + + fprintf(xml, " 0, "rc was %d\n", rc); + + rc = MQTTSNDeserialize_publish(&dup2, &qos2, &retained2, &msgid2, &topic2, + &payload2, &payloadlen2, buf, buflen); + assert("good rc from deserialize publish", rc == 1, "rc was %d\n", rc); + + /* data after should be the same as data before */ + assert("dups should be the same", dup == dup2, "dups were different %d\n", dup2); + assert("qoss should be the same", qos == qos2, "qoss were different %d\n", qos2); + assert("retaineds should be the same", retained == retained2, "retaineds were different %d\n", retained2); + assert("msgids should be the same", msgid == msgid2, "msgids were different %d\n", msgid2); + + assert("topics should be the same", + checkMQTTSNTopics(topic, topic2), "topics were different %s\n", ""); + + assert("payload lengths should be the same", + payloadlen == payloadlen2, "payload lengths were different %d\n", payloadlen2); + + assert("payloads should be the same", + memcmp(payload, payload2, payloadlen) == 0, "payloads were different %s\n", ""); + +/*exit:*/ + MyLog(LOGA_INFO, "TEST2: test %s. %d tests run, %d failures.", + (failures == 0) ? "passed" : "failed", tests, failures); + write_test_result(); + return failures; +} + + +#if 0 +int test3(struct Options options) +{ + int i = 0; + int rc = 0; + char buf[100]; + int buflen = sizeof(buf); +#define TOPIC_COUNT 2 + + int dup = 0; + int msgid = 23; + int count = TOPIC_COUNT; + MQTTString topicStrings[TOPIC_COUNT] = { MQTTString_initializer, MQTTString_initializer }; + int req_qoss[TOPIC_COUNT] = {2, 1}; + + int dup2 = 1; + int msgid2 = 2223; + int count2 = 0; + MQTTString topicStrings2[TOPIC_COUNT] = { MQTTString_initializer, MQTTString_initializer }; + int req_qoss2[TOPIC_COUNT] = {0, 0}; + + fprintf(xml, " 0, "rc was %d\n", rc); + + rc = MQTTDeserialize_subscribe(&dup2, &msgid2, 2, &count2, topicStrings2, req_qoss2, buf, buflen); + assert("good rc from deserialize subscribe", rc == 1, "rc was %d\n", rc); + + /* data after should be the same as data before */ + assert("dups should be the same", dup == dup2, "dups were different %d\n", dup2); + assert("msgids should be the same", msgid == msgid2, "msgids were different %d\n", msgid2); + + assert("count should be the same", count == count2, "counts were different %d\n", count2); + + for (i = 0; i < count2; ++i) + { + assert("topics should be the same", + checkMQTTStrings(topicStrings[i], topicStrings2[i]), "topics were different %s\n", ""); + + assert("qoss should be the same", req_qoss[i] == req_qoss2[i], "qoss were different %d\n", req_qoss2[i]); + } + +/*exit:*/ + MyLog(LOGA_INFO, "TEST3: test %s. %d tests run, %d failures.", + (failures == 0) ? "passed" : "failed", tests, failures); + write_test_result(); + return failures; +} + + +int test4(struct Options options) +{ + int i = 0; + int rc = 0; + char buf[100]; + int buflen = sizeof(buf); +#define TOPIC_COUNT 2 + + int msgid = 23; + int count = TOPIC_COUNT; + int granted_qoss[TOPIC_COUNT] = {2, 1}; +; + int msgid2 = 2223; + int count2 = 0; + int granted_qoss2[TOPIC_COUNT] = {0, 0}; + + fprintf(xml, " 0, "rc was %d\n", rc); + + rc = MQTTDeserialize_suback(&msgid2, 2, &count2, granted_qoss2, buf, buflen); + assert("good rc from deserialize suback", rc == 1, "rc was %d\n", rc); + + /* data after should be the same as data before */ + assert("msgids should be the same", msgid == msgid2, "msgids were different %d\n", msgid2); + + assert("count should be the same", count == count2, "counts were different %d\n", count2); + + for (i = 0; i < count2; ++i) + assert("qoss should be the same", granted_qoss[i] == granted_qoss2[i], "qoss were different %d\n", granted_qoss2[i]); + +/* exit: */ + MyLog(LOGA_INFO, "TEST4: test %s. %d tests run, %d failures.", + (failures == 0) ? "passed" : "failed", tests, failures); + write_test_result(); + return failures; +} + + +int test5(struct Options options) +{ + int i = 0; + int rc = 0; + char buf[100]; + int buflen = sizeof(buf); +#define TOPIC_COUNT 2 + + int dup = 0; + int msgid = 23; + int count = TOPIC_COUNT; + MQTTString topicStrings[TOPIC_COUNT] = { MQTTString_initializer, MQTTString_initializer }; + + int dup2 = 1; + int msgid2 = 2223; + int count2 = 0; + MQTTString topicStrings2[TOPIC_COUNT] = { MQTTString_initializer, MQTTString_initializer }; + + fprintf(xml, " 0, "rc was %d\n", rc); + + rc = MQTTDeserialize_unsubscribe(&dup2, &msgid2, 2, &count2, topicStrings2, buf, buflen); + assert("good rc from deserialize unsubscribe", rc == 1, "rc was %d\n", rc); + + /* data after should be the same as data before */ + assert("dups should be the same", dup == dup2, "dups were different %d\n", dup2); + assert("msgids should be the same", msgid == msgid2, "msgids were different %d\n", msgid2); + + assert("count should be the same", count == count2, "counts were different %d\n", count2); + + for (i = 0; i < count2; ++i) + assert("topics should be the same", + checkMQTTStrings(topicStrings[i], topicStrings2[i]), "topics were different %s\n", ""); + +/* exit: */ + MyLog(LOGA_INFO, "TEST5: test %s. %d tests run, %d failures.", + (failures == 0) ? "passed" : "failed", tests, failures); + write_test_result(); + return failures; +} +#endif + + +int test6(struct Options options) +{ + int rc = 0; + unsigned char buf[100]; + int buflen = sizeof(buf); + + int connack_rc = 77; + + int connack_rc2 = 0; + + fprintf(xml, " 0, "rc was %d\n", rc); + + rc = MQTTSNDeserialize_connack(&connack_rc2, buf, buflen); + assert("good rc from deserialize connack", rc == 1, "rc was %d\n", rc); + + /* data after should be the same as data before */ + assert("dups should be the same", connack_rc == connack_rc2, "dups were different %d\n", connack_rc2); + +/* exit: */ + MyLog(LOGA_INFO, "TEST6: test %s. %d tests run, %d failures.", + (failures == 0) ? "passed" : "failed", tests, failures); + write_test_result(); + return failures; +} + + +int main(int argc, char** argv) +{ + int rc = 0; + int (*tests[])() = {NULL, test1, test2, /*test3, test4, test5,*/ test6}; + + xml = fopen("TEST-test1.xml", "w"); + fprintf(xml, "\n", (int)(ARRAY_SIZE(tests) - 1)); + + getopts(argc, argv); + + if (options.test_no == 0) + { /* run all the tests */ + for (options.test_no = 1; options.test_no < ARRAY_SIZE(tests); ++options.test_no) + rc += tests[options.test_no](options); /* return number of failures. 0 = test succeeded */ + } + else + rc = tests[options.test_no](options); /* run just the selected test */ + + if (rc == 0) + MyLog(LOGA_INFO, "verdict pass"); + else + MyLog(LOGA_INFO, "verdict fail"); + + fprintf(xml, "\n"); + fclose(xml); + return rc; +}