1 /*******************************************************************************
2 * Copyright (c) 2017, 2020 IBM Corp. and others
3 *
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v2.0
6 * and Eclipse Distribution License v1.0 which accompany this distribution.
7 *
8 * The Eclipse Public License is available at
9 * https://www.eclipse.org/legal/epl-2.0/
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 "MQTTProperties.h"
18
19 #include "MQTTPacket.h"
20 #include "MQTTProtocolClient.h"
21 #include "Heap.h"
22 #include "StackTrace.h"
23
24 #include <memory.h>
25
26 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
27
28 static struct nameToType
29 {
30 enum MQTTPropertyCodes name;
31 enum MQTTPropertyTypes type;
32 } namesToTypes[] =
33 {
34 {MQTTPROPERTY_CODE_PAYLOAD_FORMAT_INDICATOR, MQTTPROPERTY_TYPE_BYTE},
35 {MQTTPROPERTY_CODE_MESSAGE_EXPIRY_INTERVAL, MQTTPROPERTY_TYPE_FOUR_BYTE_INTEGER},
36 {MQTTPROPERTY_CODE_CONTENT_TYPE, MQTTPROPERTY_TYPE_UTF_8_ENCODED_STRING},
37 {MQTTPROPERTY_CODE_RESPONSE_TOPIC, MQTTPROPERTY_TYPE_UTF_8_ENCODED_STRING},
38 {MQTTPROPERTY_CODE_CORRELATION_DATA, MQTTPROPERTY_TYPE_BINARY_DATA},
39 {MQTTPROPERTY_CODE_SUBSCRIPTION_IDENTIFIER, MQTTPROPERTY_TYPE_VARIABLE_BYTE_INTEGER},
40 {MQTTPROPERTY_CODE_SESSION_EXPIRY_INTERVAL, MQTTPROPERTY_TYPE_FOUR_BYTE_INTEGER},
41 {MQTTPROPERTY_CODE_ASSIGNED_CLIENT_IDENTIFER, MQTTPROPERTY_TYPE_UTF_8_ENCODED_STRING},
42 {MQTTPROPERTY_CODE_SERVER_KEEP_ALIVE, MQTTPROPERTY_TYPE_TWO_BYTE_INTEGER},
43 {MQTTPROPERTY_CODE_AUTHENTICATION_METHOD, MQTTPROPERTY_TYPE_UTF_8_ENCODED_STRING},
44 {MQTTPROPERTY_CODE_AUTHENTICATION_DATA, MQTTPROPERTY_TYPE_BINARY_DATA},
45 {MQTTPROPERTY_CODE_REQUEST_PROBLEM_INFORMATION, MQTTPROPERTY_TYPE_BYTE},
46 {MQTTPROPERTY_CODE_WILL_DELAY_INTERVAL, MQTTPROPERTY_TYPE_FOUR_BYTE_INTEGER},
47 {MQTTPROPERTY_CODE_REQUEST_RESPONSE_INFORMATION, MQTTPROPERTY_TYPE_BYTE},
48 {MQTTPROPERTY_CODE_RESPONSE_INFORMATION, MQTTPROPERTY_TYPE_UTF_8_ENCODED_STRING},
49 {MQTTPROPERTY_CODE_SERVER_REFERENCE, MQTTPROPERTY_TYPE_UTF_8_ENCODED_STRING},
50 {MQTTPROPERTY_CODE_REASON_STRING, MQTTPROPERTY_TYPE_UTF_8_ENCODED_STRING},
51 {MQTTPROPERTY_CODE_RECEIVE_MAXIMUM, MQTTPROPERTY_TYPE_TWO_BYTE_INTEGER},
52 {MQTTPROPERTY_CODE_TOPIC_ALIAS_MAXIMUM, MQTTPROPERTY_TYPE_TWO_BYTE_INTEGER},
53 {MQTTPROPERTY_CODE_TOPIC_ALIAS, MQTTPROPERTY_TYPE_TWO_BYTE_INTEGER},
54 {MQTTPROPERTY_CODE_MAXIMUM_QOS, MQTTPROPERTY_TYPE_BYTE},
55 {MQTTPROPERTY_CODE_RETAIN_AVAILABLE, MQTTPROPERTY_TYPE_BYTE},
56 {MQTTPROPERTY_CODE_USER_PROPERTY, MQTTPROPERTY_TYPE_UTF_8_STRING_PAIR},
57 {MQTTPROPERTY_CODE_MAXIMUM_PACKET_SIZE, MQTTPROPERTY_TYPE_FOUR_BYTE_INTEGER},
58 {MQTTPROPERTY_CODE_WILDCARD_SUBSCRIPTION_AVAILABLE, MQTTPROPERTY_TYPE_BYTE},
59 {MQTTPROPERTY_CODE_SUBSCRIPTION_IDENTIFIERS_AVAILABLE, MQTTPROPERTY_TYPE_BYTE},
60 {MQTTPROPERTY_CODE_SHARED_SUBSCRIPTION_AVAILABLE, MQTTPROPERTY_TYPE_BYTE}
61 };
62
63
datadup(const MQTTLenString * str)64 static char* datadup(const MQTTLenString* str)
65 {
66 char* temp = malloc(str->len);
67 if (temp)
68 memcpy(temp, str->data, str->len);
69 return temp;
70 }
71
72
MQTTProperty_getType(enum MQTTPropertyCodes value)73 int MQTTProperty_getType(enum MQTTPropertyCodes value)
74 {
75 int i, rc = -1;
76
77 for (i = 0; i < ARRAY_SIZE(namesToTypes); ++i)
78 {
79 if (namesToTypes[i].name == value)
80 {
81 rc = namesToTypes[i].type;
82 break;
83 }
84 }
85 return rc;
86 }
87
88
MQTTProperties_len(MQTTProperties * props)89 int MQTTProperties_len(MQTTProperties* props)
90 {
91 /* properties length is an mbi */
92 return (props == NULL) ? 1 : props->length + MQTTPacket_VBIlen(props->length);
93 }
94
95
96 /**
97 * Add a new property to a property list
98 * @param props the property list
99 * @param prop the new property
100 * @return code 0 is success
101 */
MQTTProperties_add(MQTTProperties * props,const MQTTProperty * prop)102 int MQTTProperties_add(MQTTProperties* props, const MQTTProperty* prop)
103 {
104 int rc = 0, type;
105
106 if ((type = MQTTProperty_getType(prop->identifier)) < 0)
107 {
108 /*StackTrace_printStack(stdout);*/
109 rc = MQTT_INVALID_PROPERTY_ID;
110 goto exit;
111 }
112 else if (props->array == NULL)
113 {
114 props->max_count = 10;
115 props->array = malloc(sizeof(MQTTProperty) * props->max_count);
116 }
117 else if (props->count == props->max_count)
118 {
119 props->max_count += 10;
120 props->array = realloc(props->array, sizeof(MQTTProperty) * props->max_count);
121 }
122
123 if (props->array)
124 {
125 int len = 0;
126
127 props->array[props->count++] = *prop;
128 /* calculate length */
129 switch (type)
130 {
131 case MQTTPROPERTY_TYPE_BYTE:
132 len = 1;
133 break;
134 case MQTTPROPERTY_TYPE_TWO_BYTE_INTEGER:
135 len = 2;
136 break;
137 case MQTTPROPERTY_TYPE_FOUR_BYTE_INTEGER:
138 len = 4;
139 break;
140 case MQTTPROPERTY_TYPE_VARIABLE_BYTE_INTEGER:
141 len = MQTTPacket_VBIlen(prop->value.integer4);
142 break;
143 case MQTTPROPERTY_TYPE_BINARY_DATA:
144 case MQTTPROPERTY_TYPE_UTF_8_ENCODED_STRING:
145 case MQTTPROPERTY_TYPE_UTF_8_STRING_PAIR:
146 len = 2 + prop->value.data.len;
147 props->array[props->count-1].value.data.data = datadup(&prop->value.data);
148 if (type == MQTTPROPERTY_TYPE_UTF_8_STRING_PAIR)
149 {
150 len += 2 + prop->value.value.len;
151 props->array[props->count-1].value.value.data = datadup(&prop->value.value);
152 }
153 break;
154 }
155 props->length += len + 1; /* add identifier byte */
156 }
157 else
158 rc = PAHO_MEMORY_ERROR;
159
160 exit:
161 return rc;
162 }
163
164
MQTTProperty_write(char ** pptr,MQTTProperty * prop)165 int MQTTProperty_write(char** pptr, MQTTProperty* prop)
166 {
167 int rc = -1,
168 type = -1;
169
170 type = MQTTProperty_getType(prop->identifier);
171 if (type >= MQTTPROPERTY_TYPE_BYTE && type <= MQTTPROPERTY_TYPE_UTF_8_STRING_PAIR)
172 {
173 writeChar(pptr, prop->identifier);
174 switch (type)
175 {
176 case MQTTPROPERTY_TYPE_BYTE:
177 writeChar(pptr, prop->value.byte);
178 rc = 1;
179 break;
180 case MQTTPROPERTY_TYPE_TWO_BYTE_INTEGER:
181 writeInt(pptr, prop->value.integer2);
182 rc = 2;
183 break;
184 case MQTTPROPERTY_TYPE_FOUR_BYTE_INTEGER:
185 writeInt4(pptr, prop->value.integer4);
186 rc = 4;
187 break;
188 case MQTTPROPERTY_TYPE_VARIABLE_BYTE_INTEGER:
189 rc = MQTTPacket_encode(*pptr, prop->value.integer4);
190 *pptr += rc;
191 break;
192 case MQTTPROPERTY_TYPE_BINARY_DATA:
193 case MQTTPROPERTY_TYPE_UTF_8_ENCODED_STRING:
194 writeMQTTLenString(pptr, prop->value.data);
195 rc = prop->value.data.len + 2; /* include length field */
196 break;
197 case MQTTPROPERTY_TYPE_UTF_8_STRING_PAIR:
198 writeMQTTLenString(pptr, prop->value.data);
199 writeMQTTLenString(pptr, prop->value.value);
200 rc = prop->value.data.len + prop->value.value.len + 4; /* include length fields */
201 break;
202 }
203 }
204 return rc + 1; /* include identifier byte */
205 }
206
207
MQTTProperties_write(char ** pptr,const MQTTProperties * properties)208 int MQTTProperties_write(char** pptr, const MQTTProperties* properties)
209 {
210 int rc = -1;
211 int i = 0, len = 0;
212
213 /* write the entire property list length first */
214 if (properties == NULL)
215 {
216 *pptr += MQTTPacket_encode(*pptr, 0);
217 rc = 1;
218 }
219 else
220 {
221 *pptr += MQTTPacket_encode(*pptr, properties->length);
222 len = rc = 1;
223 for (i = 0; i < properties->count; ++i)
224 {
225 rc = MQTTProperty_write(pptr, &properties->array[i]);
226 if (rc < 0)
227 break;
228 else
229 len += rc;
230 }
231 if (rc >= 0)
232 rc = len;
233 }
234 return rc;
235 }
236
237
MQTTProperty_read(MQTTProperty * prop,char ** pptr,char * enddata)238 int MQTTProperty_read(MQTTProperty* prop, char** pptr, char* enddata)
239 {
240 int type = -1,
241 len = -1;
242
243 prop->identifier = readChar(pptr);
244 type = MQTTProperty_getType(prop->identifier);
245 if (type >= MQTTPROPERTY_TYPE_BYTE && type <= MQTTPROPERTY_TYPE_UTF_8_STRING_PAIR)
246 {
247 switch (type)
248 {
249 case MQTTPROPERTY_TYPE_BYTE:
250 prop->value.byte = readChar(pptr);
251 len = 1;
252 break;
253 case MQTTPROPERTY_TYPE_TWO_BYTE_INTEGER:
254 prop->value.integer2 = readInt(pptr);
255 len = 2;
256 break;
257 case MQTTPROPERTY_TYPE_FOUR_BYTE_INTEGER:
258 prop->value.integer4 = readInt4(pptr);
259 len = 4;
260 break;
261 case MQTTPROPERTY_TYPE_VARIABLE_BYTE_INTEGER:
262 len = MQTTPacket_decodeBuf(*pptr, &prop->value.integer4);
263 *pptr += len;
264 break;
265 case MQTTPROPERTY_TYPE_BINARY_DATA:
266 case MQTTPROPERTY_TYPE_UTF_8_ENCODED_STRING:
267 case MQTTPROPERTY_TYPE_UTF_8_STRING_PAIR:
268 if ((len = MQTTLenStringRead(&prop->value.data, pptr, enddata)) == -1)
269 break; /* error */
270 if ((prop->value.data.data = datadup(&prop->value.data)) == NULL)
271 {
272 len = -1;
273 break; /* error */
274 }
275 if (type == MQTTPROPERTY_TYPE_UTF_8_STRING_PAIR)
276 {
277 int proplen = MQTTLenStringRead(&prop->value.value, pptr, enddata);
278
279 if (proplen == -1)
280 {
281 len = -1;
282 free(prop->value.data.data);
283 break;
284 }
285 len += proplen;
286 if ((prop->value.value.data = datadup(&prop->value.value)) == NULL)
287 {
288 len = -1;
289 free(prop->value.data.data);
290 break;
291 }
292 }
293 break;
294 }
295 }
296 return (len == -1) ? -1 : len + 1; /* 1 byte for identifier */
297 }
298
299
MQTTProperties_read(MQTTProperties * properties,char ** pptr,char * enddata)300 int MQTTProperties_read(MQTTProperties* properties, char** pptr, char* enddata)
301 {
302 int rc = 0;
303 unsigned int remlength = 0;
304
305 FUNC_ENTRY;
306 /* we assume an initialized properties structure */
307 if (enddata - (*pptr) > 0) /* enough length to read the VBI? */
308 {
309 int proplen = 0;
310
311 *pptr += MQTTPacket_decodeBuf(*pptr, &remlength);
312 properties->length = remlength;
313 while (remlength > 0)
314 {
315 if (properties->count == properties->max_count)
316 {
317 properties->max_count += 10;
318 if (properties->max_count == 10)
319 properties->array = malloc(sizeof(MQTTProperty) * properties->max_count);
320 else
321 properties->array = realloc(properties->array, sizeof(MQTTProperty) * properties->max_count);
322 }
323 if (properties->array == NULL)
324 {
325 rc = PAHO_MEMORY_ERROR;
326 goto exit;
327 }
328 if ((proplen = MQTTProperty_read(&properties->array[properties->count], pptr, enddata)) > 0)
329 remlength -= proplen;
330 else
331 break;
332 properties->count++;
333 }
334 if (remlength == 0)
335 rc = 1; /* data read successfully */
336 }
337
338 if (rc != 1 && properties->array != NULL)
339 MQTTProperties_free(properties);
340
341 exit:
342 FUNC_EXIT_RC(rc);
343 return rc;
344 }
345
346 struct {
347 enum MQTTPropertyCodes value;
348 const char* name;
349 } nameToString[] =
350 {
351 {MQTTPROPERTY_CODE_PAYLOAD_FORMAT_INDICATOR, "PAYLOAD_FORMAT_INDICATOR"},
352 {MQTTPROPERTY_CODE_MESSAGE_EXPIRY_INTERVAL, "MESSAGE_EXPIRY_INTERVAL"},
353 {MQTTPROPERTY_CODE_CONTENT_TYPE, "CONTENT_TYPE"},
354 {MQTTPROPERTY_CODE_RESPONSE_TOPIC, "RESPONSE_TOPIC"},
355 {MQTTPROPERTY_CODE_CORRELATION_DATA, "CORRELATION_DATA"},
356 {MQTTPROPERTY_CODE_SUBSCRIPTION_IDENTIFIER, "SUBSCRIPTION_IDENTIFIER"},
357 {MQTTPROPERTY_CODE_SESSION_EXPIRY_INTERVAL, "SESSION_EXPIRY_INTERVAL"},
358 {MQTTPROPERTY_CODE_ASSIGNED_CLIENT_IDENTIFER, "ASSIGNED_CLIENT_IDENTIFER"},
359 {MQTTPROPERTY_CODE_SERVER_KEEP_ALIVE, "SERVER_KEEP_ALIVE"},
360 {MQTTPROPERTY_CODE_AUTHENTICATION_METHOD, "AUTHENTICATION_METHOD"},
361 {MQTTPROPERTY_CODE_AUTHENTICATION_DATA, "AUTHENTICATION_DATA"},
362 {MQTTPROPERTY_CODE_REQUEST_PROBLEM_INFORMATION, "REQUEST_PROBLEM_INFORMATION"},
363 {MQTTPROPERTY_CODE_WILL_DELAY_INTERVAL, "WILL_DELAY_INTERVAL"},
364 {MQTTPROPERTY_CODE_REQUEST_RESPONSE_INFORMATION, "REQUEST_RESPONSE_INFORMATION"},
365 {MQTTPROPERTY_CODE_RESPONSE_INFORMATION, "RESPONSE_INFORMATION"},
366 {MQTTPROPERTY_CODE_SERVER_REFERENCE, "SERVER_REFERENCE"},
367 {MQTTPROPERTY_CODE_REASON_STRING, "REASON_STRING"},
368 {MQTTPROPERTY_CODE_RECEIVE_MAXIMUM, "RECEIVE_MAXIMUM"},
369 {MQTTPROPERTY_CODE_TOPIC_ALIAS_MAXIMUM, "TOPIC_ALIAS_MAXIMUM"},
370 {MQTTPROPERTY_CODE_TOPIC_ALIAS, "TOPIC_ALIAS"},
371 {MQTTPROPERTY_CODE_MAXIMUM_QOS, "MAXIMUM_QOS"},
372 {MQTTPROPERTY_CODE_RETAIN_AVAILABLE, "RETAIN_AVAILABLE"},
373 {MQTTPROPERTY_CODE_USER_PROPERTY, "USER_PROPERTY"},
374 {MQTTPROPERTY_CODE_MAXIMUM_PACKET_SIZE, "MAXIMUM_PACKET_SIZE"},
375 {MQTTPROPERTY_CODE_WILDCARD_SUBSCRIPTION_AVAILABLE, "WILDCARD_SUBSCRIPTION_AVAILABLE"},
376 {MQTTPROPERTY_CODE_SUBSCRIPTION_IDENTIFIERS_AVAILABLE, "SUBSCRIPTION_IDENTIFIERS_AVAILABLE"},
377 {MQTTPROPERTY_CODE_SHARED_SUBSCRIPTION_AVAILABLE, "SHARED_SUBSCRIPTION_AVAILABLE"}
378 };
379
MQTTPropertyName(enum MQTTPropertyCodes value)380 const char* MQTTPropertyName(enum MQTTPropertyCodes value)
381 {
382 int i = 0;
383 const char* result = NULL;
384
385 for (i = 0; i < ARRAY_SIZE(nameToString); ++i)
386 {
387 if (nameToString[i].value == value)
388 {
389 result = nameToString[i].name;
390 break;
391 }
392 }
393 return result;
394 }
395
396
MQTTProperties_free(MQTTProperties * props)397 void MQTTProperties_free(MQTTProperties* props)
398 {
399 int i = 0;
400
401 FUNC_ENTRY;
402 if (props == NULL)
403 goto exit;
404 for (i = 0; i < props->count; ++i)
405 {
406 int id = props->array[i].identifier;
407 int type = MQTTProperty_getType(id);
408
409 switch (type)
410 {
411 case MQTTPROPERTY_TYPE_BINARY_DATA:
412 case MQTTPROPERTY_TYPE_UTF_8_ENCODED_STRING:
413 case MQTTPROPERTY_TYPE_UTF_8_STRING_PAIR:
414 free(props->array[i].value.data.data);
415 if (type == MQTTPROPERTY_TYPE_UTF_8_STRING_PAIR)
416 free(props->array[i].value.value.data);
417 break;
418 }
419 }
420 if (props->array)
421 free(props->array);
422 memset(props, '\0', sizeof(MQTTProperties)); /* zero all fields */
423 exit:
424 FUNC_EXIT;
425 }
426
427
MQTTProperties_copy(const MQTTProperties * props)428 MQTTProperties MQTTProperties_copy(const MQTTProperties* props)
429 {
430 int i = 0;
431 MQTTProperties result = MQTTProperties_initializer;
432
433 FUNC_ENTRY;
434 for (i = 0; i < props->count; ++i)
435 {
436 int rc = 0;
437
438 if ((rc = MQTTProperties_add(&result, &props->array[i])) != 0)
439 Log(LOG_ERROR, -1, "Error from MQTTProperties add %d", rc);
440 }
441
442 FUNC_EXIT;
443 return result;
444 }
445
446
MQTTProperties_hasProperty(MQTTProperties * props,enum MQTTPropertyCodes propid)447 int MQTTProperties_hasProperty(MQTTProperties *props, enum MQTTPropertyCodes propid)
448 {
449 int i = 0;
450 int found = 0;
451
452 for (i = 0; i < props->count; ++i)
453 {
454 if (propid == props->array[i].identifier)
455 {
456 found = 1;
457 break;
458 }
459 }
460 return found;
461 }
462
463
MQTTProperties_propertyCount(MQTTProperties * props,enum MQTTPropertyCodes propid)464 int MQTTProperties_propertyCount(MQTTProperties *props, enum MQTTPropertyCodes propid)
465 {
466 int i = 0;
467 int count = 0;
468
469 for (i = 0; i < props->count; ++i)
470 {
471 if (propid == props->array[i].identifier)
472 count++;
473 }
474 return count;
475 }
476
477
MQTTProperties_getNumericValueAt(MQTTProperties * props,enum MQTTPropertyCodes propid,int index)478 int MQTTProperties_getNumericValueAt(MQTTProperties *props, enum MQTTPropertyCodes propid, int index)
479 {
480 int i = 0;
481 int rc = -9999999;
482 int cur_index = 0;
483
484 for (i = 0; i < props->count; ++i)
485 {
486 int id = props->array[i].identifier;
487
488 if (id == propid)
489 {
490 if (cur_index < index)
491 {
492 cur_index++;
493 continue;
494 }
495 switch (MQTTProperty_getType(id))
496 {
497 case MQTTPROPERTY_TYPE_BYTE:
498 rc = props->array[i].value.byte;
499 break;
500 case MQTTPROPERTY_TYPE_TWO_BYTE_INTEGER:
501 rc = props->array[i].value.integer2;
502 break;
503 case MQTTPROPERTY_TYPE_FOUR_BYTE_INTEGER:
504 case MQTTPROPERTY_TYPE_VARIABLE_BYTE_INTEGER:
505 rc = props->array[i].value.integer4;
506 break;
507 default:
508 rc = -999999;
509 break;
510 }
511 break;
512 }
513 }
514 return rc;
515 }
516
517
MQTTProperties_getNumericValue(MQTTProperties * props,enum MQTTPropertyCodes propid)518 int MQTTProperties_getNumericValue(MQTTProperties *props, enum MQTTPropertyCodes propid)
519 {
520 return MQTTProperties_getNumericValueAt(props, propid, 0);
521 }
522
523
MQTTProperties_getPropertyAt(MQTTProperties * props,enum MQTTPropertyCodes propid,int index)524 MQTTProperty* MQTTProperties_getPropertyAt(MQTTProperties *props, enum MQTTPropertyCodes propid, int index)
525 {
526 int i = 0;
527 MQTTProperty* result = NULL;
528 int cur_index = 0;
529
530 for (i = 0; i < props->count; ++i)
531 {
532 int id = props->array[i].identifier;
533
534 if (id == propid)
535 {
536 if (cur_index == index)
537 {
538 result = &props->array[i];
539 break;
540 }
541 else
542 cur_index++;
543 }
544 }
545 return result;
546 }
547
548
MQTTProperties_getProperty(MQTTProperties * props,enum MQTTPropertyCodes propid)549 MQTTProperty* MQTTProperties_getProperty(MQTTProperties *props, enum MQTTPropertyCodes propid)
550 {
551 return MQTTProperties_getPropertyAt(props, propid, 0);
552 }
553