Line | Count | Source |
1 | | /* |
2 | | * MQTT Protocol |
3 | | * |
4 | | * Copyright 2020 Baptiste Assmann <bedis9@gmail.com> |
5 | | * |
6 | | * This program is free software; you can redistribute it and/or |
7 | | * modify it under the terms of the GNU General Public License |
8 | | * as published by the Free Software Foundation; either version |
9 | | * 2 of the License, or (at your option) any later version. |
10 | | * |
11 | | */ |
12 | | |
13 | | #include <haproxy/chunk.h> |
14 | | #include <haproxy/mqtt.h> |
15 | | |
16 | | uint8_t mqtt_cpt_flags[MQTT_CPT_ENTRIES] = { |
17 | | [MQTT_CPT_INVALID] = 0x00, |
18 | | [MQTT_CPT_CONNECT] = 0x00, |
19 | | [MQTT_CPT_CONNACK] = 0x00, |
20 | | |
21 | | /* MQTT_CPT_PUBLISH flags can have different values (DUP, QoS, RETAIN), must be |
22 | | * check more carefully (any combination of the 4 bits is valid). |
23 | | */ |
24 | | [MQTT_CPT_PUBLISH] = 0x0F, |
25 | | |
26 | | [MQTT_CPT_PUBACK] = 0x00, |
27 | | [MQTT_CPT_PUBREC] = 0x00, |
28 | | [MQTT_CPT_PUBREL] = 0x02, |
29 | | [MQTT_CPT_PUBCOMP] = 0x00, |
30 | | [MQTT_CPT_SUBSCRIBE] = 0x02, |
31 | | [MQTT_CPT_SUBACK] = 0x00, |
32 | | [MQTT_CPT_UNSUBSCRIBE] = 0x02, |
33 | | [MQTT_CPT_UNSUBACK] = 0x00, |
34 | | [MQTT_CPT_PINGREQ] = 0x00, |
35 | | [MQTT_CPT_PINGRESP] = 0x00, |
36 | | [MQTT_CPT_DISCONNECT] = 0x00, |
37 | | [MQTT_CPT_AUTH] = 0x00, |
38 | | }; |
39 | | |
40 | | const struct ist mqtt_fields_string[MQTT_FN_ENTRIES] = { |
41 | | [MQTT_FN_INVALID] = IST(""), |
42 | | |
43 | | /* it's MQTT 3.1, 3.1.1 and 5.0, those fields have no unique id, so we use strings */ |
44 | | [MQTT_FN_FLAGS] = IST("flags"), |
45 | | [MQTT_FN_REASON_CODE] = IST("reason_code"), /* MQTT 3.1 and 3.1.1: return_code */ |
46 | | [MQTT_FN_PROTOCOL_NAME] = IST("protocol_name"), |
47 | | [MQTT_FN_PROTOCOL_VERSION] = IST("protocol_version"), /* MQTT 3.1.1: protocol_level */ |
48 | | [MQTT_FN_CLIENT_IDENTIFIER] = IST("client_identifier"), |
49 | | [MQTT_FN_WILL_TOPIC] = IST("will_topic"), |
50 | | [MQTT_FN_WILL_PAYLOAD] = IST("will_payload"), /* MQTT 3.1 and 3.1.1: will_message */ |
51 | | [MQTT_FN_USERNAME] = IST("username"), |
52 | | [MQTT_FN_PASSWORD] = IST("password"), |
53 | | [MQTT_FN_KEEPALIVE] = IST("keepalive"), |
54 | | /* from here, it's MQTT 5.0 only */ |
55 | | [MQTT_FN_PAYLOAD_FORMAT_INDICATOR] = IST("1"), |
56 | | [MQTT_FN_MESSAGE_EXPIRY_INTERVAL] = IST("2"), |
57 | | [MQTT_FN_CONTENT_TYPE] = IST("3"), |
58 | | [MQTT_FN_RESPONSE_TOPIC] = IST("8"), |
59 | | [MQTT_FN_CORRELATION_DATA] = IST("9"), |
60 | | [MQTT_FN_SUBSCRIPTION_IDENTIFIER] = IST("11"), |
61 | | [MQTT_FN_SESSION_EXPIRY_INTERVAL] = IST("17"), |
62 | | [MQTT_FN_ASSIGNED_CLIENT_IDENTIFIER] = IST("18"), |
63 | | [MQTT_FN_SERVER_KEEPALIVE] = IST("19"), |
64 | | [MQTT_FN_AUTHENTICATION_METHOD] = IST("21"), |
65 | | [MQTT_FN_AUTHENTICATION_DATA] = IST("22"), |
66 | | [MQTT_FN_REQUEST_PROBLEM_INFORMATION] = IST("23"), |
67 | | [MQTT_FN_DELAY_INTERVAL] = IST("24"), |
68 | | [MQTT_FN_REQUEST_RESPONSE_INFORMATION] = IST("25"), |
69 | | [MQTT_FN_RESPONSE_INFORMATION] = IST("26"), |
70 | | [MQTT_FN_SERVER_REFERENCE] = IST("28"), |
71 | | [MQTT_FN_REASON_STRING] = IST("31"), |
72 | | [MQTT_FN_RECEIVE_MAXIMUM] = IST("33"), |
73 | | [MQTT_FN_TOPIC_ALIAS_MAXIMUM] = IST("34"), |
74 | | [MQTT_FN_TOPIC_ALIAS] = IST("35"), |
75 | | [MQTT_FN_MAXIMUM_QOS] = IST("36"), |
76 | | [MQTT_FN_RETAIN_AVAILABLE] = IST("37"), |
77 | | [MQTT_FN_USER_PROPERTY] = IST("38"), |
78 | | [MQTT_FN_MAXIMUM_PACKET_SIZE] = IST("39"), |
79 | | [MQTT_FN_WILDCARD_SUBSCRIPTION_AVAILABLE] = IST("40"), |
80 | | [MQTT_FN_SUBSCRIPTION_IDENTIFIERS_AVAILABLE] = IST("41"), |
81 | | [MQTT_FN_SHARED_SUBSCRIPTION_AVAILABLE] = IST("42"), |
82 | | }; |
83 | | |
84 | | /* list of supported capturable field names for each MQTT control packet type */ |
85 | | const uint64_t mqtt_fields_per_packet[MQTT_CPT_ENTRIES] = { |
86 | | [MQTT_CPT_INVALID] = 0, |
87 | | |
88 | | [MQTT_CPT_CONNECT] = MQTT_FN_BIT_PROTOCOL_NAME | MQTT_FN_BIT_PROTOCOL_VERSION | |
89 | | MQTT_FN_BIT_FLAGS | MQTT_FN_BIT_KEEPALIVE | |
90 | | MQTT_FN_BIT_SESSION_EXPIRY_INTERVAL | MQTT_FN_BIT_RECEIVE_MAXIMUM | |
91 | | MQTT_FN_BIT_MAXIMUM_PACKET_SIZE | MQTT_FN_BIT_TOPIC_ALIAS_MAXIMUM | |
92 | | MQTT_FN_BIT_REQUEST_RESPONSE_INFORMATION | MQTT_FN_BIT_REQUEST_PROBLEM_INFORMATION | |
93 | | MQTT_FN_BIT_USER_PROPERTY | MQTT_FN_BIT_AUTHENTICATION_METHOD | |
94 | | MQTT_FN_BIT_AUTHENTICATION_DATA | MQTT_FN_BIT_CLIENT_IDENTIFIER | |
95 | | MQTT_FN_BIT_DELAY_INTERVAL | MQTT_FN_BIT_PAYLOAD_FORMAT_INDICATOR | |
96 | | MQTT_FN_BIT_MESSAGE_EXPIRY_INTERVAL | MQTT_FN_BIT_CONTENT_TYPE | |
97 | | MQTT_FN_BIT_RESPONSE_TOPIC | MQTT_FN_BIT_CORRELATION_DATA | |
98 | | MQTT_FN_BIT_WILL_TOPIC | |
99 | | MQTT_FN_BIT_WILL_PAYLOAD | MQTT_FN_BIT_USERNAME | |
100 | | MQTT_FN_BIT_PASSWORD, |
101 | | |
102 | | [MQTT_CPT_CONNACK] = MQTT_FN_BIT_FLAGS | MQTT_FN_BIT_PROTOCOL_VERSION | |
103 | | MQTT_FN_BIT_REASON_CODE | MQTT_FN_BIT_SESSION_EXPIRY_INTERVAL | |
104 | | MQTT_FN_BIT_RECEIVE_MAXIMUM | MQTT_FN_BIT_MAXIMUM_QOS | |
105 | | MQTT_FN_BIT_RETAIN_AVAILABLE | MQTT_FN_BIT_MAXIMUM_PACKET_SIZE | |
106 | | MQTT_FN_BIT_ASSIGNED_CLIENT_IDENTIFIER | MQTT_FN_BIT_TOPIC_ALIAS_MAXIMUM | |
107 | | MQTT_FN_BIT_REASON_STRING | MQTT_FN_BIT_WILDCARD_SUBSCRIPTION_AVAILABLE | |
108 | | MQTT_FN_BIT_SUBSCRIPTION_IDENTIFIERS_AVAILABLE| MQTT_FN_BIT_SHARED_SUBSCRIPTION_AVAILABLE | |
109 | | MQTT_FN_BIT_SERVER_KEEPALIVE | MQTT_FN_BIT_RESPONSE_INFORMATION | |
110 | | MQTT_FN_BIT_SERVER_REFERENCE | MQTT_FN_BIT_USER_PROPERTY | |
111 | | MQTT_FN_BIT_AUTHENTICATION_METHOD | MQTT_FN_BIT_AUTHENTICATION_DATA, |
112 | | |
113 | | [MQTT_CPT_PUBLISH] = MQTT_FN_BIT_PAYLOAD_FORMAT_INDICATOR | MQTT_FN_BIT_MESSAGE_EXPIRY_INTERVAL | |
114 | | MQTT_FN_BIT_CONTENT_TYPE | MQTT_FN_BIT_RESPONSE_TOPIC | |
115 | | MQTT_FN_BIT_CORRELATION_DATA | MQTT_FN_BIT_SUBSCRIPTION_IDENTIFIER | |
116 | | MQTT_FN_BIT_TOPIC_ALIAS | MQTT_FN_BIT_USER_PROPERTY, |
117 | | |
118 | | [MQTT_CPT_PUBACK] = MQTT_FN_BIT_REASON_CODE | MQTT_FN_BIT_REASON_STRING | MQTT_FN_BIT_USER_PROPERTY, |
119 | | |
120 | | [MQTT_CPT_PUBREC] = MQTT_FN_BIT_REASON_CODE | MQTT_FN_BIT_REASON_STRING | MQTT_FN_BIT_USER_PROPERTY, |
121 | | |
122 | | [MQTT_CPT_PUBREL] = MQTT_FN_BIT_REASON_CODE | MQTT_FN_BIT_REASON_STRING | MQTT_FN_BIT_USER_PROPERTY, |
123 | | |
124 | | [MQTT_CPT_PUBCOMP] = MQTT_FN_BIT_REASON_CODE | MQTT_FN_BIT_REASON_STRING | MQTT_FN_BIT_USER_PROPERTY, |
125 | | |
126 | | [MQTT_CPT_SUBSCRIBE] = MQTT_FN_BIT_SUBSCRIPTION_IDENTIFIER | MQTT_FN_BIT_USER_PROPERTY, |
127 | | |
128 | | [MQTT_CPT_SUBACK] = MQTT_FN_BIT_REASON_STRING | MQTT_FN_BIT_USER_PROPERTY, |
129 | | |
130 | | [MQTT_CPT_UNSUBSCRIBE] = MQTT_FN_BIT_USER_PROPERTY, |
131 | | |
132 | | [MQTT_CPT_UNSUBACK] = MQTT_FN_BIT_REASON_STRING | MQTT_FN_BIT_USER_PROPERTY, |
133 | | |
134 | | [MQTT_CPT_PINGREQ] = 0, |
135 | | |
136 | | [MQTT_CPT_PINGRESP] = 0, |
137 | | |
138 | | [MQTT_CPT_DISCONNECT] = MQTT_FN_BIT_REASON_CODE | MQTT_FN_BIT_SESSION_EXPIRY_INTERVAL | |
139 | | MQTT_FN_BIT_SERVER_REFERENCE | MQTT_FN_BIT_REASON_STRING | |
140 | | MQTT_FN_BIT_USER_PROPERTY, |
141 | | |
142 | | [MQTT_CPT_AUTH] = MQTT_FN_BIT_AUTHENTICATION_METHOD | MQTT_FN_BIT_AUTHENTICATION_DATA | |
143 | | MQTT_FN_BIT_REASON_STRING | MQTT_FN_BIT_USER_PROPERTY, |
144 | | }; |
145 | | |
146 | | /* Checks the first byte of a message to read the fixed header and extract the |
147 | | * packet type and flags. <parser> is supposed to point to the fixed header byte. |
148 | | * |
149 | | * Fix header looks like: |
150 | | * +-------+-----------+-----------+-----------+---------+----------+----------+---------+------------+ |
151 | | * | bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
152 | | * +-------+-----------+-----------+-----------+---------+----------+----------+---------+------------+ |
153 | | * | field | MQTT Control Packet Type | Flags specific to each Control Packet type | |
154 | | * +-------+---------------------------------------------+--------------------------------------------+ |
155 | | * |
156 | | * On success, <ptk> is updated with the packet type and flags and the new parser |
157 | | * state is returned. On error, IST_NULL is returned. |
158 | | */ |
159 | | static inline struct ist mqtt_read_fixed_hdr(struct ist parser, struct mqtt_pkt *pkt) |
160 | 0 | { |
161 | 0 | uint8_t type = (uint8_t)*istptr(parser); |
162 | 0 | uint8_t ptype = (type & 0xF0) >> 4; |
163 | 0 | uint8_t flags = type & 0x0F; |
164 | |
|
165 | 0 | if (ptype == MQTT_CPT_INVALID || ptype >= MQTT_CPT_ENTRIES || |
166 | 0 | (ptype != MQTT_CPT_PUBLISH && flags != mqtt_cpt_flags[ptype])) |
167 | 0 | return IST_NULL; |
168 | | |
169 | 0 | pkt->fixed_hdr.type = ptype; |
170 | 0 | pkt->fixed_hdr.flags = flags; |
171 | 0 | return istnext(parser); |
172 | 0 | } |
173 | | |
174 | | /* Reads a one byte integer. more information here : |
175 | | * https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901007 |
176 | | * |
177 | | * <parser> is supposed to point to the first byte of the integer. On success |
178 | | * the integer is stored in <*i>, if provided, and the new parser state is returned. On |
179 | | * error, IST_NULL is returned. |
180 | | */ |
181 | | static inline struct ist mqtt_read_1byte_int(struct ist parser, uint8_t *i) |
182 | 0 | { |
183 | 0 | if (istlen(parser) < 1) |
184 | 0 | return IST_NULL; |
185 | 0 | if (i) |
186 | 0 | *i = (uint8_t)*istptr(parser); |
187 | 0 | parser = istnext(parser); |
188 | 0 | return parser; |
189 | 0 | } |
190 | | |
191 | | /* Reads a two byte integer. more information here : |
192 | | * https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901008 |
193 | | * |
194 | | * <parser> is supposed to point to the first byte of the integer. On success |
195 | | * the integer is stored in <*i>, if provided, and the new parser state is returned. On |
196 | | * error, IST_NULL is returned. |
197 | | */ |
198 | | static inline struct ist mqtt_read_2byte_int(struct ist parser, uint16_t *i) |
199 | 0 | { |
200 | 0 | if (istlen(parser) < 2) |
201 | 0 | return IST_NULL; |
202 | 0 | if (i) { |
203 | 0 | *i = (uint8_t)*istptr(parser) << 8; |
204 | 0 | *i += (uint8_t)*(istptr(parser) + 1); |
205 | 0 | } |
206 | 0 | parser = istadv(parser, 2); |
207 | 0 | return parser; |
208 | 0 | } |
209 | | |
210 | | /* Reads a four byte integer. more information here : |
211 | | * https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901009 |
212 | | * |
213 | | * <parser> is supposed to point to the first byte of the integer. On success |
214 | | * the integer is stored in <*i>, if provided, and the new parser state is returned. On |
215 | | * error, IST_NULL is returned. |
216 | | */ |
217 | | static inline struct ist mqtt_read_4byte_int(struct ist parser, uint32_t *i) |
218 | 0 | { |
219 | 0 | if (istlen(parser) < 4) |
220 | 0 | return IST_NULL; |
221 | 0 | if (i) { |
222 | 0 | *i = (uint8_t)*istptr(parser) << 24; |
223 | 0 | *i += (uint8_t)*(istptr(parser) + 1) << 16; |
224 | 0 | *i += (uint8_t)*(istptr(parser) + 2) << 8; |
225 | 0 | *i += (uint8_t)*(istptr(parser) + 3); |
226 | 0 | } |
227 | 0 | parser = istadv(parser, 4); |
228 | 0 | return parser; |
229 | 0 | } |
230 | | |
231 | | /* Reads a variable byte integer. more information here : |
232 | | * https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718023 |
233 | | * https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901011 |
234 | | * |
235 | | * It is encoded using a variable length encoding scheme which uses a single |
236 | | * byte for values up to 127. Larger values are handled as follows. The least |
237 | | * significant seven bits of each byte encode the data, and the most significant |
238 | | * bit is used to indicate that there are following bytes in the representation. |
239 | | * Thus each byte encodes 128 values and a "continuation bit". |
240 | | * |
241 | | * The maximum number of bytes in the Remaining Length field is four |
242 | | * (MQTT_REMAINING_LENGTH_MAX_SIZE). |
243 | | * |
244 | | * <parser> is supposed to point to the first byte of the integer. On success |
245 | | * the integer is stored in <*i> and the new parser state is returned. On |
246 | | * error, IST_NULL is returned. |
247 | | */ |
248 | | static inline struct ist mqtt_read_varint(struct ist parser, uint32_t *i) |
249 | 0 | { |
250 | 0 | int off, m; |
251 | |
|
252 | 0 | off = m = 0; |
253 | 0 | if (i) |
254 | 0 | *i = 0; |
255 | 0 | for (off = 0; off < MQTT_REMAINING_LENGTH_MAX_SIZE && istlen(parser); off++) { |
256 | 0 | uint8_t byte = (uint8_t)*istptr(parser); |
257 | |
|
258 | 0 | if (i) { |
259 | 0 | *i += (byte & 127) << m; |
260 | 0 | m += 7; /* preparing <m> for next byte */ |
261 | 0 | } |
262 | 0 | parser = istnext(parser); |
263 | | |
264 | | /* we read the latest byte for the remaining length field */ |
265 | 0 | if (byte <= 127) |
266 | 0 | break; |
267 | 0 | } |
268 | |
|
269 | 0 | if (off == MQTT_REMAINING_LENGTH_MAX_SIZE) |
270 | 0 | return IST_NULL; |
271 | 0 | return parser; |
272 | 0 | } |
273 | | |
274 | | /* Reads a MQTT string. more information here : |
275 | | * http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718016 |
276 | | * https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901010 |
277 | | * |
278 | | * In MQTT, strings are prefixed by their size, encoded over 2 bytes: |
279 | | * byte 1: length MSB |
280 | | * byte 2: length LSB |
281 | | * byte 3: string |
282 | | * ... |
283 | | * |
284 | | * string size is MSB * 256 + LSB |
285 | | * |
286 | | * <parser> is supposed to point to the first byte of the string. On success the |
287 | | * string is stored in <*str>, if provided, and the new parser state is |
288 | | * returned. On error, IST_NULL is returned. |
289 | | */ |
290 | | static inline struct ist mqtt_read_string(struct ist parser, struct ist *str) |
291 | 0 | { |
292 | 0 | uint16_t len = 0; |
293 | | |
294 | | /* read and compute the string length */ |
295 | 0 | if (istlen(parser) < 2) |
296 | 0 | goto error; |
297 | | |
298 | 0 | parser = mqtt_read_2byte_int(parser, &len); |
299 | 0 | if (!isttest(parser) || istlen(parser) < len) |
300 | 0 | goto error; |
301 | | |
302 | 0 | if (str) { |
303 | 0 | str->ptr = istptr(parser); |
304 | 0 | str->len = len; |
305 | 0 | } |
306 | |
|
307 | 0 | return istadv(parser, len); |
308 | | |
309 | 0 | error: |
310 | 0 | return IST_NULL; |
311 | 0 | } |
312 | | |
313 | | /* Helper function to convert a unsigned integer to a string. The result is |
314 | | * written in <buf>. On success, the written size is returned, otherwise, on |
315 | | * error, 0 is returned. |
316 | | */ |
317 | | static inline size_t mqtt_uint2str(struct buffer *buf, uint32_t i) |
318 | 0 | { |
319 | 0 | char *end; |
320 | |
|
321 | 0 | end = ultoa_o(i, buf->area, buf->size); |
322 | 0 | if (!end) |
323 | 0 | return 0; |
324 | 0 | buf->data = end - buf->area; |
325 | 0 | return buf->data; |
326 | 0 | } |
327 | | |
328 | | /* Extracts the value of a <fieldname_id> of type <type> from a given MQTT |
329 | | * message <msg>. IST_NULL is returned if an error occurred while parsing or if |
330 | | * the field could not be found. If more data are required, the message with a |
331 | | * length set to 0 is returned. If the field is found, the response is returned |
332 | | * as a struct ist. |
333 | | */ |
334 | | struct ist mqtt_field_value(struct ist msg, int type, int fieldname_id) |
335 | 0 | { |
336 | 0 | struct buffer *trash = get_trash_chunk(); |
337 | 0 | struct mqtt_pkt mpkt; |
338 | 0 | struct ist res; |
339 | |
|
340 | 0 | switch (mqtt_validate_message(msg, &mpkt)) { |
341 | 0 | case MQTT_VALID_MESSAGE: |
342 | 0 | if (mpkt.fixed_hdr.type != type) |
343 | 0 | goto not_found_or_invalid; |
344 | 0 | break; |
345 | 0 | case MQTT_NEED_MORE_DATA: |
346 | 0 | goto need_more; |
347 | 0 | case MQTT_INVALID_MESSAGE: |
348 | 0 | goto not_found_or_invalid; |
349 | 0 | } |
350 | | |
351 | 0 | switch (type) { |
352 | 0 | case MQTT_CPT_CONNECT: |
353 | 0 | switch (fieldname_id) { |
354 | 0 | case MQTT_FN_FLAGS: |
355 | 0 | if (!mqtt_uint2str(trash, mpkt.data.connect.var_hdr.flags)) |
356 | 0 | goto not_found_or_invalid; |
357 | 0 | res = ist2(trash->area, trash->data); |
358 | 0 | goto end; |
359 | | |
360 | 0 | case MQTT_FN_PROTOCOL_NAME: |
361 | 0 | if (!istlen(mpkt.data.connect.var_hdr.protocol_name)) |
362 | 0 | goto not_found_or_invalid; |
363 | 0 | res = mpkt.data.connect.var_hdr.protocol_name; |
364 | 0 | goto end; |
365 | | |
366 | 0 | case MQTT_FN_PROTOCOL_VERSION: |
367 | 0 | if (!mqtt_uint2str(trash, mpkt.data.connect.var_hdr.protocol_version)) |
368 | 0 | goto not_found_or_invalid; |
369 | 0 | res = ist2(trash->area, trash->data); |
370 | 0 | goto end; |
371 | | |
372 | 0 | case MQTT_FN_CLIENT_IDENTIFIER: |
373 | 0 | if (!istlen(mpkt.data.connect.payload.client_identifier)) |
374 | 0 | goto not_found_or_invalid; |
375 | 0 | res = mpkt.data.connect.payload.client_identifier; |
376 | 0 | goto end; |
377 | | |
378 | 0 | case MQTT_FN_WILL_TOPIC: |
379 | 0 | if (!istlen(mpkt.data.connect.payload.will_topic)) |
380 | 0 | goto not_found_or_invalid; |
381 | 0 | res = mpkt.data.connect.payload.will_topic; |
382 | 0 | goto end; |
383 | | |
384 | 0 | case MQTT_FN_WILL_PAYLOAD: |
385 | 0 | if (!istlen(mpkt.data.connect.payload.will_payload)) |
386 | 0 | goto not_found_or_invalid; |
387 | 0 | res = mpkt.data.connect.payload.will_payload; |
388 | 0 | goto end; |
389 | | |
390 | 0 | case MQTT_FN_USERNAME: |
391 | 0 | if (!istlen(mpkt.data.connect.payload.username)) |
392 | 0 | goto not_found_or_invalid; |
393 | 0 | res = mpkt.data.connect.payload.username; |
394 | 0 | goto end; |
395 | | |
396 | 0 | case MQTT_FN_PASSWORD: |
397 | 0 | if (!istlen(mpkt.data.connect.payload.password)) |
398 | 0 | goto not_found_or_invalid; |
399 | 0 | res = mpkt.data.connect.payload.password; |
400 | 0 | goto end; |
401 | | |
402 | 0 | case MQTT_FN_KEEPALIVE: |
403 | 0 | if (!mqtt_uint2str(trash, mpkt.data.connect.var_hdr.keepalive)) |
404 | 0 | goto not_found_or_invalid; |
405 | 0 | res = ist2(trash->area, trash->data); |
406 | 0 | goto end; |
407 | | |
408 | 0 | case MQTT_FN_PAYLOAD_FORMAT_INDICATOR: |
409 | 0 | if ((mpkt.data.connect.var_hdr.protocol_version != MQTT_VERSION_5_0) || |
410 | 0 | !(mpkt.data.connect.var_hdr.flags & MQTT_CONNECT_FL_WILL)) |
411 | 0 | goto not_found_or_invalid; |
412 | 0 | if (!mqtt_uint2str(trash, mpkt.data.connect.payload.will_props.payload_format_indicator)) |
413 | 0 | goto not_found_or_invalid; |
414 | 0 | res = ist2(trash->area, trash->data); |
415 | 0 | goto end; |
416 | | |
417 | 0 | case MQTT_FN_MESSAGE_EXPIRY_INTERVAL: |
418 | 0 | if ((mpkt.data.connect.var_hdr.protocol_version != MQTT_VERSION_5_0) || |
419 | 0 | !(mpkt.data.connect.var_hdr.flags & MQTT_CONNECT_FL_WILL)) |
420 | 0 | goto not_found_or_invalid; |
421 | 0 | if (!mqtt_uint2str(trash, mpkt.data.connect.payload.will_props.message_expiry_interval)) |
422 | 0 | goto not_found_or_invalid; |
423 | 0 | res = ist2(trash->area, trash->data); |
424 | 0 | goto end; |
425 | | |
426 | 0 | case MQTT_FN_CONTENT_TYPE: |
427 | 0 | if ((mpkt.data.connect.var_hdr.protocol_version != MQTT_VERSION_5_0) || |
428 | 0 | !(mpkt.data.connect.var_hdr.flags & MQTT_CONNECT_FL_WILL)) |
429 | 0 | goto not_found_or_invalid; |
430 | 0 | if (!istlen(mpkt.data.connect.payload.will_props.content_type)) |
431 | 0 | goto not_found_or_invalid; |
432 | 0 | res = mpkt.data.connect.payload.will_props.content_type; |
433 | 0 | goto end; |
434 | | |
435 | 0 | case MQTT_FN_RESPONSE_TOPIC: |
436 | 0 | if ((mpkt.data.connect.var_hdr.protocol_version != MQTT_VERSION_5_0) || |
437 | 0 | !(mpkt.data.connect.var_hdr.flags & MQTT_CONNECT_FL_WILL)) |
438 | 0 | goto not_found_or_invalid; |
439 | 0 | if (!istlen(mpkt.data.connect.payload.will_props.response_topic)) |
440 | 0 | goto not_found_or_invalid; |
441 | 0 | res = mpkt.data.connect.payload.will_props.response_topic; |
442 | 0 | goto end; |
443 | | |
444 | 0 | case MQTT_FN_CORRELATION_DATA: |
445 | 0 | if ((mpkt.data.connect.var_hdr.protocol_version != MQTT_VERSION_5_0) || |
446 | 0 | !(mpkt.data.connect.var_hdr.flags & MQTT_CONNECT_FL_WILL)) |
447 | 0 | goto not_found_or_invalid; |
448 | 0 | if (!istlen(mpkt.data.connect.payload.will_props.correlation_data)) |
449 | 0 | goto not_found_or_invalid; |
450 | 0 | res = mpkt.data.connect.payload.will_props.correlation_data; |
451 | 0 | goto end; |
452 | | |
453 | 0 | case MQTT_FN_SESSION_EXPIRY_INTERVAL: |
454 | 0 | if (mpkt.data.connect.var_hdr.protocol_version != MQTT_VERSION_5_0) |
455 | 0 | goto not_found_or_invalid; |
456 | 0 | if (!mqtt_uint2str(trash, mpkt.data.connect.var_hdr.props.session_expiry_interval)) |
457 | 0 | goto not_found_or_invalid; |
458 | 0 | res = ist2(trash->area, trash->data); |
459 | 0 | goto end; |
460 | | |
461 | 0 | case MQTT_FN_AUTHENTICATION_METHOD: |
462 | 0 | if (mpkt.data.connect.var_hdr.protocol_version != MQTT_VERSION_5_0) |
463 | 0 | goto not_found_or_invalid; |
464 | 0 | if (!istlen(mpkt.data.connect.var_hdr.props.authentication_method)) |
465 | 0 | goto not_found_or_invalid; |
466 | 0 | res = mpkt.data.connect.var_hdr.props.authentication_method; |
467 | 0 | goto end; |
468 | | |
469 | 0 | case MQTT_FN_AUTHENTICATION_DATA: |
470 | 0 | if (mpkt.data.connect.var_hdr.protocol_version != MQTT_VERSION_5_0) |
471 | 0 | goto not_found_or_invalid; |
472 | 0 | if (!istlen(mpkt.data.connect.var_hdr.props.authentication_data)) |
473 | 0 | goto not_found_or_invalid; |
474 | 0 | res = mpkt.data.connect.var_hdr.props.authentication_data; |
475 | 0 | goto end; |
476 | | |
477 | 0 | case MQTT_FN_REQUEST_PROBLEM_INFORMATION: |
478 | 0 | if (mpkt.data.connect.var_hdr.protocol_version != MQTT_VERSION_5_0) |
479 | 0 | goto not_found_or_invalid; |
480 | 0 | if (!mqtt_uint2str(trash, mpkt.data.connect.var_hdr.props.request_problem_information)) |
481 | 0 | goto not_found_or_invalid; |
482 | 0 | res = ist2(trash->area, trash->data); |
483 | 0 | goto end; |
484 | | |
485 | 0 | case MQTT_FN_DELAY_INTERVAL: |
486 | 0 | if ((mpkt.data.connect.var_hdr.protocol_version != MQTT_VERSION_5_0) || |
487 | 0 | !(mpkt.data.connect.var_hdr.flags & MQTT_CONNECT_FL_WILL)) |
488 | 0 | goto not_found_or_invalid; |
489 | 0 | if (!mqtt_uint2str(trash, mpkt.data.connect.payload.will_props.delay_interval)) |
490 | 0 | goto not_found_or_invalid; |
491 | 0 | res = ist2(trash->area, trash->data); |
492 | 0 | goto end; |
493 | | |
494 | 0 | case MQTT_FN_REQUEST_RESPONSE_INFORMATION: |
495 | 0 | if (mpkt.data.connect.var_hdr.protocol_version != MQTT_VERSION_5_0) |
496 | 0 | goto not_found_or_invalid; |
497 | 0 | if (!mqtt_uint2str(trash, mpkt.data.connect.var_hdr.props.request_response_information)) |
498 | 0 | goto not_found_or_invalid; |
499 | 0 | res = ist2(trash->area, trash->data); |
500 | 0 | goto end; |
501 | | |
502 | 0 | case MQTT_FN_RECEIVE_MAXIMUM: |
503 | 0 | if (mpkt.data.connect.var_hdr.protocol_version != MQTT_VERSION_5_0) |
504 | 0 | goto not_found_or_invalid; |
505 | 0 | if (!mqtt_uint2str(trash, mpkt.data.connect.var_hdr.props.receive_maximum)) |
506 | 0 | goto not_found_or_invalid; |
507 | 0 | res = ist2(trash->area, trash->data); |
508 | 0 | goto end; |
509 | | |
510 | 0 | case MQTT_FN_TOPIC_ALIAS_MAXIMUM: |
511 | 0 | if (mpkt.data.connect.var_hdr.protocol_version != MQTT_VERSION_5_0) |
512 | 0 | goto not_found_or_invalid; |
513 | 0 | if (!mqtt_uint2str(trash, mpkt.data.connect.var_hdr.props.topic_alias_maximum)) |
514 | 0 | goto not_found_or_invalid; |
515 | 0 | res = ist2(trash->area, trash->data); |
516 | 0 | goto end; |
517 | | |
518 | 0 | case MQTT_FN_MAXIMUM_PACKET_SIZE: |
519 | 0 | if (mpkt.data.connect.var_hdr.protocol_version != MQTT_VERSION_5_0) |
520 | 0 | goto not_found_or_invalid; |
521 | 0 | if (!mqtt_uint2str(trash, mpkt.data.connect.var_hdr.props.maximum_packet_size)) |
522 | 0 | goto not_found_or_invalid; |
523 | 0 | res = ist2(trash->area, trash->data); |
524 | 0 | goto end; |
525 | | |
526 | 0 | default: |
527 | 0 | goto not_found_or_invalid; |
528 | 0 | } |
529 | 0 | break; |
530 | | |
531 | 0 | case MQTT_CPT_CONNACK: |
532 | 0 | switch (fieldname_id) { |
533 | 0 | case MQTT_FN_FLAGS: |
534 | 0 | if (!mqtt_uint2str(trash, mpkt.data.connack.var_hdr.flags)) |
535 | 0 | goto not_found_or_invalid; |
536 | 0 | res = ist2(trash->area, trash->data); |
537 | 0 | goto end; |
538 | | |
539 | 0 | case MQTT_FN_REASON_CODE: |
540 | 0 | if (!mqtt_uint2str(trash, mpkt.data.connack.var_hdr.reason_code)) |
541 | 0 | goto not_found_or_invalid; |
542 | 0 | res = ist2(trash->area, trash->data); |
543 | 0 | goto end; |
544 | | |
545 | 0 | case MQTT_FN_PROTOCOL_VERSION: |
546 | 0 | if (!mqtt_uint2str(trash, mpkt.data.connack.var_hdr.protocol_version)) |
547 | 0 | goto not_found_or_invalid; |
548 | 0 | res = ist2(trash->area, trash->data); |
549 | 0 | goto end; |
550 | | |
551 | 0 | case MQTT_FN_SESSION_EXPIRY_INTERVAL: |
552 | 0 | if (mpkt.data.connack.var_hdr.protocol_version != MQTT_VERSION_5_0) |
553 | 0 | goto not_found_or_invalid; |
554 | 0 | if (!mqtt_uint2str(trash, mpkt.data.connack.var_hdr.props.session_expiry_interval)) |
555 | 0 | goto not_found_or_invalid; |
556 | 0 | res = ist2(trash->area, trash->data); |
557 | 0 | goto end; |
558 | | |
559 | 0 | case MQTT_FN_ASSIGNED_CLIENT_IDENTIFIER: |
560 | 0 | if (mpkt.data.connack.var_hdr.protocol_version != MQTT_VERSION_5_0) |
561 | 0 | goto not_found_or_invalid; |
562 | 0 | if (!istlen(mpkt.data.connack.var_hdr.props.assigned_client_identifier)) |
563 | 0 | goto not_found_or_invalid; |
564 | 0 | res = mpkt.data.connack.var_hdr.props.assigned_client_identifier; |
565 | 0 | goto end; |
566 | | |
567 | 0 | case MQTT_FN_SERVER_KEEPALIVE: |
568 | 0 | if (mpkt.data.connack.var_hdr.protocol_version != MQTT_VERSION_5_0) |
569 | 0 | goto not_found_or_invalid; |
570 | 0 | if (!mqtt_uint2str(trash, mpkt.data.connack.var_hdr.props.server_keepalive)) |
571 | 0 | goto not_found_or_invalid; |
572 | 0 | res = ist2(trash->area, trash->data); |
573 | 0 | goto end; |
574 | | |
575 | 0 | case MQTT_FN_AUTHENTICATION_METHOD: |
576 | 0 | if (mpkt.data.connack.var_hdr.protocol_version != MQTT_VERSION_5_0) |
577 | 0 | goto not_found_or_invalid; |
578 | 0 | if (!istlen(mpkt.data.connack.var_hdr.props.authentication_method)) |
579 | 0 | goto not_found_or_invalid; |
580 | 0 | res = mpkt.data.connack.var_hdr.props.authentication_method; |
581 | 0 | goto end; |
582 | | |
583 | 0 | case MQTT_FN_AUTHENTICATION_DATA: |
584 | 0 | if (mpkt.data.connack.var_hdr.protocol_version != MQTT_VERSION_5_0) |
585 | 0 | goto not_found_or_invalid; |
586 | 0 | if (!istlen(mpkt.data.connack.var_hdr.props.authentication_data)) |
587 | 0 | goto not_found_or_invalid; |
588 | 0 | res = mpkt.data.connack.var_hdr.props.authentication_data; |
589 | 0 | goto end; |
590 | | |
591 | 0 | case MQTT_FN_RESPONSE_INFORMATION: |
592 | 0 | if (mpkt.data.connack.var_hdr.protocol_version != MQTT_VERSION_5_0) |
593 | 0 | goto not_found_or_invalid; |
594 | 0 | if (!istlen(mpkt.data.connack.var_hdr.props.response_information)) |
595 | 0 | goto not_found_or_invalid; |
596 | 0 | res = mpkt.data.connack.var_hdr.props.response_information; |
597 | 0 | goto end; |
598 | | |
599 | 0 | case MQTT_FN_SERVER_REFERENCE: |
600 | 0 | if (mpkt.data.connack.var_hdr.protocol_version != MQTT_VERSION_5_0) |
601 | 0 | goto not_found_or_invalid; |
602 | 0 | if (!istlen(mpkt.data.connack.var_hdr.props.server_reference)) |
603 | 0 | goto not_found_or_invalid; |
604 | 0 | res = mpkt.data.connack.var_hdr.props.server_reference; |
605 | 0 | goto end; |
606 | | |
607 | 0 | case MQTT_FN_REASON_STRING: |
608 | 0 | if (mpkt.data.connack.var_hdr.protocol_version != MQTT_VERSION_5_0) |
609 | 0 | goto not_found_or_invalid; |
610 | 0 | if (!istlen(mpkt.data.connack.var_hdr.props.reason_string)) |
611 | 0 | goto not_found_or_invalid; |
612 | 0 | res = mpkt.data.connack.var_hdr.props.reason_string; |
613 | 0 | goto end; |
614 | | |
615 | 0 | case MQTT_FN_RECEIVE_MAXIMUM: |
616 | 0 | if (mpkt.data.connack.var_hdr.protocol_version != MQTT_VERSION_5_0) |
617 | 0 | goto not_found_or_invalid; |
618 | 0 | if (!mqtt_uint2str(trash, mpkt.data.connack.var_hdr.props.receive_maximum)) |
619 | 0 | goto not_found_or_invalid; |
620 | 0 | res = ist2(trash->area, trash->data); |
621 | 0 | goto end; |
622 | | |
623 | 0 | case MQTT_FN_TOPIC_ALIAS_MAXIMUM: |
624 | 0 | if (mpkt.data.connack.var_hdr.protocol_version != MQTT_VERSION_5_0) |
625 | 0 | goto not_found_or_invalid; |
626 | 0 | if (!mqtt_uint2str(trash, mpkt.data.connack.var_hdr.props.topic_alias_maximum)) |
627 | 0 | goto not_found_or_invalid; |
628 | 0 | res = ist2(trash->area, trash->data); |
629 | 0 | goto end; |
630 | | |
631 | 0 | case MQTT_FN_MAXIMUM_QOS: |
632 | 0 | if (mpkt.data.connack.var_hdr.protocol_version != MQTT_VERSION_5_0) |
633 | 0 | goto not_found_or_invalid; |
634 | 0 | if (!mqtt_uint2str(trash, mpkt.data.connack.var_hdr.props.maximum_qos)) |
635 | 0 | goto not_found_or_invalid; |
636 | 0 | res = ist2(trash->area, trash->data); |
637 | 0 | goto end; |
638 | | |
639 | 0 | case MQTT_FN_RETAIN_AVAILABLE: |
640 | 0 | if (mpkt.data.connack.var_hdr.protocol_version != MQTT_VERSION_5_0) |
641 | 0 | goto not_found_or_invalid; |
642 | 0 | if (!mqtt_uint2str(trash, mpkt.data.connack.var_hdr.props.retain_available)) |
643 | 0 | goto not_found_or_invalid; |
644 | 0 | res = ist2(trash->area, trash->data); |
645 | 0 | goto end; |
646 | | |
647 | 0 | case MQTT_FN_MAXIMUM_PACKET_SIZE: |
648 | 0 | if (mpkt.data.connack.var_hdr.protocol_version != MQTT_VERSION_5_0) |
649 | 0 | goto not_found_or_invalid; |
650 | 0 | if (!mqtt_uint2str(trash, mpkt.data.connack.var_hdr.props.maximum_packet_size)) |
651 | 0 | goto not_found_or_invalid; |
652 | 0 | res = ist2(trash->area, trash->data); |
653 | 0 | goto end; |
654 | | |
655 | 0 | case MQTT_FN_WILDCARD_SUBSCRIPTION_AVAILABLE: |
656 | 0 | if (mpkt.data.connack.var_hdr.protocol_version != MQTT_VERSION_5_0) |
657 | 0 | goto not_found_or_invalid; |
658 | 0 | if (!mqtt_uint2str(trash, mpkt.data.connack.var_hdr.props.wildcard_subscription_available)) |
659 | 0 | goto not_found_or_invalid; |
660 | 0 | res = ist2(trash->area, trash->data); |
661 | 0 | goto end; |
662 | | |
663 | 0 | case MQTT_FN_SUBSCRIPTION_IDENTIFIERS_AVAILABLE: |
664 | 0 | if (mpkt.data.connack.var_hdr.protocol_version != MQTT_VERSION_5_0) |
665 | 0 | goto not_found_or_invalid; |
666 | 0 | if (!mqtt_uint2str(trash, mpkt.data.connack.var_hdr.props.subscription_identifiers_available)) |
667 | 0 | goto not_found_or_invalid; |
668 | 0 | res = ist2(trash->area, trash->data); |
669 | 0 | goto end; |
670 | | |
671 | 0 | case MQTT_FN_SHARED_SUBSCRIPTION_AVAILABLE: |
672 | 0 | if (mpkt.data.connack.var_hdr.protocol_version != MQTT_VERSION_5_0) |
673 | 0 | goto not_found_or_invalid; |
674 | 0 | if (!mqtt_uint2str(trash, mpkt.data.connack.var_hdr.props.shared_subscription_available)) |
675 | 0 | goto not_found_or_invalid; |
676 | 0 | res = ist2(trash->area, trash->data); |
677 | 0 | goto end; |
678 | | |
679 | 0 | default: |
680 | 0 | goto not_found_or_invalid; |
681 | 0 | } |
682 | 0 | break; |
683 | | |
684 | 0 | default: |
685 | 0 | goto not_found_or_invalid; |
686 | 0 | } |
687 | | |
688 | 0 | end: |
689 | 0 | return res; |
690 | | |
691 | 0 | need_more: |
692 | 0 | return ist2(istptr(msg), 0); |
693 | | |
694 | 0 | not_found_or_invalid: |
695 | 0 | return IST_NULL; |
696 | 0 | } |
697 | | |
698 | | /* Parses a CONNECT packet : |
699 | | * https://public.dhe.ibm.com/software/dw/webservices/ws-mqtt/mqtt-v3r1.html#connect |
700 | | * https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718028 |
701 | | * https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901033 |
702 | | * |
703 | | * <parser> should point right after the MQTT fixed header. The remaining length |
704 | | * was already checked, thus missing data is an error. On success, the result of |
705 | | * the parsing is stored in <mpkt>. |
706 | | * |
707 | | * Returns: |
708 | | * MQTT_INVALID_MESSAGE if the CONNECT message is invalid |
709 | | * MQTT_VALID_MESSAGE if the CONNECT message looks valid |
710 | | */ |
711 | | static int mqtt_parse_connect(struct ist parser, struct mqtt_pkt *mpkt) |
712 | 0 | { |
713 | | /* The parser length is stored to be sure exactly consumed the announced |
714 | | * remaining length. */ |
715 | 0 | size_t orig_len = istlen(parser); |
716 | 0 | int ret = MQTT_INVALID_MESSAGE; |
717 | | |
718 | | /* |
719 | | * parsing variable header |
720 | | */ |
721 | | /* read protocol_name */ |
722 | 0 | parser = mqtt_read_string(parser, &mpkt->data.connect.var_hdr.protocol_name); |
723 | 0 | if (!isttest(parser) || !(isteqi(mpkt->data.connect.var_hdr.protocol_name, ist("MQTT")) || isteqi(mpkt->data.connect.var_hdr.protocol_name, ist("MQIsdp")))) |
724 | 0 | goto end; |
725 | | |
726 | | /* read protocol_version */ |
727 | 0 | parser = mqtt_read_1byte_int(parser, &mpkt->data.connect.var_hdr.protocol_version); |
728 | 0 | if (!isttest(parser)) |
729 | 0 | goto end; |
730 | 0 | if (mpkt->data.connect.var_hdr.protocol_version != MQTT_VERSION_3_1 && |
731 | 0 | mpkt->data.connect.var_hdr.protocol_version != MQTT_VERSION_3_1_1 && |
732 | 0 | mpkt->data.connect.var_hdr.protocol_version != MQTT_VERSION_5_0) |
733 | 0 | goto end; |
734 | | |
735 | | /* read flags */ |
736 | | /* bit 1 is 'reserved' and must be set to 0 in CONNECT message flags */ |
737 | 0 | parser = mqtt_read_1byte_int(parser, &mpkt->data.connect.var_hdr.flags); |
738 | 0 | if (!isttest(parser) || (mpkt->data.connect.var_hdr.flags & MQTT_CONNECT_FL_RESERVED)) |
739 | 0 | goto end; |
740 | | |
741 | | /* if WILL flag must be set to have WILL_QOS flag or WILL_RETAIN set */ |
742 | 0 | if ((mpkt->data.connect.var_hdr.flags & (MQTT_CONNECT_FL_WILL|MQTT_CONNECT_FL_WILL_QOS|MQTT_CONNECT_FL_WILL_RETAIN)) == MQTT_CONNECT_FL_WILL_QOS) |
743 | 0 | goto end; |
744 | | |
745 | | /* read keepalive */ |
746 | 0 | parser = mqtt_read_2byte_int(parser, &mpkt->data.connect.var_hdr.keepalive); |
747 | 0 | if (!isttest(parser)) |
748 | 0 | goto end; |
749 | | |
750 | | /* read properties, only available in MQTT_VERSION_5_0 */ |
751 | 0 | if (mpkt->data.connect.var_hdr.protocol_version == MQTT_VERSION_5_0) { |
752 | 0 | struct ist props; |
753 | 0 | unsigned int user_prop_idx = 0; |
754 | 0 | uint64_t fields = 0; |
755 | 0 | uint32_t plen = 0; |
756 | |
|
757 | 0 | parser = mqtt_read_varint(parser, &plen); |
758 | 0 | if (!isttest(parser) || istlen(parser) < plen) |
759 | 0 | goto end; |
760 | 0 | props = ist2(istptr(parser), plen); |
761 | 0 | parser = istadv(parser, props.len); |
762 | |
|
763 | 0 | while (istlen(props) > 0) { |
764 | 0 | switch (*istptr(props)) { |
765 | 0 | case MQTT_PROP_SESSION_EXPIRY_INTERVAL: |
766 | 0 | if (fields & MQTT_FN_BIT_SESSION_EXPIRY_INTERVAL) |
767 | 0 | goto end; |
768 | 0 | props = mqtt_read_4byte_int(istnext(props), &mpkt->data.connect.var_hdr.props.session_expiry_interval); |
769 | 0 | fields |= MQTT_FN_BIT_SESSION_EXPIRY_INTERVAL; |
770 | 0 | break; |
771 | | |
772 | 0 | case MQTT_PROP_RECEIVE_MAXIMUM: |
773 | 0 | if (fields & MQTT_FN_BIT_RECEIVE_MAXIMUM) |
774 | 0 | goto end; |
775 | 0 | props = mqtt_read_2byte_int(istnext(props), &mpkt->data.connect.var_hdr.props.receive_maximum); |
776 | | /* cannot be 0 */ |
777 | 0 | if (!mpkt->data.connect.var_hdr.props.receive_maximum) |
778 | 0 | goto end; |
779 | 0 | fields |= MQTT_FN_BIT_RECEIVE_MAXIMUM; |
780 | 0 | break; |
781 | | |
782 | 0 | case MQTT_PROP_MAXIMUM_PACKET_SIZE: |
783 | 0 | if (fields & MQTT_FN_BIT_MAXIMUM_PACKET_SIZE) |
784 | 0 | goto end; |
785 | 0 | props = mqtt_read_4byte_int(istnext(props), &mpkt->data.connect.var_hdr.props.maximum_packet_size); |
786 | | /* cannot be 0 */ |
787 | 0 | if (!mpkt->data.connect.var_hdr.props.maximum_packet_size) |
788 | 0 | goto end; |
789 | 0 | fields |= MQTT_FN_BIT_MAXIMUM_PACKET_SIZE; |
790 | 0 | break; |
791 | | |
792 | 0 | case MQTT_PROP_TOPIC_ALIAS_MAXIMUM: |
793 | 0 | if (fields & MQTT_FN_BIT_TOPIC_ALIAS_MAXIMUM) |
794 | 0 | goto end; |
795 | 0 | props = mqtt_read_2byte_int(istnext(props), &mpkt->data.connect.var_hdr.props.topic_alias_maximum); |
796 | 0 | fields |= MQTT_FN_BIT_TOPIC_ALIAS_MAXIMUM; |
797 | 0 | break; |
798 | | |
799 | 0 | case MQTT_PROP_REQUEST_RESPONSE_INFORMATION: |
800 | 0 | if (fields & MQTT_FN_BIT_REQUEST_RESPONSE_INFORMATION) |
801 | 0 | goto end; |
802 | 0 | props = mqtt_read_1byte_int(istnext(props), &mpkt->data.connect.var_hdr.props.request_response_information); |
803 | | /* can have only 2 values: 0 or 1 */ |
804 | 0 | if (mpkt->data.connect.var_hdr.props.request_response_information > 1) |
805 | 0 | goto end; |
806 | 0 | fields |= MQTT_FN_BIT_REQUEST_RESPONSE_INFORMATION; |
807 | 0 | break; |
808 | | |
809 | 0 | case MQTT_PROP_REQUEST_PROBLEM_INFORMATION: |
810 | 0 | if (fields & MQTT_FN_BIT_REQUEST_PROBLEM_INFORMATION) |
811 | 0 | goto end; |
812 | 0 | props = mqtt_read_1byte_int(istnext(props), &mpkt->data.connect.var_hdr.props.request_problem_information); |
813 | | /* can have only 2 values: 0 or 1 */ |
814 | 0 | if (mpkt->data.connect.var_hdr.props.request_problem_information > 1) |
815 | 0 | goto end; |
816 | 0 | fields |= MQTT_FN_BIT_REQUEST_PROBLEM_INFORMATION; |
817 | 0 | break; |
818 | | |
819 | 0 | case MQTT_PROP_USER_PROPERTIES: |
820 | | /* if we reached MQTT_PROP_USER_PROPERTY_ENTRIES already, then |
821 | | * we start writing over the first property */ |
822 | 0 | if (user_prop_idx >= MQTT_PROP_USER_PROPERTY_ENTRIES) |
823 | 0 | user_prop_idx = 0; |
824 | | |
825 | | /* read user property name and value */ |
826 | 0 | props = mqtt_read_string(istnext(props), &mpkt->data.connect.var_hdr.props.user_props[user_prop_idx].name); |
827 | 0 | if (!isttest(props)) |
828 | 0 | goto end; |
829 | 0 | props = mqtt_read_string(props, &mpkt->data.connect.var_hdr.props.user_props[user_prop_idx].value); |
830 | 0 | ++user_prop_idx; |
831 | 0 | break; |
832 | | |
833 | 0 | case MQTT_PROP_AUTHENTICATION_METHOD: |
834 | 0 | if (fields & MQTT_FN_BIT_AUTHENTICATION_METHOD) |
835 | 0 | goto end; |
836 | 0 | props = mqtt_read_string(istnext(props), &mpkt->data.connect.var_hdr.props.authentication_method); |
837 | 0 | fields |= MQTT_FN_BIT_AUTHENTICATION_METHOD; |
838 | 0 | break; |
839 | | |
840 | 0 | case MQTT_PROP_AUTHENTICATION_DATA: |
841 | 0 | if (fields & MQTT_FN_BIT_AUTHENTICATION_DATA) |
842 | 0 | goto end; |
843 | 0 | props = mqtt_read_string(istnext(props), &mpkt->data.connect.var_hdr.props.authentication_data); |
844 | 0 | fields |= MQTT_FN_BIT_AUTHENTICATION_DATA; |
845 | 0 | break; |
846 | | |
847 | 0 | default: |
848 | 0 | goto end; |
849 | 0 | } |
850 | | |
851 | 0 | if (!isttest(props)) |
852 | 0 | goto end; |
853 | 0 | } |
854 | 0 | } |
855 | | |
856 | | /* cannot have auth data without auth method */ |
857 | 0 | if (!istlen(mpkt->data.connect.var_hdr.props.authentication_method) && |
858 | 0 | istlen(mpkt->data.connect.var_hdr.props.authentication_data)) |
859 | 0 | goto end; |
860 | | |
861 | | /* parsing payload |
862 | | * |
863 | | * Content of payload is related to flags parsed above and the field order is pre-defined: |
864 | | * Client Identifier, Will Topic, Will Message, User Name, Password |
865 | | */ |
866 | | /* read client identifier */ |
867 | 0 | parser = mqtt_read_string(parser, &mpkt->data.connect.payload.client_identifier); |
868 | 0 | if (!isttest(parser)) |
869 | 0 | goto end; |
870 | | |
871 | | /* read Will Properties, for MQTT v5 only |
872 | | * https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901060 |
873 | | */ |
874 | 0 | if ((mpkt->data.connect.var_hdr.protocol_version == MQTT_VERSION_5_0) && |
875 | 0 | (mpkt->data.connect.var_hdr.flags & MQTT_CONNECT_FL_WILL)) { |
876 | 0 | struct ist props; |
877 | 0 | unsigned int user_prop_idx = 0; |
878 | 0 | uint64_t fields = 0; |
879 | 0 | uint32_t plen = 0; |
880 | |
|
881 | 0 | parser = mqtt_read_varint(parser, &plen); |
882 | 0 | if (!isttest(parser) || istlen(parser) < plen) |
883 | 0 | goto end; |
884 | 0 | props = ist2(istptr(parser), plen); |
885 | 0 | parser = istadv(parser, props.len); |
886 | |
|
887 | 0 | while (istlen(props) > 0) { |
888 | 0 | switch (*istptr(props)) { |
889 | 0 | case MQTT_PROP_WILL_DELAY_INTERVAL: |
890 | 0 | if (fields & MQTT_FN_BIT_DELAY_INTERVAL) |
891 | 0 | goto end; |
892 | 0 | props = mqtt_read_4byte_int(istnext(props), &mpkt->data.connect.payload.will_props.delay_interval); |
893 | 0 | fields |= MQTT_FN_BIT_DELAY_INTERVAL; |
894 | 0 | break; |
895 | | |
896 | 0 | case MQTT_PROP_PAYLOAD_FORMAT_INDICATOR: |
897 | 0 | if (fields & MQTT_FN_BIT_PAYLOAD_FORMAT_INDICATOR) |
898 | 0 | goto end; |
899 | 0 | props = mqtt_read_1byte_int(istnext(props), &mpkt->data.connect.payload.will_props.payload_format_indicator); |
900 | | /* can have only 2 values: 0 or 1 */ |
901 | 0 | if (mpkt->data.connect.payload.will_props.payload_format_indicator > 1) |
902 | 0 | goto end; |
903 | 0 | fields |= MQTT_FN_BIT_PAYLOAD_FORMAT_INDICATOR; |
904 | 0 | break; |
905 | | |
906 | 0 | case MQTT_PROP_MESSAGE_EXPIRY_INTERVAL: |
907 | 0 | if (fields & MQTT_FN_BIT_MESSAGE_EXPIRY_INTERVAL) |
908 | 0 | goto end; |
909 | 0 | props = mqtt_read_4byte_int(istnext(props), &mpkt->data.connect.payload.will_props.message_expiry_interval); |
910 | 0 | fields |= MQTT_FN_BIT_MESSAGE_EXPIRY_INTERVAL; |
911 | 0 | break; |
912 | | |
913 | 0 | case MQTT_PROP_CONTENT_TYPE: |
914 | 0 | if (fields & MQTT_FN_BIT_CONTENT_TYPE) |
915 | 0 | goto end; |
916 | 0 | props = mqtt_read_string(istnext(props), &mpkt->data.connect.payload.will_props.content_type); |
917 | 0 | fields |= MQTT_FN_BIT_CONTENT_TYPE; |
918 | 0 | break; |
919 | | |
920 | 0 | case MQTT_PROP_RESPONSE_TOPIC: |
921 | 0 | if (fields & MQTT_FN_BIT_RESPONSE_TOPIC) |
922 | 0 | goto end; |
923 | 0 | props = mqtt_read_string(istnext(props), &mpkt->data.connect.payload.will_props.response_topic); |
924 | 0 | fields |= MQTT_FN_BIT_RESPONSE_TOPIC; |
925 | 0 | break; |
926 | | |
927 | 0 | case MQTT_PROP_CORRELATION_DATA: |
928 | 0 | if (fields & MQTT_FN_BIT_CORRELATION_DATA) |
929 | 0 | goto end; |
930 | 0 | props = mqtt_read_string(istnext(props), &mpkt->data.connect.payload.will_props.correlation_data); |
931 | 0 | fields |= MQTT_FN_BIT_CORRELATION_DATA; |
932 | 0 | break; |
933 | | |
934 | 0 | case MQTT_PROP_USER_PROPERTIES: |
935 | | /* if we reached MQTT_PROP_USER_PROPERTY_ENTRIES already, then |
936 | | * we start writing over the first property */ |
937 | 0 | if (user_prop_idx >= MQTT_PROP_USER_PROPERTY_ENTRIES) |
938 | 0 | user_prop_idx = 0; |
939 | | |
940 | | /* read user property name and value */ |
941 | 0 | props = mqtt_read_string(istnext(props), &mpkt->data.connect.payload.will_props.user_props[user_prop_idx].name); |
942 | 0 | if (!isttest(props)) |
943 | 0 | goto end; |
944 | 0 | props = mqtt_read_string(props, &mpkt->data.connect.payload.will_props.user_props[user_prop_idx].value); |
945 | 0 | ++user_prop_idx; |
946 | 0 | break; |
947 | | |
948 | 0 | default: |
949 | 0 | goto end; |
950 | 0 | } |
951 | | |
952 | 0 | if (!isttest(props)) |
953 | 0 | goto end; |
954 | 0 | } |
955 | 0 | } |
956 | | |
957 | | /* read Will Topic and Will Message (MQTT 3.1.1) or Payload (MQTT 5.0) */ |
958 | 0 | if (mpkt->data.connect.var_hdr.flags & MQTT_CONNECT_FL_WILL) { |
959 | 0 | parser = mqtt_read_string(parser, &mpkt->data.connect.payload.will_topic); |
960 | 0 | if (!isttest(parser)) |
961 | 0 | goto end; |
962 | 0 | parser = mqtt_read_string(parser, &mpkt->data.connect.payload.will_payload); |
963 | 0 | if (!isttest(parser)) |
964 | 0 | goto end; |
965 | 0 | } |
966 | | |
967 | | /* read User Name */ |
968 | 0 | if (mpkt->data.connect.var_hdr.flags & MQTT_CONNECT_FL_USERNAME) { |
969 | 0 | parser = mqtt_read_string(parser, &mpkt->data.connect.payload.username); |
970 | 0 | if (!isttest(parser)) |
971 | 0 | goto end; |
972 | 0 | } |
973 | | |
974 | | /* read Password */ |
975 | 0 | if (mpkt->data.connect.var_hdr.flags & MQTT_CONNECT_FL_PASSWORD) { |
976 | 0 | parser = mqtt_read_string(parser, &mpkt->data.connect.payload.password); |
977 | 0 | if (!isttest(parser)) |
978 | 0 | goto end; |
979 | 0 | } |
980 | | |
981 | 0 | if ((orig_len - istlen(parser)) == mpkt->fixed_hdr.remaining_length) |
982 | 0 | ret = MQTT_VALID_MESSAGE; |
983 | |
|
984 | 0 | end: |
985 | 0 | return ret; |
986 | 0 | } |
987 | | |
988 | | /* Parses a CONNACK packet : |
989 | | * https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718033 |
990 | | * https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901074 |
991 | | * |
992 | | * <parser> should point right after the MQTT fixed header. The remaining length |
993 | | * was already checked, thus missing data is an error. On success, the result of |
994 | | * the parsing is stored in <mpkt>. |
995 | | * |
996 | | * Returns: |
997 | | * MQTT_INVALID_MESSAGE if the CONNECT message is invalid |
998 | | * MQTT_VALID_MESSAGE if the CONNECT message looks valid |
999 | | */ |
1000 | | static int mqtt_parse_connack(struct ist parser, struct mqtt_pkt *mpkt) |
1001 | 0 | { |
1002 | | /* The parser length is stored to be sure exactly consumed the announced |
1003 | | * remaining length. */ |
1004 | 0 | size_t orig_len = istlen(parser); |
1005 | 0 | int ret = MQTT_INVALID_MESSAGE; |
1006 | |
|
1007 | 0 | if (istlen(parser) < 2) |
1008 | 0 | goto end; |
1009 | 0 | else if (istlen(parser) == 2) |
1010 | 0 | mpkt->data.connack.var_hdr.protocol_version = MQTT_VERSION_3_1_1; |
1011 | 0 | else |
1012 | 0 | mpkt->data.connack.var_hdr.protocol_version = MQTT_VERSION_5_0; |
1013 | | |
1014 | | /* |
1015 | | * parsing variable header |
1016 | | */ |
1017 | | /* read flags */ |
1018 | | /* bits 7 to 1 on flags are reserved and must be 0 */ |
1019 | 0 | parser = mqtt_read_1byte_int(parser, &mpkt->data.connack.var_hdr.flags); |
1020 | 0 | if (!isttest(parser) || (mpkt->data.connack.var_hdr.flags & 0xFE)) |
1021 | 0 | goto end; |
1022 | | |
1023 | | /* read reason_code */ |
1024 | 0 | parser = mqtt_read_1byte_int(parser, &mpkt->data.connack.var_hdr.reason_code); |
1025 | 0 | if (!isttest(parser)) |
1026 | 0 | goto end; |
1027 | | |
1028 | | /* we can leave here for MQTT 3.1.1 */ |
1029 | 0 | if (mpkt->data.connack.var_hdr.protocol_version == MQTT_VERSION_3_1_1) { |
1030 | 0 | if ((orig_len - istlen(parser)) == mpkt->fixed_hdr.remaining_length) |
1031 | 0 | ret = MQTT_VALID_MESSAGE; |
1032 | 0 | goto end; |
1033 | 0 | } |
1034 | | |
1035 | | /* read properties, only available in MQTT_VERSION_5_0 */ |
1036 | 0 | if (mpkt->data.connack.var_hdr.protocol_version == MQTT_VERSION_5_0) { |
1037 | 0 | struct ist props; |
1038 | 0 | unsigned int user_prop_idx = 0; |
1039 | 0 | uint64_t fields = 0; |
1040 | 0 | uint32_t plen = 0; |
1041 | |
|
1042 | 0 | parser = mqtt_read_varint(parser, &plen); |
1043 | 0 | if (!isttest(parser) || istlen(parser) < plen) |
1044 | 0 | goto end; |
1045 | 0 | props = ist2(istptr(parser), plen); |
1046 | 0 | parser = istadv(parser, props.len); |
1047 | |
|
1048 | 0 | while (istlen(props) > 0) { |
1049 | 0 | switch (*istptr(props)) { |
1050 | 0 | case MQTT_PROP_SESSION_EXPIRY_INTERVAL: |
1051 | 0 | if (fields & MQTT_FN_BIT_SESSION_EXPIRY_INTERVAL) |
1052 | 0 | goto end; |
1053 | 0 | props = mqtt_read_4byte_int(istnext(props), &mpkt->data.connack.var_hdr.props.session_expiry_interval); |
1054 | 0 | fields |= MQTT_FN_BIT_SESSION_EXPIRY_INTERVAL; |
1055 | 0 | break; |
1056 | | |
1057 | 0 | case MQTT_PROP_RECEIVE_MAXIMUM: |
1058 | 0 | if (fields & MQTT_FN_BIT_RECEIVE_MAXIMUM) |
1059 | 0 | goto end; |
1060 | 0 | props = mqtt_read_2byte_int(istnext(props), &mpkt->data.connack.var_hdr.props.receive_maximum); |
1061 | | /* cannot be 0 */ |
1062 | 0 | if (!mpkt->data.connack.var_hdr.props.receive_maximum) |
1063 | 0 | goto end; |
1064 | 0 | fields |= MQTT_FN_BIT_RECEIVE_MAXIMUM; |
1065 | 0 | break; |
1066 | | |
1067 | 0 | case MQTT_PROP_MAXIMUM_QOS: |
1068 | 0 | if (fields & MQTT_FN_BIT_MAXIMUM_QOS) |
1069 | 0 | goto end; |
1070 | 0 | props = mqtt_read_1byte_int(istnext(props), &mpkt->data.connack.var_hdr.props.maximum_qos); |
1071 | | /* can have only 2 values: 0 or 1 */ |
1072 | 0 | if (mpkt->data.connack.var_hdr.props.maximum_qos > 1) |
1073 | 0 | goto end; |
1074 | 0 | fields |= MQTT_FN_BIT_MAXIMUM_QOS; |
1075 | 0 | break; |
1076 | | |
1077 | 0 | case MQTT_PROP_RETAIN_AVAILABLE: |
1078 | 0 | if (fields & MQTT_FN_BIT_RETAIN_AVAILABLE) |
1079 | 0 | goto end; |
1080 | 0 | props = mqtt_read_1byte_int(istnext(props), &mpkt->data.connack.var_hdr.props.retain_available); |
1081 | | /* can have only 2 values: 0 or 1 */ |
1082 | 0 | if (mpkt->data.connack.var_hdr.props.retain_available > 1) |
1083 | 0 | goto end; |
1084 | 0 | fields |= MQTT_FN_BIT_RETAIN_AVAILABLE; |
1085 | 0 | break; |
1086 | | |
1087 | 0 | case MQTT_PROP_MAXIMUM_PACKET_SIZE: |
1088 | 0 | if (fields & MQTT_FN_BIT_MAXIMUM_PACKET_SIZE) |
1089 | 0 | goto end; |
1090 | 0 | props = mqtt_read_4byte_int(istnext(props), &mpkt->data.connack.var_hdr.props.maximum_packet_size); |
1091 | | /* cannot be 0 */ |
1092 | 0 | if (!mpkt->data.connack.var_hdr.props.maximum_packet_size) |
1093 | 0 | goto end; |
1094 | 0 | fields |= MQTT_FN_BIT_MAXIMUM_PACKET_SIZE; |
1095 | 0 | break; |
1096 | | |
1097 | 0 | case MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER: |
1098 | 0 | if (fields & MQTT_FN_BIT_ASSIGNED_CLIENT_IDENTIFIER) |
1099 | 0 | goto end; |
1100 | 0 | props = mqtt_read_string(istnext(props), &mpkt->data.connack.var_hdr.props.assigned_client_identifier); |
1101 | 0 | if (!istlen(mpkt->data.connack.var_hdr.props.assigned_client_identifier)) |
1102 | 0 | goto end; |
1103 | 0 | fields |= MQTT_FN_BIT_ASSIGNED_CLIENT_IDENTIFIER; |
1104 | 0 | break; |
1105 | | |
1106 | 0 | case MQTT_PROP_TOPIC_ALIAS_MAXIMUM: |
1107 | 0 | if (fields & MQTT_FN_BIT_TOPIC_ALIAS_MAXIMUM) |
1108 | 0 | goto end; |
1109 | 0 | props = mqtt_read_2byte_int(istnext(props), &mpkt->data.connack.var_hdr.props.topic_alias_maximum); |
1110 | 0 | fields |= MQTT_FN_BIT_TOPIC_ALIAS_MAXIMUM; |
1111 | 0 | break; |
1112 | | |
1113 | 0 | case MQTT_PROP_REASON_STRING: |
1114 | 0 | if (fields & MQTT_FN_BIT_REASON_STRING) |
1115 | 0 | goto end; |
1116 | 0 | props = mqtt_read_string(istnext(props), &mpkt->data.connack.var_hdr.props.reason_string); |
1117 | 0 | fields |= MQTT_FN_BIT_REASON_STRING; |
1118 | 0 | break; |
1119 | | |
1120 | 0 | case MQTT_PROP_WILDCARD_SUBSCRIPTION_AVAILABLE: |
1121 | 0 | if (fields & MQTT_FN_BIT_WILDCARD_SUBSCRIPTION_AVAILABLE) |
1122 | 0 | goto end; |
1123 | 0 | props = mqtt_read_1byte_int(istnext(props), &mpkt->data.connack.var_hdr.props.wildcard_subscription_available); |
1124 | | /* can have only 2 values: 0 or 1 */ |
1125 | 0 | if (mpkt->data.connack.var_hdr.props.wildcard_subscription_available > 1) |
1126 | 0 | goto end; |
1127 | 0 | fields |= MQTT_FN_BIT_WILDCARD_SUBSCRIPTION_AVAILABLE; |
1128 | 0 | break; |
1129 | | |
1130 | 0 | case MQTT_PROP_SUBSCRIPTION_IDENTIFIERS_AVAILABLE: |
1131 | 0 | if (fields & MQTT_FN_BIT_SUBSCRIPTION_IDENTIFIERS_AVAILABLE) |
1132 | 0 | goto end; |
1133 | 0 | props = mqtt_read_1byte_int(istnext(props), &mpkt->data.connack.var_hdr.props.subscription_identifiers_available); |
1134 | | /* can have only 2 values: 0 or 1 */ |
1135 | 0 | if (mpkt->data.connack.var_hdr.props.subscription_identifiers_available > 1) |
1136 | 0 | goto end; |
1137 | 0 | fields |= MQTT_FN_BIT_SUBSCRIPTION_IDENTIFIERS_AVAILABLE; |
1138 | 0 | break; |
1139 | | |
1140 | 0 | case MQTT_PROP_SHARED_SUBSRIPTION_AVAILABLE: |
1141 | 0 | if (fields & MQTT_FN_BIT_SHARED_SUBSCRIPTION_AVAILABLE) |
1142 | 0 | goto end; |
1143 | 0 | props = mqtt_read_1byte_int(istnext(props), &mpkt->data.connack.var_hdr.props.shared_subscription_available); |
1144 | | /* can have only 2 values: 0 or 1 */ |
1145 | 0 | if (mpkt->data.connack.var_hdr.props.shared_subscription_available > 1) |
1146 | 0 | goto end; |
1147 | 0 | fields |= MQTT_FN_BIT_SHARED_SUBSCRIPTION_AVAILABLE; |
1148 | 0 | break; |
1149 | | |
1150 | 0 | case MQTT_PROP_SERVER_KEEPALIVE: |
1151 | 0 | if (fields & MQTT_FN_BIT_SERVER_KEEPALIVE) |
1152 | 0 | goto end; |
1153 | 0 | props = mqtt_read_2byte_int(istnext(props), &mpkt->data.connack.var_hdr.props.server_keepalive); |
1154 | 0 | fields |= MQTT_FN_BIT_SERVER_KEEPALIVE; |
1155 | 0 | break; |
1156 | | |
1157 | 0 | case MQTT_PROP_RESPONSE_INFORMATION: |
1158 | 0 | if (fields & MQTT_FN_BIT_RESPONSE_INFORMATION) |
1159 | 0 | goto end; |
1160 | 0 | props = mqtt_read_string(istnext(props), &mpkt->data.connack.var_hdr.props.response_information); |
1161 | 0 | fields |= MQTT_FN_BIT_RESPONSE_INFORMATION; |
1162 | 0 | break; |
1163 | | |
1164 | 0 | case MQTT_PROP_SERVER_REFERENCE: |
1165 | 0 | if (fields & MQTT_FN_BIT_SERVER_REFERENCE) |
1166 | 0 | goto end; |
1167 | 0 | props = mqtt_read_string(istnext(props), &mpkt->data.connack.var_hdr.props.server_reference); |
1168 | 0 | fields |= MQTT_FN_BIT_SERVER_REFERENCE; |
1169 | 0 | break; |
1170 | | |
1171 | 0 | case MQTT_PROP_USER_PROPERTIES: |
1172 | | /* if we reached MQTT_PROP_USER_PROPERTY_ENTRIES already, then |
1173 | | * we start writing over the first property */ |
1174 | 0 | if (user_prop_idx >= MQTT_PROP_USER_PROPERTY_ENTRIES) |
1175 | 0 | user_prop_idx = 0; |
1176 | | |
1177 | | /* read user property name and value */ |
1178 | 0 | props = mqtt_read_string(istnext(props), &mpkt->data.connack.var_hdr.props.user_props[user_prop_idx].name); |
1179 | 0 | if (!isttest(props)) |
1180 | 0 | goto end; |
1181 | 0 | props = mqtt_read_string(props, &mpkt->data.connack.var_hdr.props.user_props[user_prop_idx].value); |
1182 | 0 | ++user_prop_idx; |
1183 | 0 | break; |
1184 | | |
1185 | 0 | case MQTT_PROP_AUTHENTICATION_METHOD: |
1186 | 0 | if (fields & MQTT_FN_BIT_AUTHENTICATION_METHOD) |
1187 | 0 | goto end; |
1188 | 0 | props = mqtt_read_string(istnext(props), &mpkt->data.connack.var_hdr.props.authentication_method); |
1189 | 0 | fields |= MQTT_FN_BIT_AUTHENTICATION_METHOD; |
1190 | 0 | break; |
1191 | | |
1192 | 0 | case MQTT_PROP_AUTHENTICATION_DATA: |
1193 | 0 | if (fields & MQTT_FN_BIT_AUTHENTICATION_DATA) |
1194 | 0 | goto end; |
1195 | 0 | props = mqtt_read_string(istnext(props), &mpkt->data.connack.var_hdr.props.authentication_data); |
1196 | 0 | fields |= MQTT_FN_BIT_AUTHENTICATION_DATA; |
1197 | 0 | break; |
1198 | | |
1199 | 0 | default: |
1200 | 0 | goto end; |
1201 | 0 | } |
1202 | | |
1203 | 0 | if (!isttest(props)) |
1204 | 0 | goto end; |
1205 | 0 | } |
1206 | 0 | } |
1207 | | |
1208 | 0 | if ((orig_len - istlen(parser)) == mpkt->fixed_hdr.remaining_length) |
1209 | 0 | ret = MQTT_VALID_MESSAGE; |
1210 | 0 | end: |
1211 | 0 | return ret; |
1212 | 0 | } |
1213 | | |
1214 | | |
1215 | | /* Parses and validates a MQTT packet |
1216 | | * https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html#_Toc398718028 |
1217 | | * |
1218 | | * For now, due to HAProxy limitation, only validation of CONNECT and CONNACK packets |
1219 | | * are supported. |
1220 | | * |
1221 | | * - check FIXED_HDR |
1222 | | * - check remaining length |
1223 | | * - check variable headers and payload |
1224 | | * |
1225 | | * if <mpkt> is not NULL, then this structure will be filled up as well. An |
1226 | | * unsupported packet type is considered as invalid. It is not a problem for now |
1227 | | * because only the first packet on each side can be parsed (CONNECT for the |
1228 | | * client and CONNACK for the server). |
1229 | | * |
1230 | | * Returns: |
1231 | | * MQTT_INVALID_MESSAGE if the message is invalid |
1232 | | * MQTT_NEED_MORE_DATA if we need more data to fully validate the message |
1233 | | * MQTT_VALID_MESSAGE if the message looks valid |
1234 | | */ |
1235 | | int mqtt_validate_message(const struct ist msg, struct mqtt_pkt *mpkt) |
1236 | 0 | { |
1237 | 0 | struct ist parser; |
1238 | 0 | struct mqtt_pkt tmp_mpkt; |
1239 | 0 | int ret = MQTT_INVALID_MESSAGE; |
1240 | |
|
1241 | 0 | if (!mpkt) |
1242 | 0 | mpkt = &tmp_mpkt; |
1243 | 0 | memset(mpkt, 0, sizeof(*mpkt)); |
1244 | |
|
1245 | 0 | parser = msg; |
1246 | 0 | if (istlen(msg) < MQTT_MIN_PKT_SIZE) { |
1247 | 0 | ret = MQTT_NEED_MORE_DATA; |
1248 | 0 | goto end; |
1249 | 0 | } |
1250 | | |
1251 | | /* parse the MQTT fixed header */ |
1252 | 0 | parser = mqtt_read_fixed_hdr(parser, mpkt); |
1253 | 0 | if (!isttest(parser)) { |
1254 | 0 | ret = MQTT_INVALID_MESSAGE; |
1255 | 0 | goto end; |
1256 | 0 | } |
1257 | | |
1258 | | /* Now parsing "remaining length" field */ |
1259 | 0 | parser = mqtt_read_varint(parser, &mpkt->fixed_hdr.remaining_length); |
1260 | 0 | if (!isttest(parser)) { |
1261 | 0 | ret = MQTT_INVALID_MESSAGE; |
1262 | 0 | goto end; |
1263 | 0 | } |
1264 | | |
1265 | 0 | if (istlen(parser) < mpkt->fixed_hdr.remaining_length) |
1266 | 0 | return MQTT_NEED_MORE_DATA; |
1267 | | |
1268 | | /* Now parsing the variable header and payload, which is based on the packet type */ |
1269 | 0 | switch (mpkt->fixed_hdr.type) { |
1270 | 0 | case MQTT_CPT_CONNECT: |
1271 | 0 | ret = mqtt_parse_connect(parser, mpkt); |
1272 | 0 | break; |
1273 | 0 | case MQTT_CPT_CONNACK: |
1274 | 0 | ret = mqtt_parse_connack(parser, mpkt); |
1275 | 0 | break; |
1276 | 0 | default: |
1277 | 0 | break; |
1278 | 0 | } |
1279 | | |
1280 | 0 | end: |
1281 | 0 | return ret; |
1282 | 0 | } |