mqtt5_encoder.c 49 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283
  1. /**
  2. * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  3. * SPDX-License-Identifier: Apache-2.0.
  4. */
  5. #include <aws/mqtt/private/v5/mqtt5_encoder.h>
  6. #include <aws/io/stream.h>
  7. #include <aws/mqtt/private/v5/mqtt5_topic_alias.h>
  8. #include <aws/mqtt/private/v5/mqtt5_utils.h>
  9. #include <aws/mqtt/v5/mqtt5_types.h>
  10. #include <inttypes.h>
  11. #define INITIAL_ENCODING_STEP_COUNT 64
  12. #define SUBSCRIBE_PACKET_FIXED_HEADER_RESERVED_BITS 2
  13. #define UNSUBSCRIBE_PACKET_FIXED_HEADER_RESERVED_BITS 2
  14. int aws_mqtt5_encode_variable_length_integer(struct aws_byte_buf *buf, uint32_t value) {
  15. AWS_PRECONDITION(buf);
  16. if (value > AWS_MQTT5_MAXIMUM_VARIABLE_LENGTH_INTEGER) {
  17. return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
  18. }
  19. do {
  20. uint8_t encoded_byte = value % 128;
  21. value /= 128;
  22. if (value) {
  23. encoded_byte |= 128;
  24. }
  25. if (!aws_byte_buf_write_u8(buf, encoded_byte)) {
  26. return aws_raise_error(AWS_ERROR_SHORT_BUFFER);
  27. }
  28. } while (value);
  29. return AWS_OP_SUCCESS;
  30. }
  31. int aws_mqtt5_get_variable_length_encode_size(size_t value, size_t *encode_size) {
  32. if (value > AWS_MQTT5_MAXIMUM_VARIABLE_LENGTH_INTEGER) {
  33. return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
  34. }
  35. if (value < 128) {
  36. *encode_size = 1;
  37. } else if (value < 16384) {
  38. *encode_size = 2;
  39. } else if (value < 2097152) {
  40. *encode_size = 3;
  41. } else {
  42. *encode_size = 4;
  43. }
  44. return AWS_OP_SUCCESS;
  45. }
  46. /* helper functions that add a single type of encoding step to the list of steps in an encoder */
  47. void aws_mqtt5_encoder_push_step_u8(struct aws_mqtt5_encoder *encoder, uint8_t value) {
  48. struct aws_mqtt5_encoding_step step;
  49. AWS_ZERO_STRUCT(step);
  50. step.type = AWS_MQTT5_EST_U8;
  51. step.value.value_u8 = value;
  52. aws_array_list_push_back(&encoder->encoding_steps, &step);
  53. }
  54. void aws_mqtt5_encoder_push_step_u16(struct aws_mqtt5_encoder *encoder, uint16_t value) {
  55. struct aws_mqtt5_encoding_step step;
  56. AWS_ZERO_STRUCT(step);
  57. step.type = AWS_MQTT5_EST_U16;
  58. step.value.value_u16 = value;
  59. aws_array_list_push_back(&encoder->encoding_steps, &step);
  60. }
  61. void aws_mqtt5_encoder_push_step_u32(struct aws_mqtt5_encoder *encoder, uint32_t value) {
  62. struct aws_mqtt5_encoding_step step;
  63. AWS_ZERO_STRUCT(step);
  64. step.type = AWS_MQTT5_EST_U32;
  65. step.value.value_u32 = value;
  66. aws_array_list_push_back(&encoder->encoding_steps, &step);
  67. }
  68. int aws_mqtt5_encoder_push_step_vli(struct aws_mqtt5_encoder *encoder, uint32_t value) {
  69. if (value > AWS_MQTT5_MAXIMUM_VARIABLE_LENGTH_INTEGER) {
  70. return aws_raise_error(AWS_ERROR_MQTT5_ENCODE_FAILURE);
  71. }
  72. struct aws_mqtt5_encoding_step step;
  73. AWS_ZERO_STRUCT(step);
  74. step.type = AWS_MQTT5_EST_VLI;
  75. step.value.value_u32 = value;
  76. aws_array_list_push_back(&encoder->encoding_steps, &step);
  77. return AWS_OP_SUCCESS;
  78. }
  79. void aws_mqtt5_encoder_push_step_cursor(struct aws_mqtt5_encoder *encoder, struct aws_byte_cursor value) {
  80. struct aws_mqtt5_encoding_step step;
  81. AWS_ZERO_STRUCT(step);
  82. step.type = AWS_MQTT5_EST_CURSOR;
  83. step.value.value_cursor = value;
  84. aws_array_list_push_back(&encoder->encoding_steps, &step);
  85. }
  86. /*
  87. * All size calculations are done with size_t. We assume that view validation will catch and fail all packets
  88. * that violate length constraints either from the MQTT5 spec or additional constraints that we impose on packets
  89. * to ensure that the size calculations do not need to perform checked arithmetic. The only place where we need
  90. * to use checked arithmetic is a PUBLISH packet when combining the payload size and "sizeof everything else"
  91. *
  92. * The additional beyond-spec constraints we apply to view validation ensure our results actually fit in 32 bits.
  93. */
  94. size_t aws_mqtt5_compute_user_property_encode_length(
  95. const struct aws_mqtt5_user_property *properties,
  96. size_t user_property_count) {
  97. /*
  98. * for each user property, in addition to the raw name-value bytes, we also have 5 bytes of prefix required:
  99. * 1 byte for the property type
  100. * 2 bytes for the name length
  101. * 2 bytes for the value length
  102. */
  103. size_t length = 5 * user_property_count;
  104. for (size_t i = 0; i < user_property_count; ++i) {
  105. const struct aws_mqtt5_user_property *property = &properties[i];
  106. length += property->name.len;
  107. length += property->value.len;
  108. }
  109. return length;
  110. }
  111. void aws_mqtt5_add_user_property_encoding_steps(
  112. struct aws_mqtt5_encoder *encoder,
  113. const struct aws_mqtt5_user_property *user_properties,
  114. size_t user_property_count) {
  115. for (size_t i = 0; i < user_property_count; ++i) {
  116. const struct aws_mqtt5_user_property *property = &user_properties[i];
  117. /* https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901054 */
  118. ADD_ENCODE_STEP_U8(encoder, AWS_MQTT5_PROPERTY_TYPE_USER_PROPERTY);
  119. ADD_ENCODE_STEP_U16(encoder, (uint16_t)property->name.len);
  120. ADD_ENCODE_STEP_CURSOR(encoder, property->name);
  121. ADD_ENCODE_STEP_U16(encoder, (uint16_t)property->value.len);
  122. ADD_ENCODE_STEP_CURSOR(encoder, property->value);
  123. }
  124. }
  125. static int s_aws_mqtt5_encoder_begin_pingreq(struct aws_mqtt5_encoder *encoder, const void *view) {
  126. (void)view;
  127. AWS_LOGF_DEBUG(
  128. AWS_LS_MQTT5_CLIENT, "id=%p: setting up encode for a PINGREQ packet", (void *)encoder->config.client);
  129. /* A ping is just a fixed header with a 0-valued remaining length which we encode as a 0 u8 rather than a 0 vli */
  130. ADD_ENCODE_STEP_U8(encoder, aws_mqtt5_compute_fixed_header_byte1(AWS_MQTT5_PT_PINGREQ, 0));
  131. ADD_ENCODE_STEP_U8(encoder, 0);
  132. return AWS_OP_SUCCESS;
  133. }
  134. static int s_compute_disconnect_variable_length_fields(
  135. const struct aws_mqtt5_packet_disconnect_view *disconnect_view,
  136. size_t *total_remaining_length,
  137. size_t *property_length) {
  138. size_t local_property_length = aws_mqtt5_compute_user_property_encode_length(
  139. disconnect_view->user_properties, disconnect_view->user_property_count);
  140. ADD_OPTIONAL_U32_PROPERTY_LENGTH(disconnect_view->session_expiry_interval_seconds, local_property_length);
  141. ADD_OPTIONAL_CURSOR_PROPERTY_LENGTH(disconnect_view->server_reference, local_property_length);
  142. ADD_OPTIONAL_CURSOR_PROPERTY_LENGTH(disconnect_view->reason_string, local_property_length);
  143. *property_length = local_property_length;
  144. size_t property_length_encoding_length = 0;
  145. if (aws_mqtt5_get_variable_length_encode_size(local_property_length, &property_length_encoding_length)) {
  146. return AWS_OP_ERR;
  147. }
  148. /* reason code is the only other thing to worry about */
  149. *total_remaining_length = 1 + *property_length + property_length_encoding_length;
  150. return AWS_OP_SUCCESS;
  151. }
  152. static int s_aws_mqtt5_encoder_begin_disconnect(struct aws_mqtt5_encoder *encoder, const void *view) {
  153. const struct aws_mqtt5_packet_disconnect_view *disconnect_view = view;
  154. size_t total_remaining_length = 0;
  155. size_t property_length = 0;
  156. if (s_compute_disconnect_variable_length_fields(disconnect_view, &total_remaining_length, &property_length)) {
  157. int error_code = aws_last_error();
  158. AWS_LOGF_ERROR(
  159. AWS_LS_MQTT5_CLIENT,
  160. "id=%p: failed to compute variable length values for DISCONNECT packet with error "
  161. "%d(%s)",
  162. (void *)encoder->config.client,
  163. error_code,
  164. aws_error_debug_str(error_code));
  165. return AWS_OP_ERR;
  166. }
  167. uint32_t total_remaining_length_u32 = (uint32_t)total_remaining_length;
  168. uint32_t property_length_u32 = (uint32_t)property_length;
  169. AWS_LOGF_DEBUG(
  170. AWS_LS_MQTT5_CLIENT,
  171. "id=%p: setting up encode for a DISCONNECT packet with remaining length %" PRIu32,
  172. (void *)encoder->config.client,
  173. total_remaining_length_u32);
  174. ADD_ENCODE_STEP_U8(encoder, aws_mqtt5_compute_fixed_header_byte1(AWS_MQTT5_PT_DISCONNECT, 0));
  175. ADD_ENCODE_STEP_VLI(encoder, total_remaining_length_u32);
  176. ADD_ENCODE_STEP_U8(encoder, (uint8_t)disconnect_view->reason_code);
  177. ADD_ENCODE_STEP_VLI(encoder, property_length_u32);
  178. if (property_length > 0) {
  179. ADD_ENCODE_STEP_OPTIONAL_U32_PROPERTY(
  180. encoder, AWS_MQTT5_PROPERTY_TYPE_SESSION_EXPIRY_INTERVAL, disconnect_view->session_expiry_interval_seconds);
  181. ADD_ENCODE_STEP_OPTIONAL_CURSOR_PROPERTY(
  182. encoder, AWS_MQTT5_PROPERTY_TYPE_REASON_STRING, disconnect_view->reason_string);
  183. ADD_ENCODE_STEP_OPTIONAL_CURSOR_PROPERTY(
  184. encoder, AWS_MQTT5_PROPERTY_TYPE_SERVER_REFERENCE, disconnect_view->server_reference);
  185. aws_mqtt5_add_user_property_encoding_steps(
  186. encoder, disconnect_view->user_properties, disconnect_view->user_property_count);
  187. }
  188. return AWS_OP_SUCCESS;
  189. }
  190. static int s_compute_connect_variable_length_fields(
  191. const struct aws_mqtt5_packet_connect_view *connect_view,
  192. size_t *total_remaining_length,
  193. size_t *connect_property_length,
  194. size_t *will_property_length) {
  195. size_t connect_property_section_length =
  196. aws_mqtt5_compute_user_property_encode_length(connect_view->user_properties, connect_view->user_property_count);
  197. ADD_OPTIONAL_U32_PROPERTY_LENGTH(connect_view->session_expiry_interval_seconds, connect_property_section_length);
  198. ADD_OPTIONAL_U16_PROPERTY_LENGTH(connect_view->receive_maximum, connect_property_section_length);
  199. ADD_OPTIONAL_U32_PROPERTY_LENGTH(connect_view->maximum_packet_size_bytes, connect_property_section_length);
  200. ADD_OPTIONAL_U16_PROPERTY_LENGTH(connect_view->topic_alias_maximum, connect_property_section_length);
  201. ADD_OPTIONAL_U8_PROPERTY_LENGTH(connect_view->request_response_information, connect_property_section_length);
  202. ADD_OPTIONAL_U8_PROPERTY_LENGTH(connect_view->request_problem_information, connect_property_section_length);
  203. ADD_OPTIONAL_CURSOR_PROPERTY_LENGTH(connect_view->authentication_method, connect_property_section_length);
  204. ADD_OPTIONAL_CURSOR_PROPERTY_LENGTH(connect_view->authentication_data, connect_property_section_length);
  205. *connect_property_length = (uint32_t)connect_property_section_length;
  206. /* variable header length =
  207. * 10 bytes (6 for mqtt string, 1 for protocol version, 1 for flags, 2 for keep alive)
  208. * + # bytes(variable_length_encoding(connect_property_section_length))
  209. * + connect_property_section_length
  210. */
  211. size_t variable_header_length = 0;
  212. if (aws_mqtt5_get_variable_length_encode_size(connect_property_section_length, &variable_header_length)) {
  213. return AWS_OP_ERR;
  214. }
  215. variable_header_length += 10 + connect_property_section_length;
  216. size_t payload_length = 2 + connect_view->client_id.len;
  217. *will_property_length = 0;
  218. if (connect_view->will != NULL) {
  219. const struct aws_mqtt5_packet_publish_view *publish_view = connect_view->will;
  220. *will_property_length = aws_mqtt5_compute_user_property_encode_length(
  221. publish_view->user_properties, publish_view->user_property_count);
  222. ADD_OPTIONAL_U32_PROPERTY_LENGTH(connect_view->will_delay_interval_seconds, *will_property_length);
  223. ADD_OPTIONAL_U8_PROPERTY_LENGTH(publish_view->payload_format, *will_property_length);
  224. ADD_OPTIONAL_U32_PROPERTY_LENGTH(publish_view->message_expiry_interval_seconds, *will_property_length);
  225. ADD_OPTIONAL_CURSOR_PROPERTY_LENGTH(publish_view->content_type, *will_property_length);
  226. ADD_OPTIONAL_CURSOR_PROPERTY_LENGTH(publish_view->response_topic, *will_property_length);
  227. ADD_OPTIONAL_CURSOR_PROPERTY_LENGTH(publish_view->correlation_data, *will_property_length);
  228. size_t will_properties_length_encode_size = 0;
  229. if (aws_mqtt5_get_variable_length_encode_size(
  230. (uint32_t)*will_property_length, &will_properties_length_encode_size)) {
  231. return AWS_OP_ERR;
  232. }
  233. payload_length += *will_property_length;
  234. payload_length += will_properties_length_encode_size;
  235. payload_length += 2 + publish_view->topic.len;
  236. payload_length += 2 + publish_view->payload.len;
  237. }
  238. /* Can't use the optional property macros because these don't have a leading property type byte */
  239. if (connect_view->username != NULL) {
  240. payload_length += connect_view->username->len + 2;
  241. }
  242. if (connect_view->password != NULL) {
  243. payload_length += connect_view->password->len + 2;
  244. }
  245. *total_remaining_length = payload_length + variable_header_length;
  246. return AWS_OP_SUCCESS;
  247. }
  248. static uint8_t s_aws_mqtt5_connect_compute_connect_flags(const struct aws_mqtt5_packet_connect_view *connect_view) {
  249. uint8_t flags = 0;
  250. if (connect_view->clean_start) {
  251. flags |= 1 << 1;
  252. }
  253. const struct aws_mqtt5_packet_publish_view *will = connect_view->will;
  254. if (will != NULL) {
  255. flags |= 1 << 2;
  256. flags |= ((uint8_t)will->qos) << 3;
  257. if (will->retain) {
  258. flags |= 1 << 5;
  259. }
  260. }
  261. if (connect_view->password != NULL) {
  262. flags |= 1 << 6;
  263. }
  264. if (connect_view->username != NULL) {
  265. flags |= 1 << 7;
  266. }
  267. return flags;
  268. }
  269. static int s_aws_mqtt5_encoder_begin_connect(struct aws_mqtt5_encoder *encoder, const void *view) {
  270. const struct aws_mqtt5_packet_connect_view *connect_view = view;
  271. const struct aws_mqtt5_packet_publish_view *will = connect_view->will;
  272. size_t total_remaining_length = 0;
  273. size_t connect_property_length = 0;
  274. size_t will_property_length = 0;
  275. if (s_compute_connect_variable_length_fields(
  276. connect_view, &total_remaining_length, &connect_property_length, &will_property_length)) {
  277. int error_code = aws_last_error();
  278. AWS_LOGF_ERROR(
  279. AWS_LS_MQTT5_CLIENT,
  280. "id=%p: failed to compute variable length values for CONNECT packet with error %d(%s)",
  281. (void *)encoder->config.client,
  282. error_code,
  283. aws_error_debug_str(error_code));
  284. return AWS_OP_ERR;
  285. }
  286. AWS_LOGF_DEBUG(
  287. AWS_LS_MQTT5_CLIENT,
  288. "id=%p: setting up encode for a CONNECT packet with remaining length %zu",
  289. (void *)encoder->config.client,
  290. total_remaining_length);
  291. uint32_t total_remaining_length_u32 = (uint32_t)total_remaining_length;
  292. uint32_t connect_property_length_u32 = (uint32_t)connect_property_length;
  293. uint32_t will_property_length_u32 = (uint32_t)will_property_length;
  294. ADD_ENCODE_STEP_U8(encoder, aws_mqtt5_compute_fixed_header_byte1(AWS_MQTT5_PT_CONNECT, 0));
  295. ADD_ENCODE_STEP_VLI(encoder, total_remaining_length_u32);
  296. ADD_ENCODE_STEP_CURSOR(encoder, g_aws_mqtt5_connect_protocol_cursor);
  297. ADD_ENCODE_STEP_U8(encoder, s_aws_mqtt5_connect_compute_connect_flags(connect_view));
  298. ADD_ENCODE_STEP_U16(encoder, connect_view->keep_alive_interval_seconds);
  299. ADD_ENCODE_STEP_VLI(encoder, connect_property_length_u32);
  300. ADD_ENCODE_STEP_OPTIONAL_U32_PROPERTY(
  301. encoder, AWS_MQTT5_PROPERTY_TYPE_SESSION_EXPIRY_INTERVAL, connect_view->session_expiry_interval_seconds);
  302. ADD_ENCODE_STEP_OPTIONAL_U16_PROPERTY(
  303. encoder, AWS_MQTT5_PROPERTY_TYPE_RECEIVE_MAXIMUM, connect_view->receive_maximum);
  304. ADD_ENCODE_STEP_OPTIONAL_U32_PROPERTY(
  305. encoder, AWS_MQTT5_PROPERTY_TYPE_MAXIMUM_PACKET_SIZE, connect_view->maximum_packet_size_bytes);
  306. ADD_ENCODE_STEP_OPTIONAL_U16_PROPERTY(
  307. encoder, AWS_MQTT5_PROPERTY_TYPE_TOPIC_ALIAS_MAXIMUM, connect_view->topic_alias_maximum);
  308. ADD_ENCODE_STEP_OPTIONAL_U8_PROPERTY(
  309. encoder, AWS_MQTT5_PROPERTY_TYPE_REQUEST_RESPONSE_INFORMATION, connect_view->request_response_information);
  310. ADD_ENCODE_STEP_OPTIONAL_U8_PROPERTY(
  311. encoder, AWS_MQTT5_PROPERTY_TYPE_REQUEST_PROBLEM_INFORMATION, connect_view->request_problem_information);
  312. ADD_ENCODE_STEP_OPTIONAL_CURSOR_PROPERTY(
  313. encoder, AWS_MQTT5_PROPERTY_TYPE_AUTHENTICATION_METHOD, connect_view->authentication_method);
  314. ADD_ENCODE_STEP_OPTIONAL_CURSOR_PROPERTY(
  315. encoder, AWS_MQTT5_PROPERTY_TYPE_AUTHENTICATION_DATA, connect_view->authentication_data);
  316. aws_mqtt5_add_user_property_encoding_steps(
  317. encoder, connect_view->user_properties, connect_view->user_property_count);
  318. ADD_ENCODE_STEP_LENGTH_PREFIXED_CURSOR(encoder, connect_view->client_id);
  319. if (will != NULL) {
  320. ADD_ENCODE_STEP_VLI(encoder, will_property_length_u32);
  321. ADD_ENCODE_STEP_OPTIONAL_U32_PROPERTY(
  322. encoder, AWS_MQTT5_PROPERTY_TYPE_WILL_DELAY_INTERVAL, connect_view->will_delay_interval_seconds);
  323. ADD_ENCODE_STEP_OPTIONAL_U8_PROPERTY(
  324. encoder, AWS_MQTT5_PROPERTY_TYPE_PAYLOAD_FORMAT_INDICATOR, will->payload_format);
  325. ADD_ENCODE_STEP_OPTIONAL_U32_PROPERTY(
  326. encoder, AWS_MQTT5_PROPERTY_TYPE_MESSAGE_EXPIRY_INTERVAL, will->message_expiry_interval_seconds);
  327. ADD_ENCODE_STEP_OPTIONAL_CURSOR_PROPERTY(encoder, AWS_MQTT5_PROPERTY_TYPE_CONTENT_TYPE, will->content_type);
  328. ADD_ENCODE_STEP_OPTIONAL_CURSOR_PROPERTY(encoder, AWS_MQTT5_PROPERTY_TYPE_RESPONSE_TOPIC, will->response_topic);
  329. ADD_ENCODE_STEP_OPTIONAL_CURSOR_PROPERTY(
  330. encoder, AWS_MQTT5_PROPERTY_TYPE_CORRELATION_DATA, will->correlation_data);
  331. aws_mqtt5_add_user_property_encoding_steps(encoder, will->user_properties, will->user_property_count);
  332. ADD_ENCODE_STEP_LENGTH_PREFIXED_CURSOR(encoder, will->topic);
  333. ADD_ENCODE_STEP_U16(encoder, (uint16_t)will->payload.len);
  334. ADD_ENCODE_STEP_CURSOR(encoder, will->payload);
  335. }
  336. ADD_ENCODE_STEP_OPTIONAL_LENGTH_PREFIXED_CURSOR(encoder, connect_view->username);
  337. ADD_ENCODE_STEP_OPTIONAL_LENGTH_PREFIXED_CURSOR(encoder, connect_view->password);
  338. return AWS_OP_SUCCESS;
  339. }
  340. static uint8_t s_aws_mqtt5_subscribe_compute_subscription_flags(
  341. const struct aws_mqtt5_subscription_view *subscription_view) {
  342. uint8_t flags = (uint8_t)subscription_view->qos;
  343. if (subscription_view->no_local) {
  344. flags |= 1 << 2;
  345. }
  346. if (subscription_view->retain_as_published) {
  347. flags |= 1 << 3;
  348. }
  349. flags |= ((uint8_t)subscription_view->retain_handling_type) << 4;
  350. return flags;
  351. }
  352. static void aws_mqtt5_add_subscribe_topic_filter_encoding_steps(
  353. struct aws_mqtt5_encoder *encoder,
  354. const struct aws_mqtt5_subscription_view *subscriptions,
  355. size_t subscription_count) {
  356. /* https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901169 */
  357. for (size_t i = 0; i < subscription_count; ++i) {
  358. const struct aws_mqtt5_subscription_view *subscription = &subscriptions[i];
  359. ADD_ENCODE_STEP_LENGTH_PREFIXED_CURSOR(encoder, subscription->topic_filter);
  360. ADD_ENCODE_STEP_U8(encoder, s_aws_mqtt5_subscribe_compute_subscription_flags(subscription));
  361. }
  362. }
  363. static void aws_mqtt5_add_unsubscribe_topic_filter_encoding_steps(
  364. struct aws_mqtt5_encoder *encoder,
  365. const struct aws_byte_cursor *topics,
  366. size_t unsubscription_count) {
  367. /* https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901185 */
  368. for (size_t i = 0; i < unsubscription_count; ++i) {
  369. const struct aws_byte_cursor topic_filter = topics[i];
  370. ADD_ENCODE_STEP_LENGTH_PREFIXED_CURSOR(encoder, topic_filter);
  371. }
  372. }
  373. static int s_compute_subscribe_variable_length_fields(
  374. const struct aws_mqtt5_packet_subscribe_view *subscribe_view,
  375. size_t *total_remaining_length,
  376. size_t *subscribe_properties_length) {
  377. size_t subscribe_variable_header_property_length = aws_mqtt5_compute_user_property_encode_length(
  378. subscribe_view->user_properties, subscribe_view->user_property_count);
  379. /*
  380. * Add the length of 1 byte for the identifier of a Subscription Identifier property
  381. * and the VLI of the subscription_identifier itself
  382. */
  383. if (subscribe_view->subscription_identifier != 0) {
  384. size_t subscription_identifier_length = 0;
  385. aws_mqtt5_get_variable_length_encode_size(
  386. *subscribe_view->subscription_identifier, &subscription_identifier_length);
  387. subscribe_variable_header_property_length += subscription_identifier_length + 1;
  388. }
  389. *subscribe_properties_length = subscribe_variable_header_property_length;
  390. /* variable header total length =
  391. * 2 bytes for Packet Identifier
  392. * + # bytes (variable_length_encoding(subscribe_variable_header_property_length))
  393. * + subscribe_variable_header_property_length
  394. */
  395. size_t variable_header_length = 0;
  396. if (aws_mqtt5_get_variable_length_encode_size(subscribe_variable_header_property_length, &variable_header_length)) {
  397. return AWS_OP_ERR;
  398. }
  399. variable_header_length += 2 + subscribe_variable_header_property_length;
  400. size_t payload_length = 0;
  401. /*
  402. * for each subscription view, in addition to the raw name-value bytes, we also have 2 bytes of
  403. * prefix and one byte suffix required.
  404. * 2 bytes for the Topic Filter length
  405. * 1 byte for the Subscription Options Flags
  406. */
  407. for (size_t i = 0; i < subscribe_view->subscription_count; ++i) {
  408. const struct aws_mqtt5_subscription_view *subscription = &subscribe_view->subscriptions[i];
  409. payload_length += subscription->topic_filter.len;
  410. }
  411. payload_length += (3 * subscribe_view->subscription_count);
  412. *total_remaining_length = variable_header_length + payload_length;
  413. return AWS_OP_SUCCESS;
  414. }
  415. static int s_aws_mqtt5_encoder_begin_subscribe(struct aws_mqtt5_encoder *encoder, const void *view) {
  416. const struct aws_mqtt5_packet_subscribe_view *subscription_view = view;
  417. size_t total_remaining_length = 0;
  418. size_t subscribe_properties_length = 0;
  419. if (s_compute_subscribe_variable_length_fields(
  420. subscription_view, &total_remaining_length, &subscribe_properties_length)) {
  421. int error_code = aws_last_error();
  422. AWS_LOGF_ERROR(
  423. AWS_LS_MQTT5_GENERAL,
  424. "(%p) mqtt5 client encoder - failed to compute variable length values for SUBSCRIBE packet with error "
  425. "%d(%s)",
  426. (void *)encoder->config.client,
  427. error_code,
  428. aws_error_debug_str(error_code));
  429. return AWS_OP_ERR;
  430. }
  431. AWS_LOGF_DEBUG(
  432. AWS_LS_MQTT5_GENERAL,
  433. "(%p) mqtt5 client encoder - setting up encode for a SUBSCRIBE packet with remaining length %zu",
  434. (void *)encoder->config.client,
  435. total_remaining_length);
  436. uint32_t total_remaining_length_u32 = (uint32_t)total_remaining_length;
  437. uint32_t subscribe_property_length_u32 = (uint32_t)subscribe_properties_length;
  438. /*
  439. * Fixed Header
  440. * byte 1:
  441. * bits 7-4 MQTT Control Packet Type
  442. * bits 3-0 Reserved, must be set to 0, 0, 1, 0
  443. * byte 2-x: Remaining Length as Variable Byte Integer (1-4 bytes)
  444. */
  445. ADD_ENCODE_STEP_U8(
  446. encoder,
  447. aws_mqtt5_compute_fixed_header_byte1(AWS_MQTT5_PT_SUBSCRIBE, SUBSCRIBE_PACKET_FIXED_HEADER_RESERVED_BITS));
  448. ADD_ENCODE_STEP_VLI(encoder, total_remaining_length_u32);
  449. /*
  450. * Variable Header
  451. * byte 1-2: Packet Identifier
  452. * byte 3-x: Property Length as Variable Byte Integer (1-4 bytes)
  453. */
  454. ADD_ENCODE_STEP_U16(encoder, (uint16_t)subscription_view->packet_id);
  455. ADD_ENCODE_STEP_VLI(encoder, subscribe_property_length_u32);
  456. /*
  457. * Subscribe Properties
  458. * (optional) Subscription Identifier
  459. * (optional) User Properties
  460. */
  461. if (subscription_view->subscription_identifier != 0) {
  462. ADD_ENCODE_STEP_U8(encoder, AWS_MQTT5_PROPERTY_TYPE_SUBSCRIPTION_IDENTIFIER);
  463. ADD_ENCODE_STEP_VLI(encoder, *subscription_view->subscription_identifier);
  464. }
  465. aws_mqtt5_add_user_property_encoding_steps(
  466. encoder, subscription_view->user_properties, subscription_view->user_property_count);
  467. /*
  468. * Payload
  469. * n Topic Filters
  470. * byte 1-2: Length
  471. * byte 3..N: UTF-8 encoded Topic Filter
  472. * byte N+1:
  473. * bits 7-6 Reserved
  474. * bits 5-4 Retain Handling
  475. * bit 3 Retain as Published
  476. * bit 2 No Local
  477. * bits 1-0 Maximum QoS
  478. */
  479. aws_mqtt5_add_subscribe_topic_filter_encoding_steps(
  480. encoder, subscription_view->subscriptions, subscription_view->subscription_count);
  481. return AWS_OP_SUCCESS;
  482. }
  483. static int s_compute_unsubscribe_variable_length_fields(
  484. const struct aws_mqtt5_packet_unsubscribe_view *unsubscribe_view,
  485. size_t *total_remaining_length,
  486. size_t *unsubscribe_properties_length) {
  487. size_t unsubscribe_variable_header_property_length = aws_mqtt5_compute_user_property_encode_length(
  488. unsubscribe_view->user_properties, unsubscribe_view->user_property_count);
  489. *unsubscribe_properties_length = unsubscribe_variable_header_property_length;
  490. /* variable header total length =
  491. * 2 bytes for Packet Identifier
  492. * + # bytes (variable_length_encoding(subscribe_variable_header_property_length))
  493. * + unsubscribe_variable_header_property_length
  494. */
  495. size_t variable_header_length = 0;
  496. if (aws_mqtt5_get_variable_length_encode_size(
  497. unsubscribe_variable_header_property_length, &variable_header_length)) {
  498. return AWS_OP_ERR;
  499. }
  500. variable_header_length += 2 + unsubscribe_variable_header_property_length;
  501. size_t payload_length = 0;
  502. /*
  503. * for each unsubscribe topic filter
  504. * 2 bytes for the Topic Filter length
  505. * n bytes for Topic Filter
  506. */
  507. for (size_t i = 0; i < unsubscribe_view->topic_filter_count; ++i) {
  508. const struct aws_byte_cursor topic_filter = unsubscribe_view->topic_filters[i];
  509. payload_length += topic_filter.len;
  510. }
  511. payload_length += (2 * unsubscribe_view->topic_filter_count);
  512. *total_remaining_length = variable_header_length + payload_length;
  513. return AWS_OP_SUCCESS;
  514. }
  515. static int s_aws_mqtt5_encoder_begin_unsubscribe(struct aws_mqtt5_encoder *encoder, const void *view) {
  516. const struct aws_mqtt5_packet_unsubscribe_view *unsubscribe_view = view;
  517. size_t total_remaining_length = 0;
  518. size_t unsubscribe_properties_length = 0;
  519. if (s_compute_unsubscribe_variable_length_fields(
  520. unsubscribe_view, &total_remaining_length, &unsubscribe_properties_length)) {
  521. int error_code = aws_last_error();
  522. AWS_LOGF_ERROR(
  523. AWS_LS_MQTT5_GENERAL,
  524. "(%p) mqtt5 client encoder - failed to compute variable length values for UNSUBSCRIBE packet with error "
  525. "%d(%s)",
  526. (void *)encoder->config.client,
  527. error_code,
  528. aws_error_debug_str(error_code));
  529. return AWS_OP_ERR;
  530. }
  531. AWS_LOGF_DEBUG(
  532. AWS_LS_MQTT5_GENERAL,
  533. "(%p) mqtt5 client encoder - setting up encode for a UNSUBSCRIBE packet with remaining length %zu",
  534. (void *)encoder->config.client,
  535. total_remaining_length);
  536. uint32_t total_remaining_length_u32 = (uint32_t)total_remaining_length;
  537. uint32_t unsubscribe_property_length_u32 = (uint32_t)unsubscribe_properties_length;
  538. /*
  539. * Fixed Header
  540. * byte 1:
  541. * bits 7-4 MQTT Control Packet type (10)
  542. * bits 3-0 Reserved, must be set to 0, 0, 1, 0
  543. * byte 2-x: Remaining Length as Variable Byte Integer (1-4 bytes)
  544. */
  545. ADD_ENCODE_STEP_U8(
  546. encoder,
  547. aws_mqtt5_compute_fixed_header_byte1(AWS_MQTT5_PT_UNSUBSCRIBE, UNSUBSCRIBE_PACKET_FIXED_HEADER_RESERVED_BITS));
  548. ADD_ENCODE_STEP_VLI(encoder, total_remaining_length_u32);
  549. /*
  550. * Variable Header
  551. * byte 1-2: Packet Identifier
  552. * byte 3-x: Properties length as Variable Byte Integer (1-4 bytes)
  553. */
  554. ADD_ENCODE_STEP_U16(encoder, (uint16_t)unsubscribe_view->packet_id);
  555. ADD_ENCODE_STEP_VLI(encoder, unsubscribe_property_length_u32);
  556. /*
  557. * (optional) User Properties
  558. */
  559. aws_mqtt5_add_user_property_encoding_steps(
  560. encoder, unsubscribe_view->user_properties, unsubscribe_view->user_property_count);
  561. /*
  562. * Payload
  563. * n Topic Filters
  564. * byte 1-2: Length
  565. * byte 3..N: UTF-8 encoded Topic Filter
  566. */
  567. aws_mqtt5_add_unsubscribe_topic_filter_encoding_steps(
  568. encoder, unsubscribe_view->topic_filters, unsubscribe_view->topic_filter_count);
  569. return AWS_OP_SUCCESS;
  570. }
  571. static int s_compute_publish_variable_length_fields(
  572. const struct aws_mqtt5_packet_publish_view *publish_view,
  573. size_t *total_remaining_length,
  574. size_t *publish_properties_length) {
  575. size_t publish_property_section_length =
  576. aws_mqtt5_compute_user_property_encode_length(publish_view->user_properties, publish_view->user_property_count);
  577. ADD_OPTIONAL_U8_PROPERTY_LENGTH(publish_view->payload_format, publish_property_section_length);
  578. ADD_OPTIONAL_U32_PROPERTY_LENGTH(publish_view->message_expiry_interval_seconds, publish_property_section_length);
  579. ADD_OPTIONAL_U16_PROPERTY_LENGTH(publish_view->topic_alias, publish_property_section_length);
  580. ADD_OPTIONAL_CURSOR_PROPERTY_LENGTH(publish_view->response_topic, publish_property_section_length);
  581. ADD_OPTIONAL_CURSOR_PROPERTY_LENGTH(publish_view->correlation_data, publish_property_section_length);
  582. ADD_OPTIONAL_CURSOR_PROPERTY_LENGTH(publish_view->content_type, publish_property_section_length);
  583. for (size_t i = 0; i < publish_view->subscription_identifier_count; ++i) {
  584. size_t encoding_size = 0;
  585. if (aws_mqtt5_get_variable_length_encode_size(publish_view->subscription_identifiers[i], &encoding_size)) {
  586. return AWS_OP_ERR;
  587. }
  588. publish_property_section_length += 1 + encoding_size;
  589. }
  590. *publish_properties_length = (uint32_t)publish_property_section_length;
  591. /*
  592. * Remaining Length:
  593. * Variable Header
  594. * - Topic Name
  595. * - Packet Identifier
  596. * - Property Length as VLI x
  597. * - All Properties x
  598. * Payload
  599. */
  600. size_t remaining_length = 0;
  601. /* Property Length VLI size */
  602. if (aws_mqtt5_get_variable_length_encode_size(publish_property_section_length, &remaining_length)) {
  603. return AWS_OP_ERR;
  604. }
  605. /* Topic name */
  606. remaining_length += 2 + publish_view->topic.len;
  607. /* Optional packet id */
  608. if (publish_view->packet_id != 0) {
  609. remaining_length += 2;
  610. }
  611. /* Properties */
  612. remaining_length += publish_property_section_length;
  613. /* Payload */
  614. remaining_length += publish_view->payload.len;
  615. *total_remaining_length = remaining_length;
  616. return AWS_OP_SUCCESS;
  617. }
  618. static int s_aws_mqtt5_encoder_begin_publish(struct aws_mqtt5_encoder *encoder, const void *view) {
  619. /* We do a shallow copy of the stored view in order to temporarily side affect it for topic aliasing */
  620. struct aws_mqtt5_packet_publish_view local_publish_view = *((const struct aws_mqtt5_packet_publish_view *)view);
  621. uint16_t outbound_topic_alias = 0;
  622. struct aws_byte_cursor outbound_topic;
  623. if (encoder->topic_alias_resolver != NULL) {
  624. AWS_ZERO_STRUCT(outbound_topic);
  625. if (aws_mqtt5_outbound_topic_alias_resolver_resolve_outbound_publish(
  626. encoder->topic_alias_resolver, &local_publish_view, &outbound_topic_alias, &outbound_topic)) {
  627. int error_code = aws_last_error();
  628. AWS_LOGF_ERROR(
  629. AWS_LS_MQTT5_GENERAL,
  630. "(%p) mqtt5 client encoder - failed to perform outbound topic alias resolution on PUBLISH packet with "
  631. "error "
  632. "%d(%s)",
  633. (void *)encoder->config.client,
  634. error_code,
  635. aws_error_debug_str(error_code));
  636. return AWS_OP_ERR;
  637. }
  638. local_publish_view.topic = outbound_topic;
  639. if (outbound_topic_alias != 0) {
  640. local_publish_view.topic_alias = &outbound_topic_alias;
  641. }
  642. }
  643. /*
  644. * We're going to encode the local mutated view copy, not the stored view. This lets the original packet stay
  645. * unchanged for the entire time it is owned by the client. Otherwise, events that disrupt the alias cache
  646. * (like disconnections) would make correct aliasing impossible (because we'd have mutated and potentially lost
  647. * topic information).
  648. */
  649. const struct aws_mqtt5_packet_publish_view *publish_view = &local_publish_view;
  650. size_t total_remaining_length = 0;
  651. size_t publish_properties_length = 0;
  652. if (s_compute_publish_variable_length_fields(publish_view, &total_remaining_length, &publish_properties_length)) {
  653. int error_code = aws_last_error();
  654. AWS_LOGF_ERROR(
  655. AWS_LS_MQTT5_GENERAL,
  656. "(%p) mqtt5 client encoder - failed to compute variable length values for PUBLISH packet with error "
  657. "%d(%s)",
  658. (void *)encoder->config.client,
  659. error_code,
  660. aws_error_debug_str(error_code));
  661. return AWS_OP_ERR;
  662. }
  663. AWS_LOGF_DEBUG(
  664. AWS_LS_MQTT5_GENERAL,
  665. "(%p) mqtt5 client encoder - setting up encode for a PUBLISH packet with remaining length %zu",
  666. (void *)encoder->config.client,
  667. total_remaining_length);
  668. uint32_t total_remaining_length_u32 = (uint32_t)total_remaining_length;
  669. uint32_t publish_property_length_u32 = (uint32_t)publish_properties_length;
  670. /*
  671. * Fixed Header
  672. * byte 1:
  673. * bits 4-7: MQTT Control Packet Type
  674. * bit 3: DUP flag
  675. * bit 1-2: QoS level
  676. * bit 0: RETAIN
  677. * byte 2-x: Remaining Length as Variable Byte Integer (1-4 bytes)
  678. */
  679. uint8_t flags = 0;
  680. if (publish_view->duplicate) {
  681. flags |= 1 << 3;
  682. }
  683. flags |= ((uint8_t)publish_view->qos) << 1;
  684. if (publish_view->retain) {
  685. flags |= 1;
  686. }
  687. ADD_ENCODE_STEP_U8(encoder, aws_mqtt5_compute_fixed_header_byte1(AWS_MQTT5_PT_PUBLISH, flags));
  688. ADD_ENCODE_STEP_VLI(encoder, total_remaining_length_u32);
  689. /*
  690. * Variable Header
  691. * UTF-8 Encoded Topic Name
  692. * 2 byte Packet Identifier
  693. * 1-4 byte Property Length as Variable Byte Integer
  694. * n bytes Properties
  695. */
  696. ADD_ENCODE_STEP_LENGTH_PREFIXED_CURSOR(encoder, publish_view->topic);
  697. if (publish_view->qos != AWS_MQTT5_QOS_AT_MOST_ONCE) {
  698. ADD_ENCODE_STEP_U16(encoder, (uint16_t)publish_view->packet_id);
  699. }
  700. ADD_ENCODE_STEP_VLI(encoder, publish_property_length_u32);
  701. ADD_ENCODE_STEP_OPTIONAL_U8_PROPERTY(
  702. encoder, AWS_MQTT5_PROPERTY_TYPE_PAYLOAD_FORMAT_INDICATOR, publish_view->payload_format);
  703. ADD_ENCODE_STEP_OPTIONAL_U32_PROPERTY(
  704. encoder, AWS_MQTT5_PROPERTY_TYPE_MESSAGE_EXPIRY_INTERVAL, publish_view->message_expiry_interval_seconds);
  705. ADD_ENCODE_STEP_OPTIONAL_U16_PROPERTY(encoder, AWS_MQTT5_PROPERTY_TYPE_TOPIC_ALIAS, publish_view->topic_alias);
  706. ADD_ENCODE_STEP_OPTIONAL_CURSOR_PROPERTY(
  707. encoder, AWS_MQTT5_PROPERTY_TYPE_RESPONSE_TOPIC, publish_view->response_topic);
  708. ADD_ENCODE_STEP_OPTIONAL_CURSOR_PROPERTY(
  709. encoder, AWS_MQTT5_PROPERTY_TYPE_CORRELATION_DATA, publish_view->correlation_data);
  710. for (size_t i = 0; i < publish_view->subscription_identifier_count; ++i) {
  711. ADD_ENCODE_STEP_OPTIONAL_VLI_PROPERTY(
  712. encoder, AWS_MQTT5_PROPERTY_TYPE_SUBSCRIPTION_IDENTIFIER, &publish_view->subscription_identifiers[i]);
  713. }
  714. ADD_ENCODE_STEP_OPTIONAL_CURSOR_PROPERTY(encoder, AWS_MQTT5_PROPERTY_TYPE_CONTENT_TYPE, publish_view->content_type);
  715. aws_mqtt5_add_user_property_encoding_steps(
  716. encoder, publish_view->user_properties, publish_view->user_property_count);
  717. /*
  718. * Payload
  719. * Content and format of data is application specific
  720. */
  721. if (publish_view->payload.len > 0) {
  722. ADD_ENCODE_STEP_CURSOR(encoder, publish_view->payload);
  723. }
  724. return AWS_OP_SUCCESS;
  725. }
  726. static int s_compute_puback_variable_length_fields(
  727. const struct aws_mqtt5_packet_puback_view *puback_view,
  728. size_t *total_remaining_length,
  729. size_t *puback_properties_length) {
  730. size_t local_property_length =
  731. aws_mqtt5_compute_user_property_encode_length(puback_view->user_properties, puback_view->user_property_count);
  732. ADD_OPTIONAL_CURSOR_PROPERTY_LENGTH(puback_view->reason_string, local_property_length);
  733. *puback_properties_length = (uint32_t)local_property_length;
  734. /* variable header total length =
  735. * 2 bytes for Packet Identifier
  736. * + 1 byte for PUBACK reason code if it exists
  737. * + subscribe_variable_header_property_length
  738. *
  739. * https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901124
  740. * If there are no properties and Reason Code is success, PUBACK ends with the packet id
  741. *
  742. * https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901124
  743. * If there are no properties and Reason Code is not success, PUBACK ends with the reason code
  744. */
  745. if (local_property_length == 0) {
  746. if (puback_view->reason_code == AWS_MQTT5_PARC_SUCCESS) {
  747. *total_remaining_length = 2;
  748. } else {
  749. *total_remaining_length = 3;
  750. }
  751. return AWS_OP_SUCCESS;
  752. }
  753. size_t variable_property_length_size = 0;
  754. if (aws_mqtt5_get_variable_length_encode_size(local_property_length, &variable_property_length_size)) {
  755. return AWS_OP_ERR;
  756. }
  757. /* vli of property length + packet id + reason code + properties length */
  758. *total_remaining_length = variable_property_length_size + 3 + local_property_length;
  759. return AWS_OP_SUCCESS;
  760. }
  761. static int s_aws_mqtt5_encoder_begin_puback(struct aws_mqtt5_encoder *encoder, const void *view) {
  762. const struct aws_mqtt5_packet_puback_view *puback_view = view;
  763. size_t total_remaining_length = 0;
  764. size_t puback_properties_length = 0;
  765. if (s_compute_puback_variable_length_fields(puback_view, &total_remaining_length, &puback_properties_length)) {
  766. int error_code = aws_last_error();
  767. AWS_LOGF_ERROR(
  768. AWS_LS_MQTT5_GENERAL,
  769. "(%p) mqtt5 client encoder - failed to compute variable length values for PUBACK packet with error "
  770. "%d(%s)",
  771. (void *)encoder->config.client,
  772. error_code,
  773. aws_error_debug_str(error_code));
  774. return AWS_OP_ERR;
  775. }
  776. AWS_LOGF_DEBUG(
  777. AWS_LS_MQTT5_GENERAL,
  778. "(%p) mqtt5 client encoder - setting up encode for a PUBACK packet with remaining length %zu",
  779. (void *)encoder->config.client,
  780. total_remaining_length);
  781. uint32_t total_remaining_length_u32 = (uint32_t)total_remaining_length;
  782. uint32_t puback_property_length_u32 = (uint32_t)puback_properties_length;
  783. /*
  784. * Fixed Header
  785. * byte 1:
  786. * bits 7-4 MQTT Control Packet Type
  787. * bits 3-0 Reserved, bust be set to 0, 0, 0, 0
  788. * byte 2-x: Remaining Length as a Variable Byte Integer (1-4 bytes)
  789. */
  790. ADD_ENCODE_STEP_U8(encoder, aws_mqtt5_compute_fixed_header_byte1(AWS_MQTT5_PT_PUBACK, 0));
  791. ADD_ENCODE_STEP_VLI(encoder, total_remaining_length_u32);
  792. /*
  793. * Variable Header
  794. * byte 1-2: Packet Identifier
  795. * byte 3: PUBACK Reason Code
  796. * byte 4-x: Property Length
  797. * Properties
  798. */
  799. ADD_ENCODE_STEP_U16(encoder, (uint16_t)puback_view->packet_id);
  800. /*
  801. * https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901124
  802. * If Reason Code is success and there are no properties, PUBACK ends with the packet id
  803. */
  804. if (total_remaining_length == 2) {
  805. return AWS_OP_SUCCESS;
  806. }
  807. ADD_ENCODE_STEP_U8(encoder, puback_view->reason_code);
  808. /*
  809. * https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901126
  810. * If remaining length < 4 there is no property length
  811. */
  812. if (total_remaining_length < 4) {
  813. return AWS_OP_SUCCESS;
  814. }
  815. ADD_ENCODE_STEP_VLI(encoder, puback_property_length_u32);
  816. ADD_ENCODE_STEP_OPTIONAL_CURSOR_PROPERTY(
  817. encoder, AWS_MQTT5_PROPERTY_TYPE_REASON_STRING, puback_view->reason_string);
  818. aws_mqtt5_add_user_property_encoding_steps(encoder, puback_view->user_properties, puback_view->user_property_count);
  819. return AWS_OP_SUCCESS;
  820. }
  821. static enum aws_mqtt5_encoding_result s_execute_encode_step(
  822. struct aws_mqtt5_encoder *encoder,
  823. struct aws_mqtt5_encoding_step *step,
  824. struct aws_byte_buf *buffer) {
  825. size_t buffer_room = buffer->capacity - buffer->len;
  826. switch (step->type) {
  827. case AWS_MQTT5_EST_U8:
  828. if (buffer_room < 1) {
  829. return AWS_MQTT5_ER_OUT_OF_ROOM;
  830. }
  831. aws_byte_buf_write_u8(buffer, step->value.value_u8);
  832. return AWS_MQTT5_ER_FINISHED;
  833. case AWS_MQTT5_EST_U16:
  834. if (buffer_room < 2) {
  835. return AWS_MQTT5_ER_OUT_OF_ROOM;
  836. }
  837. aws_byte_buf_write_be16(buffer, step->value.value_u16);
  838. return AWS_MQTT5_ER_FINISHED;
  839. case AWS_MQTT5_EST_U32:
  840. if (buffer_room < 4) {
  841. return AWS_MQTT5_ER_OUT_OF_ROOM;
  842. }
  843. aws_byte_buf_write_be32(buffer, step->value.value_u32);
  844. return AWS_MQTT5_ER_FINISHED;
  845. case AWS_MQTT5_EST_VLI:
  846. /* being lazy here and just assuming the worst case */
  847. if (buffer_room < 4) {
  848. return AWS_MQTT5_ER_OUT_OF_ROOM;
  849. }
  850. /* This can't fail. We've already validated the vli value when we made the step */
  851. aws_mqtt5_encode_variable_length_integer(buffer, step->value.value_u32);
  852. return AWS_MQTT5_ER_FINISHED;
  853. case AWS_MQTT5_EST_CURSOR:
  854. if (buffer_room < 1) {
  855. return AWS_MQTT5_ER_OUT_OF_ROOM;
  856. }
  857. aws_byte_buf_write_to_capacity(buffer, &step->value.value_cursor);
  858. return (step->value.value_cursor.len == 0) ? AWS_MQTT5_ER_FINISHED : AWS_MQTT5_ER_OUT_OF_ROOM;
  859. case AWS_MQTT5_EST_STREAM:
  860. while (buffer->len < buffer->capacity) {
  861. if (aws_input_stream_read(step->value.value_stream, buffer)) {
  862. int error_code = aws_last_error();
  863. AWS_LOGF_ERROR(
  864. AWS_LS_MQTT5_CLIENT,
  865. "id=%p: failed to read from stream with error %d(%s)",
  866. (void *)encoder->config.client,
  867. error_code,
  868. aws_error_debug_str(error_code));
  869. return AWS_MQTT5_ER_ERROR;
  870. }
  871. struct aws_stream_status status;
  872. if (aws_input_stream_get_status(step->value.value_stream, &status)) {
  873. int error_code = aws_last_error();
  874. AWS_LOGF_ERROR(
  875. AWS_LS_MQTT5_CLIENT,
  876. "id=%p: failed to query stream status with error %d(%s)",
  877. (void *)encoder->config.client,
  878. error_code,
  879. aws_error_debug_str(error_code));
  880. return AWS_MQTT5_ER_ERROR;
  881. }
  882. if (status.is_end_of_stream) {
  883. return AWS_MQTT5_ER_FINISHED;
  884. }
  885. }
  886. if (buffer->len == buffer->capacity) {
  887. return AWS_MQTT5_ER_OUT_OF_ROOM;
  888. }
  889. /* fall through intentional */
  890. }
  891. /* shouldn't be reachable */
  892. AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "id=%p: encoder reached an unreachable state", (void *)encoder->config.client);
  893. aws_raise_error(AWS_ERROR_INVALID_STATE);
  894. return AWS_MQTT5_ER_ERROR;
  895. }
  896. enum aws_mqtt5_encoding_result aws_mqtt5_encoder_encode_to_buffer(
  897. struct aws_mqtt5_encoder *encoder,
  898. struct aws_byte_buf *buffer) {
  899. enum aws_mqtt5_encoding_result result = AWS_MQTT5_ER_FINISHED;
  900. size_t step_count = aws_array_list_length(&encoder->encoding_steps);
  901. while (result == AWS_MQTT5_ER_FINISHED && encoder->current_encoding_step_index < step_count) {
  902. struct aws_mqtt5_encoding_step *step = NULL;
  903. aws_array_list_get_at_ptr(&encoder->encoding_steps, (void **)&step, encoder->current_encoding_step_index);
  904. result = s_execute_encode_step(encoder, step, buffer);
  905. if (result == AWS_MQTT5_ER_FINISHED) {
  906. encoder->current_encoding_step_index++;
  907. }
  908. }
  909. if (result == AWS_MQTT5_ER_FINISHED) {
  910. AWS_LOGF_DEBUG(
  911. AWS_LS_MQTT5_CLIENT, "id=%p: finished encoding current operation", (void *)encoder->config.client);
  912. aws_mqtt5_encoder_reset(encoder);
  913. }
  914. return result;
  915. }
  916. static struct aws_mqtt5_encoder_function_table s_aws_mqtt5_encoder_default_function_table = {
  917. .encoders_by_packet_type =
  918. {
  919. NULL, /* RESERVED = 0 */
  920. &s_aws_mqtt5_encoder_begin_connect, /* CONNECT */
  921. NULL, /* CONNACK */
  922. &s_aws_mqtt5_encoder_begin_publish, /* PUBLISH */
  923. &s_aws_mqtt5_encoder_begin_puback, /* PUBACK */
  924. NULL, /* PUBREC */
  925. NULL, /* PUBREL */
  926. NULL, /* PUBCOMP */
  927. &s_aws_mqtt5_encoder_begin_subscribe, /* SUBSCRIBE */
  928. NULL, /* SUBACK */
  929. &s_aws_mqtt5_encoder_begin_unsubscribe, /* UNSUBSCRIBE */
  930. NULL, /* UNSUBACK */
  931. &s_aws_mqtt5_encoder_begin_pingreq, /* PINGREQ */
  932. NULL, /* PINGRESP */
  933. &s_aws_mqtt5_encoder_begin_disconnect, /* DISCONNECT */
  934. NULL /* AUTH */
  935. },
  936. };
  937. const struct aws_mqtt5_encoder_function_table *g_aws_mqtt5_encoder_default_function_table =
  938. &s_aws_mqtt5_encoder_default_function_table;
  939. int aws_mqtt5_encoder_init(
  940. struct aws_mqtt5_encoder *encoder,
  941. struct aws_allocator *allocator,
  942. struct aws_mqtt5_encoder_options *options) {
  943. AWS_ZERO_STRUCT(*encoder);
  944. encoder->config = *options;
  945. if (encoder->config.encoders == NULL) {
  946. encoder->config.encoders = &s_aws_mqtt5_encoder_default_function_table;
  947. }
  948. if (aws_array_list_init_dynamic(
  949. &encoder->encoding_steps, allocator, INITIAL_ENCODING_STEP_COUNT, sizeof(struct aws_mqtt5_encoding_step))) {
  950. return AWS_OP_ERR;
  951. }
  952. return AWS_OP_SUCCESS;
  953. }
  954. void aws_mqtt5_encoder_clean_up(struct aws_mqtt5_encoder *encoder) {
  955. aws_array_list_clean_up(&encoder->encoding_steps);
  956. }
  957. void aws_mqtt5_encoder_reset(struct aws_mqtt5_encoder *encoder) {
  958. aws_array_list_clear(&encoder->encoding_steps);
  959. encoder->current_encoding_step_index = 0;
  960. }
  961. int aws_mqtt5_encoder_append_packet_encoding(
  962. struct aws_mqtt5_encoder *encoder,
  963. enum aws_mqtt5_packet_type packet_type,
  964. const void *packet_view) {
  965. aws_mqtt5_encode_begin_packet_type_fn *encoding_fn = encoder->config.encoders->encoders_by_packet_type[packet_type];
  966. if (encoding_fn == NULL) {
  967. return aws_raise_error(AWS_ERROR_MQTT5_ENCODE_FAILURE);
  968. }
  969. return (*encoding_fn)(encoder, packet_view);
  970. }
  971. static int s_compute_packet_size(size_t total_remaining_length, size_t *packet_size) {
  972. /* 1 (packet type + flags) + vli_length(total_remaining_length) + total_remaining_length */
  973. size_t encode_size = 0;
  974. if (aws_mqtt5_get_variable_length_encode_size(total_remaining_length, &encode_size)) {
  975. return AWS_OP_ERR;
  976. }
  977. size_t prefix = (size_t)1 + encode_size;
  978. if (aws_add_size_checked(prefix, total_remaining_length, packet_size)) {
  979. return AWS_OP_ERR;
  980. }
  981. return AWS_OP_SUCCESS;
  982. }
  983. int aws_mqtt5_packet_view_get_encoded_size(
  984. enum aws_mqtt5_packet_type packet_type,
  985. const void *packet_view,
  986. size_t *packet_size) {
  987. size_t total_remaining_length = 0;
  988. size_t properties_length = 0;
  989. if (packet_type == AWS_MQTT5_PT_PINGREQ) {
  990. *packet_size = AWS_MQTT5_PINGREQ_ENCODED_SIZE;
  991. return AWS_OP_SUCCESS;
  992. }
  993. switch (packet_type) {
  994. case AWS_MQTT5_PT_PUBLISH:
  995. if (s_compute_publish_variable_length_fields(packet_view, &total_remaining_length, &properties_length)) {
  996. return AWS_OP_ERR;
  997. }
  998. break;
  999. case AWS_MQTT5_PT_SUBSCRIBE:
  1000. if (s_compute_subscribe_variable_length_fields(packet_view, &total_remaining_length, &properties_length)) {
  1001. return AWS_OP_ERR;
  1002. }
  1003. break;
  1004. case AWS_MQTT5_PT_UNSUBSCRIBE:
  1005. if (s_compute_unsubscribe_variable_length_fields(
  1006. packet_view, &total_remaining_length, &properties_length)) {
  1007. return AWS_OP_ERR;
  1008. }
  1009. break;
  1010. case AWS_MQTT5_PT_DISCONNECT:
  1011. if (s_compute_disconnect_variable_length_fields(packet_view, &total_remaining_length, &properties_length)) {
  1012. return AWS_OP_ERR;
  1013. }
  1014. break;
  1015. case AWS_MQTT5_PT_PUBACK:
  1016. if (s_compute_puback_variable_length_fields(packet_view, &total_remaining_length, &properties_length)) {
  1017. return AWS_OP_ERR;
  1018. }
  1019. break;
  1020. default:
  1021. return aws_raise_error(AWS_ERROR_MQTT5_ENCODE_SIZE_UNSUPPORTED_PACKET_TYPE);
  1022. }
  1023. return s_compute_packet_size(total_remaining_length, packet_size);
  1024. }
  1025. void aws_mqtt5_encoder_set_outbound_topic_alias_resolver(
  1026. struct aws_mqtt5_encoder *encoder,
  1027. struct aws_mqtt5_outbound_topic_alias_resolver *resolver) {
  1028. encoder->topic_alias_resolver = resolver;
  1029. }