mqtt.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. /**
  2. * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  3. * SPDX-License-Identifier: Apache-2.0.
  4. */
  5. #include <aws/mqtt/mqtt.h>
  6. #include <aws/io/logging.h>
  7. #ifdef AWS_MQTT_WITH_WEBSOCKETS
  8. # include <aws/http/http.h>
  9. #endif
  10. /*******************************************************************************
  11. * Topic Validation
  12. ******************************************************************************/
  13. static bool s_is_valid_topic(const struct aws_byte_cursor *topic, bool is_filter) {
  14. /* [MQTT-4.7.3-1] Check existance and length */
  15. if (!topic->ptr || !topic->len) {
  16. return false;
  17. }
  18. /* [MQTT-4.7.3-2] Check for the null character */
  19. if (memchr(topic->ptr, 0, topic->len)) {
  20. return false;
  21. }
  22. /* [MQTT-4.7.3-3] Topic must not be too long */
  23. if (topic->len > 65535) {
  24. return false;
  25. }
  26. bool saw_hash = false;
  27. struct aws_byte_cursor topic_part;
  28. AWS_ZERO_STRUCT(topic_part);
  29. while (aws_byte_cursor_next_split(topic, '/', &topic_part)) {
  30. if (saw_hash) {
  31. /* [MQTT-4.7.1-2] If last part was a '#' and there's still another part, it's an invalid topic */
  32. return false;
  33. }
  34. if (topic_part.len == 0) {
  35. /* 0 length parts are fine */
  36. continue;
  37. }
  38. /* Check single level wildcard */
  39. if (memchr(topic_part.ptr, '+', topic_part.len)) {
  40. if (!is_filter) {
  41. /* [MQTT-4.7.1-3] + only allowed on filters */
  42. return false;
  43. }
  44. if (topic_part.len > 1) {
  45. /* topic part must be 1 character long */
  46. return false;
  47. }
  48. }
  49. /* Check multi level wildcard */
  50. if (memchr(topic_part.ptr, '#', topic_part.len)) {
  51. if (!is_filter) {
  52. /* [MQTT-4.7.1-2] # only allowed on filters */
  53. return false;
  54. }
  55. if (topic_part.len > 1) {
  56. /* topic part must be 1 character long */
  57. return false;
  58. }
  59. saw_hash = true;
  60. }
  61. }
  62. return true;
  63. }
  64. bool aws_mqtt_is_valid_topic(const struct aws_byte_cursor *topic) {
  65. return s_is_valid_topic(topic, false);
  66. }
  67. bool aws_mqtt_is_valid_topic_filter(const struct aws_byte_cursor *topic_filter) {
  68. return s_is_valid_topic(topic_filter, true);
  69. }
  70. /*******************************************************************************
  71. * Library Init
  72. ******************************************************************************/
  73. #define AWS_DEFINE_ERROR_INFO_MQTT(C, ES) AWS_DEFINE_ERROR_INFO(C, ES, "libaws-c-mqtt")
  74. /* clang-format off */
  75. static struct aws_error_info s_errors[] = {
  76. AWS_DEFINE_ERROR_INFO_MQTT(
  77. AWS_ERROR_MQTT_INVALID_RESERVED_BITS,
  78. "Bits marked as reserved in the MQTT spec were incorrectly set."),
  79. AWS_DEFINE_ERROR_INFO_MQTT(
  80. AWS_ERROR_MQTT_BUFFER_TOO_BIG,
  81. "[MQTT-1.5.3] Encoded UTF-8 buffers may be no bigger than 65535 bytes."),
  82. AWS_DEFINE_ERROR_INFO_MQTT(
  83. AWS_ERROR_MQTT_INVALID_REMAINING_LENGTH,
  84. "[MQTT-2.2.3] Encoded remaining length field is malformed."),
  85. AWS_DEFINE_ERROR_INFO_MQTT(
  86. AWS_ERROR_MQTT_UNSUPPORTED_PROTOCOL_NAME,
  87. "[MQTT-3.1.2-1] Protocol name specified is unsupported."),
  88. AWS_DEFINE_ERROR_INFO_MQTT(
  89. AWS_ERROR_MQTT_UNSUPPORTED_PROTOCOL_LEVEL,
  90. "[MQTT-3.1.2-2] Protocol level specified is unsupported."),
  91. AWS_DEFINE_ERROR_INFO_MQTT(
  92. AWS_ERROR_MQTT_INVALID_CREDENTIALS,
  93. "[MQTT-3.1.2-21] Connect packet may not include password when no username is present."),
  94. AWS_DEFINE_ERROR_INFO_MQTT(
  95. AWS_ERROR_MQTT_INVALID_QOS,
  96. "Both bits in a QoS field must not be set."),
  97. AWS_DEFINE_ERROR_INFO_MQTT(
  98. AWS_ERROR_MQTT_INVALID_PACKET_TYPE,
  99. "Packet type in packet fixed header is invalid."),
  100. AWS_DEFINE_ERROR_INFO_MQTT(
  101. AWS_ERROR_MQTT_INVALID_TOPIC,
  102. "Topic or filter is invalid."),
  103. AWS_DEFINE_ERROR_INFO_MQTT(
  104. AWS_ERROR_MQTT_TIMEOUT,
  105. "Time limit between request and response has been exceeded."),
  106. AWS_DEFINE_ERROR_INFO_MQTT(
  107. AWS_ERROR_MQTT_PROTOCOL_ERROR,
  108. "Protocol error occurred."),
  109. AWS_DEFINE_ERROR_INFO_MQTT(
  110. AWS_ERROR_MQTT_NOT_CONNECTED,
  111. "The requested operation is invalid as the connection is not open."),
  112. AWS_DEFINE_ERROR_INFO_MQTT(
  113. AWS_ERROR_MQTT_ALREADY_CONNECTED,
  114. "The requested operation is invalid as the connection is already open."),
  115. AWS_DEFINE_ERROR_INFO_MQTT(
  116. AWS_ERROR_MQTT_BUILT_WITHOUT_WEBSOCKETS,
  117. "Library built without MQTT_WITH_WEBSOCKETS option."),
  118. AWS_DEFINE_ERROR_INFO_MQTT(
  119. AWS_ERROR_MQTT_UNEXPECTED_HANGUP,
  120. "The connection was closed unexpectedly."),
  121. AWS_DEFINE_ERROR_INFO_MQTT(
  122. AWS_ERROR_MQTT_CONNECTION_SHUTDOWN,
  123. "MQTT operation interrupted by connection shutdown."),
  124. AWS_DEFINE_ERROR_INFO_MQTT(
  125. AWS_ERROR_MQTT_CONNECTION_DESTROYED,
  126. "Connection has started destroying process, all uncompleted requests will fail."),
  127. AWS_DEFINE_ERROR_INFO_MQTT(
  128. AWS_ERROR_MQTT_CONNECTION_DISCONNECTING,
  129. "Connection is disconnecting, it's not safe to do this operation until the connection finishes shutdown."),
  130. AWS_DEFINE_ERROR_INFO_MQTT(
  131. AWS_ERROR_MQTT_CANCELLED_FOR_CLEAN_SESSION,
  132. "Old requests from the previous session are cancelled, and offline request will not be accept."),
  133. AWS_DEFINE_ERROR_INFO_MQTT(
  134. AWS_ERROR_MQTT_QUEUE_FULL,
  135. "MQTT request queue is full."),
  136. AWS_DEFINE_ERROR_INFO_MQTT(
  137. AWS_ERROR_MQTT5_CLIENT_OPTIONS_VALIDATION,
  138. "Invalid mqtt5 client options value."),
  139. AWS_DEFINE_ERROR_INFO_MQTT(
  140. AWS_ERROR_MQTT5_CONNECT_OPTIONS_VALIDATION,
  141. "Invalid mqtt5 connect packet options value."),
  142. AWS_DEFINE_ERROR_INFO_MQTT(
  143. AWS_ERROR_MQTT5_DISCONNECT_OPTIONS_VALIDATION,
  144. "Invalid mqtt5 disconnect packet options value."),
  145. AWS_DEFINE_ERROR_INFO_MQTT(
  146. AWS_ERROR_MQTT5_PUBLISH_OPTIONS_VALIDATION,
  147. "Invalid mqtt5 publish packet options value."),
  148. AWS_DEFINE_ERROR_INFO_MQTT(
  149. AWS_ERROR_MQTT5_SUBSCRIBE_OPTIONS_VALIDATION,
  150. "Invalid mqtt5 subscribe packet options value."),
  151. AWS_DEFINE_ERROR_INFO_MQTT(
  152. AWS_ERROR_MQTT5_UNSUBSCRIBE_OPTIONS_VALIDATION,
  153. "Invalid mqtt5 unsubscribe packet options value."),
  154. AWS_DEFINE_ERROR_INFO_MQTT(
  155. AWS_ERROR_MQTT5_USER_PROPERTY_VALIDATION,
  156. "Invalid mqtt5 user property value."),
  157. AWS_DEFINE_ERROR_INFO_MQTT(
  158. AWS_ERROR_MQTT5_PACKET_VALIDATION,
  159. "General mqtt5 packet validation error"),
  160. AWS_DEFINE_ERROR_INFO_MQTT(
  161. AWS_ERROR_MQTT5_ENCODE_FAILURE,
  162. "Error occurred while encoding an outgoing mqtt5 packet"),
  163. AWS_DEFINE_ERROR_INFO_MQTT(
  164. AWS_ERROR_MQTT5_DECODE_PROTOCOL_ERROR,
  165. "Mqtt5 decoder received an invalid packet that broke mqtt5 protocol rules"),
  166. AWS_DEFINE_ERROR_INFO_MQTT(
  167. AWS_ERROR_MQTT5_CONNACK_CONNECTION_REFUSED,
  168. "Remote endpoint rejected the CONNECT attempt by returning an unsuccessful CONNACK"),
  169. AWS_DEFINE_ERROR_INFO_MQTT(
  170. AWS_ERROR_MQTT5_CONNACK_TIMEOUT,
  171. "Remote endpoint did not respond to a CONNECT request before timeout exceeded"),
  172. AWS_DEFINE_ERROR_INFO_MQTT(
  173. AWS_ERROR_MQTT5_PING_RESPONSE_TIMEOUT,
  174. "Remote endpoint did not respond to a PINGREQ before timeout exceeded"),
  175. AWS_DEFINE_ERROR_INFO_MQTT(
  176. AWS_ERROR_MQTT5_USER_REQUESTED_STOP,
  177. "Mqtt5 client connection interrupted by user request."),
  178. AWS_DEFINE_ERROR_INFO_MQTT(
  179. AWS_ERROR_MQTT5_DISCONNECT_RECEIVED,
  180. "Mqtt5 client connection interrupted by server DISCONNECT."),
  181. AWS_DEFINE_ERROR_INFO_MQTT(
  182. AWS_ERROR_MQTT5_CLIENT_TERMINATED,
  183. "Mqtt5 client terminated by user request."),
  184. AWS_DEFINE_ERROR_INFO_MQTT(
  185. AWS_ERROR_MQTT5_OPERATION_FAILED_DUE_TO_OFFLINE_QUEUE_POLICY,
  186. "Mqtt5 operation failed due to a disconnection event in conjunction with the client's offline queue retention policy."),
  187. AWS_DEFINE_ERROR_INFO_MQTT(
  188. AWS_ERROR_MQTT5_ENCODE_SIZE_UNSUPPORTED_PACKET_TYPE,
  189. "Unsupported packet type for encode size calculation"),
  190. AWS_DEFINE_ERROR_INFO_MQTT(
  191. AWS_ERROR_MQTT5_OPERATION_PROCESSING_FAILURE,
  192. "Error while processing mqtt5 operational state"),
  193. AWS_DEFINE_ERROR_INFO_MQTT(
  194. AWS_ERROR_MQTT5_INVALID_INBOUND_TOPIC_ALIAS,
  195. "Incoming publish contained an invalid (too large or unknown) topic alias"),
  196. AWS_DEFINE_ERROR_INFO_MQTT(
  197. AWS_ERROR_MQTT5_INVALID_OUTBOUND_TOPIC_ALIAS,
  198. "Outgoing publish contained an invalid (too large or unknown) topic alias"),
  199. };
  200. /* clang-format on */
  201. #undef AWS_DEFINE_ERROR_INFO_MQTT
  202. static struct aws_error_info_list s_error_list = {
  203. .error_list = s_errors,
  204. .count = AWS_ARRAY_SIZE(s_errors),
  205. };
  206. /* clang-format off */
  207. static struct aws_log_subject_info s_logging_subjects[] = {
  208. DEFINE_LOG_SUBJECT_INFO(AWS_LS_MQTT_GENERAL, "mqtt", "Misc MQTT logging"),
  209. DEFINE_LOG_SUBJECT_INFO(AWS_LS_MQTT_CLIENT, "mqtt-client", "MQTT client and connections"),
  210. DEFINE_LOG_SUBJECT_INFO(AWS_LS_MQTT_TOPIC_TREE, "mqtt-topic-tree", "MQTT subscription tree"),
  211. DEFINE_LOG_SUBJECT_INFO(AWS_LS_MQTT5_GENERAL, "mqtt5-general", "Misc MQTT5 logging"),
  212. DEFINE_LOG_SUBJECT_INFO(AWS_LS_MQTT5_CLIENT, "mqtt5-client", "MQTT5 client and connections"),
  213. DEFINE_LOG_SUBJECT_INFO(AWS_LS_MQTT5_CANARY, "mqtt5-canary", "MQTT5 canary logging"),
  214. };
  215. /* clang-format on */
  216. static struct aws_log_subject_info_list s_logging_subjects_list = {
  217. .subject_list = s_logging_subjects,
  218. .count = AWS_ARRAY_SIZE(s_logging_subjects),
  219. };
  220. static bool s_mqtt_library_initialized = false;
  221. void aws_mqtt_library_init(struct aws_allocator *allocator) {
  222. (void)allocator;
  223. if (!s_mqtt_library_initialized) {
  224. s_mqtt_library_initialized = true;
  225. aws_io_library_init(allocator);
  226. #ifdef AWS_MQTT_WITH_WEBSOCKETS
  227. aws_http_library_init(allocator);
  228. #endif
  229. aws_register_error_info(&s_error_list);
  230. aws_register_log_subject_info_list(&s_logging_subjects_list);
  231. }
  232. }
  233. void aws_mqtt_library_clean_up(void) {
  234. if (s_mqtt_library_initialized) {
  235. s_mqtt_library_initialized = false;
  236. aws_thread_join_all_managed();
  237. aws_unregister_error_info(&s_error_list);
  238. aws_unregister_log_subject_info_list(&s_logging_subjects_list);
  239. #ifdef AWS_MQTT_WITH_WEBSOCKETS
  240. aws_http_library_clean_up();
  241. #endif
  242. aws_io_library_clean_up();
  243. }
  244. }
  245. void aws_mqtt_fatal_assert_library_initialized(void) {
  246. if (!s_mqtt_library_initialized) {
  247. AWS_LOGF_FATAL(
  248. AWS_LS_MQTT_GENERAL,
  249. "aws_mqtt_library_init() must be called before using any functionality in aws-c-mqtt.");
  250. AWS_FATAL_ASSERT(s_mqtt_library_initialized);
  251. }
  252. }