Example #1
0
def generate_outgoing_response(outgoing_message: Message,
                               incoming_packet: IncomingPacketBundle = None) -> OutgoingPacketBundle:
    """
    generate the outgoing packet and check the RelayServerMessage around it.

    :param outgoing_message: The reply message
    :param incoming_packet: The original received packet, only used to sanity-check the reply
    :return: The parsed message
    """
    # Verify that the outer relay message makes sense
    if not isinstance(outgoing_message, RelayReplyMessage):
        raise ValueError("The reply has to be wrapped in a RelayReplyMessage")

    if incoming_packet is not None:
        # Verify the contents of the outgoing message
        if outgoing_message.link_address != incoming_packet.link_address:
            raise ValueError("The relay-reply link-address does not match the relay-forward link-address")

        interface_id_option = outgoing_message.get_option_of_type(InterfaceIdOption)
        if interface_id_option and interface_id_option.interface_id != incoming_packet.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 = outgoing_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 = outgoing_message.peer_address
    data = reply.save()

    return OutgoingPacketBundle(message_id=incoming_packet.message_id, data=data, destination=destination, port=port)
    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")
Example #3
0
    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
Example #4
0
    def send(self, message: Message) -> IPv6Address:
        """
        Send a DHCPv6 message

        :param message: The message
        """
        packet = message.save()
        self.socket.sendall(pack("!H", len(packet)) + packet)
        return self.options.server
Example #5
0
    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
Example #6
0
    def send(self, message: Message) -> IPv6Address:
        """
        Send a DHCPv6 message

        :param message: The message
        """
        packet = message.save()
        self.socket.sendto(packet, (str(self.options.server), SERVER_PORT, 0, self.if_index))
        return self.options.server
Example #7
0
    def send(self, message: Message) -> IPv6Address:
        """
        Send a DHCPv6 message

        :param message: The message
        """
        packet = message.save()
        self.socket.sendall(pack("!H", len(packet)) + packet)
        return self.options.server
Example #8
0
    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
Example #9
0
    def send(self, message: Message) -> IPv6Address:
        """
        Send a DHCPv6 message

        :param message: The message
        """
        packet = message.save()
        self.socket.sendto(
            packet, (str(self.options.server), SERVER_PORT, 0, self.if_index))
        return self.options.server
Example #10
0
    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]
Example #11
0
    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]
Example #12
0
    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)
                                   ])
Example #13
0
    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")
Example #14
0
def generate_outgoing_response(
        outgoing_message: Message,
        incoming_packet: IncomingPacketBundle = None) -> OutgoingPacketBundle:
    """
    generate the outgoing packet and check the RelayServerMessage around it.

    :param outgoing_message: The reply message
    :param incoming_packet: The original received packet, only used to sanity-check the reply
    :return: The parsed message
    """
    # Verify that the outer relay message makes sense
    if not isinstance(outgoing_message, RelayReplyMessage):
        raise ValueError("The reply has to be wrapped in a RelayReplyMessage")

    if incoming_packet is not None:
        # Verify the contents of the outgoing message
        if outgoing_message.link_address != incoming_packet.link_address:
            raise ValueError(
                "The relay-reply link-address does not match the relay-forward link-address"
            )

        interface_id_option = outgoing_message.get_option_of_type(
            InterfaceIdOption)
        if interface_id_option and interface_id_option.interface_id != incoming_packet.interface_name.encode(
                'utf-8'):
            # 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 = outgoing_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 = outgoing_message.peer_address
    data = reply.save()

    return OutgoingPacketBundle(message_id=incoming_packet.message_id,
                                data=data,
                                destination=destination,
                                port=port)
Example #15
0
    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
Example #16
0
    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
Example #17
0
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)
Example #18
0
    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
Example #19
0
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)
Example #20
0
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)
                               ])
Example #21
0
 def parse_packet(self):
     self.length, self.message = Message.parse(self.packet_fixture)
     self.assertIsInstance(self.message, Message)
     self.message_class = type(self.message)
Example #22
0
 def parse_packet(self):
     self.length, self.message = Message.parse(self.packet_fixture)
     self.assertIsInstance(self.message, Message)
     self.message_class = type(self.message)