def setUp(self):
     self.option_bytes = bytes.fromhex(
         '0003'  # option_type: OPTION_IA_NA
         '0044'  # option_length
         '41424344'  # iaid: ABCD
         '00000029'  # t1: 41
         '0000002a'  # t2: 42
         '0005'  # option_type: OPTION_IAADDR
         '0018'  # option_length
         '20010db8000000000000000000000001'  # address: 2001:db8::1
         '00000000'  # preferred_lifetime
         '00000000'  # valid_lifetime
         '000d'  # option_type: OPTION_STATUS_CODE
         '0018'  # option_length
         '0000'  # status_code
         '45766572797468696e6720697320617765736f6d6521')  # status_message
     self.option_object = IANAOption(
         iaid=b'ABCD',
         t1=41,
         t2=42,
         options=[
             IAAddressOption(address=IPv6Address('2001:db8::1')),
             StatusCodeOption(status_code=STATUS_SUCCESS,
                              status_message='Everything is awesome!')
         ])
     self.parse_option()
Beispiel #2
0
 def setUp(self):
     self.option_bytes = bytes.fromhex(
         '0019'  # option_type: OPTION_IA_PD
         '0045'  # option_length
         '41424344'  # iaid: ABCD
         '00000029'  # t1: 41
         '0000002a'  # t2: 42
         '001a'  # option_type: OPTION_IAPREFIX
         '0019'  # option_length
         '00000000'  # preferred_lifetime
         '00000000'  # valid_lifetime
         '30'  # prefix_length: 48
         '20010db8000000000000000000000000'  # prefix: 2001:db8::
         '000d'  # option_type: OPTION_STATUS_CODE
         '0018'  # option_length
         '0000'  # status_code
         '45766572797468696e6720697320617765736f6d6521')  # status_message
     self.option_object = IAPDOption(
         iaid=b'ABCD',
         t1=41,
         t2=42,
         options=[
             IAPrefixOption(prefix=IPv6Network('2001:db8::/48')),
             StatusCodeOption(status_code=STATUS_SUCCESS,
                              status_message='Everything is awesome!')
         ])
     self.parse_option()
Beispiel #3
0
 def setUp(self):
     self.option_bytes = bytes.fromhex(
         '000d001d00044fc3b920c3aa7465732d766f7573206d6f6e206d61c3ae7472653f'
     )
     self.option_object = StatusCodeOption(STATUS_NOT_ON_LINK,
                                           'Où êtes-vous mon maître?')
     self.parse_option()
 def setUp(self):
     self.option_bytes = bytes.fromhex(
         '0005003520010db800010023045678900bc0cafe0001518000093a80'
         '000d0019000457686572652064696420796f752067657420746861743f')
     self.option_object = IAAddressOption(
         address=IPv6Address('2001:db8:1:23:456:7890:bc0:cafe'),
         preferred_lifetime=86400,
         valid_lifetime=7 * 86400,
         options=[
             StatusCodeOption(STATUS_NOT_ON_LINK, 'Where did you get that?')
         ])
     self.parse_option()
Beispiel #5
0
    def generate_data_messages(transaction_id: bytes, leases: Iterator[Tuple[IPv6Address, ClientDataOption]]) \
            -> Iterator[Union[LeasequeryDataMessage, LeasequeryDoneMessage]]:
        """
        Generate a leasequery data message for each of the leases, followed by a leasequery done message.

        :param transaction_id: The transaction ID to use in the messages
        :param leases: An open iterator for the data we still need to return
        :return: Leasequery messages to send to the client
        """
        for link_address, data_option in leases:
            yield LeasequeryDataMessage(transaction_id, options=[data_option])

        yield LeasequeryDoneMessage(
            transaction_id,
            options=[StatusCodeOption(STATUS_SUCCESS, "That's all folks")])
Beispiel #6
0
 def setUp(self):
     self.option_bytes = bytes.fromhex(
         '001a0036'
         '00015180'
         '00093a80'
         '30'
         '20010db8000100000000000000000000'
         '000d0019000457686572652064696420796f752067657420746861743f')
     self.option_object = IAPrefixOption(
         preferred_lifetime=86400,
         valid_lifetime=7 * 86400,
         prefix=IPv6Network('2001:db8:1::/48'),
         options=[
             StatusCodeOption(STATUS_NOT_ON_LINK, 'Where did you get that?')
         ])
     self.parse_option()
Beispiel #7
0
    def construct_use_multicast_reply(self, bundle: TransactionBundle) -> ReplyMessage:
        """
        Construct a message signalling to the client that they should have used multicast.

        :param bundle: The transaction bundle containing the incoming request
        :return: The proper answer to tell a client to use multicast
        """
        # Make sure we only tell this to requests that came in over unicast
        if bundle.received_over_multicast:
            logger.error("Not telling client to use multicast, they already did...")
            return None

        return ReplyMessage(bundle.request.transaction_id, options=[
            bundle.request.get_option_of_type(ClientIdOption),
            ServerIdOption(duid=self.server_id),
            StatusCodeOption(STATUS_USE_MULTICAST, "You cannot send requests directly to this server, "
                                                   "please use the proper multicast addresses")
        ])
Beispiel #8
0
    def handle(self, bundle: TransactionBundle):
        """
        Update the status of the reply to :class:`.ConfirmMessage`, :class:`.ReleaseMessage` and
        :class:`.DeclineMessage`.

        :param bundle: The transaction bundle
        """
        if isinstance(bundle.request, ConfirmMessage):
            message = "Assignments confirmed"
        elif isinstance(bundle.request, ReleaseMessage):
            message = "Thank you for releasing your resources"
        elif isinstance(bundle.request, DeclineMessage):
            message = "Our apologies for assigning you unusable addresses"
        else:
            # Not a message type we're interested in
            return

        existing = bundle.response.get_option_of_type(StatusCodeOption)
        if not existing:
            bundle.response.options.append(
                StatusCodeOption(STATUS_SUCCESS, status_message=message))
Beispiel #9
0
    def handle(self, bundle: TransactionBundle):
        """
        Make sure that every :class:`.IANAOption` and :class:`.IATAOption` is answered.

        :param bundle: The transaction bundle
        """
        for option in bundle.get_unhandled_options((IANAOption, IATAOption)):
            ia_class = type(option)

            if isinstance(bundle.request, (SolicitMessage, RequestMessage)):
                # If the server will not assign any addresses to any IAs in a subsequent Request from the client, the
                # server MUST send an Advertise message to the client that includes only a Status Code option with code
                # NoAddrsAvail and a status message for the user
                #
                # We do the same for unanswered requests
                bundle.response.options.append(
                    ia_class(option.iaid,
                             options=[
                                 StatusCodeOption(STATUS_NO_ADDRS_AVAIL,
                                                  "No addresses available")
                             ]))

            elif isinstance(bundle.request, ConfirmMessage):
                # When the server receives a Confirm message, the server determines whether the addresses in the
                # Confirm message are appropriate for the link to which the client is attached.  If all of the
                # addresses in the Confirm message pass this test, the server returns a status of Success.  If any of
                # the addresses do not pass this test, the server returns a status of NotOnLink.  If the server is
                # unable to perform this test (for example, the server does not have information about prefixes on the
                # link to which the client is connected), or there were no addresses in any of the IAs sent by the
                # client, the server MUST NOT send a reply to the client.
                #
                # The "there were no addresses in any of the IAs sent by the client" check is done by the message
                # handler.
                if not self.authoritative:
                    raise CannotRespondError(
                        "Server is not authoritative and cannot reject confirm"
                    )

                addresses = ', '.join(map(str, option.get_addresses()))
                logger.warning(
                    "No handler confirmed {}: sending NotOnLink status".format(
                        addresses))

                force_status(
                    bundle.response.options,
                    StatusCodeOption(
                        STATUS_NOT_ON_LINK,
                        "Those addresses are not appropriate on this link"))

            elif isinstance(bundle.request, RenewMessage):
                # If the server cannot find a client entry for the IA the server returns the IA containing no addresses
                # with a Status Code option set to NoBinding in the Reply message.
                #
                # If the server finds that any of the addresses are not appropriate for the link to which the client is
                # attached, the server returns the address to the client with lifetimes of 0.
                addresses = ', '.join(map(str, option.get_addresses()))

                if self.authoritative:
                    logger.warning(
                        "No handler renewed {}: withdrawing addresses".format(
                            addresses))

                    reply_suboptions = []
                    for suboption in option.get_options_of_type(
                            IAAddressOption):
                        reply_suboptions.append(
                            IAAddressOption(suboption.address,
                                            preferred_lifetime=0,
                                            valid_lifetime=0))

                    bundle.response.options.append(
                        ia_class(option.iaid, options=reply_suboptions))
                else:
                    logger.warning(
                        "No handler renewed {}: sending NoBinding status".
                        format(addresses))

                    bundle.response.options.append(
                        ia_class(option.iaid,
                                 options=[
                                     StatusCodeOption(
                                         STATUS_NO_BINDING,
                                         "No addresses assigned to you")
                                 ]))

            elif isinstance(bundle.request, RebindMessage):
                # If the server cannot find a client entry for the IA and the server determines that the addresses in
                # the IA are not appropriate for the link to which the client's interface is attached according to the
                # server's explicit configuration information, the server MAY send a Reply message to the client
                # containing the client's IA, with the lifetimes for the addresses in the IA set to zero.  This Reply
                # constitutes an explicit notification to the client that the addresses in the IA are no longer valid.
                # In this situation, if the server does not send a Reply message it silently discards the Rebind
                # message.
                #
                # If the server finds that any of the addresses are no longer appropriate for the link to which the
                # client is attached, the server returns the address to the client with lifetimes of 0.
                if not self.authoritative:
                    raise CannotRespondError(
                        "Server is not authoritative and cannot reject rebind")

                addresses = ', '.join(map(str, option.get_addresses()))
                logger.warning(
                    "No handler answered rebind of {}: withdrawing addresses".
                    format(addresses))

                reply_suboptions = []
                for suboption in option.get_options_of_type(IAAddressOption):
                    reply_suboptions.append(
                        IAAddressOption(suboption.address,
                                        preferred_lifetime=0,
                                        valid_lifetime=0))

                bundle.response.options.append(
                    ia_class(option.iaid, options=reply_suboptions))

            elif isinstance(bundle.request, DeclineMessage):
                # For each IA in the Decline message for which the server has no binding information, the server adds
                # an IA option using the IAID from the Release message and includes a Status Code option with the value
                # NoBinding in the IA option.  No other options are included in the IA option.
                bundle.response.options.append(
                    ia_class(option.iaid,
                             options=[
                                 StatusCodeOption(
                                     STATUS_NO_BINDING,
                                     "No addresses assigned to you")
                             ]))

            elif isinstance(bundle.request, ReleaseMessage):
                # For each IA in the Release message for which the server has no binding information, the server adds an
                # IA option using the IAID from the Release message, and includes a Status Code option with the value
                # NoBinding in the IA option.  No other options are included in the IA option.
                bundle.response.options.append(
                    ia_class(option.iaid,
                             options=[
                                 StatusCodeOption(
                                     STATUS_NO_BINDING,
                                     "No addresses assigned to you")
                             ]))
Beispiel #10
0
 def __init__(self, status_code: int = 0, status_message: str = ''):
     super().__init__(status_code, status_message)
     self.option = StatusCodeOption(status_code, status_message)