/__w/emqttsn/emqttsn/_build/test/cover/ct/emqttsn_frame.html

1 %%-------------------------------------------------------------------------
2 %% Copyright (c) 2020-2022 EMQ Technologies Co., Ltd. All Rights Reserved.
3 %%
4 %% Licensed under the Apache License, Version 2.0 (the "License");
5 %% you may not use this file except in compliance with the License.
6 %% You may obtain a copy of the License at
7 %%
8 %% http://www.apache.org/licenses/LICENSE-2.0
9 %%
10 %% Unless required by applicable law or agreed to in writing, software
11 %% distributed under the License is distributed on an "AS IS" BASIS,
12 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 %% See the License for the specific language governing permissions and
14 %% limitations under the License.
15 %%-------------------------------------------------------------------------
16
17 %% @doc MQTT-SN frame parser and serializer
18 %%
19 %% @private
20 -module(emqttsn_frame).
21
22 -include_lib("stdlib/include/assert.hrl").
23
24 -include("emqttsn.hrl").
25
26 -export([parse/1, parse/2, serialize/1, serialize/2, parse_leading_len/2]).
27
28 -type parse_result() :: {ok, #mqttsn_packet{}} | {error, term()}.
29
30 -spec boolean_to_integer(boolean()) -> 0 | 1.
31 boolean_to_integer(Bool) ->
32 348 case Bool of
33 true ->
34 60 1;
35 false ->
36 288 0
37 end.
38
39 -spec integer_to_boolean(0 | 1) -> boolean().
40 integer_to_boolean(Value) ->
41 56 case Value of
42 1 ->
43
:-(
true;
44 0 ->
45 56 false
46 end.
47
48 %%--------------------------------------------------------------------
49 %% Init Config State
50 %%--------------------------------------------------------------------
51
52 %%--------------------------------------------------------------------
53 %% Parse MQTT-SN Frame
54 %%--------------------------------------------------------------------
55
56 % parser API for packet
57 -spec parse(bitstring()) -> parse_result().
58 parse(Bin) ->
59 64 parse(Bin, #config{}).
60
61 -spec parse(bitstring(), config()) -> parse_result().
62 parse(Bin, Config) ->
63 64 case parse_leading_len(Bin, Config) of
64 {ok, Length, LeadingLength, Rest} ->
65 64 #config{strict_mode = StrictMode} = Config,
66 64 StrictMode andalso ?assertEqual(byte_size(Rest) + LeadingLength, Length),
67 64 {ok, parse_payload_from_type(Rest, Config)};
68 {error, Reason} ->
69
:-(
{error, Reason}
70 end.
71
72 -spec parse_leading_len(bitstring(), #config{}) ->
73 {ok, pos_integer(), pos_integer(), bitstring()} | {error, term()}.
74 parse_leading_len(<<16#01:8/integer, Length:16/integer, Rest/binary>>, Config) ->
75 3 SelfLength = 3,
76 3 #config{max_size = MaxSize} = Config,
77 3 case Length =< MaxSize of
78 true ->
79 2 {ok, Length, SelfLength, Rest};
80 false ->
81 1 {error, frame_too_large}
82 end;
83 parse_leading_len(<<Length:8/integer, Rest/binary>>, Config) ->
84 67 SelfLength = 1,
85 67 #config{max_size = MaxSize} = Config,
86 67 case Length =< MaxSize of
87 true ->
88 66 {ok, Length, SelfLength, Rest};
89 false ->
90 1 {error, frame_too_large}
91 end.
92
93 % parse the message type to a header of packet
94 -spec parse_payload_from_type(binary(), #config{}) -> mqttsn_packet().
95 parse_payload_from_type(<<Type:8/integer, Rest/binary>>, Config) ->
96 64 Header = #mqttsn_packet_header{type = Type},
97 64 Payload = parse_payload(Rest, Header, Config),
98 64 #mqttsn_packet{header = Header, payload = Payload}.
99
100 % dispatch to the parser of different message type
101 -spec parse_payload(binary(), #mqttsn_packet_header{}, #config{}) -> packet_payload().
102 parse_payload(<<GwId:8/integer, Duration:16/integer>>,
103 #mqttsn_packet_header{type = ?ADVERTISE},
104 _Config) ->
105 1 #mqttsn_packet_advertise{gateway_id = GwId, duration = Duration};
106 parse_payload(<<Radius:8/integer>>, #mqttsn_packet_header{type = ?SEARCHGW}, _Config) ->
107
:-(
#mqttsn_packet_searchgw{radius = Radius};
108 parse_payload(Bin, #mqttsn_packet_header{type = ?GWINFO}, Config) ->
109 3 parse_gwinfo_msg(Bin, Config);
110 parse_payload(<<ReturnCode:8/integer>>,
111 #mqttsn_packet_header{type = ?CONNACK},
112 _Config) ->
113 18 #mqttsn_packet_connack{return_code = ReturnCode};
114 parse_payload(<<>>, #mqttsn_packet_header{type = ?WILLTOPICREQ}, _Config) ->
115 1 #mqttsn_packet_willtopicreq{};
116 parse_payload(<<>>, #mqttsn_packet_header{type = ?WILLMSGREQ}, _Config) ->
117 1 #mqttsn_packet_willmsgreq{};
118 parse_payload(<<TopicId:16/integer, MsgId:16/integer, TopicNameBin/binary>>,
119 #mqttsn_packet_header{type = ?REGISTER},
120 _Config) ->
121
:-(
TopicName = binary_to_list(TopicNameBin),
122
:-(
#mqttsn_packet_register{source = ?SERVER,
123 topic_id = TopicId,
124 packet_id = MsgId,
125 topic_name = TopicName};
126 parse_payload(<<TopicId:16/integer, MsgId:16/integer, ReturnCode:8/integer>>,
127 #mqttsn_packet_header{type = ?REGACK},
128 _Config) ->
129 6 #mqttsn_packet_regack{topic_id = TopicId,
130 packet_id = MsgId,
131 return_code = ReturnCode};
132 parse_payload(<<SerFlag:1/binary,
133 TopicIdOrNameBin:2/binary,
134 MsgId:16/integer,
135 MessageBin/bitstring>>,
136 #mqttsn_packet_header{type = ?PUBLISH},
137 Config) ->
138 6 Message = binary_to_list(MessageBin),
139 6 Flag = parse_flag(SerFlag, Config),
140 6 #mqttsn_packet_flag{topic_id_type = TopicIdType} = Flag,
141 6 case TopicIdType of
142 ?SHORT_TOPIC_NAME ->
143 4 #mqttsn_packet_publish{flag = Flag,
144 topic_id_or_name = binary_to_list(TopicIdOrNameBin),
145 packet_id = MsgId,
146 message = Message};
147 _ ->
148 2 <<TopicIdOrName:16/integer>> = TopicIdOrNameBin,
149 2 #mqttsn_packet_publish{flag = Flag,
150 topic_id_or_name = TopicIdOrName,
151 packet_id = MsgId,
152 message = Message}
153 end;
154 parse_payload(<<TopicId:16/integer, MsgId:16/integer, ReturnCode:8/integer>>,
155 #mqttsn_packet_header{type = ?PUBACK},
156 _Config) ->
157 2 #mqttsn_packet_puback{topic_id = TopicId,
158 packet_id = MsgId,
159 return_code = ReturnCode};
160 parse_payload(<<MsgId:16/integer>>, #mqttsn_packet_header{type = ?PUBREC}, _Config) ->
161 1 #mqttsn_packet_pubrec{packet_id = MsgId};
162 parse_payload(<<MsgId:16/integer>>, #mqttsn_packet_header{type = ?PUBREL}, _Config) ->
163
:-(
#mqttsn_packet_pubrel{packet_id = MsgId};
164 parse_payload(<<MsgId:16/integer>>, #mqttsn_packet_header{type = ?PUBCOMP}, _Config) ->
165 1 #mqttsn_packet_pubcomp{packet_id = MsgId};
166 parse_payload(<<Flag:1/binary,
167 TopicId:16/integer,
168 MsgId:16/integer,
169 ReturnCode:8/integer>>,
170 #mqttsn_packet_header{type = ?SUBACK},
171 Config) ->
172 8 #mqttsn_packet_suback{flag = parse_flag(Flag, Config),
173 topic_id = TopicId,
174 packet_id = MsgId,
175 return_code = ReturnCode};
176 parse_payload(<<MsgId:16/integer>>, #mqttsn_packet_header{type = ?UNSUBACK}, _Config) ->
177 1 #mqttsn_packet_unsuback{packet_id = MsgId};
178 parse_payload(<<ClienIdBin/binary>>, #mqttsn_packet_header{type = ?PINGREQ}, _Config) ->
179 1 ClienId = binary_to_list(ClienIdBin),
180 1 #mqttsn_packet_pingreq{empty_packet = false, client_id = ClienId};
181 parse_payload(<<>>, #mqttsn_packet_header{type = ?PINGRESP}, _Config) ->
182 2 #mqttsn_packet_pingresp{};
183 parse_payload(Bin, #mqttsn_packet_header{type = ?DISCONNECT}, Config) ->
184 12 parse_disconnect_msg(Bin, Config);
185 parse_payload(<<ReturnCode:8/integer>>,
186 #mqttsn_packet_header{type = ?WILLTOPICRESP},
187 _Config) ->
188
:-(
#mqttsn_packet_willtopicresp{return_code = ReturnCode};
189 parse_payload(<<ReturnCode:8/integer>>,
190 #mqttsn_packet_header{type = ?WILLMSGRESP},
191 _Config) ->
192
:-(
#mqttsn_packet_willmsgresp{return_code = ReturnCode}.
193
194 -spec parse_flag(binary(), #config{}) -> flag().
195 parse_flag(<<DUP:1/integer,
196 Qos:2/integer,
197 Retain:1/integer,
198 Will:1/integer,
199 CleanSession:1/integer,
200 TopicIdType:2/integer>>,
201 _Config) ->
202 14 #mqttsn_packet_flag{dup = integer_to_boolean(DUP),
203 qos = Qos,
204 retain = integer_to_boolean(Retain),
205 will = integer_to_boolean(Will),
206 clean_session = integer_to_boolean(CleanSession),
207 topic_id_type = TopicIdType}.
208
209 -spec parse_gwinfo_msg(binary(), #config{}) -> packet_payload().
210 parse_gwinfo_msg(<<GWId:8/integer>>, _Config) ->
211 2 #mqttsn_packet_gwinfo{source = ?SERVER, gateway_id = GWId};
212 parse_gwinfo_msg(<<GWId:8/integer,
213 Adr1:8/integer,
214 Adr2:8/integer,
215 Adr3:8/integer,
216 Adr4:8/integer>>,
217 _Config) ->
218 1 #mqttsn_packet_gwinfo{source = ?CLIENT,
219 gateway_id = GWId,
220 gateway_add = {Adr1, Adr2, Adr3, Adr4}}.
221
222 -spec parse_disconnect_msg(binary(), #config{}) -> packet_payload().
223 parse_disconnect_msg(<<>>, _Config) ->
224 12 #mqttsn_packet_disconnect{empty_packet = true};
225 parse_disconnect_msg(<<Duration:16/integer>>, _Config) ->
226
:-(
#mqttsn_packet_disconnect{empty_packet = false, duration = Duration}.
227
228 %%--------------------------------------------------------------------
229 %% Serialize MQTT Packet
230 %%--------------------------------------------------------------------
231
232 -spec serialize(#mqttsn_packet{}) -> bitstring().
233 serialize(Packet) ->
234
:-(
serialize(Packet, #config{}).
235
236 -spec serialize(#mqttsn_packet{}, #config{}) -> bitstring().
237 serialize(#mqttsn_packet{header = Header, payload = Payload}, Config) ->
238 192 PayloadBin = serialize_payload(Payload, Config),
239 192 Length = iolist_size(PayloadBin),
240 192 WholeLength =
241 case Length of
242 L when L =< 256 ->
243 190 Length + 2;
244 L when 256 < L andalso L < ?MAX_PACKET_SIZE ->
245 2 Length + 4
246 end,
247 192 HeaderBin = serialize_header(Header, WholeLength, Config),
248
249 192 <<HeaderBin/binary, PayloadBin/binary>>.
250
251 -spec serialize_header(#mqttsn_packet_header{}, non_neg_integer(), #config{}) -> binary().
252 serialize_header(#mqttsn_packet_header{type = Type}, Length, _Config) when Length < 256 ->
253 190 <<Length:8/integer, Type:8/integer>>;
254 serialize_header(#mqttsn_packet_header{type = Type}, Length, _Config)
255 when 256 =< Length andalso Length =< ?MAX_PACKET_SIZE ->
256 2 <<1:8/integer, Length:16/integer, Type:8/integer>>.
257
258 -spec serialize_flag(#mqttsn_packet_flag{}, #config{}) -> bin_1_byte().
259 serialize_flag(#mqttsn_packet_flag{dup = Dup,
260 qos = Qos,
261 retain = Retain,
262 will = Will,
263 clean_session = CleanSession,
264 topic_id_type = TopicIdType},
265 _Config) ->
266 87 DupValue = boolean_to_integer(Dup),
267 87 RetainValue = boolean_to_integer(Retain),
268 87 WillValue = boolean_to_integer(Will),
269 87 CleanSessionValue = boolean_to_integer(CleanSession),
270 87 <<DupValue:1/integer,
271 Qos:2/integer,
272 RetainValue:1/integer,
273 WillValue:1/integer,
274 CleanSessionValue:1/integer,
275 TopicIdType:2/integer>>.
276
277 -spec serialize_payload(packet_payload(), #config{}) -> iodata().
278 serialize_payload(#mqttsn_packet_advertise{gateway_id = GwId, duration = Duration},
279 _Config) ->
280 1 <<GwId:8/integer, Duration:16/integer>>;
281 serialize_payload(#mqttsn_packet_searchgw{radius = Radius}, _Config) ->
282 54 <<Radius:8>>;
283 serialize_payload(#mqttsn_packet_gwinfo{source = ?CLIENT,
284 gateway_id = GWId,
285 gateway_add = GateWayAdd},
286 _Config) ->
287 1 {Adr1, Adr2, Adr3, Adr4} = GateWayAdd,
288 1 <<GWId:8/integer, Adr1:8/integer, Adr2:8/integer, Adr3:8/integer, Adr4:8/integer>>;
289 serialize_payload(#mqttsn_packet_gwinfo{source = ?SERVER, gateway_id = GWId}, _Config) ->
290 1 <<GWId:8/integer>>;
291 serialize_payload(#mqttsn_packet_connect{flag = Flag,
292 duration = Duration,
293 client_id = ClientId},
294 Config) ->
295 48 SerFlag = serialize_flag(Flag, Config),
296 48 ClientIdBin = list_to_binary(ClientId),
297 48 <<SerFlag/bitstring, 1:8/integer, Duration:16/integer, ClientIdBin/binary>>;
298 serialize_payload(#mqttsn_packet_connack{return_code = ReturnCode}, _Config) ->
299 1 <<ReturnCode:8/integer>>;
300 serialize_payload(#mqttsn_packet_willtopicreq{}, _Config) ->
301
:-(
<<>>;
302 serialize_payload(#mqttsn_packet_willtopic{} = Bin, Config) ->
303 1 serialize_will_topic(Bin, Config);
304 serialize_payload(#mqttsn_packet_willmsgreq{}, _Config) ->
305
:-(
<<>>;
306 serialize_payload(#mqttsn_packet_willmsg{will_msg = WillMsg}, _Config) ->
307 1 WillMsgBin = list_to_binary(WillMsg),
308 1 WillMsgBin;
309 serialize_payload(#mqttsn_packet_register{source = ?SERVER,
310 topic_id = TopicId,
311 packet_id = MsgId,
312 topic_name = TopicName},
313 _Config) ->
314
:-(
TopicNameBin = list_to_binary(TopicName),
315
:-(
<<TopicId:16/integer, MsgId:16/integer, TopicNameBin/binary>>;
316 serialize_payload(#mqttsn_packet_register{source = ?CLIENT,
317 topic_id = _TopicId,
318 packet_id = MsgId,
319 topic_name = TopicName},
320 _Config) ->
321 12 TopicNameBin = list_to_binary(TopicName),
322 12 <<0:16/integer, MsgId:16/integer, TopicNameBin/binary>>;
323 serialize_payload(#mqttsn_packet_regack{topic_id = TopicId,
324 packet_id = MsgId,
325 return_code = _ReturnCode},
326 _Config) ->
327 1 <<TopicId:16/integer, MsgId:16/integer, 8/integer>>;
328 serialize_payload(#mqttsn_packet_publish{flag = Flag,
329 topic_id_or_name = TopicIdOrName,
330 packet_id = MsgId,
331 message = Message},
332 Config) ->
333 16 MessageBin = list_to_binary(Message),
334 16 SerFlag = serialize_flag(Flag, Config),
335 16 #mqttsn_packet_flag{topic_id_type = TopicIdType} = Flag,
336 16 case TopicIdType of
337 ?SHORT_TOPIC_NAME ->
338 12 TopicNameBin = list_to_binary(TopicIdOrName),
339 12 <<SerFlag:1/binary, TopicNameBin:2/binary, MsgId:16/integer, MessageBin/binary>>;
340 _ ->
341 4 TopicId = TopicIdOrName,
342 4 <<SerFlag:1/binary, TopicId:16/integer, MsgId:16/integer, MessageBin/binary>>
343 end;
344 serialize_payload(#mqttsn_packet_puback{topic_id = TopicId,
345 packet_id = MsgId,
346 return_code = ReturnCode},
347 _Config) ->
348 1 <<TopicId:16/integer, MsgId:16/integer, ReturnCode:8/integer>>;
349 serialize_payload(#mqttsn_packet_pubrec{packet_id = MsgId}, _Config) ->
350
:-(
<<MsgId:16/integer>>;
351 serialize_payload(#mqttsn_packet_pubrel{packet_id = MsgId}, _Config) ->
352 1 <<MsgId:16/integer>>;
353 serialize_payload(#mqttsn_packet_pubcomp{packet_id = MsgId}, _Config) ->
354
:-(
<<MsgId:16/integer>>;
355 serialize_payload(#mqttsn_packet_suback{flag = Flag,
356 topic_id = TopicId,
357 packet_id = MsgId,
358 return_code = ReturnCode},
359 Config) ->
360 1 SerFlag = serialize_flag(Flag, Config),
361 1 <<SerFlag:1/binary, TopicId:16/integer, MsgId:16/integer, ReturnCode:8/integer>>;
362 serialize_payload(#mqttsn_packet_subscribe{flag = Flag,
363 packet_id = MsgId,
364 topic_name = TopicName,
365 topic_id = TopicId},
366 Config) ->
367 14 SerFlag = serialize_flag(Flag, Config),
368 14 Bin = serialize_topic_name_or_id(Flag, TopicName, TopicId, Config),
369 14 <<SerFlag:1/binary, MsgId:16/integer, Bin/binary>>;
370 serialize_payload(#mqttsn_packet_unsubscribe{flag = Flag,
371 packet_id = MsgId,
372 topic_name = TopicName,
373 topic_id = TopicId},
374 Config) ->
375 7 SerFlag = serialize_flag(Flag, Config),
376 7 Bin = serialize_topic_name_or_id(Flag, TopicName, TopicId, Config),
377 7 <<SerFlag:1/binary, MsgId:16/integer, Bin/binary>>;
378 serialize_payload(#mqttsn_packet_unsuback{packet_id = MsgId}, _Config) ->
379
:-(
<<MsgId:16/integer>>;
380 serialize_payload(#mqttsn_packet_pingreq{} = Bin, Config) ->
381 3 serialize_pingreq(Bin, Config);
382 serialize_payload(#mqttsn_packet_pingresp{}, _Config) ->
383 2 <<>>;
384 serialize_payload(#mqttsn_packet_disconnect{} = Bin, Config) ->
385 26 serialize_disconnect(Bin, Config);
386 serialize_payload(#mqttsn_packet_willtopicupd{} = Bin, Config) ->
387
:-(
serialize_willtopicupd(Bin, Config);
388 serialize_payload(#mqttsn_packet_willtopicresp{return_code = ReturnCode}, _Config) ->
389
:-(
<<ReturnCode:8/integer>>;
390 serialize_payload(#mqttsn_packet_willmsgupd{will_msg = WillMsg}, _Config) ->
391
:-(
list_to_binary(WillMsg);
392 serialize_payload(#mqttsn_packet_willmsgresp{return_code = ReturnCode}, _Config) ->
393
:-(
<<ReturnCode:8/integer>>.
394
395 % serialize willTopic packet payload
396 -spec serialize_will_topic(#mqttsn_packet_willtopic{}, #config{}) -> iodata().
397 serialize_will_topic(#mqttsn_packet_willtopic{empty_packet = true}, _Config) ->
398
:-(
<<>>;
399 serialize_will_topic(#mqttsn_packet_willtopic{empty_packet = false,
400 flag = Flag,
401 will_topic = WillTopic},
402 Config) ->
403 1 WillTopicBin = list_to_binary(WillTopic),
404 1 SerFlag = serialize_flag(Flag, Config),
405 1 <<SerFlag/binary, WillTopicBin/binary>>.
406
407 % serialize topicName or topicId by Flag argument topicIdType
408 -spec serialize_topic_name_or_id(#mqttsn_packet_flag{},
409 string(),
410 topic_id(),
411 #config{}) ->
412 bitstring().
413 serialize_topic_name_or_id(#mqttsn_packet_flag{topic_id_type = TopicIdType},
414 _TopicName,
415 TopicId,
416 _Config)
417 when TopicIdType == ?PRE_DEF_TOPIC_ID orelse TopicIdType == ?TOPIC_ID ->
418 2 <<TopicId:16/integer>>;
419 serialize_topic_name_or_id(#mqttsn_packet_flag{topic_id_type = TopicIdType},
420 TopicName,
421 _TopicId,
422 _Config)
423 when TopicIdType == ?SHORT_TOPIC_NAME ->
424 19 list_to_binary(TopicName).
425
426 % serialize pingReq packet payload
427 -spec serialize_pingreq(#mqttsn_packet_pingreq{}, #config{}) -> iodata().
428 serialize_pingreq(#mqttsn_packet_pingreq{empty_packet = true}, _Config) ->
429 2 <<>>;
430 serialize_pingreq(#mqttsn_packet_pingreq{empty_packet = false, client_id = ClienId},
431 _Config) ->
432 1 list_to_binary(ClienId).
433
434 -spec serialize_disconnect(#mqttsn_packet_disconnect{}, #config{}) -> iodata().
435 serialize_disconnect(#mqttsn_packet_disconnect{empty_packet = true}, _Config) ->
436 25 <<>>;
437 serialize_disconnect(#mqttsn_packet_disconnect{empty_packet = false, duration = Duration},
438 _Config) ->
439 1 <<Duration:16/integer>>.
440
441 -spec serialize_willtopicupd(#mqttsn_packet_willtopicupd{}, #config{}) -> iodata().
442 serialize_willtopicupd(#mqttsn_packet_willtopicupd{empty_packet = true}, _Config) ->
443
:-(
<<>>;
444 serialize_willtopicupd(#mqttsn_packet_willtopicupd{empty_packet = false,
445 flag = Flag,
446 will_topic = WillTopic},
447 Config) ->
448
:-(
SerFlag = serialize_flag(Flag, Config),
449
:-(
WillTopicBin = list_to_binary(WillTopic),
450
:-(
<<SerFlag/binary, WillTopicBin/binary>>.
Line Hits Source