예제 #1
0
    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)
예제 #2
0
    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)
예제 #3
0
    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)
예제 #4
0
파일: udp.py 프로젝트: sjm-steffann/dhcpkit
    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
예제 #5
0
파일: udp.py 프로젝트: jackjack821/dhcpkit
    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
예제 #6
0
        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)
예제 #7
0
    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
예제 #8
0
        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)
예제 #9
0
        :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)
예제 #10
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)

        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')]),
                    ],
                ))
            ],
        ))
    ],
)