]> git.sur5r.net Git - freertos/blob - FreeRTOS-Plus/Source/FreeRTOS-IoT-Libraries/c_sdk/standard/mqtt/src/iot_mqtt_validate.c
Correct an err in queue.c introduced when previously updating behaviour when queue...
[freertos] / FreeRTOS-Plus / Source / FreeRTOS-IoT-Libraries / c_sdk / standard / mqtt / src / iot_mqtt_validate.c
1 /*\r
2  * Amazon FreeRTOS MQTT V2.0.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  * http://aws.amazon.com/freertos\r
23  * http://www.FreeRTOS.org\r
24  */\r
25 \r
26 /**\r
27  * @file iot_mqtt_validate.c\r
28  * @brief Implements functions that validate the structs of the MQTT library.\r
29  */\r
30 \r
31 /* The config header is always included first. */\r
32 #include "iot_config.h"\r
33 \r
34 /* Error handling include. */\r
35 #include "private/iot_error.h"\r
36 \r
37 /* MQTT internal include. */\r
38 #include "private/iot_mqtt_internal.h"\r
39 \r
40 /*-----------------------------------------------------------*/\r
41 \r
42 bool _IotMqtt_ValidateConnect( const IotMqttConnectInfo_t * pConnectInfo )\r
43 {\r
44     IOT_FUNCTION_ENTRY( bool, true );\r
45 \r
46     /* Check for NULL. */\r
47     if( pConnectInfo == NULL )\r
48     {\r
49         IotLogError( "MQTT connection information cannot be NULL." );\r
50 \r
51         IOT_SET_AND_GOTO_CLEANUP( false );\r
52     }\r
53     else\r
54     {\r
55         EMPTY_ELSE_MARKER;\r
56     }\r
57 \r
58     /* Check that a client identifier was set. */\r
59     if( pConnectInfo->pClientIdentifier == NULL )\r
60     {\r
61         IotLogError( "Client identifier must be set." );\r
62 \r
63         IOT_SET_AND_GOTO_CLEANUP( false );\r
64     }\r
65     else\r
66     {\r
67         EMPTY_ELSE_MARKER;\r
68     }\r
69 \r
70     /* Check for a zero-length client identifier. Zero-length client identifiers\r
71      * are not allowed with clean sessions. */\r
72     if( pConnectInfo->clientIdentifierLength == 0 )\r
73     {\r
74         IotLogWarn( "A zero-length client identifier was provided." );\r
75 \r
76         if( pConnectInfo->cleanSession == true )\r
77         {\r
78             IotLogError( "A zero-length client identifier cannot be used with a clean session." );\r
79 \r
80             IOT_SET_AND_GOTO_CLEANUP( false );\r
81         }\r
82         else\r
83         {\r
84             EMPTY_ELSE_MARKER;\r
85         }\r
86     }\r
87     else\r
88     {\r
89         EMPTY_ELSE_MARKER;\r
90     }\r
91 \r
92     /* Check that the number of persistent session subscriptions is valid. */\r
93     if( pConnectInfo->cleanSession == false )\r
94     {\r
95         if( pConnectInfo->pPreviousSubscriptions != NULL )\r
96         {\r
97             if( pConnectInfo->previousSubscriptionCount == 0 )\r
98             {\r
99                 IotLogError( "Previous subscription count cannot be 0." );\r
100 \r
101                 IOT_SET_AND_GOTO_CLEANUP( false );\r
102             }\r
103             else\r
104             {\r
105                 EMPTY_ELSE_MARKER;\r
106             }\r
107         }\r
108         else\r
109         {\r
110             EMPTY_ELSE_MARKER;\r
111         }\r
112     }\r
113     else\r
114     {\r
115         EMPTY_ELSE_MARKER;\r
116     }\r
117 \r
118     /* In MQTT 3.1.1, servers are not obligated to accept client identifiers longer\r
119      * than 23 characters. */\r
120     if( pConnectInfo->clientIdentifierLength > 23 )\r
121     {\r
122         IotLogWarn( "A client identifier length of %hu is longer than 23, which is "\r
123                     "the longest client identifier a server must accept.",\r
124                     pConnectInfo->clientIdentifierLength );\r
125     }\r
126     else\r
127     {\r
128         EMPTY_ELSE_MARKER;\r
129     }\r
130 \r
131     /* Check for compatibility with the AWS IoT MQTT service limits. */\r
132     if( pConnectInfo->awsIotMqttMode == true )\r
133     {\r
134         /* Check that client identifier is within the service limit. */\r
135         if( pConnectInfo->clientIdentifierLength > AWS_IOT_MQTT_SERVER_MAX_CLIENTID )\r
136         {\r
137             IotLogError( "AWS IoT does not support client identifiers longer than %d bytes.",\r
138                          AWS_IOT_MQTT_SERVER_MAX_CLIENTID );\r
139 \r
140             IOT_SET_AND_GOTO_CLEANUP( false );\r
141         }\r
142         else\r
143         {\r
144             EMPTY_ELSE_MARKER;\r
145         }\r
146 \r
147         /* Check for compliance with AWS IoT keep-alive limits. */\r
148         if( pConnectInfo->keepAliveSeconds == 0 )\r
149         {\r
150             IotLogWarn( "AWS IoT does not support disabling keep-alive. Default keep-alive "\r
151                         "of %d seconds will be used.",\r
152                         AWS_IOT_MQTT_SERVER_MAX_KEEPALIVE );\r
153         }\r
154         else if( pConnectInfo->keepAliveSeconds < AWS_IOT_MQTT_SERVER_MIN_KEEPALIVE )\r
155         {\r
156             IotLogWarn( "AWS IoT does not support keep-alive intervals less than %d seconds. "\r
157                         "An interval of %d seconds will be used.",\r
158                         AWS_IOT_MQTT_SERVER_MIN_KEEPALIVE,\r
159                         AWS_IOT_MQTT_SERVER_MIN_KEEPALIVE );\r
160         }\r
161         else if( pConnectInfo->keepAliveSeconds > AWS_IOT_MQTT_SERVER_MAX_KEEPALIVE )\r
162         {\r
163             IotLogWarn( "AWS IoT does not support keep-alive intervals greater than %d seconds. "\r
164                         "An interval of %d seconds will be used.",\r
165                         AWS_IOT_MQTT_SERVER_MAX_KEEPALIVE,\r
166                         AWS_IOT_MQTT_SERVER_MAX_KEEPALIVE );\r
167         }\r
168         else\r
169         {\r
170             EMPTY_ELSE_MARKER;\r
171         }\r
172     }\r
173     else\r
174     {\r
175         EMPTY_ELSE_MARKER;\r
176     }\r
177 \r
178     IOT_FUNCTION_EXIT_NO_CLEANUP();\r
179 }\r
180 \r
181 /*-----------------------------------------------------------*/\r
182 \r
183 bool _IotMqtt_ValidatePublish( bool awsIotMqttMode,\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     /* Only allow NULL payloads with zero length. */\r
222     if( pPublishInfo->pPayload == NULL )\r
223     {\r
224         if( pPublishInfo->payloadLength != 0 )\r
225         {\r
226             IotLogError( "Nonzero payload length cannot have a NULL payload." );\r
227 \r
228             IOT_SET_AND_GOTO_CLEANUP( false );\r
229         }\r
230         else\r
231         {\r
232             EMPTY_ELSE_MARKER;\r
233         }\r
234     }\r
235     else\r
236     {\r
237         EMPTY_ELSE_MARKER;\r
238     }\r
239 \r
240     /* Check for a valid QoS. */\r
241     if( pPublishInfo->qos != IOT_MQTT_QOS_0 )\r
242     {\r
243         if( pPublishInfo->qos != IOT_MQTT_QOS_1 )\r
244         {\r
245             IotLogError( "Publish QoS must be either 0 or 1." );\r
246 \r
247             IOT_SET_AND_GOTO_CLEANUP( false );\r
248         }\r
249         else\r
250         {\r
251             EMPTY_ELSE_MARKER;\r
252         }\r
253     }\r
254     else\r
255     {\r
256         EMPTY_ELSE_MARKER;\r
257     }\r
258 \r
259     /* Check the retry parameters. */\r
260     if( pPublishInfo->retryLimit > 0 )\r
261     {\r
262         if( pPublishInfo->retryMs == 0 )\r
263         {\r
264             IotLogError( "Publish retry time must be positive." );\r
265 \r
266             IOT_SET_AND_GOTO_CLEANUP( false );\r
267         }\r
268         else\r
269         {\r
270             EMPTY_ELSE_MARKER;\r
271         }\r
272     }\r
273     else\r
274     {\r
275         EMPTY_ELSE_MARKER;\r
276     }\r
277 \r
278     /* Check for compatibility with AWS IoT MQTT server. */\r
279     if( awsIotMqttMode == true )\r
280     {\r
281         /* Check for retained message. */\r
282         if( pPublishInfo->retain == true )\r
283         {\r
284             IotLogError( "AWS IoT does not support retained publish messages." );\r
285 \r
286             IOT_SET_AND_GOTO_CLEANUP( false );\r
287         }\r
288         else\r
289         {\r
290             EMPTY_ELSE_MARKER;\r
291         }\r
292 \r
293         /* Check topic name length. */\r
294         if( pPublishInfo->topicNameLength > AWS_IOT_MQTT_SERVER_MAX_TOPIC_LENGTH )\r
295         {\r
296             IotLogError( "AWS IoT does not support topic names longer than %d bytes.",\r
297                          AWS_IOT_MQTT_SERVER_MAX_TOPIC_LENGTH );\r
298 \r
299             IOT_SET_AND_GOTO_CLEANUP( false );\r
300         }\r
301         else\r
302         {\r
303             EMPTY_ELSE_MARKER;\r
304         }\r
305     }\r
306     else\r
307     {\r
308         EMPTY_ELSE_MARKER;\r
309     }\r
310 \r
311     IOT_FUNCTION_EXIT_NO_CLEANUP();\r
312 }\r
313 \r
314 /*-----------------------------------------------------------*/\r
315 \r
316 bool _IotMqtt_ValidateOperation( IotMqttOperation_t operation )\r
317 {\r
318     IOT_FUNCTION_ENTRY( bool, true );\r
319 \r
320     /* Check for NULL. */\r
321     if( operation == NULL )\r
322     {\r
323         IotLogError( "Operation reference cannot be NULL." );\r
324 \r
325         IOT_SET_AND_GOTO_CLEANUP( false );\r
326     }\r
327     else\r
328     {\r
329         EMPTY_ELSE_MARKER;\r
330     }\r
331 \r
332     /* Check that reference is waitable. */\r
333     if( ( operation->u.operation.flags & IOT_MQTT_FLAG_WAITABLE ) != IOT_MQTT_FLAG_WAITABLE )\r
334     {\r
335         IotLogError( "Operation is not waitable." );\r
336 \r
337         IOT_SET_AND_GOTO_CLEANUP( false );\r
338     }\r
339     else\r
340     {\r
341         EMPTY_ELSE_MARKER;\r
342     }\r
343 \r
344     IOT_FUNCTION_EXIT_NO_CLEANUP();\r
345 }\r
346 \r
347 /*-----------------------------------------------------------*/\r
348 \r
349 bool _IotMqtt_ValidateSubscriptionList( IotMqttOperationType_t operation,\r
350                                         bool awsIotMqttMode,\r
351                                         const IotMqttSubscription_t * pListStart,\r
352                                         size_t listSize )\r
353 {\r
354     IOT_FUNCTION_ENTRY( bool, true );\r
355     size_t i = 0;\r
356     uint16_t j = 0;\r
357     const IotMqttSubscription_t * pListElement = NULL;\r
358 \r
359     /* Operation must be either subscribe or unsubscribe. */\r
360     IotMqtt_Assert( ( operation == IOT_MQTT_SUBSCRIBE ) ||\r
361                     ( operation == IOT_MQTT_UNSUBSCRIBE ) );\r
362 \r
363     /* Check for empty list. */\r
364     if( pListStart == NULL )\r
365     {\r
366         IotLogError( "Subscription list pointer cannot be NULL." );\r
367 \r
368         IOT_SET_AND_GOTO_CLEANUP( false );\r
369     }\r
370     else\r
371     {\r
372         EMPTY_ELSE_MARKER;\r
373     }\r
374 \r
375     if( listSize == 0 )\r
376     {\r
377         IotLogError( "Empty subscription list." );\r
378 \r
379         IOT_SET_AND_GOTO_CLEANUP( false );\r
380     }\r
381     else\r
382     {\r
383         EMPTY_ELSE_MARKER;\r
384     }\r
385 \r
386     /* AWS IoT supports at most 8 topic filters in a single SUBSCRIBE packet. */\r
387     if( awsIotMqttMode == true )\r
388     {\r
389         if( listSize > AWS_IOT_MQTT_SERVER_MAX_TOPIC_FILTERS_PER_SUBSCRIBE )\r
390         {\r
391             IotLogError( "AWS IoT does not support more than %d topic filters per "\r
392                          "subscription request.",\r
393                          AWS_IOT_MQTT_SERVER_MAX_TOPIC_FILTERS_PER_SUBSCRIBE );\r
394 \r
395             IOT_SET_AND_GOTO_CLEANUP( false );\r
396         }\r
397         else\r
398         {\r
399             EMPTY_ELSE_MARKER;\r
400         }\r
401     }\r
402     else\r
403     {\r
404         EMPTY_ELSE_MARKER;\r
405     }\r
406 \r
407     for( i = 0; i < listSize; i++ )\r
408     {\r
409         pListElement = &( pListStart[ i ] );\r
410 \r
411         /* Check for a valid QoS and callback function when subscribing. */\r
412         if( operation == IOT_MQTT_SUBSCRIBE )\r
413         {\r
414             if( pListElement->qos != IOT_MQTT_QOS_0 )\r
415             {\r
416                 if( pListElement->qos != IOT_MQTT_QOS_1 )\r
417                 {\r
418                     IotLogError( "Subscription QoS must be either 0 or 1." );\r
419 \r
420                     IOT_SET_AND_GOTO_CLEANUP( false );\r
421                 }\r
422                 else\r
423                 {\r
424                     EMPTY_ELSE_MARKER;\r
425                 }\r
426             }\r
427             else\r
428             {\r
429                 EMPTY_ELSE_MARKER;\r
430             }\r
431 \r
432             if( pListElement->callback.function == NULL )\r
433             {\r
434                 IotLogError( "Callback function must be set." );\r
435 \r
436                 IOT_SET_AND_GOTO_CLEANUP( false );\r
437             }\r
438             else\r
439             {\r
440                 EMPTY_ELSE_MARKER;\r
441             }\r
442         }\r
443         else\r
444         {\r
445             EMPTY_ELSE_MARKER;\r
446         }\r
447 \r
448         /* Check subscription topic filter. */\r
449         if( pListElement->pTopicFilter == NULL )\r
450         {\r
451             IotLogError( "Subscription topic filter cannot be NULL." );\r
452 \r
453             IOT_SET_AND_GOTO_CLEANUP( false );\r
454         }\r
455         else\r
456         {\r
457             EMPTY_ELSE_MARKER;\r
458         }\r
459 \r
460         if( pListElement->topicFilterLength == 0 )\r
461         {\r
462             IotLogError( "Subscription topic filter length cannot be 0." );\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         /* Check for compatibility with AWS IoT MQTT server. */\r
472         if( awsIotMqttMode == true )\r
473         {\r
474             /* Check topic filter length. */\r
475             if( pListElement->topicFilterLength > AWS_IOT_MQTT_SERVER_MAX_TOPIC_LENGTH )\r
476             {\r
477                 IotLogError( "AWS IoT does not support topic filters longer than %d bytes.",\r
478                              AWS_IOT_MQTT_SERVER_MAX_TOPIC_LENGTH );\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 that the wildcards '+' and '#' are being used correctly. */\r
493         for( j = 0; j < pListElement->topicFilterLength; j++ )\r
494         {\r
495             switch( pListElement->pTopicFilter[ j ] )\r
496             {\r
497                 /* Check that the single level wildcard '+' is used correctly. */\r
498                 case '+':\r
499 \r
500                     /* Unless '+' is the first character in the filter, it must be preceded by '/'. */\r
501                     if( j > 0 )\r
502                     {\r
503                         if( pListElement->pTopicFilter[ j - 1 ] != '/' )\r
504                         {\r
505                             IotLogError( "Invalid topic filter %.*s -- '+' must be preceded by '/'.",\r
506                                          pListElement->topicFilterLength,\r
507                                          pListElement->pTopicFilter );\r
508 \r
509                             IOT_SET_AND_GOTO_CLEANUP( false );\r
510                         }\r
511                         else\r
512                         {\r
513                             EMPTY_ELSE_MARKER;\r
514                         }\r
515                     }\r
516                     else\r
517                     {\r
518                         EMPTY_ELSE_MARKER;\r
519                     }\r
520 \r
521                     /* Unless '+' is the last character in the filter, it must be succeeded by '/'. */\r
522                     if( j < pListElement->topicFilterLength - 1 )\r
523                     {\r
524                         if( pListElement->pTopicFilter[ j + 1 ] != '/' )\r
525                         {\r
526                             IotLogError( "Invalid topic filter %.*s -- '+' must be succeeded by '/'.",\r
527                                          pListElement->topicFilterLength,\r
528                                          pListElement->pTopicFilter );\r
529 \r
530                             IOT_SET_AND_GOTO_CLEANUP( false );\r
531                         }\r
532                         else\r
533                         {\r
534                             EMPTY_ELSE_MARKER;\r
535                         }\r
536                     }\r
537                     else\r
538                     {\r
539                         EMPTY_ELSE_MARKER;\r
540                     }\r
541 \r
542                     break;\r
543 \r
544                 /* Check that the multi-level wildcard '#' is used correctly. */\r
545                 case '#':\r
546 \r
547                     /* '#' must be the last character in the filter. */\r
548                     if( j != pListElement->topicFilterLength - 1 )\r
549                     {\r
550                         IotLogError( "Invalid topic filter %.*s -- '#' must be the last character.",\r
551                                      pListElement->topicFilterLength,\r
552                                      pListElement->pTopicFilter );\r
553 \r
554                         IOT_SET_AND_GOTO_CLEANUP( false );\r
555                     }\r
556                     else\r
557                     {\r
558                         EMPTY_ELSE_MARKER;\r
559                     }\r
560 \r
561                     /* Unless '#' is standalone, it must be preceded by '/'. */\r
562                     if( pListElement->topicFilterLength > 1 )\r
563                     {\r
564                         if( pListElement->pTopicFilter[ j - 1 ] != '/' )\r
565                         {\r
566                             IotLogError( "Invalid topic filter %.*s -- '#' must be preceded by '/'.",\r
567                                          pListElement->topicFilterLength,\r
568                                          pListElement->pTopicFilter );\r
569 \r
570                             IOT_SET_AND_GOTO_CLEANUP( false );\r
571                         }\r
572                         else\r
573                         {\r
574                             EMPTY_ELSE_MARKER;\r
575                         }\r
576                     }\r
577                     else\r
578                     {\r
579                         EMPTY_ELSE_MARKER;\r
580                     }\r
581 \r
582                     break;\r
583 \r
584                 default:\r
585                     break;\r
586             }\r
587         }\r
588     }\r
589 \r
590     IOT_FUNCTION_EXIT_NO_CLEANUP();\r
591 }\r
592 \r
593 /*-----------------------------------------------------------*/\r