1#!/usr/bin/env python3
2#
3#  Copyright (c) 2019, The OpenThread Authors.
4#  All rights reserved.
5#
6#  Redistribution and use in source and binary forms, with or without
7#  modification, are permitted provided that the following conditions are met:
8#  1. Redistributions of source code must retain the above copyright
9#     notice, this list of conditions and the following disclaimer.
10#  2. Redistributions in binary form must reproduce the above copyright
11#     notice, this list of conditions and the following disclaimer in the
12#     documentation and/or other materials provided with the distribution.
13#  3. Neither the name of the copyright holder nor the
14#     names of its contributors may be used to endorse or promote products
15#     derived from this software without specific prior written permission.
16#
17#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18#  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19#  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20#  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21#  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22#  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23#  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24#  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25#  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26#  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27#  POSSIBILITY OF SUCH DAMAGE.
28#
29
30from binascii import hexlify
31from enum import IntEnum
32import io
33import logging
34import struct
35
36from network_data import SubTlvsFactory
37from tlvs_parsing import UnknownTlvFactory
38import common
39
40
41class TlvType(IntEnum):
42    CHANNEL = 0
43    PAN_ID = 1
44    EXTENDED_PANID = 2
45    NETWORK_NAME = 3
46    PSKC = 4
47    NETWORK_KEY = 5
48    NETWORK_KEY_SEQUENCE_COUNTER = 6
49    NETWORK_MESH_LOCAL_PREFIX = 7
50    STEERING_DATA = 8
51    BORDER_AGENT_LOCATOR = 9
52    COMMISSIONER_ID = 10
53    COMMISSIONER_SESSION_ID = 11
54    SECURITY_POLICY = 12
55    GET = 13
56    ACTIVE_TIMESTAMP = 14
57    COMMISSIONER_UDP_PORT = 15
58    STATE = 16
59    JOINER_DTLS_ENCAPSULATION = 17
60    JOINER_UDP_PORT = 18
61    JOINER_IID = 19
62    JOINER_ROUTER_LOCATOR = 20
63    JOINER_ROUTER_KEK = 21
64    PROVISIONING_URL = 32
65    VENDOR_NAME = 33
66    VENDOR_MODEL = 34
67    VENDOR_SW_VERSION = 35
68    VENDOR_DATA = 36
69    VENDOR_STACK_VERSION = 37
70    UDP_ENCAPSULATION = 48
71    IPV6_ADDRESS = 49
72    PENDING_TIMESTAMP = 51
73    DELAY_TIMER = 52
74    CHANNEL_MASK = 53
75    COUNT = 54
76    PERIOD = 55
77    SCAN_DURATION = 56
78    ENERGY_LIST = 57
79    CSL_SYNCHRONIZED_TIMEOUT = 85
80    CSL_CLOCK_ACCURACY = 86
81    DISCOVERY_REQUEST = 128
82    DISCOVERY_RESPONSE = 129
83
84
85class MeshCopState(IntEnum):
86    ACCEPT = 0x1
87    REJECT = 0xff
88
89
90class MeshCopMessageType(IntEnum):
91    JOIN_FIN_REQ = (1,)
92    JOIN_FIN_RSP = (2,)
93    JOIN_ENT_NTF = (3,)
94    JOIN_ENT_RSP = 4
95
96
97def create_mesh_cop_message_type_set():
98    return [
99        MeshCopMessageType.JOIN_FIN_REQ,
100        MeshCopMessageType.JOIN_FIN_RSP,
101        MeshCopMessageType.JOIN_ENT_NTF,
102        MeshCopMessageType.JOIN_ENT_RSP,
103    ]
104
105
106# Channel TLV (0)
107class Channel(object):
108
109    def __init__(self, channel_page, channel):
110        self._channel_page = channel_page
111        self._channel = channel
112
113    @property
114    def channel_page(self):
115        return self._channel_page
116
117    @property
118    def channel(self):
119        return self._channel
120
121    def __eq__(self, other):
122        common.expect_the_same_class(self, other)
123
124        return (self._channel_page == other._channel_page and self._channel == other.__channel)
125
126    def __repr__(self):
127        return 'Channel(channel_page={},channel={})'.format(self._channel_page, self._channel)
128
129    def to_hex(self):
130        return struct.pack('>BBBH', TlvType.CHANNEL, 3, self.channel_page, self.channel)
131
132
133class ChannelFactory(object):
134
135    def parse(self, data, message_info):
136        data_tp = struct.unpack('>BH', data.read(3))
137        channel_page = data_tp[0]
138        channel = data_tp[1]
139        return Channel(channel_page, channel)
140
141
142# PanId TLV (1)
143class Panid(object):
144    # TODO: Not implemented yet
145    pass
146
147
148class PanidFactory(object):
149    # TODO: Not implemented yet
150
151    def parse(self, data, message_info):
152        raise NotImplementedError("TODO: Not implemented yet")
153
154
155# ExtendedPanid TLV (2)
156class ExtendedPanid(object):
157
158    def __init__(self, extended_panid):
159        self._extended_panid = extended_panid
160
161    @property
162    def extended_panid(self):
163        return self._extended_panid
164
165    def __eq__(self, other):
166        return (isinstance(self, type(other)) and self.extended_panid == other.extended_panid)
167
168    def __repr__(self):
169        return "ExtendedPanid(extended_panid={})".format(self.extended_panid)
170
171
172class ExtendedPanidFactory(object):
173
174    def parse(self, data, message_info):
175        extended_panid = struct.unpack(">Q", data.read(8))[0]
176        return ExtendedPanid(extended_panid)
177
178
179# NetworkName TLV (3)
180class NetworkName(object):
181
182    def __init__(self, network_name):
183        self._network_name = network_name
184
185    @property
186    def network_name(self):
187        return self._network_name
188
189    def __eq__(self, other):
190        return (isinstance(self, type(other)) and self.network_name == other.network_name)
191
192    def __repr__(self):
193        return "NetworkName(network_name={})".format(self.network_name)
194
195
196class NetworkNameFactory(object):
197
198    def parse(self, data, message_info):
199        len = message_info.length
200        network_name = struct.unpack("{}s".format(10), data.read(len))[0]
201        return NetworkName(network_name)
202
203
204# PSKc TLV (4)
205class PSKc(object):
206    # TODO: Not implemented yet
207    pass
208
209
210class PSKcFactory(object):
211    # TODO: Not implemented yet
212
213    def parse(self, data, message_info):
214        raise NotImplementedError("TODO: Not implemented yet")
215
216
217# NetworkKey TLV (5)
218class NetworkKey(object):
219    # TODO: Not implemented yet
220    pass
221
222
223class NetworkKeyFactory(object):
224    # TODO: Not implemented yet
225
226    def parse(self, data, message_info):
227        raise NotImplementedError("TODO: Not implemented yet")
228
229
230# NetworkKeySequenceCounter TLV (6)
231class NetworkKeySequenceCounter(object):
232    # TODO: Not implemented yet
233    pass
234
235
236class NetworkKeySequenceCounterFactory(object):
237    # TODO: Not implemented yet
238
239    def parse(self, data, message_info):
240        raise NotImplementedError("TODO: Not implemented yet")
241
242
243# NetworkMeshLocalPrefix TLV (7)
244class NetworkMeshLocalPrefix(object):
245    # TODO: Not implemented yet
246    pass
247
248
249class NetworkMeshLocalPrefixFactory(object):
250    # TODO: Not implemented yet
251
252    def parse(self, data, message_info):
253        raise NotImplementedError("TODO: Not implemented yet")
254
255
256# Steering Data TLV (8)
257class SteeringData(object):
258
259    def __init__(self, bloom_filter):
260        self._bloom_filter = bloom_filter
261
262    @property
263    def bloom_filter(self):
264        return self._bloom_filter
265
266    def __eq__(self, other):
267        common.expect_the_same_class(self, other)
268
269        return self._bloom_filter == other._bloom_filter
270
271    def __repr__(self):
272        return "SteeringData(bloom_filter={})".format(hexlify(self._bloom_filter))
273
274    def to_hex(self):
275        bloom_filter_len = len(self.bloom_filter)
276        return (struct.pack('>BB', TlvType.STEERING_DATA, bloom_filter_len) + self.bloom_filter)
277
278
279class SteeringDataFactory:
280
281    def parse(self, data, message_info):
282        bloom_filter = data.read(message_info.length)
283        return SteeringData(bloom_filter)
284
285
286# Border Agent Locator TLV (9)
287class BorderAgentLocator(object):
288
289    def __init__(self, address):
290        self._border_agent_locator = address
291
292    @property
293    def border_agent_locator(self):
294        return self._border_agent_locator
295
296    def __eq__(self, other):
297        common.expect_the_same_class(self, other)
298
299        return self._border_agent_locator == other._border_agent_locator
300
301    def __repr__(self):
302        return "BorderAgentLocator(rloc16={})".format(hex(self._border_agent_locator))
303
304    def to_hex(self):
305        return struct.pack('>BBH', TlvType.BORDER_AGENT_LOCATOR, 2, self.border_agent_locator)
306
307
308class BorderAgentLocatorFactory:
309
310    def parse(self, data, message_info):
311        border_agent_locator = struct.unpack(">H", data.read(2))[0]
312        return BorderAgentLocator(border_agent_locator)
313
314
315# CommissionerId TLV (10)
316class CommissionerId(object):
317
318    def __init__(self, commissioner_id):
319        self._commissioner_id = commissioner_id
320
321    @property
322    def commissioner_id(self):
323        return self._commissioner_id
324
325    def __eq__(self, other):
326        return self.commissioner_id == other.commissioner_id
327
328    def __repr__(self):
329        return "CommissionerId(commissioner_id={})".format(self.commissioner_id)
330
331
332class CommissionerIdFactory(object):
333
334    def parse(self, data, message_info):
335        commissioner_id = data.getvalue().decode('utf-8')
336        return CommissionerId(commissioner_id)
337
338
339# Commissioner Session ID TLV (11)
340class CommissionerSessionId(object):
341
342    def __init__(self, commissioner_session_id):
343        self._commissioner_session_id = commissioner_session_id
344
345    @property
346    def commissioner_session_id(self):
347        return self._commissioner_session_id
348
349    def __eq__(self, other):
350        common.expect_the_same_class(self, other)
351
352        return self._commissioner_session_id == other._commissioner_session_id
353
354    def __repr__(self):
355        return "CommissionerSessionId(commissioner_session_id={})".format(self._commissioner_session_id)
356
357    def to_hex(self):
358        return struct.pack(
359            '>BBH',
360            TlvType.COMMISSIONER_SESSION_ID,
361            2,
362            self.commissioner_session_id,
363        )
364
365
366class CommissionerSessionIdFactory:
367
368    def parse(self, data, message_info):
369        session_id = struct.unpack(">H", data.read(2))[0]
370        return CommissionerSessionId(session_id)
371
372
373# SecurityPolicy TLV (12)
374class SecurityPolicy(object):
375    # TODO: Not implemented yet
376    pass
377
378
379class SecurityPolicyFactory(object):
380    # TODO: Not implemented yet
381
382    def parse(self, data, message_info):
383        raise NotImplementedError("TODO: Not implemented yet")
384
385
386# Get TLV (13)
387class Get(object):
388    # TODO: Not implemented yet
389    pass
390
391
392class GetFactory(object):
393    # TODO: Not implemented yet
394
395    def parse(self, data, message_info):
396        raise NotImplementedError("TODO: Not implemented yet")
397
398
399# ActiveTimestamp TLV (14)
400class ActiveTimestamp(object):
401    # TODO: Not implemented yet
402    pass
403
404
405class ActiveTimestampFactory(object):
406    # TODO: Not implemented yet
407
408    def parse(self, data, message_info):
409        raise NotImplementedError("TODO: Not implemented yet")
410
411
412# Commissioner UDP Port TLV (15)
413class CommissionerUdpPort(object):
414
415    def __init__(self, udp_port):
416        self._udp_port = udp_port
417
418    @property
419    def udp_port(self):
420        return self._udp_port
421
422    def __eq__(self, other):
423        common.expect_the_same_class(self, other)
424
425        return self._udp_port == other._udp_port
426
427    def __repr__(self):
428        return "CommissionerUdpPort(udp_port={})".format(self._udp_port)
429
430
431class CommissionerUdpPortFactory:
432
433    def parse(self, data, message_info):
434        udp_port = struct.unpack(">H", data.read(2))[0]
435        return CommissionerUdpPort(udp_port)
436
437
438# State TLV (16)
439class State(object):
440
441    def __init__(self, state):
442        self._state = state
443
444    @property
445    def state(self):
446        return self._state
447
448    def __eq__(self, other):
449        return self.state == other.state
450
451    def __repr__(self):
452        return "State(state={})".format(self.state)
453
454
455class StateFactory:
456
457    def parse(self, data, message_info):
458        state = ord(data.read(1))
459        return State(state)
460
461
462# JoinerDtlsEncapsulation TLV (17)
463class JoinerDtlsEncapsulation(object):
464    # TODO: Not implemented yet
465    pass
466
467
468class JoinerDtlsEncapsulationFactory(object):
469    # TODO: Not implemented yet
470
471    def parse(self, data, message_info):
472        raise NotImplementedError("TODO: Not implemented yet")
473
474
475# JoinerUdpPort TLV (18)
476class JoinerUdpPort(object):
477
478    def __init__(self, udp_port):
479        self._udp_port = udp_port
480
481    @property
482    def udp_port(self):
483        return self._udp_port
484
485    def __eq__(self, other):
486        return (isinstance(self, type(other)) and self.udp_port == other.udp_port)
487
488    def __repr__(self):
489        return "JoinerUdpPort(udp_port={})".format(self.udp_port)
490
491
492class JoinerUdpPortFactory(object):
493
494    def parse(self, data, message_info):
495        udp_port = struct.unpack(">H", data.read(2))[0]
496        return JoinerUdpPort(udp_port)
497
498
499# JoinerIID TLV (19)
500class JoinerIID(object):
501    # TODO: Not implemented yet
502    pass
503
504
505class JoinerIIDFactory(object):
506    # TODO: Not implemented yet
507
508    def parse(self, data, message_info):
509        raise NotImplementedError("TODO: Not implemented yet")
510
511
512# JoinerRouterLocator TLV (20)
513class JoinerRouterLocator(object):
514    # TODO: Not implemented yet
515    pass
516
517
518class JoinerRouterLocatorFactory(object):
519    # TODO: Not implemented yet
520
521    def parse(self, data, message_info):
522        raise NotImplementedError("TODO: Not implemented yet")
523
524
525# JoinerRouterKEK TLV (21)
526class JoinerRouterKEK(object):
527    # TODO: Not implemented yet
528    pass
529
530
531class JoinerRouterKEKFactory(object):
532    # TODO: Not implemented yet
533
534    def parse(self, data, message_info):
535        raise NotImplementedError("TODO: Not implemented yet")
536
537
538# ProvisioningURL TLV (32)
539class ProvisioningUrl(object):
540
541    def __init__(self, url):
542        self._url = url
543
544    @property
545    def url(self):
546        return self._url
547
548    def __repr__(self):
549        return "ProvisioningUrl(url={})".format(self.url)
550
551
552class ProvisioningUrlFactory:
553
554    def parse(self, data, message_info):
555        url = data.getvalue().decode('utf-8')
556        return ProvisioningUrl(url)
557
558
559# VendorName TLV (33)
560class VendorName(object):
561
562    def __init__(self, vendor_name):
563        self._vendor_name = vendor_name
564
565    @property
566    def vendor_name(self):
567        return self._vendor_name
568
569    def __eq__(self, other):
570        return self.vendor_name == other.vendor_name
571
572    def __repr__(self):
573        return "VendorName(vendor_name={})".format(self.vendor_name)
574
575
576class VendorNameFactory:
577
578    def parse(self, data, message_info):
579        vendor_name = data.getvalue().decode('utf-8')
580        return VendorName(vendor_name)
581
582
583# VendorModel TLV (34)
584class VendorModel(object):
585
586    def __init__(self, vendor_model):
587        self._vendor_model = vendor_model
588
589    @property
590    def vendor_model(self):
591        return self._vendor_model
592
593    def __eq__(self, other):
594        return self.vendor_model == other.vendor_model
595
596    def __repr__(self):
597        return "VendorModel(vendor_model={})".format(self.vendor_model)
598
599
600class VendorModelFactory:
601
602    def parse(self, data, message_info):
603        vendor_model = data.getvalue().decode('utf-8')
604        return VendorModel(vendor_model)
605
606
607# VendorSWVersion TLV (35)
608class VendorSWVersion(object):
609
610    def __init__(self, vendor_sw_version):
611        self._vendor_sw_version = vendor_sw_version
612
613    @property
614    def vendor_sw_version(self):
615        return self._vendor_sw_version
616
617    def __eq__(self, other):
618        return self.vendor_sw_version == other.vendor_sw_version
619
620    def __repr__(self):
621        return "VendorName(vendor_sw_version={})".format(self.vendor_sw_version)
622
623
624class VendorSWVersionFactory:
625
626    def parse(self, data, message_info):
627        vendor_sw_version = data.getvalue()
628        return VendorSWVersion(vendor_sw_version)
629
630
631# VendorData TLV (36)
632class VendorData(object):
633
634    def __init__(self, data):
635        self._vendor_data = data
636
637    @property
638    def vendor_data(self):
639        return self._vendor_data
640
641    def __repr__(self):
642        return "Vendor(url={})".format(self.vendor_data)
643
644
645class VendorDataFactory(object):
646
647    def parse(self, data, message_info):
648        return VendorData(data)
649
650
651# VendorStackVersion TLV (37)
652class VendorStackVersion(object):
653
654    def __init__(self, stack_vendor_oui, build, rev, minor, major):
655        self._stack_vendor_oui = stack_vendor_oui
656        self._build = build
657        self._rev = rev
658        self._minor = minor
659        self._major = major
660        return
661
662    @property
663    def stack_vendor_oui(self):
664        return self._stack_vendor_oui
665
666    @property
667    def build(self):
668        return self._build
669
670    @property
671    def rev(self):
672        return self._rev
673
674    @property
675    def minor(self):
676        return self._minor
677
678    @property
679    def major(self):
680        return self._major
681
682    def __repr__(self):
683        return "VendorStackVersion(vendor_stack_version={}, build={}, rev={}, minor={}, major={})".format(
684            self.stack_vendor_oui, self.build, self.rev, self.minor, self.major)
685
686
687class VendorStackVersionFactory:
688
689    def parse(self, data, message_info):
690        stack_vendor_oui = struct.unpack(">H", data.read(2))[0]
691        rest = struct.unpack(">BBBB", data.read(4))
692        build = rest[1] << 4 | (0xf0 & rest[2])
693        rev = 0xF & rest[2]
694        minor = rest[3] & 0xf0
695        major = rest[3] & 0xF
696        return VendorStackVersion(stack_vendor_oui, build, rev, minor, major)
697
698
699# UdpEncapsulation TLV (48)
700class UdpEncapsulation(object):
701    # TODO: Not implemented yet
702    pass
703
704
705class UdpEncapsulationFactory(object):
706    # TODO: Not implemented yet
707
708    def parse(self, data, message_info):
709        raise NotImplementedError("TODO: Not implemented yet")
710
711
712# Ipv6Address TLV (49)
713class Ipv6Address(object):
714    # TODO: Not implemented yet
715    pass
716
717
718class Ipv6AddressFactory(object):
719    # TODO: Not implemented yet
720
721    def parse(self, data, message_info):
722        raise NotImplementedError("TODO: Not implemented yet")
723
724
725# PendingTimestamp TLV (51)
726class PendingTimestamp(object):
727    # TODO: Not implemented yet
728    pass
729
730
731class PendingTimestampFactory(object):
732    # TODO: Not implemented yet
733
734    def parse(self, data, message_info):
735        raise NotImplementedError("TODO: Not implemented yet")
736
737
738# DelayTimer TLV (52)
739class DelayTimer(object):
740    # TODO: Not implemented yet
741    pass
742
743
744class DelayTimerFactory(object):
745    # TODO: Not implemented yet
746
747    def parse(self, data, message_info):
748        raise NotImplementedError("TODO: Not implemented yet")
749
750
751# ChannelMask TLV (53)
752class ChannelMask(object):
753    # TODO: Not implemented yet
754    pass
755
756
757class ChannelMaskFactory(object):
758    # TODO: Not implemented yet
759
760    def parse(self, data, message_info):
761        raise NotImplementedError("TODO: Not implemented yet")
762
763
764# Count TLV (54)
765class Count(object):
766    # TODO: Not implemented yet
767    pass
768
769
770class CountFactory(object):
771    # TODO: Not implemented yet
772
773    def parse(self, data, message_info):
774        raise NotImplementedError("TODO: Not implemented yet")
775
776
777# Period TLV (55)
778class Period(object):
779    # TODO: Not implemented yet
780    pass
781
782
783class PeriodFactory(object):
784    # TODO: Not implemented yet
785
786    def parse(self, data, message_info):
787        raise NotImplementedError("TODO: Not implemented yet")
788
789
790# ScanDuration TLV (56)
791class ScanDuration(object):
792    # TODO: Not implemented yet
793    pass
794
795
796class ScanDurationFactory(object):
797    # TODO: Not implemented yet
798
799    def parse(self, data, message_info):
800        raise NotImplementedError("TODO: Not implemented yet")
801
802
803# EnergyList TLV (57)
804class EnergyList(object):
805    # TODO: Not implemented yet
806    pass
807
808
809class EnergyListFactory(object):
810    # TODO: Not implemented yet
811
812    def parse(self, data, message_info):
813        raise NotImplementedError("TODO: Not implemented yet")
814
815
816# Discovery Request TLV (128)
817class DiscoveryRequest(object):
818
819    def __init__(self, version, joiner_flag):
820        self._version = version
821        self._joiner_flag = joiner_flag
822
823    @property
824    def version(self):
825        return self._version
826
827    @property
828    def joiner_flag(self):
829        return self._joiner_flag
830
831    def __eq__(self, other):
832        return (isinstance(self, type(other)) and self.version == other.version and
833                self.joiner_flag == other.joiner_flag)
834
835    def __repr__(self):
836        return "DiscoveryRequest(version={}, joiner_flag={})".format(self.version, self.joiner_flag)
837
838
839class DiscoveryRequestFactory(object):
840
841    def parse(self, data, message_info):
842        data_byte = struct.unpack(">B", data.read(1))[0]
843        version = (data_byte & 0xf0) >> 4
844        joiner_flag = (data_byte & 0x08) >> 3
845
846        return DiscoveryRequest(version, joiner_flag)
847
848
849# Discovery Response TLV (128)
850class DiscoveryResponse(object):
851
852    def __init__(self, version, native_flag):
853        self._version = version
854        self._native_flag = native_flag
855
856    @property
857    def version(self):
858        return self._version
859
860    @property
861    def native_flag(self):
862        return self._native_flag
863
864    def __eq__(self, other):
865        return (isinstance(self, type(other)) and self.version == other.version and
866                self.native_flag == other.native_flag)
867
868    def __repr__(self):
869        return "DiscoveryResponse(version={}, native_flag={})".format(self.version, self.native_flag)
870
871
872class DiscoveryResponseFactory(object):
873
874    def parse(self, data, message_info):
875        data_byte = struct.unpack(">B", data.read(1))[0]
876        version = (data_byte & 0xf0) >> 4
877        native_flag = (data_byte & 0x08) >> 3
878
879        return DiscoveryResponse(version, native_flag)
880
881
882class MeshCopCommand(object):
883
884    def __init__(self, _type, tlvs):
885        self._type = _type
886        self._tlvs = tlvs
887
888    @property
889    def type(self):
890        return self._type
891
892    @property
893    def tlvs(self):
894        return self._tlvs
895
896    def __repr__(self):
897        tlvs_str = ", ".join(["{}".format(tlv) for tlv in self.tlvs])
898        return "MeshCopCommand(type={}, tlvs=[{}])".format(self.type, tlvs_str)
899
900
901def create_deault_mesh_cop_msg_type_map():
902    return {
903        'JOIN_FIN.req': MeshCopMessageType.JOIN_FIN_REQ,
904        'JOIN_FIN.rsp': MeshCopMessageType.JOIN_FIN_RSP,
905        'JOIN_ENT.ntf': MeshCopMessageType.JOIN_ENT_NTF,
906        'JOIN_ENT.rsp': MeshCopMessageType.JOIN_ENT_RSP,
907    }
908
909
910class MeshCopCommandFactory:
911
912    def __init__(self, tlvs_factories):
913        self._tlvs_factories = tlvs_factories
914        self._mesh_cop_msg_type_map = create_deault_mesh_cop_msg_type_map()
915
916    def _get_length(self, data):
917        return ord(data.read(1))
918
919    def _get_tlv_factory(self, _type):
920        try:
921            return self._tlvs_factories[_type]
922        except KeyError:
923            logging.error('Could not find TLV factory. Unsupported TLV type: {}'.format(_type))
924            return UnknownTlvFactory(_type)
925
926    def _parse_tlv(self, data):
927        _type = TlvType(ord(data.read(1)))
928        length = self._get_length(data)
929        value = data.read(length)
930        factory = self._get_tlv_factory(_type)
931        return factory.parse(io.BytesIO(value), None)  # message_info not needed here
932
933    def _get_mesh_cop_msg_type(self, msg_type_str):
934        try:
935            return self._mesh_cop_msg_type_map[msg_type_str]
936        except KeyError:
937            raise KeyError('Mesh cop message type not found: {}'.format(msg_type_str))
938
939    def parse(self, cmd_type_str, data):
940        cmd_type = self._get_mesh_cop_msg_type(cmd_type_str)
941
942        tlvs = []
943        while data.tell() < len(data.getvalue()):
944            tlv = self._parse_tlv(data)
945            tlvs.append(tlv)
946
947        return MeshCopCommand(cmd_type, tlvs)
948
949
950def create_default_mesh_cop_tlv_factories():
951    return {
952        TlvType.STATE: StateFactory(),
953        TlvType.PROVISIONING_URL: ProvisioningUrlFactory(),
954        TlvType.VENDOR_NAME: VendorNameFactory(),
955        TlvType.VENDOR_MODEL: VendorModelFactory(),
956        TlvType.VENDOR_SW_VERSION: VendorSWVersionFactory(),
957        TlvType.VENDOR_DATA: VendorDataFactory(),
958        TlvType.VENDOR_STACK_VERSION: VendorStackVersionFactory(),
959    }
960
961
962class ThreadDiscoveryTlvsFactory(SubTlvsFactory):
963
964    def __init__(self, sub_tlvs_factories):
965        super(ThreadDiscoveryTlvsFactory, self).__init__(sub_tlvs_factories)
966