def test_validate_length(self): good_duid_object = EnterpriseDUID(0, 122 * b'x') good_duid_object.validate() bad_duid_object = EnterpriseDUID(0, 123 * b'x') with self.assertRaisesRegex(ValueError, 'cannot be longer than 122 bytes'): bad_duid_object.validate()
def test_validate_enterprise_number(self): good_duid_object = EnterpriseDUID(0, b'demo') good_duid_object.validate() bad_duid_object = EnterpriseDUID(-1, b'demo') with self.assertRaisesRegex(ValueError, 'unsigned 32 bit integer'): bad_duid_object.validate() bad_duid_object = EnterpriseDUID(2**32, b'demo') with self.assertRaisesRegex(ValueError, 'unsigned 32 bit integer'): bad_duid_object.validate()
def setUp(self): self.option_bytes = bytes.fromhex( '002d' # Option type 45: OPTION_CLIENT_DATA '0099' # Option length: 153 '0001' # Option type 1: OPTION_CLIENT_ID '0015' # Option length: 21 '0002' # DUID type: DUID_EN '00009d10' # Enterprise ID: 40208 '303132333435363738396162636465' # Identifier: '0123456789abcde' '0005' # Option type: OPTION_IAADDR '0018' # Option length: 24 '20010db800000000000000000000cafe' # IPv6 address: 2001:db8::cafe '00000708' # Preferred lifetime: 1800 '00000e10' # Valid lifetime: 3600 '001a' # Option type: OPTION_IAPREFIX '0019' # Option length: 25 '00000708' # Preferred lifetime: 1800 '00000e10' # Valid lifetime: 3600 '30' # Prefix length: 48 '20010db8000100000000000000000000' '002e' # Option type: OPTION_CLT_TIME '0004' # Option length: 4 '00000384' # Client-Last-Transaction time: 900 '002f' # Option type: OPTION_LQ_RELAY_DATA '003b' # Option length: 59 '20010db8000000000000000000000002' # Peer address: 2001:db8::2 '0c' # Message type: MSG_RELAY_FORW '00' # Hop count: 0 '20010db8000000000000000000000002' # Link address: 2001:db8::2 'fe800000000000000000000000000022' # Peer address: fe80::22 '0012' # Option type: OPTION_INTERFACE_ID '0005' # Option length: 5 '4661322f33' # Interface ID: 'Fa2/3' ) self.option_object = ClientDataOption(options=[ ClientIdOption(EnterpriseDUID(40208, b'0123456789abcde')), IAAddressOption(address=IPv6Address('2001:db8::cafe'), preferred_lifetime=1800, valid_lifetime=3600), IAPrefixOption(prefix=IPv6Network('2001:db8:1::/48'), preferred_lifetime=1800, valid_lifetime=3600), CLTTimeOption(clt_time=900), LQRelayDataOption(peer_address=IPv6Address('2001:db8::2'), relay_message=RelayForwardMessage( hop_count=0, link_address=IPv6Address('2001:db8::2'), peer_address=IPv6Address('fe80::22'), options=[ InterfaceIdOption(interface_id=b'Fa2/3'), ] )) ]) self.parse_option()
def duid_en(section) -> EnterpriseDUID: """ Create a EnterpriseDUID from the data provided in the config section. :param section: The section data :return: The DUID object """ duid = EnterpriseDUID(enterprise_number=section.enterprise_number, identifier=section.identifier) duid.validate() return duid
def parse_duid(duid_str: str) -> DUID: """ Parse a string representing a DUID into a real DUID :param duid_str: The string representation :return: The DUID object """ duid_parts = duid_str.split(':') duid_type = duid_parts[0] if duid_type == 'enterprise': if len(duid_parts) == 3: hardware_type = int(duid_parts[1], 10) if duid_parts[2][:2] == '0x': identifier = bytes.fromhex(duid_parts[2][2:]) else: identifier = duid_parts[2].encode('utf-8') return EnterpriseDUID(hardware_type, identifier) else: logger.critical( "Enterprise DUIDs must have format 'enterprise:<enterprise-nr>:<identifier>'" ) raise ValueError elif duid_type == 'linklayer': if len(duid_parts) == 3: hardware_type = int(duid_parts[1], 10) address = bytes.fromhex(duid_parts[2]) return LinkLayerDUID(hardware_type, address) else: logger.critical( "Link Layer DUIDs must have format 'linklayer:<hardware-type>:<address-hex>'" ) raise ValueError elif duid_type == 'linklayer-time': if len(duid_parts) == 4: hardware_type = int(duid_parts[1], 10) timestamp = int(duid_parts[2], 10) address = bytes.fromhex(duid_parts[3]) return LinkLayerTimeDUID(hardware_type, timestamp, address) else: logger.critical( "Link Layer + Time DUIDs must have format " "'linklayer-time:<hardware-type>:<time>:<address-hex>'") raise ValueError else: logger.critical("Unknown DUID type: {}".format(duid_type)) raise ValueError
def setUp(self): # The following attributes must be overruled by child classes # The basics are tested with a simple SolicitMessage self.packet_fixture = bytes.fromhex( '01' # message_type '58595a' # transaction_id '0001' # option_type: OPTION_CLIENTID '0015' # option_length '0002' # duid_type: DUID_EN '00009d10' # enterprise_number '444843504b6974556e697454657374' # "DHCPKitUnitTest" '0008' # option_type: OPTION_ELAPSED_TIME '0002' # option_length '0000') # elapsed_time self.message_fixture = SolicitMessage( transaction_id=b'XYZ', options=[ ClientIdOption(duid=EnterpriseDUID( enterprise_number=40208, identifier=b'DHCPKitUnitTest')), ElapsedTimeOption(elapsed_time=0) ]) self.parse_packet()
def setUp(self): self.duid_object = EnterpriseDUID( enterprise_number=40208, identifier=b'DHCPKitUnitTestIdentifier') self.duid_bytes = bytes.fromhex( '000200009d10444843504b6974556e6974546573744964656e746966696572')
def test_wrong_parser(self): with self.assertRaisesRegex(ValueError, 'does not contain EnterpriseDUID'): duid = EnterpriseDUID() duid.load_from(self.duid_bytes, length=len(self.duid_bytes))
def test_validate_identifier(self): # noinspection PyTypeChecker bad_duid_object = EnterpriseDUID(0, 'demo') with self.assertRaisesRegex(ValueError, 'sequence of bytes'): bad_duid_object.validate()
def setUp(self): self.option_bytes = b'\x00\x01\x00\x15\x00\x02\x00\x00\x9d\x100123456789abcde' self.option_object = ClientIdOption(EnterpriseDUID(40208, b'0123456789abcde')) self.parse_option()
def main(args: Iterable[str]) -> int: """ The main program :param args: Command line arguments :return: The program exit code """ # Handle command line arguments options = handle_args(args) set_verbosity_logger(logger, options.verbosity) query = options.create(options) # Add ORO for relay data if options.relay_data: query.options.append(OptionRequestOption([OPTION_LQ_RELAY_DATA])) # Generate the outgoing message transaction_id = random.getrandbits(24).to_bytes(3, 'big') message_out = LeasequeryMessage( transaction_id, [ClientIdOption(EnterpriseDUID(40208, b'LeaseQueryTester')), query]) # Create client socket if options.tcp: client = TCPClientSocket(options) else: # Check permission if os.getuid() != 0: raise RuntimeError("This tool needs to be run as root") client = UDPClientSocket(options) destination = client.send(message_out) logger.info("Sent to {}:\n{}".format(destination, message_out)) # Wait for responses wait_for_multiple = options.server.is_multicast or options.tcp start = time.time() deadline = start + 3 received = 0 while time.time() < deadline: client.set_timeout(deadline - time.time()) try: sender, message_in = client.recv() received += 1 logger.info("Received from {}:\n{}".format(sender, message_in)) if options.tcp: # Check bulk leasequery ending if isinstance(message_in, LeasequeryReplyMessage): if not message_in.get_option_of_type(ClientDataOption): # Reply without data, the end break if isinstance(message_in, LeasequeryDoneMessage): break if not wait_for_multiple: break except socket.timeout: pass logger.info( gettext.ngettext("{} response received", "{} responses received", received).format(received)) return 0