def test_send_relayed_without_interface_id(self): multicast_socket = MockSocket(AF_INET6, IPPROTO_UDP, All_DHCP_Relay_Agents_and_Servers, SERVER_PORT, 42, 1608) link_local_socket = MockSocket(AF_INET6, IPPROTO_UDP, 'fe80::1%eth0', SERVER_PORT, 42, 1608) # noinspection PyTypeChecker listening_socket = ListeningSocket('eth0', multicast_socket, link_local_socket, global_address=IPv6Address('2001:db8::1')) # Start with a clean parse and then change interface-id new_message = Message.parse(relayed_advertise_packet)[1] interface_id_option = new_message.inner_relay_message.get_option_of_type(InterfaceIdOption) new_message.inner_relay_message.options.remove(interface_id_option) outgoing_message = RelayReplyMessage(hop_count=0, link_address=IPv6Address('2001:db8::1'), peer_address=IPv6Address('fe80::babe'), options=[ InterfaceIdOption(interface_id=b'eth0'), RelayMessageOption(relayed_message=new_message) ]) with self.assertLogs(level=logging.DEBUG) as logged: listening_socket.send_reply(outgoing_message) log_output = '\n'.join(logged.output) self.assertRegex(log_output, r'Sent AdvertiseMessage') self.assertRegex(log_output, r'to fe80::3631:c4ff:fe3c:b2f1') self.assertRegex(log_output, r"via relay fe80::babe")
def load_from(self, buffer: bytes, offset: int = 0, length: int = None) -> int: """ Load the internal state of this object from the given buffer. The buffer may contain more data after the structured element is parsed. This data is ignored. :param buffer: The buffer to read data from :param offset: The offset in the buffer where to start reading :param length: The amount of data we are allowed to read from the buffer :return: The number of bytes used from the buffer """ my_offset, option_len = self.parse_option_header(buffer, offset, length, min_length=20) self.peer_address = IPv6Address(buffer[offset + my_offset:offset + my_offset + 16]) my_offset += 16 # Parse the message message_len, self.relay_message = Message.parse(buffer, offset=offset + my_offset, length=option_len - 16) my_offset += message_len if message_len != option_len - 16: raise ValueError( 'The embedded message has a different length than the Relay Data Option' ) return my_offset
def recv(self) -> Tuple[IPv6Address, Message]: """ Receive a DHCPv6 message :return: The message """ packet, sender = self.socket.recvfrom(65535) message_length, message = Message.parse(packet) return IPv6Address(sender[0].split('%')[0]), message
def decode_relay_messages(data: bytes) -> Optional[RelayForwardMessage]: """ Decode a chain of relay messages from bytes. :param data: The bytes :return: The relay message """ if not data: return None return Message.parse(data)[1]
def recv_request(self) -> RelayForwardMessage: """ Receive incoming messages :return: The address of the sender of the message and the received message """ pkt, sender = self.listen_socket.recvfrom(65536) try: length, msg_in = Message.parse(pkt) except ValueError as e: raise InvalidPacketError(str(e), sender=sender) # Determine the next hop count if isinstance(msg_in, RelayForwardMessage): next_hop_count = msg_in.hop_count + 1 else: next_hop_count = 0 # Construct useful log messages if isinstance(msg_in, RelayForwardMessage): inner_relay_message = msg_in.inner_relay_message inner_message = inner_relay_message.relayed_message relay_interface_id_option = inner_relay_message.get_option_of_type(InterfaceIdOption) if relay_interface_id_option: interface_id = relay_interface_id_option.interface_id try: interface_id = interface_id.decode('ascii') except ValueError: pass interface_id_str = '{} of '.format(interface_id) else: interface_id_str = '' logger.debug("Received {msg_type} from {client_addr} via {interface}relay {relay_addr}".format( msg_type=type(inner_message).__name__, client_addr=inner_relay_message.peer_address, relay_addr=sender[0], interface=interface_id_str)) else: logger.debug("Received {msg_type} from {client_addr}".format( msg_type=type(msg_in).__name__, client_addr=sender[0])) # Pretend to be an internal relay and wrap the message like a relay would return RelayForwardMessage(hop_count=next_hop_count, link_address=self.global_address, peer_address=IPv6Address(sender[0].split('%')[0]), options=[ InterfaceIdOption(interface_id=self.interface_id), RelayMessageOption(relayed_message=msg_in) ])
def test_receive_relayed_with_unprintable_interface_id(self): global_unicast_socket = MockSocket(AF_INET6, IPPROTO_UDP, '2001:db8::1', SERVER_PORT, 42, 1608) # noinspection PyTypeChecker listening_socket = ListeningSocket('eth0', global_unicast_socket) # Start with a clean parse and then change interface-id new_message = Message.parse(relayed_solicit_packet)[1] new_message.inner_relay_message.get_option_of_type(InterfaceIdOption).interface_id = b'\x80\x81\x82' global_unicast_socket.add_to_incoming_queue(bytes(new_message.save()), ('2001:db8::babe', 546, 0, 42)) with self.assertLogs(level=logging.DEBUG) as logged: listening_socket.recv_request() log_output = '\n'.join(logged.output) self.assertRegex(log_output, r'Received SolicitMessage') self.assertRegex(log_output, r'from fe80::3631:c4ff:fe3c:b2f1') self.assertRegex(log_output, r"via b'\\x80\\x81\\x82' of relay 2001:db8::babe")
def recv(self) -> Tuple[IPv6Address, Message]: """ Receive a DHCPv6 message :return: The message """ # Receive message length packet = b'' while len(packet) < 2: packet += self.socket.recv(2 - len(packet)) message_length = unpack('!H', packet)[0] # Receive message packet = b'' while len(packet) < message_length: packet += self.socket.recv(message_length - len(packet)) read_length, message = Message.parse(packet) return self.options.server, message
def parse_incoming_request( incoming_packet: IncomingPacketBundle) -> TransactionBundle: """ Parse the incoming packet and add a RelayServerMessage around it containing the meta-data received from the listener. :param incoming_packet: The received packet :return: The parsed message in a transaction bundle """ # Parse message and validate length, incoming_message = Message.parse(incoming_packet.data) incoming_message.validate() # Determine the next hop count and construct useful log messages if isinstance(incoming_message, RelayForwardMessage): next_hop_count = incoming_message.hop_count + 1 else: next_hop_count = 0 # Collect the relay options relay_options = [] """:type: List[Option]""" relay_options.append( InterfaceIdOption( interface_id=incoming_packet.interface_name.encode('utf-8'))) relay_options.extend(incoming_packet.extra_options) relay_options.append(RelayMessageOption(relayed_message=incoming_message)) # Pretend to be an internal relay and wrap the message like a relay would wrapped_message = RelayForwardMessage( hop_count=next_hop_count, link_address=incoming_packet.link_address, peer_address=incoming_packet.sender, options=relay_options) # Create the transaction bundle return TransactionBundle( incoming_message=wrapped_message, received_over_multicast=incoming_packet.received_over_multicast, marks=incoming_packet.marks)
def load_from(self, buffer: bytes, offset: int = 0, length: int = None) -> int: """ Load the internal state of this object from the given buffer. The buffer may contain more data after the structured element is parsed. This data is ignored. :param buffer: The buffer to read data from :param offset: The offset in the buffer where to start reading :param length: The amount of data we are allowed to read from the buffer :return: The number of bytes used from the buffer """ my_offset, option_len = self.parse_option_header(buffer, offset, length, min_length=20) self.peer_address = IPv6Address(buffer[offset + my_offset:offset + my_offset + 16]) my_offset += 16 # Parse the message message_len, self.relay_message = Message.parse(buffer, offset=offset + my_offset, length=option_len - 16) my_offset += message_len if message_len != option_len - 16: raise ValueError('The embedded message has a different length than the Relay Data Option') return my_offset
def parse_incoming_request(incoming_packet: IncomingPacketBundle) -> RelayForwardMessage: """ Parse the incoming packet and add a RelayServerMessage around it containing the meta-data received from the listener. :param incoming_packet: The received packet :return: The parsed message """ length, incoming_message = Message.parse(incoming_packet.data) # Determine the next hop count and construct useful log messages if isinstance(incoming_message, RelayForwardMessage): next_hop_count = incoming_message.hop_count + 1 else: next_hop_count = 0 # Pretend to be an internal relay and wrap the message like a relay would return RelayForwardMessage(hop_count=next_hop_count, link_address=incoming_packet.link_address, peer_address=incoming_packet.sender, options=[ InterfaceIdOption(interface_id=incoming_packet.interface_id), RelayMessageOption(relayed_message=incoming_message) ])
def parse_incoming_request(incoming_packet: IncomingPacketBundle) -> TransactionBundle: """ Parse the incoming packet and add a RelayServerMessage around it containing the meta-data received from the listener. :param incoming_packet: The received packet :return: The parsed message in a transaction bundle """ # Parse message and validate length, incoming_message = Message.parse(incoming_packet.data) incoming_message.validate() # Determine the next hop count and construct useful log messages if isinstance(incoming_message, RelayForwardMessage): next_hop_count = incoming_message.hop_count + 1 else: next_hop_count = 0 # Collect the relay options relay_options = [] """:type: List[Option]""" relay_options.extend(incoming_packet.relay_options) relay_options.append(RelayMessageOption(relayed_message=incoming_message)) # Pretend to be an internal relay and wrap the message like a relay would wrapped_message = RelayForwardMessage(hop_count=next_hop_count, link_address=incoming_packet.link_address, peer_address=incoming_packet.source_address, options=relay_options) # Create the transaction bundle return TransactionBundle(incoming_message=wrapped_message, received_over_multicast=incoming_packet.received_over_multicast, received_over_tcp=incoming_packet.received_over_tcp, marks=incoming_packet.marks)
def parse_packet(self): self.length, self.message = Message.parse(self.packet_fixture) self.assertIsInstance(self.message, Message) self.message_class = type(self.message)