]> git.sur5r.net Git - freertos/blob - FreeRTOS-Labs/Source/FreeRTOS-IoT-Libraries/c_sdk/standard/mqtt/src/iot_mqtt_validate.c
Add the Labs projects provided in the V10.2.1_191129 zip file.
[freertos] / FreeRTOS-Labs / Source / FreeRTOS-IoT-Libraries / c_sdk / standard / mqtt / src / iot_mqtt_validate.c
1 /*\r
2  * IoT MQTT V2.1.0\r
3  * Copyright (C) 2018 Amazon.com, Inc. or its affiliates.  All Rights Reserved.\r
4  *\r
5  * Permission is hereby granted, free of charge, to any person obtaining a copy of\r
6  * this software and associated documentation files (the "Software"), to deal in\r
7  * the Software without restriction, including without limitation the rights to\r
8  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\r
9  * the Software, and to permit persons to whom the Software is furnished to do so,\r
10  * subject to the following conditions:\r
11  *\r
12  * The above copyright notice and this permission notice shall be included in all\r
13  * copies or substantial portions of the Software.\r
14  *\r
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r
17  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r
18  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r
19  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r
20  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
21  */\r
22 \r
23 /**\r
24  * @file iot_mqtt_validate.c\r
25  * @brief Implements functions that validate the structs of the MQTT library.\r
26  */\r
27 \r
28 /* The config header is always included first. */\r
29 #include "iot_config.h"\r
30 \r
31 /* Error handling include. */\r
32 #include "iot_error.h"\r
33 \r
34 /* MQTT internal include. */\r
35 #include "private/iot_mqtt_internal.h"\r
36 \r
37 /**\r
38  * @brief Check that an #IotMqttPublishInfo_t is valid.\r
39  *\r
40  * @param[in] awsIotMqttMode Specifies if this PUBLISH packet is being sent to\r
41  * an AWS IoT MQTT server.\r
42  * @param[in] maximumPayloadLength Maximum payload length.\r
43  * @param[in] pPublishTypeDescription String describing the publish type.\r
44  * @param[in] pPublishInfo The #IotMqttPublishInfo_t to validate.\r
45  *\r
46  * @return `true` if `pPublishInfo` is valid; `false` otherwise.\r
47  */\r
48 static bool _validatePublish( bool awsIotMqttMode,\r
49                               size_t maximumPayloadLength,\r
50                               const char * pPublishTypeDescription,\r
51                               const IotMqttPublishInfo_t * pPublishInfo );\r
52 \r
53 /*-----------------------------------------------------------*/\r
54 \r
55 bool _IotMqtt_ValidateConnect( const IotMqttConnectInfo_t * pConnectInfo )\r
56 {\r
57     IOT_FUNCTION_ENTRY( bool, true );\r
58     uint16_t maxClientIdLength = MQTT_SERVER_MAX_CLIENTID_LENGTH;\r
59     bool enforceMaxClientIdLength = false;\r
60 \r
61     /* Check for NULL. */\r
62     if( pConnectInfo == NULL )\r
63     {\r
64         IotLogError( "MQTT connection information cannot be NULL." );\r
65 \r
66         IOT_SET_AND_GOTO_CLEANUP( false );\r
67     }\r
68     else\r
69     {\r
70         EMPTY_ELSE_MARKER;\r
71     }\r
72 \r
73     /* Check that a client identifier was set. */\r
74     if( pConnectInfo->pClientIdentifier == NULL )\r
75     {\r
76         IotLogError( "Client identifier must be set." );\r
77 \r
78         IOT_SET_AND_GOTO_CLEANUP( false );\r
79     }\r
80     else\r
81     {\r
82         EMPTY_ELSE_MARKER;\r
83     }\r
84 \r
85     /* Check for a zero-length client identifier. Zero-length client identifiers\r
86      * are not allowed with clean sessions. */\r
87     if( pConnectInfo->clientIdentifierLength == 0 )\r
88     {\r
89         IotLogWarn( "A zero-length client identifier was provided." );\r
90 \r
91         if( pConnectInfo->cleanSession == true )\r
92         {\r
93             IotLogError( "A zero-length client identifier cannot be used with a clean session." );\r
94 \r
95             IOT_SET_AND_GOTO_CLEANUP( false );\r
96         }\r
97         else\r
98         {\r
99             EMPTY_ELSE_MARKER;\r
100         }\r
101     }\r
102     else\r
103     {\r
104         EMPTY_ELSE_MARKER;\r
105     }\r
106 \r
107     /* Check that the number of persistent session subscriptions is valid. */\r
108     if( pConnectInfo->pPreviousSubscriptions != NULL )\r
109     {\r
110         if( _IotMqtt_ValidateSubscriptionList( IOT_MQTT_SUBSCRIBE,\r
111                                                 pConnectInfo->awsIotMqttMode,\r
112                                                 pConnectInfo->pPreviousSubscriptions,\r
113                                                 pConnectInfo->previousSubscriptionCount ) == false )\r
114         {\r
115             IOT_SET_AND_GOTO_CLEANUP( false );\r
116         }\r
117         else\r
118         {\r
119             EMPTY_ELSE_MARKER;\r
120         }\r
121     }\r
122     else\r
123     {\r
124         EMPTY_ELSE_MARKER;\r
125     }\r
126 \r
127     /* If will info is provided, check that it is valid. */\r
128     if( pConnectInfo->pWillInfo != NULL )\r
129     {\r
130         if( _IotMqtt_ValidateLwtPublish( pConnectInfo->awsIotMqttMode,\r
131                                          pConnectInfo->pWillInfo ) == false )\r
132         {\r
133             IOT_SET_AND_GOTO_CLEANUP( false );\r
134         }\r
135         else\r
136         {\r
137             EMPTY_ELSE_MARKER;\r
138         }\r
139     }\r
140     else\r
141     {\r
142         EMPTY_ELSE_MARKER;\r
143     }\r
144 \r
145     /* The AWS IoT MQTT service enforces a client ID length limit. */\r
146     if( pConnectInfo->awsIotMqttMode == true )\r
147     {\r
148         maxClientIdLength = AWS_IOT_MQTT_SERVER_MAX_CLIENTID_LENGTH;\r
149         enforceMaxClientIdLength = true;\r
150     }\r
151 \r
152     if( pConnectInfo->clientIdentifierLength > maxClientIdLength )\r
153     {\r
154         if( enforceMaxClientIdLength == false )\r
155         {\r
156             IotLogWarn( "A client identifier length of %hu is longer than %hu, "\r
157                         "which is "\r
158                         "the longest client identifier a server must accept.",\r
159                         pConnectInfo->clientIdentifierLength,\r
160                         maxClientIdLength );\r
161         }\r
162         else\r
163         {\r
164             IotLogError( "A client identifier length of %hu exceeds the "\r
165                          "maximum supported length of %hu.",\r
166                          pConnectInfo->clientIdentifierLength,\r
167                          maxClientIdLength );\r
168 \r
169             IOT_SET_AND_GOTO_CLEANUP( false );\r
170         }\r
171     }\r
172     else\r
173     {\r
174         EMPTY_ELSE_MARKER;\r
175     }\r
176 \r
177     IOT_FUNCTION_EXIT_NO_CLEANUP();\r
178 }\r
179 \r
180 /*-----------------------------------------------------------*/\r
181 static bool _validatePublish( bool awsIotMqttMode,\r
182                               size_t maximumPayloadLength,\r
183                               const char * pPublishTypeDescription,\r
184                               const IotMqttPublishInfo_t * pPublishInfo )\r
185 {\r
186     IOT_FUNCTION_ENTRY( bool, true );\r
187 \r
188     /* Check for NULL. */\r
189     if( pPublishInfo == NULL )\r
190     {\r
191         IotLogError( "Publish information cannot be NULL." );\r
192 \r
193         IOT_SET_AND_GOTO_CLEANUP( false );\r
194     }\r
195     else\r
196     {\r
197         EMPTY_ELSE_MARKER;\r
198     }\r
199 \r
200     /* Check topic name for NULL or zero-length. */\r
201     if( pPublishInfo->pTopicName == NULL )\r
202     {\r
203         IotLogError( "Publish topic name must be set." );\r
204     }\r
205     else\r
206     {\r
207         EMPTY_ELSE_MARKER;\r
208     }\r
209 \r
210     if( pPublishInfo->topicNameLength == 0 )\r
211     {\r
212         IotLogError( "Publish topic name length cannot be 0." );\r
213 \r
214         IOT_SET_AND_GOTO_CLEANUP( false );\r
215     }\r
216     else\r
217     {\r
218         EMPTY_ELSE_MARKER;\r
219     }\r
220 \r
221     if( pPublishInfo->payloadLength != 0 )\r
222     {\r
223         if( pPublishInfo->payloadLength > maximumPayloadLength )\r
224         {\r
225             IotLogError( "%s payload size of %zu exceeds maximum length of %zu.",\r
226                          pPublishTypeDescription,\r
227                          pPublishInfo->payloadLength,\r
228                          maximumPayloadLength );\r
229 \r
230             IOT_SET_AND_GOTO_CLEANUP( false );\r
231         }\r
232         else\r
233         {\r
234             if( pPublishInfo->pPayload == NULL )\r
235             {\r
236                 IotLogError( "Nonzero payload length cannot have a NULL payload." );\r
237 \r
238                 IOT_SET_AND_GOTO_CLEANUP( false );\r
239             }\r
240             else\r
241             {\r
242                 EMPTY_ELSE_MARKER;\r
243             }\r
244         }\r
245     }\r
246     else\r
247     {\r
248         EMPTY_ELSE_MARKER;\r
249     }\r
250 \r
251     /* Check for a valid QoS. */\r
252     if( pPublishInfo->qos != IOT_MQTT_QOS_0 )\r
253     {\r
254         if( pPublishInfo->qos != IOT_MQTT_QOS_1 )\r
255         {\r
256             IotLogError( "Publish QoS must be either 0 or 1." );\r
257 \r
258             IOT_SET_AND_GOTO_CLEANUP( false );\r
259         }\r
260         else\r
261         {\r
262             EMPTY_ELSE_MARKER;\r
263         }\r
264     }\r
265     else\r
266     {\r
267         EMPTY_ELSE_MARKER;\r
268     }\r
269 \r
270     /* Check the retry parameters. */\r
271     if( pPublishInfo->retryLimit > 0 )\r
272     {\r
273         if( pPublishInfo->retryMs == 0 )\r
274         {\r
275             IotLogError( "Publish retry time must be positive." );\r
276 \r
277             IOT_SET_AND_GOTO_CLEANUP( false );\r
278         }\r
279         else\r
280         {\r
281             EMPTY_ELSE_MARKER;\r
282         }\r
283     }\r
284     else\r
285     {\r
286         EMPTY_ELSE_MARKER;\r
287     }\r
288 \r
289     /* Check for compatibility with AWS IoT MQTT server. */\r
290     if( awsIotMqttMode == true )\r
291     {\r
292         /* Check for retained message. */\r
293         if( pPublishInfo->retain == true )\r
294         {\r
295             IotLogError( "AWS IoT does not support retained publish messages." );\r
296 \r
297             IOT_SET_AND_GOTO_CLEANUP( false );\r
298         }\r
299         else\r
300         {\r
301             EMPTY_ELSE_MARKER;\r
302         }\r
303 \r
304         /* Check topic name length. */\r
305         if( pPublishInfo->topicNameLength > AWS_IOT_MQTT_SERVER_MAX_TOPIC_LENGTH )\r
306         {\r
307             IotLogError( "AWS IoT does not support topic names longer than %d bytes.",\r
308                          AWS_IOT_MQTT_SERVER_MAX_TOPIC_LENGTH );\r
309 \r
310             IOT_SET_AND_GOTO_CLEANUP( false );\r
311         }\r
312         else\r
313         {\r
314             EMPTY_ELSE_MARKER;\r
315         }\r
316     }\r
317     else\r
318     {\r
319         EMPTY_ELSE_MARKER;\r
320     }\r
321 \r
322     IOT_FUNCTION_EXIT_NO_CLEANUP();\r
323 }\r
324 \r
325 /*-----------------------------------------------------------*/\r
326 \r
327 bool _IotMqtt_ValidatePublish( bool awsIotMqttMode,\r
328                                const IotMqttPublishInfo_t * pPublishInfo )\r
329 {\r
330     size_t maximumPayloadLength = MQTT_SERVER_MAX_PUBLISH_PAYLOAD_LENGTH;\r
331 \r
332     if( awsIotMqttMode == true )\r
333     {\r
334         maximumPayloadLength = AWS_IOT_MQTT_SERVER_MAX_PUBLISH_PAYLOAD_LENGTH;\r
335     }\r
336     else\r
337     {\r
338         EMPTY_ELSE_MARKER;\r
339     }\r
340 \r
341     return _validatePublish( awsIotMqttMode,\r
342                              maximumPayloadLength,\r
343                              "Publish",\r
344                              pPublishInfo );\r
345 }\r
346 \r
347 /*-----------------------------------------------------------*/\r
348 \r
349 bool _IotMqtt_ValidateLwtPublish( bool awsIotMqttMode,\r
350                                   const IotMqttPublishInfo_t * pLwtPublishInfo )\r
351 {\r
352     return _validatePublish( awsIotMqttMode,\r
353                              MQTT_SERVER_MAX_LWT_PAYLOAD_LENGTH,\r
354                              "LWT",\r
355                              pLwtPublishInfo );\r
356 }\r
357 \r
358 /*-----------------------------------------------------------*/\r
359 \r
360 bool _IotMqtt_ValidateOperation( IotMqttOperation_t operation )\r
361 {\r
362     IOT_FUNCTION_ENTRY( bool, true );\r
363 \r
364     /* Check for NULL. */\r
365     if( operation == NULL )\r
366     {\r
367         IotLogError( "Operation reference cannot be NULL." );\r
368 \r
369         IOT_SET_AND_GOTO_CLEANUP( false );\r
370     }\r
371     else\r
372     {\r
373         EMPTY_ELSE_MARKER;\r
374     }\r
375 \r
376     /* Check that reference is waitable. */\r
377     if( ( operation->u.operation.flags & IOT_MQTT_FLAG_WAITABLE ) != IOT_MQTT_FLAG_WAITABLE )\r
378     {\r
379         IotLogError( "Operation is not waitable." );\r
380 \r
381         IOT_SET_AND_GOTO_CLEANUP( false );\r
382     }\r
383     else\r
384     {\r
385         EMPTY_ELSE_MARKER;\r
386     }\r
387 \r
388     IOT_FUNCTION_EXIT_NO_CLEANUP();\r
389 }\r
390 \r
391 /*-----------------------------------------------------------*/\r
392 \r
393 bool _IotMqtt_ValidateSubscriptionList( IotMqttOperationType_t operation,\r
394                                         bool awsIotMqttMode,\r
395                                         const IotMqttSubscription_t * pListStart,\r
396                                         size_t listSize )\r
397 {\r
398     IOT_FUNCTION_ENTRY( bool, true );\r
399     size_t i = 0;\r
400     uint16_t j = 0;\r
401     const IotMqttSubscription_t * pListElement = NULL;\r
402 \r
403     /* Operation must be either subscribe or unsubscribe. */\r
404     IotMqtt_Assert( ( operation == IOT_MQTT_SUBSCRIBE ) ||\r
405                     ( operation == IOT_MQTT_UNSUBSCRIBE ) );\r
406 \r
407     /* Check for empty list. */\r
408     if( pListStart == NULL )\r
409     {\r
410         IotLogError( "Subscription list pointer cannot be NULL." );\r
411 \r
412         IOT_SET_AND_GOTO_CLEANUP( false );\r
413     }\r
414     else\r
415     {\r
416         EMPTY_ELSE_MARKER;\r
417     }\r
418 \r
419     if( listSize == 0 )\r
420     {\r
421         IotLogError( "Empty subscription list." );\r
422 \r
423         IOT_SET_AND_GOTO_CLEANUP( false );\r
424     }\r
425     else\r
426     {\r
427         EMPTY_ELSE_MARKER;\r
428     }\r
429 \r
430     /* AWS IoT supports at most 8 topic filters in a single SUBSCRIBE packet. */\r
431     if( awsIotMqttMode == true )\r
432     {\r
433         if( listSize > AWS_IOT_MQTT_SERVER_MAX_TOPIC_FILTERS_PER_SUBSCRIBE )\r
434         {\r
435             IotLogError( "AWS IoT does not support more than %d topic filters per "\r
436                          "subscription request.",\r
437                          AWS_IOT_MQTT_SERVER_MAX_TOPIC_FILTERS_PER_SUBSCRIBE );\r
438 \r
439             IOT_SET_AND_GOTO_CLEANUP( false );\r
440         }\r
441         else\r
442         {\r
443             EMPTY_ELSE_MARKER;\r
444         }\r
445     }\r
446     else\r
447     {\r
448         EMPTY_ELSE_MARKER;\r
449     }\r
450 \r
451     for( i = 0; i < listSize; i++ )\r
452     {\r
453         pListElement = &( pListStart[ i ] );\r
454 \r
455         /* Check for a valid QoS and callback function when subscribing. */\r
456         if( operation == IOT_MQTT_SUBSCRIBE )\r
457         {\r
458             if( pListElement->qos != IOT_MQTT_QOS_0 )\r
459             {\r
460                 if( pListElement->qos != IOT_MQTT_QOS_1 )\r
461                 {\r
462                     IotLogError( "Subscription QoS must be either 0 or 1." );\r
463 \r
464                     IOT_SET_AND_GOTO_CLEANUP( false );\r
465                 }\r
466                 else\r
467                 {\r
468                     EMPTY_ELSE_MARKER;\r
469                 }\r
470             }\r
471             else\r
472             {\r
473                 EMPTY_ELSE_MARKER;\r
474             }\r
475 \r
476             if( pListElement->callback.function == NULL )\r
477             {\r
478                 IotLogError( "Callback function must be set." );\r
479 \r
480                 IOT_SET_AND_GOTO_CLEANUP( false );\r
481             }\r
482             else\r
483             {\r
484                 EMPTY_ELSE_MARKER;\r
485             }\r
486         }\r
487         else\r
488         {\r
489             EMPTY_ELSE_MARKER;\r
490         }\r
491 \r
492         /* Check subscription topic filter. */\r
493         if( pListElement->pTopicFilter == NULL )\r
494         {\r
495             IotLogError( "Subscription topic filter cannot be NULL." );\r
496 \r
497             IOT_SET_AND_GOTO_CLEANUP( false );\r
498         }\r
499         else\r
500         {\r
501             EMPTY_ELSE_MARKER;\r
502         }\r
503 \r
504         if( pListElement->topicFilterLength == 0 )\r
505         {\r
506             IotLogError( "Subscription topic filter length cannot be 0." );\r
507 \r
508             IOT_SET_AND_GOTO_CLEANUP( false );\r
509         }\r
510         else\r
511         {\r
512             EMPTY_ELSE_MARKER;\r
513         }\r
514 \r
515         /* Check for compatibility with AWS IoT MQTT server. */\r
516         if( awsIotMqttMode == true )\r
517         {\r
518             /* Check topic filter length. */\r
519             if( pListElement->topicFilterLength > AWS_IOT_MQTT_SERVER_MAX_TOPIC_LENGTH )\r
520             {\r
521                 IotLogError( "AWS IoT does not support topic filters longer than %d bytes.",\r
522                              AWS_IOT_MQTT_SERVER_MAX_TOPIC_LENGTH );\r
523 \r
524                 IOT_SET_AND_GOTO_CLEANUP( false );\r
525             }\r
526             else\r
527             {\r
528                 EMPTY_ELSE_MARKER;\r
529             }\r
530         }\r
531         else\r
532         {\r
533             EMPTY_ELSE_MARKER;\r
534         }\r
535 \r
536         /* Check that the wildcards '+' and '#' are being used correctly. */\r
537         for( j = 0; j < pListElement->topicFilterLength; j++ )\r
538         {\r
539             switch( pListElement->pTopicFilter[ j ] )\r
540             {\r
541                 /* Check that the single level wildcard '+' is used correctly. */\r
542                 case '+':\r
543 \r
544                     /* Unless '+' is the first character in the filter, it must be preceded by '/'. */\r
545                     if( j > 0 )\r
546                     {\r
547                         if( pListElement->pTopicFilter[ j - 1 ] != '/' )\r
548                         {\r
549                             IotLogError( "Invalid topic filter %.*s -- '+' must be preceded by '/'.",\r
550                                          pListElement->topicFilterLength,\r
551                                          pListElement->pTopicFilter );\r
552 \r
553                             IOT_SET_AND_GOTO_CLEANUP( false );\r
554                         }\r
555                         else\r
556                         {\r
557                             EMPTY_ELSE_MARKER;\r
558                         }\r
559                     }\r
560                     else\r
561                     {\r
562                         EMPTY_ELSE_MARKER;\r
563                     }\r
564 \r
565                     /* Unless '+' is the last character in the filter, it must be succeeded by '/'. */\r
566                     if( j < pListElement->topicFilterLength - 1 )\r
567                     {\r
568                         if( pListElement->pTopicFilter[ j + 1 ] != '/' )\r
569                         {\r
570                             IotLogError( "Invalid topic filter %.*s -- '+' must be succeeded by '/'.",\r
571                                          pListElement->topicFilterLength,\r
572                                          pListElement->pTopicFilter );\r
573 \r
574                             IOT_SET_AND_GOTO_CLEANUP( false );\r
575                         }\r
576                         else\r
577                         {\r
578                             EMPTY_ELSE_MARKER;\r
579                         }\r
580                     }\r
581                     else\r
582                     {\r
583                         EMPTY_ELSE_MARKER;\r
584                     }\r
585 \r
586                     break;\r
587 \r
588                 /* Check that the multi-level wildcard '#' is used correctly. */\r
589                 case '#':\r
590 \r
591                     /* '#' must be the last character in the filter. */\r
592                     if( j != pListElement->topicFilterLength - 1 )\r
593                     {\r
594                         IotLogError( "Invalid topic filter %.*s -- '#' must be the last character.",\r
595                                      pListElement->topicFilterLength,\r
596                                      pListElement->pTopicFilter );\r
597 \r
598                         IOT_SET_AND_GOTO_CLEANUP( false );\r
599                     }\r
600                     else\r
601                     {\r
602                         EMPTY_ELSE_MARKER;\r
603                     }\r
604 \r
605                     /* Unless '#' is standalone, it must be preceded by '/'. */\r
606                     if( pListElement->topicFilterLength > 1 )\r
607                     {\r
608                         if( pListElement->pTopicFilter[ j - 1 ] != '/' )\r
609                         {\r
610                             IotLogError( "Invalid topic filter %.*s -- '#' must be preceded by '/'.",\r
611                                          pListElement->topicFilterLength,\r
612                                          pListElement->pTopicFilter );\r
613 \r
614                             IOT_SET_AND_GOTO_CLEANUP( false );\r
615                         }\r
616                         else\r
617                         {\r
618                             EMPTY_ELSE_MARKER;\r
619                         }\r
620                     }\r
621                     else\r
622                     {\r
623                         EMPTY_ELSE_MARKER;\r
624                     }\r
625 \r
626                     break;\r
627 \r
628                 default:\r
629                     break;\r
630             }\r
631         }\r
632     }\r
633 \r
634     IOT_FUNCTION_EXIT_NO_CLEANUP();\r
635 }\r
636 \r
637 /*-----------------------------------------------------------*/\r