mqtt-ccutrer 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/mqtt/proxy.rb ADDED
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MQTT
4
+ # Class for implementing a proxy to filter/mangle MQTT packets.
5
+ class Proxy
6
+ # Address to bind listening socket to
7
+ attr_reader :local_host
8
+
9
+ # Port to bind listening socket to
10
+ attr_reader :local_port
11
+
12
+ # Address of upstream server to send packets upstream to
13
+ attr_reader :server_host
14
+
15
+ # Port of upstream server to send packets upstream to.
16
+ attr_reader :server_port
17
+
18
+ # Time in seconds before disconnecting an idle connection
19
+ attr_reader :select_timeout
20
+
21
+ # Ruby Logger object to send informational messages to
22
+ attr_reader :logger
23
+
24
+ # A filter Proc for packets coming from the client (to the server).
25
+ attr_writer :client_filter
26
+
27
+ # A filter Proc for packets coming from the server (to the client).
28
+ attr_writer :server_filter
29
+
30
+ # Create a new MQTT Proxy instance.
31
+ #
32
+ # Possible argument keys:
33
+ #
34
+ # :local_host Address to bind listening socket to.
35
+ # :local_port Port to bind listening socket to.
36
+ # :server_host Address of upstream server to send packets upstream to.
37
+ # :server_port Port of upstream server to send packets upstream to.
38
+ # :select_timeout Time in seconds before disconnecting a connection.
39
+ # :logger Ruby Logger object to send informational messages to.
40
+ #
41
+ # NOTE: be careful not to connect to yourself!
42
+ def initialize(args = {})
43
+ @local_host = args[:local_host] || '0.0.0.0'
44
+ @local_port = args[:local_port] || MQTT::DEFAULT_PORT
45
+ @server_host = args[:server_host]
46
+ @server_port = args[:server_port] || 18_830
47
+ @select_timeout = args[:select_timeout] || 60
48
+
49
+ # Setup a logger
50
+ @logger = args[:logger]
51
+ if @logger.nil?
52
+ @logger = Logger.new($stdout)
53
+ @logger.level = Logger::INFO
54
+ end
55
+
56
+ # Default is not to have any filters
57
+ @client_filter = nil
58
+ @server_filter = nil
59
+
60
+ # Create TCP server socket
61
+ @server = TCPServer.open(@local_host, @local_port)
62
+ @logger.info "MQTT::Proxy listening on #{@local_host}:#{@local_port}"
63
+ end
64
+
65
+ # Start accepting connections and processing packets.
66
+ def run
67
+ loop do
68
+ # Wait for a client to connect and then create a thread for it
69
+ Thread.new(@server.accept) do |client_socket|
70
+ logger.info "Accepted client: #{client_socket.peeraddr.join(':')}"
71
+ server_socket = TCPSocket.new(@server_host, @server_port)
72
+ begin
73
+ process_packets(client_socket, server_socket)
74
+ rescue => e
75
+ logger.error e.to_s
76
+ end
77
+ logger.info "Disconnected: #{client_socket.peeraddr.join(':')}"
78
+ server_socket.close
79
+ client_socket.close
80
+ end
81
+ end
82
+ end
83
+
84
+ private
85
+
86
+ def process_packets(client_socket, server_socket)
87
+ loop do
88
+ # Wait for some data on either socket
89
+ selected = IO.select([client_socket, server_socket], nil, nil, @select_timeout)
90
+
91
+ # Timeout
92
+ raise 'Timeout in select' if selected.nil?
93
+
94
+ # Iterate through each of the sockets with data to read
95
+ if selected[0].include?(client_socket)
96
+ packet = MQTT::Packet.read(client_socket)
97
+ logger.debug "client -> <#{packet.type_name}>"
98
+ packet = @client_filter.call(packet) unless @client_filter.nil?
99
+ unless packet.nil?
100
+ server_socket.write(packet)
101
+ logger.debug "<#{packet.type_name}> -> server"
102
+ end
103
+ elsif selected[0].include?(server_socket)
104
+ packet = MQTT::Packet.read(server_socket)
105
+ logger.debug "server -> <#{packet.type_name}>"
106
+ packet = @server_filter.call(packet) unless @server_filter.nil?
107
+ unless packet.nil?
108
+ client_socket.write(packet)
109
+ logger.debug "<#{packet.type_name}> -> client"
110
+ end
111
+ else
112
+ logger.error 'Problem with select: socket is neither server or client'
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,714 @@
1
+ # encoding: BINARY
2
+ # frozen_string_literal: true
3
+
4
+ module MQTT
5
+ module SN
6
+ # Class representing a MQTT::SN Packet
7
+ # Performs binary encoding and decoding of headers
8
+ class Packet
9
+ attr_accessor :duplicate # Duplicate delivery flag
10
+ attr_accessor :qos # Quality of Service level
11
+ attr_accessor :retain # Retain flag
12
+ attr_accessor :request_will # Request that gateway prompts for Will
13
+ attr_accessor :clean_session # When true, subscriptions are deleted after disconnect
14
+ attr_accessor :topic_id_type # One of :normal, :predefined or :short
15
+
16
+ DEFAULTS = {}.freeze
17
+
18
+ # Parse buffer into new packet object
19
+ def self.parse(buffer)
20
+ # Parse the fixed header (length and type)
21
+ length, type_id, body = buffer.unpack('CCa*')
22
+ length, type_id, body = buffer.unpack('xnCa*') if length == 1
23
+
24
+ # Double-check the length
25
+ raise ProtocolException, 'Length of packet is not the same as the length header' if buffer.length != length
26
+
27
+ packet_class = PACKET_TYPES[type_id]
28
+ raise ProtocolException, "Invalid packet type identifier: #{type_id}" if packet_class.nil?
29
+
30
+ # Create a new packet object
31
+ packet = packet_class.new
32
+ packet.parse_body(body)
33
+
34
+ packet
35
+ end
36
+
37
+ # Create a new empty packet
38
+ def initialize(args = {})
39
+ update_attributes(self.class::DEFAULTS.merge(args))
40
+ end
41
+
42
+ def update_attributes(attr = {})
43
+ attr.each_pair do |k, v|
44
+ send("#{k}=", v)
45
+ end
46
+ end
47
+
48
+ # Get the identifer for this packet type
49
+ def type_id
50
+ PACKET_TYPES.each_pair do |key, value|
51
+ return key if instance_of?(value)
52
+ end
53
+ raise "Invalid packet type: #{self.class}"
54
+ end
55
+
56
+ # Serialise the packet
57
+ def to_s
58
+ # Get the packet's variable header and payload
59
+ body = encode_body
60
+
61
+ # Build up the body length field bytes
62
+ body_length = body.length
63
+ raise 'MQTT-SN Packet is too big, maximum packet body size is 65531' if body_length > 65_531
64
+
65
+ if body_length > 253
66
+ [0x01, body_length + 4, type_id].pack('CnC') + body
67
+ else
68
+ [body_length + 2, type_id].pack('CC') + body
69
+ end
70
+ end
71
+
72
+ def parse_body(buffer); end
73
+
74
+ protected
75
+
76
+ def parse_flags(flags)
77
+ self.duplicate = ((flags & 0x80) >> 7) == 0x01
78
+ self.qos = (flags & 0x60) >> 5
79
+ self.qos = -1 if qos == 3
80
+ self.retain = ((flags & 0x10) >> 4) == 0x01
81
+ self.request_will = ((flags & 0x08) >> 3) == 0x01
82
+ self.clean_session = ((flags & 0x04) >> 2) == 0x01
83
+
84
+ self.topic_id_type =
85
+ case (flags & 0x03)
86
+ when 0x0
87
+ :normal
88
+ when 0x1
89
+ :predefined
90
+ when 0x2
91
+ :short
92
+ end
93
+ end
94
+
95
+ # Get serialisation of packet's body (variable header and payload)
96
+ def encode_body
97
+ '' # No body by default
98
+ end
99
+
100
+ def encode_flags
101
+ flags = 0x00
102
+ flags += 0x80 if duplicate
103
+ case qos
104
+ when -1
105
+ flags += 0x60
106
+ when 1
107
+ flags += 0x20
108
+ when 2
109
+ flags += 0x40
110
+ end
111
+ flags += 0x10 if retain
112
+ flags += 0x08 if request_will
113
+ flags += 0x04 if clean_session
114
+ case topic_id_type
115
+ when :normal
116
+ flags += 0x0
117
+ when :predefined
118
+ flags += 0x1
119
+ when :short
120
+ flags += 0x2
121
+ end
122
+ flags
123
+ end
124
+
125
+ def encode_topic_id
126
+ if topic_id_type == :short
127
+ raise "topic_id must be an String for type #{topic_id_type}" unless topic_id.is_a?(String)
128
+
129
+ (topic_id[0].ord << 8) + topic_id[1].ord
130
+ else
131
+ raise "topic_id must be an Integer for type #{topic_id_type}" unless topic_id.is_a?(Integer)
132
+
133
+ topic_id
134
+ end
135
+ end
136
+
137
+ def parse_topic_id(topic_id)
138
+ if topic_id_type == :short
139
+ int = topic_id.to_i
140
+ self.topic_id = [(int >> 8) & 0xFF, int & 0xFF].pack('CC')
141
+ else
142
+ self.topic_id = topic_id
143
+ end
144
+ end
145
+
146
+ # Used where a field can either be a Topic Id or a Topic Name
147
+ # (the Subscribe and Unsubscribe packet types)
148
+ def encode_topic
149
+ case topic_id_type
150
+ when :normal
151
+ topic_name
152
+ when :short
153
+ if topic_name.nil?
154
+ topic_id
155
+ else
156
+ topic_name
157
+ end
158
+ when :predefined
159
+ [topic_id].pack('n')
160
+ end
161
+ end
162
+
163
+ # Used where a field can either be a Topic Id or a Topic Name
164
+ # (the Subscribe and Unsubscribe packet types)
165
+ def parse_topic(topic)
166
+ case topic_id_type
167
+ when :normal
168
+ self.topic_name = topic
169
+ when :short
170
+ self.topic_name = topic
171
+ self.topic_id = topic
172
+ when :predefined
173
+ self.topic_id = topic.unpack1('n')
174
+ end
175
+ end
176
+
177
+ class Advertise < Packet
178
+ attr_accessor :gateway_id
179
+ attr_accessor :duration
180
+
181
+ DEFAULTS = {
182
+ gateway_id: 0x00,
183
+ duration: 0
184
+ }.freeze
185
+
186
+ def encode_body
187
+ [gateway_id, duration].pack('Cn')
188
+ end
189
+
190
+ def parse_body(buffer)
191
+ self.gateway_id, self.duration = buffer.unpack('Cn')
192
+ end
193
+ end
194
+
195
+ class Searchgw < Packet
196
+ attr_accessor :radius
197
+
198
+ DEFAULTS = {
199
+ radius: 1
200
+ }.freeze
201
+
202
+ def encode_body
203
+ [radius].pack('C')
204
+ end
205
+
206
+ def parse_body(buffer)
207
+ self.radius, _ignore = buffer.unpack('C')
208
+ end
209
+ end
210
+
211
+ class Gwinfo < Packet
212
+ attr_accessor :gateway_id, :gateway_address
213
+
214
+ DEFAULTS = {
215
+ gateway_id: 0,
216
+ gateway_address: nil
217
+ }.freeze
218
+
219
+ def encode_body
220
+ [gateway_id, gateway_address].pack('Ca*')
221
+ end
222
+
223
+ def parse_body(buffer)
224
+ if buffer.length > 1
225
+ self.gateway_id, self.gateway_address = buffer.unpack('Ca*')
226
+ else
227
+ self.gateway_id, _ignore = buffer.unpack('C')
228
+ self.gateway_address = nil
229
+ end
230
+ end
231
+ end
232
+
233
+ class Connect < Packet
234
+ attr_accessor :keep_alive
235
+ attr_accessor :client_id
236
+
237
+ DEFAULTS = {
238
+ request_will: false,
239
+ clean_session: true,
240
+ keep_alive: 15
241
+ }.freeze
242
+
243
+ # Get serialisation of packet's body
244
+ def encode_body
245
+ if @client_id.nil? || @client_id.empty? || @client_id.length > 23
246
+ raise 'Invalid client identifier when serialising packet'
247
+ end
248
+
249
+ [encode_flags, 0x01, keep_alive, client_id].pack('CCna*')
250
+ end
251
+
252
+ def parse_body(buffer)
253
+ flags, protocol_id, self.keep_alive, self.client_id = buffer.unpack('CCna*')
254
+
255
+ raise ProtocolException, "Unsupported protocol ID number: #{protocol_id}" if protocol_id != 0x01
256
+
257
+ parse_flags(flags)
258
+ end
259
+ end
260
+
261
+ class Connack < Packet
262
+ attr_accessor :return_code
263
+
264
+ # Get a string message corresponding to a return code
265
+ def return_msg
266
+ case return_code
267
+ when 0x00
268
+ 'Accepted'
269
+ when 0x01
270
+ 'Rejected: congestion'
271
+ when 0x02
272
+ 'Rejected: invalid topic ID'
273
+ when 0x03
274
+ 'Rejected: not supported'
275
+ else
276
+ "Rejected: error code #{return_code}"
277
+ end
278
+ end
279
+
280
+ def encode_body
281
+ raise 'return_code must be an Integer' unless return_code.is_a?(Integer)
282
+
283
+ [return_code].pack('C')
284
+ end
285
+
286
+ def parse_body(buffer)
287
+ self.return_code = buffer.unpack1('C')
288
+ end
289
+ end
290
+
291
+ class Willtopicreq < Packet
292
+ # No attributes
293
+ end
294
+
295
+ class Willtopic < Packet
296
+ attr_accessor :topic_name
297
+
298
+ DEFAULTS = {
299
+ qos: 0,
300
+ retain: false,
301
+ topic_name: nil
302
+ }.freeze
303
+
304
+ def encode_body
305
+ if topic_name.nil? || topic_name.empty?
306
+ ''
307
+ else
308
+ [encode_flags, topic_name].pack('Ca*')
309
+ end
310
+ end
311
+
312
+ def parse_body(buffer)
313
+ if buffer.length > 1
314
+ flags, self.topic_name = buffer.unpack('Ca*')
315
+ else
316
+ flags, _ignore = buffer.unpack('C')
317
+ self.topic_name = nil
318
+ end
319
+ parse_flags(flags)
320
+ end
321
+ end
322
+
323
+ class Willmsgreq < Packet
324
+ # No attributes
325
+ end
326
+
327
+ class Willmsg < Packet
328
+ attr_accessor :data
329
+
330
+ def encode_body
331
+ data
332
+ end
333
+
334
+ def parse_body(buffer)
335
+ self.data = buffer
336
+ end
337
+ end
338
+
339
+ class Register < Packet
340
+ attr_accessor :id
341
+ attr_accessor :topic_id
342
+ attr_accessor :topic_name
343
+
344
+ DEFAULTS = {
345
+ id: 0x00,
346
+ topic_id_type: :normal
347
+ }.freeze
348
+
349
+ def encode_body
350
+ raise 'id must be an Integer' unless id.is_a?(Integer)
351
+
352
+ raise 'topic_id must be an Integer' unless topic_id.is_a?(Integer)
353
+
354
+ [topic_id, id, topic_name].pack('nna*')
355
+ end
356
+
357
+ def parse_body(buffer)
358
+ self.topic_id, self.id, self.topic_name = buffer.unpack('nna*')
359
+ end
360
+ end
361
+
362
+ class Regack < Packet
363
+ attr_accessor :id
364
+ attr_accessor :topic_id
365
+ attr_accessor :return_code
366
+
367
+ DEFAULTS = {
368
+ id: 0x00,
369
+ topic_id: 0x00,
370
+ topic_id_type: :normal
371
+ }.freeze
372
+
373
+ def encode_body
374
+ raise 'id must be an Integer' unless id.is_a?(Integer)
375
+
376
+ raise 'topic_id must be an Integer' unless topic_id.is_a?(Integer)
377
+
378
+ [topic_id, id, return_code].pack('nnC')
379
+ end
380
+
381
+ def parse_body(buffer)
382
+ self.topic_id, self.id, self.return_code = buffer.unpack('nnC')
383
+ end
384
+ end
385
+
386
+ class Publish < Packet
387
+ attr_accessor :topic_id
388
+ attr_accessor :id
389
+ attr_accessor :data
390
+
391
+ DEFAULTS = {
392
+ id: 0x00,
393
+ duplicate: false,
394
+ qos: 0,
395
+ retain: false,
396
+ topic_id_type: :normal
397
+ }.freeze
398
+
399
+ def encode_body
400
+ raise 'id must be an Integer' unless id.is_a?(Integer)
401
+
402
+ [encode_flags, encode_topic_id, id, data].pack('Cnna*')
403
+ end
404
+
405
+ def parse_body(buffer)
406
+ flags, topic_id, self.id, self.data = buffer.unpack('Cnna*')
407
+ parse_flags(flags)
408
+ parse_topic_id(topic_id)
409
+ end
410
+ end
411
+
412
+ class Puback < Packet
413
+ attr_accessor :topic_id
414
+ attr_accessor :id
415
+ attr_accessor :return_code
416
+
417
+ DEFAULTS = {
418
+ id: 0x00,
419
+ topic_id: nil,
420
+ return_code: 0x00
421
+ }.freeze
422
+
423
+ def encode_body
424
+ raise 'id must be an Integer' unless id.is_a?(Integer)
425
+
426
+ raise 'topic_id must be an Integer' unless topic_id.is_a?(Integer)
427
+
428
+ [topic_id, id, return_code].pack('nnC')
429
+ end
430
+
431
+ def parse_body(buffer)
432
+ self.topic_id, self.id, self.return_code = buffer.unpack('nnC')
433
+ end
434
+ end
435
+
436
+ class Pubcomp < Packet
437
+ attr_accessor :id
438
+
439
+ DEFAULTS = {
440
+ id: 0x00
441
+ }.freeze
442
+
443
+ def encode_body
444
+ raise 'id must be an Integer' unless id.is_a?(Integer)
445
+
446
+ [id].pack('n')
447
+ end
448
+
449
+ def parse_body(buffer)
450
+ self.id, _ignore = buffer.unpack('n')
451
+ end
452
+ end
453
+
454
+ class Pubrec < Packet
455
+ attr_accessor :id
456
+
457
+ DEFAULTS = {
458
+ id: 0x00
459
+ }.freeze
460
+
461
+ def encode_body
462
+ raise 'id must be an Integer' unless id.is_a?(Integer)
463
+
464
+ [id].pack('n')
465
+ end
466
+
467
+ def parse_body(buffer)
468
+ self.id, _ignore = buffer.unpack('n')
469
+ end
470
+ end
471
+
472
+ class Pubrel < Packet
473
+ attr_accessor :id
474
+
475
+ DEFAULTS = {
476
+ id: 0x00
477
+ }.freeze
478
+
479
+ def encode_body
480
+ raise 'id must be an Integer' unless id.is_a?(Integer)
481
+
482
+ [id].pack('n')
483
+ end
484
+
485
+ def parse_body(buffer)
486
+ self.id, _ignore = buffer.unpack('n')
487
+ end
488
+ end
489
+
490
+ class Subscribe < Packet
491
+ attr_accessor :id
492
+ attr_accessor :topic_id
493
+ attr_accessor :topic_name
494
+
495
+ DEFAULTS = {
496
+ id: 0x00,
497
+ topic_id_type: :normal
498
+ }.freeze
499
+
500
+ def encode_body
501
+ raise 'id must be an Integer' unless id.is_a?(Integer)
502
+
503
+ [encode_flags, id, encode_topic].pack('Cna*')
504
+ end
505
+
506
+ def parse_body(buffer)
507
+ flags, self.id, topic = buffer.unpack('Cna*')
508
+ parse_flags(flags)
509
+ parse_topic(topic)
510
+ end
511
+ end
512
+
513
+ class Suback < Packet
514
+ attr_accessor :id
515
+ attr_accessor :topic_id
516
+ attr_accessor :return_code
517
+
518
+ DEFAULTS = {
519
+ qos: 0,
520
+ id: 0x00,
521
+ topic_id: 0x00,
522
+ topic_id_type: :normal
523
+ }.freeze
524
+
525
+ def encode_body
526
+ raise 'id must be an Integer' unless id.is_a?(Integer)
527
+
528
+ [encode_flags, encode_topic_id, id, return_code].pack('CnnC')
529
+ end
530
+
531
+ def parse_body(buffer)
532
+ flags, topic_id, self.id, self.return_code = buffer.unpack('CnnC')
533
+ parse_flags(flags)
534
+ parse_topic_id(topic_id)
535
+ end
536
+ end
537
+
538
+ class Unsubscribe < Packet
539
+ attr_accessor :id
540
+ attr_accessor :topic_id
541
+ attr_accessor :topic_name
542
+
543
+ DEFAULTS = {
544
+ id: 0x00,
545
+ topic_id_type: :normal
546
+ }.freeze
547
+
548
+ def encode_body
549
+ raise 'id must be an Integer' unless id.is_a?(Integer)
550
+
551
+ [encode_flags, id, encode_topic].pack('Cna*')
552
+ end
553
+
554
+ def parse_body(buffer)
555
+ flags, self.id, topic = buffer.unpack('Cna*')
556
+ parse_flags(flags)
557
+ parse_topic(topic)
558
+ end
559
+ end
560
+
561
+ class Unsuback < Packet
562
+ attr_accessor :id
563
+
564
+ DEFAULTS = {
565
+ id: 0x00
566
+ }.freeze
567
+
568
+ def encode_body
569
+ raise 'id must be an Integer' unless id.is_a?(Integer)
570
+
571
+ [id].pack('n')
572
+ end
573
+
574
+ def parse_body(buffer)
575
+ self.id = buffer.unpack1('n')
576
+ end
577
+ end
578
+
579
+ class Pingreq < Packet
580
+ # No attributes
581
+ end
582
+
583
+ class Pingresp < Packet
584
+ # No attributes
585
+ end
586
+
587
+ class Disconnect < Packet
588
+ attr_accessor :duration
589
+
590
+ DEFAULTS = {
591
+ duration: nil
592
+ }.freeze
593
+
594
+ def encode_body
595
+ if duration.nil? || duration.zero?
596
+ ''
597
+ else
598
+ [duration].pack('n')
599
+ end
600
+ end
601
+
602
+ def parse_body(buffer)
603
+ self.duration = buffer.length == 2 ? buffer.unpack1('n') : nil
604
+ end
605
+ end
606
+
607
+ class Willtopicupd < Packet
608
+ attr_accessor :topic_name
609
+
610
+ DEFAULTS = {
611
+ qos: 0,
612
+ retain: false,
613
+ topic_name: nil
614
+ }.freeze
615
+
616
+ def encode_body
617
+ if topic_name.nil? || topic_name.empty?
618
+ ''
619
+ else
620
+ [encode_flags, topic_name].pack('Ca*')
621
+ end
622
+ end
623
+
624
+ def parse_body(buffer)
625
+ if buffer.length > 1
626
+ flags, self.topic_name = buffer.unpack('Ca*')
627
+ parse_flags(flags)
628
+ else
629
+ self.topic_name = nil
630
+ end
631
+ end
632
+ end
633
+
634
+ class Willtopicresp < Packet
635
+ attr_accessor :return_code
636
+
637
+ DEFAULTS = {
638
+ return_code: 0x00
639
+ }.freeze
640
+
641
+ def encode_body
642
+ raise 'return_code must be an Integer' unless return_code.is_a?(Integer)
643
+
644
+ [return_code].pack('C')
645
+ end
646
+
647
+ def parse_body(buffer)
648
+ self.return_code, _ignore = buffer.unpack('C')
649
+ end
650
+ end
651
+
652
+ class Willmsgupd < Packet
653
+ attr_accessor :data
654
+
655
+ def encode_body
656
+ data
657
+ end
658
+
659
+ def parse_body(buffer)
660
+ self.data = buffer
661
+ end
662
+ end
663
+
664
+ class Willmsgresp < Packet
665
+ attr_accessor :return_code
666
+
667
+ DEFAULTS = {
668
+ return_code: 0x00
669
+ }.freeze
670
+
671
+ def encode_body
672
+ raise 'return_code must be an Integer' unless return_code.is_a?(Integer)
673
+
674
+ [return_code].pack('C')
675
+ end
676
+
677
+ def parse_body(buffer)
678
+ self.return_code, _ignore = buffer.unpack('C')
679
+ end
680
+ end
681
+ end
682
+
683
+ # An enumeration of the MQTT-SN packet types
684
+ PACKET_TYPES = {
685
+ 0x00 => MQTT::SN::Packet::Advertise,
686
+ 0x01 => MQTT::SN::Packet::Searchgw,
687
+ 0x02 => MQTT::SN::Packet::Gwinfo,
688
+ 0x04 => MQTT::SN::Packet::Connect,
689
+ 0x05 => MQTT::SN::Packet::Connack,
690
+ 0x06 => MQTT::SN::Packet::Willtopicreq,
691
+ 0x07 => MQTT::SN::Packet::Willtopic,
692
+ 0x08 => MQTT::SN::Packet::Willmsgreq,
693
+ 0x09 => MQTT::SN::Packet::Willmsg,
694
+ 0x0a => MQTT::SN::Packet::Register,
695
+ 0x0b => MQTT::SN::Packet::Regack,
696
+ 0x0c => MQTT::SN::Packet::Publish,
697
+ 0x0d => MQTT::SN::Packet::Puback,
698
+ 0x0e => MQTT::SN::Packet::Pubcomp,
699
+ 0x0f => MQTT::SN::Packet::Pubrec,
700
+ 0x10 => MQTT::SN::Packet::Pubrel,
701
+ 0x12 => MQTT::SN::Packet::Subscribe,
702
+ 0x13 => MQTT::SN::Packet::Suback,
703
+ 0x14 => MQTT::SN::Packet::Unsubscribe,
704
+ 0x15 => MQTT::SN::Packet::Unsuback,
705
+ 0x16 => MQTT::SN::Packet::Pingreq,
706
+ 0x17 => MQTT::SN::Packet::Pingresp,
707
+ 0x18 => MQTT::SN::Packet::Disconnect,
708
+ 0x1a => MQTT::SN::Packet::Willtopicupd,
709
+ 0x1b => MQTT::SN::Packet::Willtopicresp,
710
+ 0x1c => MQTT::SN::Packet::Willmsgupd,
711
+ 0x1d => MQTT::SN::Packet::Willmsgresp
712
+ }.freeze
713
+ end
714
+ end