Пример #1
0
    def find_client_by_remote_id(self, query: LQQueryOption) -> List[int]:
        """
        Get the row ids of the clients we want to return.

        :param query: The query
        :return: A list of row ids
        """
        # Get the requested remote ID from the query
        remote_id_option = query.get_option_of_type(RemoteIdOption)
        if not remote_id_option:
            raise ReplyWithLeasequeryError(
                STATUS_MALFORMED_QUERY,
                "Remote-ID queries must contain a remote ID")

        remote_id_str = self.encode_remote_id(remote_id_option)

        if query.link_address.is_unspecified:
            cur = self.db.execute(
                "SELECT client_fk FROM relay_ids WHERE relay_id=?",
                (remote_id_str, ))

            return [row['client_fk'] for row in cur]
        else:
            cur = self.db.execute(
                "SELECT id FROM clients "
                "WHERE link_address=? AND id IN (SELECT client_fk FROM relay_ids WHERE relay_id=?)",
                (query.link_address.exploded, remote_id_str))

            return [row['id'] for row in cur]
Пример #2
0
    def find_client_by_client_id(self, query: LQQueryOption) -> List[int]:
        """
        Get the row ids of the clients we want to return.

        :param query: The query
        :return: A list of row ids
        """
        # Get the requested client ID from the query
        client_id_option = query.get_option_of_type(ClientIdOption)
        if not client_id_option:
            raise ReplyWithLeasequeryError(
                STATUS_MALFORMED_QUERY,
                "Client-ID queries must contain a client ID")

        client_id_str = self.encode_duid(client_id_option.duid)

        if query.link_address.is_unspecified:
            cur = self.db.execute("SELECT id FROM clients WHERE client_id=?",
                                  (client_id_str, ))
        else:
            cur = self.db.execute(
                "SELECT id FROM clients WHERE client_id=? AND link_address=?",
                (client_id_str, query.link_address.exploded))

        return [row['id'] for row in cur]
Пример #3
0
    def find_client_by_address(self, query: LQQueryOption) -> List[int]:
        """
        Get the row ids of the clients we want to return.

        :param query: The query
        :return: A list of row ids
        """
        # Get the requested address from the query
        address_option = query.get_option_of_type(IAAddressOption)
        if not address_option:
            raise ReplyWithLeasequeryError(
                STATUS_MALFORMED_QUERY,
                "Address queries must contain an address")

        address = address_option.address.exploded

        if query.link_address.is_unspecified:
            cur = self.db.execute(
                "SELECT client_fk FROM addresses WHERE address=?"
                " UNION "
                "SELECT client_fk FROM prefixes WHERE ? BETWEEN first_address AND last_address",
                (address, address))
            return [row['client_fk'] for row in cur]
        else:
            cur = self.db.execute(
                "SELECT id FROM clients WHERE link_address=? AND ("
                "id IN (SELECT client_fk FROM addresses WHERE address=?)"
                " OR "
                "id IN (SELECT client_fk FROM prefixes WHERE ? BETWEEN first_address AND last_address)"
                ")", (query.link_address.exploded, address, address))
            return [row['id'] for row in cur]
Пример #4
0
    def pre(self, bundle: TransactionBundle):
        """
        Make sure we allow this client to make leasequery requests.

        :param bundle: The transaction bundle
        """
        if not isinstance(bundle.request, LeasequeryMessage):
            # Not a leasequery, not our business
            return

        # Check access based on relay closest to the client
        if not any([
                bundle.incoming_relay_messages[0].peer_address in allow_from
                for allow_from in self.allow_from
        ]):
            raise ReplyWithLeasequeryError(
                STATUS_NOT_ALLOWED, "Leasequery not allowed from your address")
Пример #5
0
    def post(self, bundle: TransactionBundle):
        """
        Check for unhandled leasequeries.

        :param bundle: The transaction bundle
        """
        if not isinstance(bundle.request, LeasequeryMessage):
            # Only leasequeries are relevant
            return

        unhandled_queries = bundle.get_unhandled_options(LQQueryOption)
        if unhandled_queries:
            query = unhandled_queries[0]
            raise ReplyWithLeasequeryError(
                STATUS_UNKNOWN_QUERY_TYPE,
                "This server can't handle query type {}".format(
                    query.query_type))
Пример #6
0
    def pre(self, bundle: TransactionBundle):
        """
        Make sure that bulk leasequery options are not coming in over UDP.

        :param bundle: The transaction bundle
        """
        if bundle.received_over_tcp:
            # This is over TCP, so we allow all query types
            return

        if not isinstance(bundle.request, LeasequeryMessage):
            # Not a leasequery question, we don't care
            return

        query = bundle.request.get_option_of_type(LQQueryOption)
        if query.query_type in (QUERY_BY_RELAY_ID, QUERY_BY_LINK_ADDRESS, QUERY_BY_REMOTE_ID):
            raise ReplyWithLeasequeryError(STATUS_NOT_ALLOWED,
                                           "Query type {} is only allowed over bulk leasequery".format(
                                               query.query_type))
Пример #7
0
    def handle(self, bundle: TransactionBundle):
        """
        Perform leasequery if requested.

        :param bundle: The transaction bundle
        """
        if not isinstance(bundle.request, LeasequeryMessage):
            # Not a leasequery, not our business
            return

        # Extract the query
        queries = bundle.get_unhandled_options(LQQueryOption)
        if not queries:
            # No unhandled queries
            return

        query = queries[0]

        # Get the leases from the store
        lease_count, leases = self.store.find_leases(query)

        # A count of -1 means unsupported query, so we stop handling
        if lease_count < 0:
            return

        # Otherwise mark this query as handled
        bundle.mark_handled(query)

        # What we do now depends on the protocol
        if bundle.received_over_tcp:
            try:
                if lease_count > 0:
                    # We're doing bulk leasequery, return all the records in separate messages
                    leases_iterator = iter(leases)
                    first_link_address, first_data_option = next(
                        leases_iterator)
                    first_message = bundle.response
                    first_message.options.append(first_data_option)

                    bundle.responses = MessagesList(
                        first_message,
                        self.generate_data_messages(
                            first_message.transaction_id, leases_iterator))
                else:
                    # If the server does not find any bindings satisfying a query, it
                    # SHOULD send a LEASEQUERY-REPLY without an OPTION_STATUS_CODE option
                    # and without any OPTION_CLIENT_DATA option.
                    pass
            except:
                # Something went wrong (database changes while reading?), abort
                logger.exception(
                    "Error while building bulk leasequery response")
                raise ReplyWithLeasequeryError(
                    STATUS_QUERY_TERMINATED,
                    "Error constructing your reply, please try again")
        else:
            try:
                if lease_count == 1:
                    # One entry found, return it
                    leases_iterator = iter(leases)
                    first_link_address, first_data_option = next(
                        leases_iterator)
                    bundle.response.options.append(first_data_option)
                elif lease_count > 1:
                    # The Client Link option is used only in a LEASEQUERY-REPLY message and
                    # identifies the links on which the client has one or more bindings.
                    # It is used in reply to a query when no link-address was specified and
                    # the client is found to be on more than one link.
                    link_addresses = set(
                        [link_address for link_address, data_option in leases])
                    bundle.response.options.append(
                        LQClientLink(link_addresses))
            except:
                # Something went wrong (database changes while reading?), abort
                logger.exception("Error while building leasequery response")
                raise ReplyWithLeasequeryError(
                    STATUS_UNSPEC_FAIL,
                    "Error constructing your reply, please try again")