def handle_relay(self, bundle: TransactionBundle, relay_message_in: RelayForwardMessage, relay_message_out: RelayReplyMessage): """ Handle the options for each relay message pair. :param bundle: The transaction bundle :param relay_message_in: The incoming relay message :param relay_message_out: Thr outgoing relay message """ # See if the relay message contains an ERO ero = relay_message_in.get_option_of_type(EchoRequestOption) if not ero: # Nothing to do return for option_type in ero.requested_options: # Don't do anything if the outgoing relay message already has this one if any(option.option_type == option_type for option in relay_message_out.options): continue # Get the incoming options of the requested type incoming_options = [ option for option in relay_message_in.options if option.option_type == option_type ] for option in incoming_options: # Make sure this option can go into this type of response if not relay_message_out.may_contain(option): return # And append them to the outgoing message if possible relay_message_out.options.append(option)
def handle_relay(self, bundle: TransactionBundle, relay_message_in: RelayForwardMessage, relay_message_out: RelayReplyMessage): """ Copy the options for each relay message pair. :param bundle: The transaction bundle :param relay_message_in: The incoming relay message :param relay_message_out: Thr outgoing relay message """ # Make sure this option can go into this type of response if not relay_message_out.may_contain(self.option_class): return # Make sure this option isn't present and then copy those from the request relay_message_out.options = [existing_option for existing_option in relay_message_out.options if not isinstance(existing_option, self.option_class)] relay_message_out.options[:0] = relay_message_in.get_options_of_type(self.option_class)
def handle_relay(self, bundle: TransactionBundle, relay_message_in: RelayForwardMessage, relay_message_out: RelayReplyMessage): """ Copy the options for each relay message pair. :param bundle: The transaction bundle :param relay_message_in: The incoming relay message :param relay_message_out: Thr outgoing relay message """ # Make sure this option can go into this type of response if not relay_message_out.may_contain(self.option_class): return # Make sure this option isn't present and then copy those from the request relay_message_out.options = [ existing_option for existing_option in relay_message_out.options if not isinstance(existing_option, self.option_class) ] relay_message_out.options[:0] = relay_message_in.get_options_of_type( self.option_class)
def send_reply(self, outgoing_message: RelayReplyMessage) -> bool: """ Send a reply to the client :param outgoing_message: The message to send, including a wrapping RelayReplyMessage :return: Whether sending was successful """ # Determine network addresses and bytes reply = outgoing_message.relayed_message port = isinstance(reply, RelayReplyMessage) and SERVER_PORT or CLIENT_PORT destination_address = str(outgoing_message.peer_address) data = reply.save() # Try to determine the interface index from the outgoing relay options interface_index = 0 interface_name = "unknown" interface_id_option = outgoing_message.get_option_of_type(InterfaceIdOption) if interface_id_option: try: interface_name = interface_id_option.interface_id.decode(encoding="utf-8", errors="replace") interface_index = socket.if_nametoindex(interface_id_option.interface_id) except OSError: pass destination = (destination_address, port, 0, interface_index) sent_length = self.reply_socket.sendto(data, destination) success = len(data) == sent_length if success: logger.log( DEBUG_PACKETS, "Sent {message_type} to {client_addr} port {port} on {interface}".format( message_type=outgoing_message.inner_message.__class__.__name__, client_addr=destination_address, port=port, interface=interface_name, ), ) else: logger.error( "Could not send {message_type} to {client_addr} port {port} on {interface}".format( message_type=outgoing_message.inner_message.__class__.__name__, client_addr=destination_address, port=port, interface=interface_name, ) ) return success
def send_reply(self, outgoing_message: RelayReplyMessage) -> bool: """ Send a reply to the client :param outgoing_message: The message to send, including a wrapping RelayReplyMessage :return: Whether sending was successful """ # Determine network addresses and bytes reply = outgoing_message.relayed_message port = isinstance(reply, RelayReplyMessage) and SERVER_PORT or CLIENT_PORT destination_address = str(outgoing_message.peer_address) data = reply.save() # Try to determine the interface index from the outgoing relay options interface_index = 0 interface_name = 'unknown' interface_id_option = outgoing_message.get_option_of_type(InterfaceIdOption) if interface_id_option: try: interface_name = interface_id_option.interface_id.decode(encoding='utf-8', errors='replace') interface_index = socket.if_nametoindex(interface_id_option.interface_id) except OSError: pass destination = (destination_address, port, 0, interface_index) sent_length = self.reply_socket.sendto(data, destination) success = len(data) == sent_length if success: logger.log(DEBUG_PACKETS, "Sent {message_type} to {client_addr} port {port} on {interface}".format( message_type=outgoing_message.inner_message.__class__.__name__, client_addr=destination_address, port=port, interface=interface_name)) else: logger.error("Could not send {message_type} to {client_addr} port {port} on {interface}".format( message_type=outgoing_message.inner_message.__class__.__name__, client_addr=destination_address, port=port, interface=interface_name)) return success
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) self.subscriber_id = buffer[offset + my_offset:offset + my_offset + option_len] my_offset += option_len return my_offset def save(self) -> Union[bytes, bytearray]: """ Save the internal state of this object as a buffer. :return: The buffer with the data from this element """ return pack('!HH', self.option_type, len( self.subscriber_id)) + self.subscriber_id RelayForwardMessage.add_may_contain(SubscriberIdOption) # The RFC says there is no requirement for servers to include this option in replies, but it is not forbidden RelayReplyMessage.add_may_contain(SubscriberIdOption)
def send_reply(self, message: RelayReplyMessage) -> bool: """ Send a reply using the information in the outer RelayReplyMessage :param message: The message to reply with :return: Whether sending has succeeded """ # Verify that the outer relay message makes sense if not isinstance(message, RelayReplyMessage): raise ValueError("The reply has to be wrapped in a RelayReplyMessage") if message.link_address != self.global_address: raise ValueError("The relay-reply link-address does not match the relay-forward link-address") interface_id_option = message.get_option_of_type(InterfaceIdOption) if interface_id_option and interface_id_option.interface_id != self.interface_id: # If there is an interface-id option its contents have to match raise ValueError("The interface-id in the reply does not match the interface-id of the request") reply = message.relayed_message if not reply: raise ValueError("The RelayReplyMessage does not contain a message") # Down to network addresses and bytes port = isinstance(reply, RelayReplyMessage) and SERVER_PORT or CLIENT_PORT destination = (str(message.peer_address), port, 0, self.interface_index) data = reply.save() data_length = len(data) sent_length = self.reply_socket.sendto(data, destination) success = data_length == sent_length # Construct useful log messages if isinstance(reply, RelayReplyMessage): inner_relay_message = reply.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 = '' if success: logger.debug("Sent {msg_type} to {client_addr} via {interface}relay {relay_addr}".format( msg_type=type(inner_message).__name__, client_addr=inner_relay_message.peer_address, relay_addr=destination[0], interface=interface_id_str)) else: logger.error("{msg_type} to {client_addr} via {interface}relay {relay_addr} could not be sent".format( msg_type=type(inner_message).__name__, client_addr=inner_relay_message.peer_address, relay_addr=destination[0], interface=interface_id_str)) else: if success: logger.debug("Sent {msg_type} to {client_addr}".format( msg_type=type(reply).__name__, client_addr=destination[0])) else: logger.error("{msg_type} to {client_addr} could not be sent".format( msg_type=type(reply).__name__, client_addr=destination[0])) return success
my_offset, option_len = self.parse_option_header( buffer, offset, length) self.enterprise_number = unpack_from('!I', buffer, offset=offset + my_offset)[0] my_offset += 4 remote_id_length = option_len - 4 self.remote_id = buffer[offset + my_offset:offset + my_offset + remote_id_length] my_offset += remote_id_length return my_offset def save(self) -> Union[bytes, bytearray]: """ Save the internal state of this object as a buffer. :return: The buffer with the data from this element """ return pack('!HHI', self.option_type, len(self.remote_id) + 4, self.enterprise_number) + self.remote_id RelayForwardMessage.add_may_contain(RemoteIdOption) # The RFC says there is no requirement for servers to include this option in replies, but it is not forbidden RelayReplyMessage.add_may_contain(RemoteIdOption)
: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) self.enterprise_number = unpack_from('!I', buffer, offset=offset + my_offset)[0] my_offset += 4 remote_id_length = option_len - 4 self.remote_id = buffer[offset + my_offset:offset + my_offset + remote_id_length] my_offset += remote_id_length return my_offset def save(self) -> bytes: """ Save the internal state of this object as a buffer. :return: The buffer with the data from this element """ return pack('!HHI', self.option_type, len(self.remote_id) + 4, self.enterprise_number) + self.remote_id RelayForwardMessage.add_may_contain(RemoteIdOption) # The RFC says there is no requirement for servers to include this option in replies, but it is not forbidden RelayReplyMessage.add_may_contain(RemoteIdOption)
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) self.subscriber_id = buffer[offset + my_offset : offset + my_offset + option_len] my_offset += option_len return my_offset def save(self) -> bytes: """ Save the internal state of this object as a buffer. :return: The buffer with the data from this element """ return pack("!HH", self.option_type, len(self.subscriber_id)) + self.subscriber_id RelayForwardMessage.add_may_contain(SubscriberIdOption) # The RFC says there is no requirement for servers to include this option in replies, but it is not forbidden RelayReplyMessage.add_may_contain(SubscriberIdOption)
relayed_advertise_message = RelayReplyMessage( hop_count=1, link_address=IPv6Address('2001:db8:ffff:1::1'), peer_address=IPv6Address('fe80::3631:c4ff:fe3c:b2f1'), options=[ InterfaceIdOption(interface_id=b'Gi0/0/0'), RelayMessageOption(relayed_message=RelayReplyMessage( hop_count=0, link_address=IPv6Address('::'), peer_address=IPv6Address('fe80::3631:c4ff:fe3c:b2f1'), options=[ InterfaceIdOption(interface_id=b'Fa2/3'), RelayMessageOption(relayed_message=AdvertiseMessage( transaction_id=bytes.fromhex('f350d6'), options=[ IANAOption(iaid=bytes.fromhex('c43cb2f1'), options=[ IAAddressOption(address=IPv6Address( '2001:db8:ffff:1:c::e09c'), preferred_lifetime=375, valid_lifetime=600), ]), IAPDOption(iaid=bytes.fromhex('c43cb2f1'), options=[ IAPrefixOption(prefix=IPv6Network( '2001:db8:ffcc:fe00::/56'), preferred_lifetime=375, valid_lifetime=600), ]), ClientIdOption(duid=LinkLayerDUID( hardware_type=1, link_layer_address=bytes.fromhex('3431c43cb2f1'))), ServerIdOption(duid=LinkLayerTimeDUID( hardware_type=1, time=488458703, link_layer_address=bytes.fromhex('00137265ca42'))), ReconfigureAcceptOption(), RecursiveNameServersOption( dns_servers=[IPv6Address('2001:4860:4860::8888')]), ], )) ], )) ], )