1 /*******************************************************************************
2  * Copyright (c) 2014 IBM Corp.
3  *
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * and Eclipse Distribution License v1.0 which accompany this distribution.
7  *
8  * The Eclipse Public License is available at
9  *    http://www.eclipse.org/legal/epl-v10.html
10  * and the Eclipse Distribution License is available at
11  *   http://www.eclipse.org/org/documents/edl-v10.php.
12  *
13  * Contributors:
14  *    Ian Craggs - initial API and implementation and/or initial documentation
15  *******************************************************************************/
16 
17 #include "MQTTPacket.h"
18 #include "StackTrace.h"
19 
20 #include <string.h>
21 
22 /**
23   * Determines the length of the MQTT connect packet that would be produced using the supplied connect options.
24   * @param options the options to be used to build the connect packet
25   * @return the length of buffer needed to contain the serialized version of the packet
26   */
MQTTSerialize_connectLength(MQTTPacket_connectData * options)27 int MQTTSerialize_connectLength(MQTTPacket_connectData* options)
28 {
29 	int len = 0;
30 
31 	FUNC_ENTRY;
32 
33 	if (options->MQTTVersion == 3)
34 		len = 12; /* variable depending on MQTT or MQIsdp */
35 	else if (options->MQTTVersion == 4)
36 		len = 10;
37 
38 	len += MQTTstrlen(options->clientID)+2;
39 	if (options->willFlag)
40 		len += MQTTstrlen(options->will.topicName)+2 + MQTTstrlen(options->will.message)+2;
41 	if (options->username.cstring || options->username.lenstring.data)
42 		len += MQTTstrlen(options->username)+2;
43 	if (options->password.cstring || options->password.lenstring.data)
44 		len += MQTTstrlen(options->password)+2;
45 
46 	FUNC_EXIT_RC(len);
47 	return len;
48 }
49 
50 
51 /**
52   * Serializes the connect options into the buffer.
53   * @param buf the buffer into which the packet will be serialized
54   * @param len the length in bytes of the supplied buffer
55   * @param options the options to be used to build the connect packet
56   * @return serialized length, or error if 0
57   */
MQTTSerialize_connect(unsigned char * buf,int buflen,MQTTPacket_connectData * options)58 int MQTTSerialize_connect(unsigned char* buf, int buflen, MQTTPacket_connectData* options)
59 {
60 	unsigned char *ptr = buf;
61 	MQTTHeader header = {0};
62 	MQTTConnectFlags flags = {0};
63 	int len = 0;
64 	int rc = -1;
65 
66 	FUNC_ENTRY;
67 	if (MQTTPacket_len(len = MQTTSerialize_connectLength(options)) > buflen)
68 	{
69 		rc = MQTTPACKET_BUFFER_TOO_SHORT;
70 		goto exit;
71 	}
72 
73 	header.byte = 0;
74 	header.bits.type = CONNECT;
75 	writeChar(&ptr, header.byte); /* write header */
76 
77 	ptr += MQTTPacket_encode(ptr, len); /* write remaining length */
78 
79 	if (options->MQTTVersion == 4)
80 	{
81 		writeCString(&ptr, "MQTT");
82 		writeChar(&ptr, (char) 4);
83 	}
84 	else
85 	{
86 		writeCString(&ptr, "MQIsdp");
87 		writeChar(&ptr, (char) 3);
88 	}
89 
90 	flags.all = 0;
91 	flags.bits.cleansession = options->cleansession;
92 	flags.bits.will = (options->willFlag) ? 1 : 0;
93 	if (flags.bits.will)
94 	{
95 		flags.bits.willQoS = options->will.qos;
96 		flags.bits.willRetain = options->will.retained;
97 	}
98 
99 	if (options->username.cstring || options->username.lenstring.data)
100 		flags.bits.username = 1;
101 	if (options->password.cstring || options->password.lenstring.data)
102 		flags.bits.password = 1;
103 
104 	writeChar(&ptr, flags.all);
105 	writeInt(&ptr, options->keepAliveInterval);
106 	writeMQTTString(&ptr, options->clientID);
107 	if (options->willFlag)
108 	{
109 		writeMQTTString(&ptr, options->will.topicName);
110 		writeMQTTString(&ptr, options->will.message);
111 	}
112 	if (flags.bits.username)
113 		writeMQTTString(&ptr, options->username);
114 	if (flags.bits.password)
115 		writeMQTTString(&ptr, options->password);
116 
117 	rc = ptr - buf;
118 
119 	exit: FUNC_EXIT_RC(rc);
120 	return rc;
121 }
122 
123 
124 /**
125   * Deserializes the supplied (wire) buffer into connack data - return code
126   * @param sessionPresent the session present flag returned (only for MQTT 3.1.1)
127   * @param connack_rc returned integer value of the connack return code
128   * @param buf the raw buffer data, of the correct length determined by the remaining length field
129   * @param len the length in bytes of the data in the supplied buffer
130   * @return error code.  1 is success, 0 is failure
131   */
MQTTDeserialize_connack(unsigned char * sessionPresent,unsigned char * connack_rc,unsigned char * buf,int buflen)132 int MQTTDeserialize_connack(unsigned char* sessionPresent, unsigned char* connack_rc, unsigned char* buf, int buflen)
133 {
134 	MQTTHeader header = {0};
135 	unsigned char* curdata = buf;
136 	unsigned char* enddata = NULL;
137 	int rc = 0;
138 	int mylen;
139 	MQTTConnackFlags flags = {0};
140 
141 	FUNC_ENTRY;
142 	header.byte = readChar(&curdata);
143 	if (header.bits.type != CONNACK)
144 		goto exit;
145 
146 	curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */
147 	enddata = curdata + mylen;
148 	if (enddata - curdata < 2)
149 		goto exit;
150 
151 	flags.all = readChar(&curdata);
152 	*sessionPresent = flags.bits.sessionpresent;
153 	*connack_rc = readChar(&curdata);
154 
155 	rc = 1;
156 exit:
157 	FUNC_EXIT_RC(rc);
158 	return rc;
159 }
160 
161 
162 /**
163   * Serializes a 0-length packet into the supplied buffer, ready for writing to a socket
164   * @param buf the buffer into which the packet will be serialized
165   * @param buflen the length in bytes of the supplied buffer, to avoid overruns
166   * @param packettype the message type
167   * @return serialized length, or error if 0
168   */
MQTTSerialize_zero(unsigned char * buf,int buflen,unsigned char packettype)169 int MQTTSerialize_zero(unsigned char* buf, int buflen, unsigned char packettype)
170 {
171 	MQTTHeader header = {0};
172 	int rc = -1;
173 	unsigned char *ptr = buf;
174 
175 	FUNC_ENTRY;
176 	if (buflen < 2)
177 	{
178 		rc = MQTTPACKET_BUFFER_TOO_SHORT;
179 		goto exit;
180 	}
181 	header.byte = 0;
182 	header.bits.type = packettype;
183 	writeChar(&ptr, header.byte); /* write header */
184 
185 	ptr += MQTTPacket_encode(ptr, 0); /* write remaining length */
186 	rc = ptr - buf;
187 exit:
188 	FUNC_EXIT_RC(rc);
189 	return rc;
190 }
191 
192 
193 /**
194   * Serializes a disconnect packet into the supplied buffer, ready for writing to a socket
195   * @param buf the buffer into which the packet will be serialized
196   * @param buflen the length in bytes of the supplied buffer, to avoid overruns
197   * @return serialized length, or error if 0
198   */
MQTTSerialize_disconnect(unsigned char * buf,int buflen)199 int MQTTSerialize_disconnect(unsigned char* buf, int buflen)
200 {
201 	return MQTTSerialize_zero(buf, buflen, DISCONNECT);
202 }
203 
204 
205 /**
206   * Serializes a disconnect packet into the supplied buffer, ready for writing to a socket
207   * @param buf the buffer into which the packet will be serialized
208   * @param buflen the length in bytes of the supplied buffer, to avoid overruns
209   * @return serialized length, or error if 0
210   */
MQTTSerialize_pingreq(unsigned char * buf,int buflen)211 int MQTTSerialize_pingreq(unsigned char* buf, int buflen)
212 {
213 	return MQTTSerialize_zero(buf, buflen, PINGREQ);
214 }
215