'The function for retrieving and parsing information for an IP
address via RDAP (HTTP).
Args:
inc_raw: Boolean for whether to include the raw results in the
returned dictionary.
retry_count: The number of times to retry in case socket errors,
timeouts, connection resets, etc. are encountered.
asn_data: Result dictionary from ipwhois.net.Net.lookup_asn().
Optional if the bootstrap parameter is True.
depth: How many levels deep to run queries when additional
referenced objects are found.
excluded_entities: A list of entity handles to not perform lookups.
response: Optional response object, this bypasses the RDAP lookup.
bootstrap: If True, performs lookups via ARIN bootstrap rather
than lookups based on ASN data.
rate_limit_timeout: The number of seconds to wait before retrying
when a rate limit notice is returned via rdap+json.
Returns:
Dictionary:
:query: The IP address (String)
:network: Dictionary of values returned by _RDAPNetwork. The raw
result is included for each entity if the inc_raw parameter is
True.
:entities: List of entity keys referenced by the top level IP
address query.
:objects: Dictionary of objects with the handles as keys, and the
dictionary returned by _RDAPEntity, etc as the values. The raw
result is included for each object if the inc_raw parameter is
True.'

def lookup(self, inc_raw=False, retry_count=3, asn_data=None, depth=0, excluded_entities=None, response=None, bootstrap=False, rate_limit_timeout=120):

    if (not excluded_entities):
        excluded_entities = []
    results = {'query': self._net.address_str, 'network': None, 'entities': None, 'objects': None, 'raw': None}
    if bootstrap:
        ip_url = '{0}/ip/{1}'.format(BOOTSTRAP_URL, self._net.address_str)
    else:
        ip_url = str(RIR_RDAP[asn_data['asn_registry']]['ip_url']).format(self._net.address_str)
    if (response is None):
        log.debug('Response    not    given,    perform    RDAP    lookup    for    {0}'.format(ip_url))
        response = self._net.get_http_json(url=ip_url, retry_count=retry_count, rate_limit_timeout=rate_limit_timeout)
    if inc_raw:
        results['raw'] = response
    log.debug('Parsing    RDAP    network    object')
    result_net = _RDAPNetwork(response)
    result_net.parse()
    results['network'] = result_net.vars
    results['entities'] = []
    results['objects'] = {}
    log.debug('Parsing    RDAP    root    level    entities')
    try:
        for ent in response['entities']:
            if (ent['handle'] not in [results['entities'], excluded_entities]):
                result_ent = _RDAPEntity(ent)
                result_ent.parse()
                results['objects'][ent['handle']] = result_ent.vars
                results['entities'].append(ent['handle'])
    except KeyError:
        pass
    temp_objects = results['objects']
    if ((depth > 0) and (len(temp_objects) > 0)):
        log.debug('Parsing    RDAP    sub-entities    to    depth:    {0}'.format(str(depth)))
    while ((depth > 0) and (len(temp_objects) > 0)):
        new_objects = {}
        for obj in temp_objects.values():
            try:
                for ent in obj['entities']:
                    if (ent not in ((list(results['objects'].keys()) + list(new_objects.keys())) + excluded_entities)):
                        if bootstrap:
                            entity_url = '{0}/entity/{1}'.format(BOOTSTRAP_URL, ent)
                        else:
                            tmp_reg = asn_data['asn_registry']
                            entity_url = RIR_RDAP[tmp_reg]['entity_url']
                            entity_url = str(entity_url).format(ent)
                        try:
                            response = self._net.get_http_json(url=entity_url, retry_count=retry_count, rate_limit_timeout=rate_limit_timeout)
                            result_ent = _RDAPEntity(response)
                            result_ent.parse()
                            new_objects[ent] = result_ent.vars
                            if inc_raw:
                                new_objects[ent]['raw'] = response
                        except (HTTPLookupError, InvalidEntityObject):
                            pass
            except TypeError:
                pass
        results['objects'].update(new_objects)
        temp_objects = new_objects
        depth -= 1
    return results
