| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130 |
- /*******************************************************************************
- * Copyright (c) 2009, 2024 IBM Corp. and Ian Craggs
- *
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v2.0
- * and Eclipse Distribution License v1.0 which accompany this distribution.
- *
- * The Eclipse Public License is available at
- * https://www.eclipse.org/legal/epl-2.0/
- * and the Eclipse Distribution License is available at
- * http://www.eclipse.org/org/documents/edl-v10.php.
- *
- * Contributors:
- * Ian Craggs - initial API and implementation and/or initial documentation
- * Ian Craggs, Allan Stockdill-Mander - SSL updates
- * Ian Craggs - MQTT 3.1.1 support
- * Ian Craggs - fix for issue 453
- * Ian Craggs - MQTT 5.0 support
- *******************************************************************************/
- /**
- * @file
- * \brief functions to deal with reading and writing of MQTT packets from and to sockets
- *
- * Some other related functions are in the MQTTPacketOut module
- */
- #include "MQTTPacket.h"
- #include "Log.h"
- #if !defined(NO_PERSISTENCE)
- #include "MQTTPersistence.h"
- #endif
- #include <ctype.h>
- #include "Messages.h"
- #include "StackTrace.h"
- #include "WebSocket.h"
- #include "MQTTTime.h"
- #include <stdlib.h>
- #include <string.h>
- #include "Heap.h"
- #if !defined(min)
- #define min(A,B) ( (A) < (B) ? (A):(B))
- #endif
- /**
- * List of the predefined MQTT v3/v5 packet names.
- */
- static const char *packet_names[] =
- {
- "RESERVED", "CONNECT", "CONNACK", "PUBLISH", "PUBACK", "PUBREC", "PUBREL",
- "PUBCOMP", "SUBSCRIBE", "SUBACK", "UNSUBSCRIBE", "UNSUBACK",
- "PINGREQ", "PINGRESP", "DISCONNECT", "AUTH"
- };
- const char** MQTTClient_packet_names = packet_names;
- /**
- * Converts an MQTT packet code into its name
- * @param ptype packet code
- * @return the corresponding string, or "UNKNOWN"
- */
- const char* MQTTPacket_name(int ptype)
- {
- return (ptype >= 0 && ptype <= AUTH) ? packet_names[ptype] : "UNKNOWN";
- }
- /**
- * Array of functions to build packets, indexed according to packet code
- */
- pf new_packets[] =
- {
- NULL, /**< reserved */
- NULL, /**< MQTTPacket_connect*/
- MQTTPacket_connack, /**< CONNACK */
- MQTTPacket_publish, /**< PUBLISH */
- MQTTPacket_ack, /**< PUBACK */
- MQTTPacket_ack, /**< PUBREC */
- MQTTPacket_ack, /**< PUBREL */
- MQTTPacket_ack, /**< PUBCOMP */
- NULL, /**< MQTTPacket_subscribe*/
- MQTTPacket_suback, /**< SUBACK */
- NULL, /**< MQTTPacket_unsubscribe*/
- MQTTPacket_unsuback, /**< UNSUBACK */
- MQTTPacket_header_only, /**< PINGREQ */
- MQTTPacket_header_only, /**< PINGRESP */
- MQTTPacket_ack, /**< DISCONNECT */
- MQTTPacket_ack /**< AUTH */
- };
- static char* readUTFlen(char** pptr, char* enddata, int* len);
- static int MQTTPacket_send_ack(int MQTTVersion, int type, int msgid, int dup, networkHandles *net);
- /**
- * Reads one MQTT packet from a socket.
- * @param socket a socket from which to read an MQTT packet
- * @param error pointer to the error code which is completed if no packet is returned
- * @return the packet structure or NULL if there was an error
- */
- void* MQTTPacket_Factory(int MQTTVersion, networkHandles* net, int* error)
- {
- char* data = NULL;
- static Header header;
- size_t remaining_length;
- int ptype;
- void* pack = NULL;
- size_t actual_len = 0;
- FUNC_ENTRY;
- *error = SOCKET_ERROR; /* indicate whether an error occurred, or not */
- const size_t headerWsFramePos = WebSocket_framePos();
- /* read the packet data from the socket */
- *error = WebSocket_getch(net, &header.byte);
- if (*error != TCPSOCKET_COMPLETE) /* first byte is the header byte */
- goto exit; /* packet not read, *error indicates whether SOCKET_ERROR occurred */
- /* now read the remaining length, so we know how much more to read */
- if ((*error = MQTTPacket_decode(net, &remaining_length)) != TCPSOCKET_COMPLETE)
- goto exit; /* packet not read, *error indicates whether SOCKET_ERROR occurred */
- /* now read the rest, the variable header and payload */
- data = WebSocket_getdata(net, remaining_length, &actual_len);
- if (remaining_length && data == NULL)
- {
- *error = SOCKET_ERROR;
- goto exit; /* socket error */
- }
- if (actual_len < remaining_length)
- {
- *error = TCPSOCKET_INTERRUPTED;
- net->lastReceived = MQTTTime_now();
- }
- else
- {
- ptype = header.bits.type;
- if (ptype < CONNECT || (MQTTVersion < MQTTVERSION_5 && ptype >= DISCONNECT) ||
- (MQTTVersion >= MQTTVERSION_5 && ptype > AUTH) ||
- new_packets[ptype] == NULL)
- Log(TRACE_MIN, 2, NULL, ptype);
- else
- {
- if ((pack = (*new_packets[ptype])(MQTTVersion, header.byte, data, remaining_length)) == NULL)
- {
- *error = SOCKET_ERROR; // was BAD_MQTT_PACKET;
- Log(LOG_ERROR, -1, "Bad MQTT packet, type %d", ptype);
- }
- #if !defined(NO_PERSISTENCE)
- else if (header.bits.type == PUBLISH && header.bits.qos == 2)
- {
- int buf0len;
- char *buf = malloc(10);
- if (buf == NULL)
- {
- *error = SOCKET_ERROR;
- goto exit;
- }
- buf[0] = header.byte;
- buf0len = 1 + MQTTPacket_encode(&buf[1], remaining_length);
- *error = MQTTPersistence_putPacket(net->socket, buf, buf0len, 1,
- &data, &remaining_length, header.bits.type, ((Publish *)pack)->msgId, 1, MQTTVersion);
- free(buf);
- }
- #endif
- }
- }
- if (pack)
- net->lastReceived = MQTTTime_now();
- exit:
- if (*error == TCPSOCKET_INTERRUPTED)
- WebSocket_framePosSeekTo(headerWsFramePos);
- FUNC_EXIT_RC(*error);
- return pack;
- }
- /**
- * Sends an MQTT packet in one system call write
- * @param socket the socket to which to write the data
- * @param header the one-byte MQTT header
- * @param buffer the rest of the buffer to write (not including remaining length)
- * @param buflen the length of the data in buffer to be written
- * @param MQTTVersion the version of MQTT being used
- * @return the completion code (TCPSOCKET_COMPLETE etc)
- */
- int MQTTPacket_send(networkHandles* net, Header header, char* buffer, size_t buflen, int freeData,
- int MQTTVersion)
- {
- int rc = SOCKET_ERROR;
- size_t buf0len;
- char *buf;
- PacketBuffers packetbufs;
- FUNC_ENTRY;
- buf0len = 1 + MQTTPacket_encode(NULL, buflen);
- buf = malloc(buf0len);
- if (buf == NULL)
- {
- rc = SOCKET_ERROR;
- goto exit;
- }
- buf[0] = header.byte;
- MQTTPacket_encode(&buf[1], buflen);
- #if !defined(NO_PERSISTENCE)
- if (header.bits.type == PUBREL)
- {
- char* ptraux = buffer;
- int msgId = readInt(&ptraux);
- rc = MQTTPersistence_putPacket(net->socket, buf, buf0len, 1, &buffer, &buflen,
- header.bits.type, msgId, 0, MQTTVersion);
- }
- #endif
- packetbufs.count = 1;
- packetbufs.buffers = &buffer;
- packetbufs.buflens = &buflen;
- packetbufs.frees = &freeData;
- memset(packetbufs.mask, '\0', sizeof(packetbufs.mask));
- rc = WebSocket_putdatas(net, &buf, &buf0len, &packetbufs);
- if (rc == TCPSOCKET_COMPLETE)
- net->lastSent = MQTTTime_now();
-
- if (rc != TCPSOCKET_INTERRUPTED)
- free(buf);
- exit:
- FUNC_EXIT_RC(rc);
- return rc;
- }
- /**
- * Sends an MQTT packet from multiple buffers in one system call write
- * @param socket the socket to which to write the data
- * @param header the one-byte MQTT header
- * @param count the number of buffers
- * @param buffers the rest of the buffers to write (not including remaining length)
- * @param buflens the lengths of the data in the array of buffers to be written
- * @param the MQTT version being used
- * @return the completion code (TCPSOCKET_COMPLETE etc)
- */
- int MQTTPacket_sends(networkHandles* net, Header header, PacketBuffers* bufs, int MQTTVersion)
- {
- int i, rc = SOCKET_ERROR;
- size_t buf0len, total = 0;
- char *buf;
- FUNC_ENTRY;
- for (i = 0; i < bufs->count; i++)
- total += bufs->buflens[i];
- buf0len = 1 + MQTTPacket_encode(NULL, total);
- buf = malloc(buf0len);
- if (buf == NULL)
- {
- rc = SOCKET_ERROR;
- goto exit;
- }
- buf[0] = header.byte;
- MQTTPacket_encode(&buf[1], total);
- #if !defined(NO_PERSISTENCE)
- if (header.bits.type == PUBLISH && header.bits.qos != 0)
- { /* persist PUBLISH QoS1 and Qo2 */
- char *ptraux = bufs->buffers[2];
- int msgId = readInt(&ptraux);
- rc = MQTTPersistence_putPacket(net->socket, buf, buf0len, bufs->count, bufs->buffers, bufs->buflens,
- header.bits.type, msgId, 0, MQTTVersion);
- }
- #endif
- rc = WebSocket_putdatas(net, &buf, &buf0len, bufs);
- if (rc == TCPSOCKET_COMPLETE)
- net->lastSent = MQTTTime_now();
-
- if (rc != TCPSOCKET_INTERRUPTED)
- free(buf);
- exit:
- FUNC_EXIT_RC(rc);
- return rc;
- }
- /**
- * Encodes the message length according to the MQTT algorithm
- * @param buf the buffer into which the encoded data is written
- * @param length the length to be encoded
- * @return the number of bytes written to buffer
- */
- int MQTTPacket_encode(char* buf, size_t 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;
- if (buf)
- buf[rc++] = d;
- else
- rc++;
- } while (length > 0);
- FUNC_EXIT_RC(rc);
- return rc;
- }
- /**
- * Decodes the message length according to the MQTT algorithm
- * @param socket the socket from which to read the bytes
- * @param value the decoded length returned
- * @return the number of bytes read from the socket
- */
- int MQTTPacket_decode(networkHandles* net, size_t* value)
- {
- int rc = SOCKET_ERROR;
- char c;
- int multiplier = 1;
- int len = 0;
- #define MAX_NO_OF_REMAINING_LENGTH_BYTES 4
- FUNC_ENTRY;
- *value = 0;
- do
- {
- if (++len > MAX_NO_OF_REMAINING_LENGTH_BYTES)
- {
- rc = SOCKET_ERROR; /* bad data */
- goto exit;
- }
- rc = WebSocket_getch(net, &c);
- if (rc != TCPSOCKET_COMPLETE)
- goto exit;
- *value += (c & 127) * multiplier;
- multiplier *= 128;
- } while ((c & 128) != 0);
- exit:
- FUNC_EXIT_RC(rc);
- return rc;
- }
- /**
- * Calculates an integer from two bytes read from the input buffer
- * @param pptr pointer to the input buffer - incremented by the number of bytes used & returned
- * @return the integer value calculated
- */
- int readInt(char** pptr)
- {
- char* ptr = *pptr;
- int len = 256*((unsigned char)(*ptr)) + (unsigned char)(*(ptr+1));
- *pptr += 2;
- return len;
- }
- /**
- * Reads a "UTF" string from the input buffer. UTF as in the MQTT v3 spec which really means
- * a length delimited string. So it reads the two byte length then the data according to
- * that length. The end of the buffer is provided too, so we can prevent buffer overruns caused
- * by an incorrect length.
- * @param pptr pointer to the input buffer - incremented by the number of bytes used & returned
- * @param enddata pointer to the end of the buffer not to be read beyond
- * @param len returns the calculcated value of the length bytes read
- * @return an allocated C string holding the characters read, or NULL if the length read would
- * have caused an overrun.
- *
- */
- static char* readUTFlen(char** pptr, char* enddata, int* len)
- {
- char* string = NULL;
- FUNC_ENTRY;
- if (enddata - (*pptr) > 1) /* enough length to read the integer? */
- {
- *len = readInt(pptr);
- if (&(*pptr)[*len] <= enddata)
- {
- if ((string = malloc(*len+1)) == NULL)
- goto exit;
- memcpy(string, *pptr, *len);
- string[*len] = '\0';
- *pptr += *len;
- }
- }
- exit:
- FUNC_EXIT;
- return string;
- }
- /**
- * Reads a "UTF" string from the input buffer. UTF as in the MQTT v3 spec which really means
- * a length delimited string. So it reads the two byte length then the data according to
- * that length. The end of the buffer is provided too, so we can prevent buffer overruns caused
- * by an incorrect length.
- * @param pptr pointer to the input buffer - incremented by the number of bytes used & returned
- * @param enddata pointer to the end of the buffer not to be read beyond
- * @return an allocated C string holding the characters read, or NULL if the length read would
- * have caused an overrun.
- */
- char* readUTF(char** pptr, char* enddata)
- {
- int len;
- return readUTFlen(pptr, enddata, &len);
- }
- /**
- * Reads one character from the input buffer.
- * @param pptr pointer to the input buffer - incremented by the number of bytes used & returned
- * @return the character read
- */
- unsigned char readChar(char** pptr)
- {
- unsigned char c = **pptr;
- (*pptr)++;
- return c;
- }
- /**
- * Writes one character to an output buffer.
- * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned
- * @param c the character to write
- */
- void writeChar(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(char** pptr, int anInt)
- {
- **pptr = (char)(anInt / 256);
- (*pptr)++;
- **pptr = (char)(anInt % 256);
- (*pptr)++;
- }
- /**
- * Writes a "UTF" string to an output buffer. Converts C string to length-delimited.
- * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned
- * @param string the C string to write
- */
- void writeUTF(char** pptr, const char* string)
- {
- size_t len = strlen(string);
- writeInt(pptr, (int)len);
- memcpy(*pptr, string, len);
- *pptr += len;
- }
- /**
- * Writes length delimited data to an output buffer
- * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned
- * @param data the data to write
- * @param datalen the length of the data to write
- */
- void writeData(char** pptr, const void* data, int datalen)
- {
- writeInt(pptr, datalen);
- memcpy(*pptr, data, datalen);
- *pptr += datalen;
- }
- /**
- * Function used in the new packets table to create packets which have only a header.
- * @param MQTTVersion the version of MQTT
- * @param aHeader the MQTT header byte
- * @param data the rest of the packet
- * @param datalen the length of the rest of the packet
- * @return pointer to the packet structure
- */
- void* MQTTPacket_header_only(int MQTTVersion, unsigned char aHeader, char* data, size_t datalen)
- {
- static unsigned char header = 0;
- header = aHeader;
- return &header;
- }
- /**
- * Send an MQTT disconnect packet down a socket.
- * @param socket the open socket to send the data to
- * @return the completion code (e.g. TCPSOCKET_COMPLETE)
- */
- int MQTTPacket_send_disconnect(Clients* client, enum MQTTReasonCodes reason, MQTTProperties* props)
- {
- Header header;
- int rc = 0;
- FUNC_ENTRY;
- header.byte = 0;
- header.bits.type = DISCONNECT;
- if (client->MQTTVersion >= 5 && (props || reason != MQTTREASONCODE_SUCCESS))
- {
- size_t buflen = 1 + ((props == NULL) ? 0 : MQTTProperties_len(props));
- char *buf = NULL;
- char *ptr = NULL;
- if ((buf = malloc(buflen)) == NULL)
- {
- rc = SOCKET_ERROR;
- goto exit;
- }
- ptr = buf;
- writeChar(&ptr, reason);
- if (props)
- MQTTProperties_write(&ptr, props);
- if ((rc = MQTTPacket_send(&client->net, header, buf, buflen, 1,
- client->MQTTVersion)) != TCPSOCKET_INTERRUPTED)
- free(buf);
- }
- else
- rc = MQTTPacket_send(&client->net, header, NULL, 0, 0, client->MQTTVersion);
- exit:
- Log(LOG_PROTOCOL, 28, NULL, client->net.socket, client->clientID, rc);
- FUNC_EXIT_RC(rc);
- return rc;
- }
- /**
- * Function used in the new packets table to create publish packets.
- * @param MQTTVersion
- * @param aHeader the MQTT header byte
- * @param data the rest of the packet
- * @param datalen the length of the rest of the packet
- * @return pointer to the packet structure
- */
- void* MQTTPacket_publish(int MQTTVersion, unsigned char aHeader, char* data, size_t datalen)
- {
- Publish* pack = NULL;
- char* curdata = data;
- char* enddata = &data[datalen];
- FUNC_ENTRY;
- if ((pack = malloc(sizeof(Publish))) == NULL)
- goto exit;
- memset(pack, '\0', sizeof(Publish));
- pack->MQTTVersion = MQTTVersion;
- pack->header.byte = aHeader;
- if ((pack->topic = readUTFlen(&curdata, enddata, &pack->topiclen)) == NULL) /* Topic name on which to publish */
- {
- free(pack);
- pack = NULL;
- goto exit;
- }
- if (pack->header.bits.qos > 0) /* Msgid only exists for QoS 1 or 2 */
- {
- if (enddata - curdata < 2) /* Is there enough data for the msgid? */
- {
- free(pack);
- pack = NULL;
- goto exit;
- }
- pack->msgId = readInt(&curdata);
- }
- else
- pack->msgId = 0;
- if (MQTTVersion >= MQTTVERSION_5)
- {
- MQTTProperties props = MQTTProperties_initializer;
- pack->properties = props;
- if (MQTTProperties_read(&pack->properties, &curdata, enddata) != 1)
- {
- if (pack->properties.array)
- free(pack->properties.array);
- if (pack)
- free(pack);
- pack = NULL; /* signal protocol error */
- goto exit;
- }
- }
- pack->payload = curdata;
- pack->payloadlen = (int)(datalen-(curdata-data));
- exit:
- FUNC_EXIT;
- return pack;
- }
- /**
- * Free allocated storage for a publish packet.
- * @param pack pointer to the publish packet structure
- */
- void MQTTPacket_freePublish(Publish* pack)
- {
- FUNC_ENTRY;
- if (pack->topic != NULL)
- free(pack->topic);
- if (pack->MQTTVersion >= MQTTVERSION_5)
- MQTTProperties_free(&pack->properties);
- free(pack);
- FUNC_EXIT;
- }
- /**
- * Free allocated storage for an ack packet.
- * @param pack pointer to the publish packet structure
- */
- void MQTTPacket_freeAck(Ack* pack)
- {
- FUNC_ENTRY;
- if (pack->MQTTVersion >= MQTTVERSION_5)
- MQTTProperties_free(&pack->properties);
- free(pack);
- FUNC_EXIT;
- }
- /**
- * Send an MQTT acknowledgement packet down a socket.
- * @param MQTTVersion the version of MQTT being used
- * @param type the MQTT packet type e.g. SUBACK
- * @param msgid the MQTT message id to use
- * @param dup boolean - whether to set the MQTT DUP flag
- * @param net the network handle to send the data to
- * @return the completion code (e.g. TCPSOCKET_COMPLETE)
- */
- static int MQTTPacket_send_ack(int MQTTVersion, int type, int msgid, int dup, networkHandles *net)
- {
- Header header;
- int rc = SOCKET_ERROR;
- char *buf = NULL;
- char *ptr = NULL;
- FUNC_ENTRY;
- if ((ptr = buf = malloc(2)) == NULL)
- goto exit;
- header.byte = 0;
- header.bits.type = type;
- header.bits.dup = dup;
- if (type == PUBREL)
- header.bits.qos = 1;
- writeInt(&ptr, msgid);
- if ((rc = MQTTPacket_send(net, header, buf, 2, 1, MQTTVersion)) != TCPSOCKET_INTERRUPTED)
- free(buf);
- exit:
- FUNC_EXIT_RC(rc);
- return rc;
- }
- /**
- * Send an MQTT PUBACK packet down a socket.
- * @param MQTTVersion the version of MQTT being used
- * @param msgid the MQTT message id to use
- * @param socket the open socket to send the data to
- * @param clientID the string client identifier, only used for tracing
- * @return the completion code (e.g. TCPSOCKET_COMPLETE)
- */
- int MQTTPacket_send_puback(int MQTTVersion, int msgid, networkHandles* net, const char* clientID)
- {
- int rc = 0;
- FUNC_ENTRY;
- rc = MQTTPacket_send_ack(MQTTVersion, PUBACK, msgid, 0, net);
- Log(LOG_PROTOCOL, 12, NULL, net->socket, clientID, msgid, rc);
- FUNC_EXIT_RC(rc);
- return rc;
- }
- /**
- * Free allocated storage for a suback packet.
- * @param pack pointer to the suback packet structure
- */
- void MQTTPacket_freeSuback(Suback* pack)
- {
- FUNC_ENTRY;
- if (pack->MQTTVersion >= MQTTVERSION_5)
- MQTTProperties_free(&pack->properties);
- if (pack->qoss != NULL)
- ListFree(pack->qoss);
- free(pack);
- FUNC_EXIT;
- }
- /**
- * Free allocated storage for a suback packet.
- * @param pack pointer to the suback packet structure
- */
- void MQTTPacket_freeUnsuback(Unsuback* pack)
- {
- FUNC_ENTRY;
- if (pack->MQTTVersion >= MQTTVERSION_5)
- {
- MQTTProperties_free(&pack->properties);
- if (pack->reasonCodes != NULL)
- ListFree(pack->reasonCodes);
- }
- free(pack);
- FUNC_EXIT;
- }
- /**
- * Send an MQTT PUBREC packet down a socket.
- * @param MQTTVersion the version of MQTT being used
- * @param msgid the MQTT message id to use
- * @param socket the open socket to send the data to
- * @param clientID the string client identifier, only used for tracing
- * @return the completion code (e.g. TCPSOCKET_COMPLETE)
- */
- int MQTTPacket_send_pubrec(int MQTTVersion, int msgid, networkHandles* net, const char* clientID)
- {
- int rc = 0;
- FUNC_ENTRY;
- rc = MQTTPacket_send_ack(MQTTVersion, PUBREC, msgid, 0, net);
- Log(LOG_PROTOCOL, 13, NULL, net->socket, clientID, msgid, rc);
- FUNC_EXIT_RC(rc);
- return rc;
- }
- /**
- * Send an MQTT PUBREL packet down a socket.
- * @param MQTTVersion the version of MQTT being used
- * @param msgid the MQTT message id to use
- * @param dup boolean - whether to set the MQTT DUP flag
- * @param socket the open socket to send the data to
- * @param clientID the string client identifier, only used for tracing
- * @return the completion code (e.g. TCPSOCKET_COMPLETE)
- */
- int MQTTPacket_send_pubrel(int MQTTVersion, int msgid, int dup, networkHandles* net, const char* clientID)
- {
- int rc = 0;
- FUNC_ENTRY;
- rc = MQTTPacket_send_ack(MQTTVersion, PUBREL, msgid, dup, net);
- Log(LOG_PROTOCOL, 16, NULL, net->socket, clientID, msgid, rc);
- FUNC_EXIT_RC(rc);
- return rc;
- }
- /**
- * Send an MQTT PUBCOMP packet down a socket.
- * @param MQTTVersion the version of MQTT being used
- * @param msgid the MQTT message id to use
- * @param socket the open socket to send the data to
- * @param clientID the string client identifier, only used for tracing
- * @return the completion code (e.g. TCPSOCKET_COMPLETE)
- */
- int MQTTPacket_send_pubcomp(int MQTTVersion, int msgid, networkHandles* net, const char* clientID)
- {
- int rc = 0;
- FUNC_ENTRY;
- rc = MQTTPacket_send_ack(MQTTVersion, PUBCOMP, msgid, 0, net);
- Log(LOG_PROTOCOL, 18, NULL, net->socket, clientID, msgid, rc);
- FUNC_EXIT_RC(rc);
- return rc;
- }
- /**
- * Function used in the new packets table to create acknowledgement packets.
- * @param MQTTVersion the version of MQTT being used
- * @param aHeader the MQTT header byte
- * @param data the rest of the packet
- * @param datalen the length of the rest of the packet
- * @return pointer to the packet structure
- */
- void* MQTTPacket_ack(int MQTTVersion, unsigned char aHeader, char* data, size_t datalen)
- {
- Ack* pack = NULL;
- char* curdata = data;
- char* enddata = &data[datalen];
- FUNC_ENTRY;
- if ((pack = malloc(sizeof(Ack))) == NULL)
- goto exit;
- pack->MQTTVersion = MQTTVersion;
- pack->header.byte = aHeader;
- if (pack->header.bits.type != DISCONNECT)
- {
- if (enddata - curdata < 2) /* Is there enough data for the msgid? */
- {
- free(pack);
- pack = NULL;
- goto exit;
- }
- pack->msgId = readInt(&curdata);
- }
- if (MQTTVersion >= MQTTVERSION_5)
- {
- MQTTProperties props = MQTTProperties_initializer;
- pack->rc = MQTTREASONCODE_SUCCESS;
- pack->properties = props;
- /* disconnect has no msgid */
- if (datalen > 2 || (pack->header.bits.type == DISCONNECT && datalen > 0))
- pack->rc = readChar(&curdata); /* reason code */
- if (datalen > 3 || (pack->header.bits.type == DISCONNECT && datalen > 1))
- {
- if (MQTTProperties_read(&pack->properties, &curdata, enddata) != 1)
- {
- if (pack->properties.array)
- free(pack->properties.array);
- if (pack)
- free(pack);
- pack = NULL; /* signal protocol error */
- goto exit;
- }
- }
- }
- exit:
- FUNC_EXIT;
- return pack;
- }
- /**
- * Format the payload for printing in the trace.
- * Any unprintable characters output as hex.
- * @param buflen the length of the supplied print buffer
- * @param buf the supplied print buffer
- * @param payloadlen the length of the payload to be printed
- * @param payload the payload data to be printed
- * @return the length of the data output to the print buffer
- */
- int MQTTPacket_formatPayload(int buflen, char* buf, int payloadlen, char* payload)
- {
- int pos = 0;
- int i = 0;
- for (i = 0; i < payloadlen; i++)
- {
- if (isprint(payload[i]))
- {
- if (pos >= buflen)
- break;
- buf[pos++] = payload[i];
- }
- else
- {
- static char *hexdigit = "0123456789ABCDEF";
- if (pos >= buflen - 3)
- break;
- buf[pos++] = '\\';
- buf[pos++] = 'x';
- buf[pos++] = hexdigit[payload[i] & 0xF0];
- buf[pos++] = hexdigit[payload[i] & 0x0F];
- }
- }
- return pos;
- }
- /**
- * Send an MQTT PUBLISH packet down a socket.
- * @param pack a structure from which to get some values to use, e.g topic, payload
- * @param dup boolean - whether to set the MQTT DUP flag
- * @param qos the value to use for the MQTT QoS setting
- * @param retained boolean - whether to set the MQTT retained flag
- * @param socket the open socket to send the data to
- * @param clientID the string client identifier, only used for tracing
- * @return the completion code (e.g. TCPSOCKET_COMPLETE)
- */
- int MQTTPacket_send_publish(Publish* pack, int dup, int qos, int retained, networkHandles* net, const char* clientID)
- {
- Header header;
- char *topiclen;
- int rc = SOCKET_ERROR;
- FUNC_ENTRY;
- topiclen = malloc(2);
- if (topiclen == NULL)
- goto exit;
- header.bits.type = PUBLISH;
- header.bits.dup = dup;
- header.bits.qos = qos;
- header.bits.retain = retained;
- if (qos > 0 || pack->MQTTVersion >= 5)
- {
- int buflen = ((qos > 0) ? 2 : 0) + ((pack->MQTTVersion >= 5) ? MQTTProperties_len(&pack->properties) : 0);
- char *ptr = NULL;
- char* bufs[4] = {topiclen, pack->topic, NULL, pack->payload};
- size_t lens[4] = {2, strlen(pack->topic), buflen, pack->payloadlen};
- int frees[4] = {1, 0, 1, 0};
- PacketBuffers packetbufs = {4, bufs, lens, frees, {pack->mask[0], pack->mask[1], pack->mask[2], pack->mask[3]}};
- bufs[2] = ptr = malloc(buflen);
- if (ptr == NULL)
- goto exit_free;
- if (qos > 0)
- writeInt(&ptr, pack->msgId);
- if (pack->MQTTVersion >= 5)
- MQTTProperties_write(&ptr, &pack->properties);
- ptr = topiclen;
- writeInt(&ptr, (int)lens[1]);
- rc = MQTTPacket_sends(net, header, &packetbufs, pack->MQTTVersion);
- if (rc != TCPSOCKET_INTERRUPTED)
- free(bufs[2]);
- memcpy(pack->mask, packetbufs.mask, sizeof(pack->mask));
- }
- else
- {
- char* ptr = topiclen;
- char* bufs[3] = {topiclen, pack->topic, pack->payload};
- size_t lens[3] = {2, strlen(pack->topic), pack->payloadlen};
- int frees[3] = {1, 0, 0};
- PacketBuffers packetbufs = {3, bufs, lens, frees, {pack->mask[0], pack->mask[1], pack->mask[2], pack->mask[3]}};
- writeInt(&ptr, (int)lens[1]);
- rc = MQTTPacket_sends(net, header, &packetbufs, pack->MQTTVersion);
- memcpy(pack->mask, packetbufs.mask, sizeof(pack->mask));
- }
- {
- #if defined(_WIN32) || defined(_WIN64)
- #define buflen 30
- #else
- const int buflen = 30;
- #endif
- char buf[buflen];
- int len = 0;
- len = MQTTPacket_formatPayload(buflen, buf, pack->payloadlen, pack->payload);
- if (qos == 0)
- Log(LOG_PROTOCOL, 27, NULL, net->socket, clientID, retained, rc, pack->payloadlen, len, buf);
- else
- Log(LOG_PROTOCOL, 10, NULL, net->socket, clientID, pack->msgId, qos, retained, rc, pack->payloadlen,
- len, buf);
- }
- exit_free:
- if (rc != TCPSOCKET_INTERRUPTED)
- free(topiclen);
- exit:
- FUNC_EXIT_RC(rc);
- return rc;
- }
- /**
- * Free allocated storage for a various packet tyoes
- * @param pack pointer to the suback packet structure
- */
- void MQTTPacket_free_packet(MQTTPacket* pack)
- {
- FUNC_ENTRY;
- if (pack->header.bits.type == PUBLISH)
- MQTTPacket_freePublish((Publish*)pack);
- /*else if (pack->header.type == SUBSCRIBE)
- MQTTPacket_freeSubscribe((Subscribe*)pack, 1);
- else if (pack->header.type == UNSUBSCRIBE)
- MQTTPacket_freeUnsubscribe((Unsubscribe*)pack);*/
- else
- free(pack);
- FUNC_EXIT;
- }
- /**
- * Writes an integer as 4 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 writeInt4(char** pptr, unsigned int anInt)
- {
- **pptr = (char)(anInt / 16777216);
- (*pptr)++;
- anInt %= 16777216;
- **pptr = (char)(anInt / 65536);
- (*pptr)++;
- anInt %= 65536;
- **pptr = (char)(anInt / 256);
- (*pptr)++;
- **pptr = (char)(anInt % 256);
- (*pptr)++;
- }
- /**
- * 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
- */
- unsigned int readInt4(char** pptr)
- {
- unsigned char* ptr = (unsigned char*)*pptr;
- unsigned int value = 16777216*(*ptr) + 65536*(*(ptr+1)) + 256*(*(ptr+2)) + (*(ptr+3));
- *pptr += 4;
- return value;
- }
- void writeMQTTLenString(char** pptr, MQTTLenString lenstring)
- {
- writeInt(pptr, lenstring.len);
- memcpy(*pptr, lenstring.data, lenstring.len);
- *pptr += lenstring.len;
- }
- int MQTTLenStringRead(MQTTLenString* lenstring, char** pptr, char* enddata)
- {
- int len = -1;
- /* the first two bytes are the length of the string */
- if (enddata - (*pptr) > 1) /* enough length to read the integer? */
- {
- lenstring->len = readInt(pptr); /* increments pptr to point past length */
- if (&(*pptr)[lenstring->len] <= enddata)
- {
- lenstring->data = (char*)*pptr;
- *pptr += lenstring->len;
- len = 2 + lenstring->len;
- }
- }
- return len;
- }
- /*
- if (prop->value.integer4 >= 0 && prop->value.integer4 <= 127)
- len = 1;
- else if (prop->value.integer4 >= 128 && prop->value.integer4 <= 16383)
- len = 2;
- else if (prop->value.integer4 >= 16384 && prop->value.integer4 < 2097151)
- len = 3;
- else if (prop->value.integer4 >= 2097152 && prop->value.integer4 < 268435455)
- len = 4;
- */
- int MQTTPacket_VBIlen(int rem_len)
- {
- int rc = 0;
- if (rem_len < 128)
- rc = 1;
- else if (rem_len < 16384)
- rc = 2;
- else if (rem_len < 2097152)
- rc = 3;
- else
- rc = 4;
- 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_VBIdecode(int (*getcharfn)(char*, int), unsigned int* value)
- {
- char c;
- int multiplier = 1;
- int len = 0;
- #define 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 = (*getcharfn)(&c, 1);
- if (rc != 1)
- goto exit;
- *value += (c & 127) * multiplier;
- multiplier *= 128;
- } while ((c & 128) != 0);
- exit:
- return len;
- }
- static char* bufptr;
- int bufchar(char* c, int count)
- {
- int i;
- for (i = 0; i < count; ++i)
- *c = *bufptr++;
- return count;
- }
- int MQTTPacket_decodeBuf(char* buf, unsigned int* value)
- {
- bufptr = buf;
- return MQTTPacket_VBIdecode(bufchar, value);
- }
|