mqtt_client_misc.c
Go to the documentation of this file.
1 /**
2  * @file mqtt_client_misc.c
3  * @brief Helper functions for MQTT client
4  *
5  * @section License
6  *
7  * Copyright (C) 2010-2018 Oryx Embedded SARL. All rights reserved.
8  *
9  * This file is part of CycloneTCP Open.
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software Foundation,
23  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24  *
25  * @author Oryx Embedded SARL (www.oryx-embedded.com)
26  * @version 1.9.0
27  **/
28 
29 //Switch to the appropriate trace level
30 #define TRACE_LEVEL MQTT_TRACE_LEVEL
31 
32 //Dependencies
33 #include "core/net.h"
34 #include "mqtt/mqtt_client.h"
37 #include "mqtt/mqtt_client_misc.h"
38 #include "debug.h"
39 
40 //Check TCP/IP stack configuration
41 #if (MQTT_CLIENT_SUPPORT == ENABLED)
42 
43 
44 /**
45  * @brief Update MQTT client state
46  * @param[in] context Pointer to the MQTT client context
47  * @param[in] newState New state to switch to
48  **/
49 
51 {
52  //Switch to the new state
53  context->state = newState;
54 }
55 
56 
57 /**
58  * @brief Check keep-alive time interval
59  * @param[in] context Pointer to the MQTT client context
60  * @return Error code
61  **/
62 
64 {
65  error_t error;
67  systime_t keepAlive;
68 
69  //Initialize status code
70  error = NO_ERROR;
71 
72  //In the absence of sending any other control packets, the client must
73  //send a PINGREQ packet
74  if(context->state == MQTT_CLIENT_STATE_IDLE ||
75  context->state == MQTT_CLIENT_STATE_PACKET_SENT)
76  {
77  //A keep-alive value of zero has the effect of turning off the keep
78  //alive mechanism
79  if(context->settings.keepAlive != 0)
80  {
81  //Get current time
83 
84  //Convert the keep-alive value to milliseconds
85  keepAlive = context->settings.keepAlive * 1000;
86 
87  //It is the responsibility of the client to ensure that the interval
88  //between control packets being sent does not exceed the keep-alive value
89  if(timeCompare(time, context->keepAliveTimestamp + keepAlive) >= 0)
90  {
91  //Format PINGREQ packet
92  error = mqttClientFormatPingReq(context);
93 
94  //Check status code
95  if(!error)
96  {
97  //Debug message
98  TRACE_INFO("MQTT: Sending PINGREQ packet (%" PRIuSIZE " bytes)...\r\n", context->packetLen);
99  TRACE_DEBUG_ARRAY(" ", context->packet, context->packetLen);
100 
101  //Point to the beginning of the packet
102  context->packetPos = 0;
103 
104  //Send PINGREQ packet
106  }
107  }
108  }
109  }
110 
111  //Return status code
112  return error;
113 }
114 
115 
116 /**
117  * @brief Serialize fixed header
118  * @param[in] buffer Pointer to the output buffer
119  * @param[in,out] pos Current position
120  * @param[in] type MQTT control packet type
121  * @param[in] dup DUP flag
122  * @param[in] qos QoS field
123  * @param[in] retain RETAIN flag
124  * @param[in] remainingLen Length of the variable header and the payload
125  * @return Error code
126  **/
127 
128 error_t mqttSerializeHeader(uint8_t *buffer, size_t *pos, MqttPacketType type,
129  bool_t dup, MqttQosLevel qos, bool_t retain, size_t remainingLen)
130 {
131  uint_t i;
132  uint_t k;
133  size_t n;
134  MqttPacketHeader *header;
135 
136  //Point to the current position
137  n = *pos;
138 
139  //The Remaining Length is encoded using a variable length encoding scheme
140  if(remainingLen < 128)
141  k = 1;
142  else if(remainingLen < 16384)
143  k = 2;
144  else if(remainingLen < 2097152)
145  k = 3;
146  else if(remainingLen < 268435456)
147  k = 4;
148  else
149  return ERROR_INVALID_LENGTH;
150 
151  //Sanity check
152  if(n < (sizeof(MqttPacketHeader) + k))
153  return ERROR_BUFFER_OVERFLOW;
154 
155  //Position where to format the header
156  n -= sizeof(MqttPacketHeader) + k;
157 
158  //Point to the MQTT packet header
159  header = (MqttPacketHeader *) (buffer + n);
160 
161  //Encode the first byte of the header
162  header->type = type;
163  header->dup = dup;
164  header->qos = qos;
165  header->retain = retain;
166 
167  //Encode the Remaining Length field
168  for(i = 0; i < k; i++)
169  {
170  //The least significant seven bits of each byte encode the data
171  header->length[i] = remainingLen & 0xFF;
172  remainingLen >>= 7;
173 
174  //The most significant bit is used to indicate that there are
175  //following bytes in the representation
176  if(remainingLen > 0)
177  header->length[i] |= 0x80;
178  }
179 
180  //Update current position
181  *pos = n;
182 
183  //Successful processing
184  return NO_ERROR;
185 }
186 
187 
188 /**
189  * @brief Write a 8-bit integer to the output buffer
190  * @param[in] buffer Pointer to the output buffer
191  * @param[in] bufferLen Maximum number of bytes the output buffer can hold
192  * @param[in,out] pos Current position
193  * @param[in] value 8-bit integer to be serialized
194  * @return Error code
195  **/
196 
197 error_t mqttSerializeByte(uint8_t *buffer, size_t bufferLen,
198  size_t *pos, uint8_t value)
199 {
200  size_t n;
201 
202  //Point to the current position
203  n = *pos;
204 
205  //Make sure the output buffer is large enough
206  if((n + sizeof(uint8_t)) > bufferLen)
207  return ERROR_BUFFER_OVERFLOW;
208 
209  //Write the byte to the output buffer
210  buffer[n++] = value;
211 
212  //Advance current position
213  *pos = n;
214 
215  //Successful processing
216  return NO_ERROR;
217 }
218 
219 
220 /**
221  * @brief Write a 16-bit integer to the output buffer
222  * @param[in] buffer Pointer to the output buffer
223  * @param[in] bufferLen Maximum number of bytes the output buffer can hold
224  * @param[in,out] pos Current position
225  * @param[in] value 16-bit integer to be serialized
226  * @return Error code
227  **/
228 
229 error_t mqttSerializeShort(uint8_t *buffer, size_t bufferLen,
230  size_t *pos, uint16_t value)
231 {
232  size_t n;
233 
234  //Point to the current position
235  n = *pos;
236 
237  //Make sure the output buffer is large enough
238  if((n + sizeof(uint16_t)) > bufferLen)
239  return ERROR_BUFFER_OVERFLOW;
240 
241  //Write the short integer to the output buffer
242  buffer[n++] = MSB(value);
243  buffer[n++] = LSB(value);
244 
245  //Advance current position
246  *pos = n;
247 
248  //Successful processing
249  return NO_ERROR;
250 }
251 
252 
253 /**
254  * @brief Serialize string
255  * @param[in] buffer Pointer to the output buffer
256  * @param[in] bufferLen Maximum number of bytes the output buffer can hold
257  * @param[in,out] pos Current position
258  * @param[in] string Pointer to the string to be serialized
259  * @param[in] stringLen Length of the string, in bytes
260  * @return Error code
261  **/
262 
263 error_t mqttSerializeString(uint8_t *buffer, size_t bufferLen,
264  size_t *pos, const void *string, size_t stringLen)
265 {
266  size_t n;
267 
268  //Point to the current position
269  n = *pos;
270 
271  //Make sure the output buffer is large enough to hold the string
272  if((n + sizeof(uint16_t) + stringLen) > bufferLen)
273  return ERROR_BUFFER_OVERFLOW;
274 
275  //Encode the length field
276  buffer[n++] = MSB(stringLen);
277  buffer[n++] = LSB(stringLen);
278 
279  //Write the string to the output buffer
280  memcpy(buffer + n, string, stringLen);
281 
282  //Advance current position
283  *pos = n + stringLen;
284 
285  //Successful processing
286  return NO_ERROR;
287 }
288 
289 
290 /**
291  * @brief Serialize raw data
292  * @param[in] buffer Pointer to the output buffer
293  * @param[in] bufferLen Maximum number of bytes the output buffer can hold
294  * @param[in,out] pos Current position
295  * @param[in] data Pointer to the raw data to be serialized
296  * @param[in] dataLen Length of the raw data, in bytes
297  * @return Error code
298  **/
299 
300 error_t mqttSerializeData(uint8_t *buffer, size_t bufferLen,
301  size_t *pos, const void *data, size_t dataLen)
302 {
303  size_t n;
304 
305  //Point to the current position
306  n = *pos;
307 
308  //Make sure the output buffer is large enough to hold the data
309  if((n + dataLen) > bufferLen)
310  return ERROR_BUFFER_OVERFLOW;
311 
312  //Write the data to the output buffer
313  memcpy(buffer + n, data, dataLen);
314 
315  //Advance current position
316  *pos = n + dataLen;
317 
318  //Successful processing
319  return NO_ERROR;
320 }
321 
322 
323 /**
324  * @brief Deserialize fixed header
325  * @param[in] buffer Pointer to the input buffer
326  * @param[in] bufferLen Length of the input buffer
327  * @param[in,out] pos Current position
328  * @param[out] type MQTT control packet type
329  * @param[out] dup DUP flag from the fixed header
330  * @param[out] qos QoS field from the fixed header
331  * @param[out] retain RETAIN flag from the fixed header
332  * @param[out] remainingLen Length of the variable header and the payload
333  * @return Error code
334  **/
335 
336 error_t mqttDeserializeHeader(uint8_t *buffer, size_t bufferLen, size_t *pos,
337  MqttPacketType *type, bool_t *dup, MqttQosLevel *qos, bool_t *retain, size_t *remainingLen)
338 {
339  uint_t i;
340  size_t n;
341  MqttPacketHeader *header;
342 
343  //Point to the current position
344  n = *pos;
345 
346  //Make sure the input buffer is large enough
347  if((n + sizeof(MqttPacketHeader)) > bufferLen)
348  return ERROR_INVALID_LENGTH;
349 
350  //Point to the MQTT packet header
351  header = (MqttPacketHeader *) (buffer + n);
352 
353  //Save MQTT control packet type
354  *type = (MqttPacketType) header->type;
355 
356  //Save flags
357  *dup = header->dup;
358  *qos = (MqttQosLevel) header->qos;
359  *retain = header->retain;
360 
361  //Advance current position
362  n += sizeof(MqttPacketHeader);
363 
364  //Prepare to decode the Remaining Length field
365  *remainingLen = 0;
366 
367  //The Remaining Length is encoded using a variable length encoding scheme
368  for(i = 0; i < 4; i++)
369  {
370  //Sanity check
371  if((n + sizeof(uint8_t)) > bufferLen)
372  return ERROR_INVALID_LENGTH;
373 
374  //Advance current position
375  n += sizeof(uint8_t);
376 
377  //The most significant bit is used to indicate that there are
378  //following bytes in the representation
379  if(header->length[i] & 0x80)
380  {
381  //Applications can send control packets of size up to 256 MB
382  if(i == 3)
383  return ERROR_INVALID_SYNTAX;
384 
385  //The least significant seven bits of each byte encode the data
386  *remainingLen |= (header->length[i] & 0x7F) << (7 * i);
387  }
388  else
389  {
390  //The least significant seven bits of each byte encode the data
391  *remainingLen |= header->length[i] << (7 * i);
392  //This is the last byte
393  break;
394  }
395  }
396 
397  //Return the current position
398  *pos = n;
399 
400  //Successful processing
401  return NO_ERROR;
402 }
403 
404 
405 /**
406  * @brief Read a 8-bit integer from the input buffer
407  * @param[in] buffer Pointer to the input buffer
408  * @param[in] bufferLen Length of the input buffer
409  * @param[in,out] pos Current position
410  * @param[out] value Value of the 8-bit integer
411  * @return Error code
412  **/
413 
414 error_t mqttDeserializeByte(uint8_t *buffer, size_t bufferLen,
415  size_t *pos, uint8_t *value)
416 {
417  size_t n;
418 
419  //Point to the current position
420  n = *pos;
421 
422  //Make sure the input buffer is large enough
423  if((n + sizeof(uint8_t)) > bufferLen)
424  return ERROR_BUFFER_OVERFLOW;
425 
426  //Read the short integer from the input buffer
427  *value = buffer[n];
428 
429  //Advance current position
430  *pos = n + sizeof(uint8_t);
431 
432  //Successful processing
433  return NO_ERROR;
434 }
435 
436 
437 /**
438  * @brief Read a 16-bit integer from the input buffer
439  * @param[in] buffer Pointer to the input buffer
440  * @param[in] bufferLen Length of the input buffer
441  * @param[in,out] pos Current position
442  * @param[out] value Value of the 16-bit integer
443  * @return Error code
444  **/
445 
446 error_t mqttDeserializeShort(uint8_t *buffer, size_t bufferLen,
447  size_t *pos, uint16_t *value)
448 {
449  size_t n;
450 
451  //Point to the current position
452  n = *pos;
453 
454  //Make sure the input buffer is large enough
455  if((n + sizeof(uint16_t)) > bufferLen)
456  return ERROR_BUFFER_OVERFLOW;
457 
458  //Read the short integer from the input buffer
459  *value = (buffer[n] << 8) | buffer[n + 1];
460 
461  //Advance current position
462  *pos = n + sizeof(uint16_t);
463 
464  //Successful processing
465  return NO_ERROR;
466 }
467 
468 
469 /**
470  * @brief Deserialize string
471  * @param[in] buffer Pointer to the input buffer
472  * @param[in] bufferLen Length of the input buffer
473  * @param[in,out] pos Current position
474  * @param[out] string Pointer to the string
475  * @param[out] stringLen Length of the string, in bytes
476  * @return Error code
477  **/
478 
479 error_t mqttDeserializeString(uint8_t *buffer, size_t bufferLen,
480  size_t *pos, char_t **string, size_t *stringLen)
481 {
482  size_t n;
483 
484  //Point to the current position
485  n = *pos;
486 
487  //Make sure the input buffer is large enough
488  if((n + sizeof(uint16_t)) > bufferLen)
489  return ERROR_BUFFER_OVERFLOW;
490 
491  //Decode the length field
492  *stringLen = (buffer[n] << 8) | buffer[n + 1];
493 
494  //Make sure the input buffer is large enough
495  if((n + sizeof(uint16_t) + *stringLen) > bufferLen)
496  return ERROR_BUFFER_OVERFLOW;
497 
498  //Read the string from the input buffer
499  *string = (char_t *) buffer + n + 2;
500 
501  //Advance current position
502  *pos = n + 2 + *stringLen;
503 
504  //Successful processing
505  return NO_ERROR;
506 }
507 
508 #endif
error_t mqttDeserializeByte(uint8_t *buffer, size_t bufferLen, size_t *pos, uint8_t *value)
Read a 8-bit integer from the input buffer.
uint32_t systime_t
Definition: compiler_port.h:44
#define timeCompare(t1, t2)
Definition: os_port.h:40
char char_t
Definition: compiler_port.h:41
systime_t osGetSystemTime(void)
Retrieve system time.
MqttPacketType
MQTT control packet type.
Definition: mqtt_common.h:96
#define MSB(x)
Definition: os_port.h:56
uint32_t time
TCP/IP stack core.
void mqttClientChangeState(MqttClientContext *context, MqttClientState newState)
Update MQTT client state.
Helper functions for MQTT client.
Debugging facilities.
error_t mqttSerializeByte(uint8_t *buffer, size_t bufferLen, size_t *pos, uint8_t value)
Write a 8-bit integer to the output buffer.
error_t mqttDeserializeShort(uint8_t *buffer, size_t bufferLen, size_t *pos, uint16_t *value)
Read a 16-bit integer from the input buffer.
MQTT client.
error_t mqttClientFormatPingReq(MqttClientContext *context)
Format PINGREQ packet.
char_t type
#define TRACE_DEBUG_ARRAY(p, a, n)
Definition: debug.h:99
#define LSB(x)
Definition: os_port.h:52
MQTT packet parsing and formatting.
error_t mqttSerializeString(uint8_t *buffer, size_t bufferLen, size_t *pos, const void *string, size_t stringLen)
Serialize string.
uint8_t qos
Definition: mqtt_common.h:177
error_t mqttClientCheckKeepAlive(MqttClientContext *context)
Check keep-alive time interval.
error_t mqttSerializeData(uint8_t *buffer, size_t bufferLen, size_t *pos, const void *data, size_t dataLen)
Serialize raw data.
__start_packed struct @233 MqttPacketHeader
Fixed header.
Transport protocol abstraction layer.
MqttQosLevel
Quality of service level.
Definition: mqtt_common.h:84
error_t mqttDeserializeHeader(uint8_t *buffer, size_t bufferLen, size_t *pos, MqttPacketType *type, bool_t *dup, MqttQosLevel *qos, bool_t *retain, size_t *remainingLen)
Deserialize fixed header.
error_t mqttSerializeShort(uint8_t *buffer, size_t bufferLen, size_t *pos, uint16_t value)
Write a 16-bit integer to the output buffer.
#define TRACE_INFO(...)
Definition: debug.h:86
Success.
Definition: error.h:42
error_t
Error codes.
Definition: error.h:40
uint8_t retain
Definition: mqtt_common.h:176
unsigned int uint_t
Definition: compiler_port.h:43
uint8_t data[]
Definition: dtls_misc.h:167
#define PRIuSIZE
Definition: compiler_port.h:72
uint8_t value[]
Definition: dtls_misc.h:141
uint8_t dup
Definition: mqtt_common.h:178
uint8_t n
#define MqttClientContext
Definition: mqtt_client.h:140
MqttClientState
MQTT client states.
Definition: mqtt_client.h:152
int bool_t
Definition: compiler_port.h:47
error_t mqttDeserializeString(uint8_t *buffer, size_t bufferLen, size_t *pos, char_t **string, size_t *stringLen)
Deserialize string.
error_t mqttSerializeHeader(uint8_t *buffer, size_t *pos, MqttPacketType type, bool_t dup, MqttQosLevel qos, bool_t retain, size_t remainingLen)
Serialize fixed header.