Skip to main content

whodap

PyPI version example workflow codecov Code style: black Spectra Assure Community Badge

whodap | Simple RDAP Utility for Python

  • Support for asyncio HTTP requests (httpx2)
  • Leverages the SimpleNamespace type for cleaner RDAP Response traversal
  • Keeps the familiar look of WHOIS via the to_whois_dict method for DNS lookups

Quickstart

import asyncio
from pprint import pprint

import whodap

# Looking up a domain name
response = whodap.lookup_domain(domain='bitcoin', tld='org')
# Equivalent asyncio call
loop = asyncio.get_event_loop()
response = loop.run_until_complete(whodap.aio_lookup_domain(domain='bitcoin', tld='org'))
# "response" is a DomainResponse object. It contains the output from the RDAP lookup.
print(response)
# Traverse the DomainResponse via "dot" notation
print(response.events)
"""
[{
  "eventAction": "last update of RDAP database",
  "eventDate": "2021-04-23T21:50:03"
},
 {
  "eventAction": "registration",
  "eventDate": "2008-08-18T13:19:55"
}, ... ]
"""
# Retrieving the registration date from above:
print(response.events[1].eventDate)
"""
2008-08-18 13:19:55
"""
# Don't want "dot" notation? Use `to_dict` to get the RDAP response as a dictionary
pprint(response.to_dict())
# Use `to_whois_dict` for the familiar look of WHOIS output
pprint(response.to_whois_dict())
"""
{abuse_email: 'abuse@namecheap.com',
 abuse_phone: 'tel:+1.6613102107',
 admin_address: 'P.O. Box 0823-03411, Panama, Panama, PA',
 admin_email: '2603423f6ed44178a3b9d728827aa19a.protect@whoisguard.com',
 admin_fax: 'fax:+51.17057182',
 admin_name: 'WhoisGuard Protected',
 admin_organization: 'WhoisGuard, Inc.',
 admin_phone: 'tel:+507.8365503',
 billing_address: None,
 billing_email: None,
 billing_fax: None,
 billing_name: None,
 billing_organization: None,
 billing_phone: None,
 created_date: datetime.datetime(2008, 8, 18, 13, 19, 55),
 domain_name: 'bitcoin.org',
 expires_date: datetime.datetime(2029, 8, 18, 13, 19, 55),
 nameservers: ['dns1.registrar-servers.com', 'dns2.registrar-servers.com'],
 registrant_address: 'P.O. Box 0823-03411, Panama, Panama, PA',
 registrant_email: '2603423f6ed44178a3b9d728827aa19a.protect@whoisguard.com',
 registrant_fax: 'fax:+51.17057182',
 registrant_name: 'WhoisGuard Protected',
 registrant_organization: None,
 registrant_phone: 'tel:+507.8365503',
 registrar_address: '4600 E Washington St #305, Phoenix, Arizona, 85034',
 registrar_email: 'support@namecheap.com',
 registrar_fax: None,
 registrar_name: 'NAMECHEAP INC',
 registrar_phone: 'tel:+1.6613102107',
 status: ['client transfer prohibited'],
 technical_address: 'P.O. Box 0823-03411, Panama, Panama, PA',
 technical_email: '2603423f6ed44178a3b9d728827aa19a.protect@whoisguard.com',
 technical_fax: 'fax:+51.17057182',
 technical_name: 'WhoisGuard Protected',
 technical_organization: 'WhoisGuard, Inc.',
 technical_phone: 'tel:+507.8365503',
 updated_date: datetime.datetime(2019, 11, 24, 13, 58, 35)}
"""

Exported Functions and Classes

Object Description
lookup_domain Performs an RDAP query for the given Domain and TLD
lookup_ipv4 Performs an RDAP query for the given IPv4 address
lookup_ipv6 Performs an RDAP query for the given IPv6 address
lookup_asn Performs an RDAP query for the Autonomous System with the given Number
aio_lookup_domain async counterpart to lookup_domain
aio_lookup_ipv4 async counterpart to lookup_ipv4
aio_lookup_ipv6 async counterpart to lookup_ipv6
aio_lookup_asn async counterpart to lookup_asn
DNSClient Reusable client for RDAP DNS queries
IPv4Client Reusable client for RDAP IPv4 queries
IPv6Client Reusable client for RDAP IPv6 queries
ASNClient Reusable client for RDAP ASN queries

Common Usage Patterns

  • Using the DNSClient:
import whodap

# Initialize an instance of DNSClient using classmethods: `new_client` or `new_aio_client`
dns_client = whodap.DNSClient.new_client()
for domain, tld in [('google', 'com'), ('google', 'buzz')]:
    response = dns_client.lookup(domain, tld)

# Equivalent asyncio call
dns_client = await whodap.DNSClient.new_aio_client()
for domain, tld in [('google', 'com'), ('google', 'buzz')]:
    response = await dns_client.aio_lookup(domain, tld)

# Use the DNSClient contextmanagers: `new_client_context` or `new_aio_client_context`
with whodap.DNSClient.new_client_context() as dns_client:
    for domain, tld in [('google', 'com'), ('google', 'buzz')]:
        response = dns_client.lookup(domain, tld)

# Equivalent asyncio call
async with whodap.DNSClient.new_aio_client_context() as dns_client:
    for domain, tld in [('google', 'com'), ('google', 'buzz')]:
        response = await dns_client.aio_lookup(domain, tld)
  • Configurable httpx2 client:
import asyncio

import httpx2
import whodap

# Initialize a custom, pre-configured httpx2 client ...
httpx_client = httpx2.Client(proxy=httpx2.Proxy('https://user:pw@proxy_url.net'))
# ... or an async client
aio_httpx_client = httpx2.AsyncClient(proxy=httpx2.Proxy('http://user:pw@proxy_url.net'))

# Three common methods for leveraging httpx2 clients are outlined below:

# 1) Pass the httpx2 client directly into the convenience functions: `lookup_domain` or `aio_lookup_domain`
# Important: In this scenario, you are responsible for closing the httpx2 client.
# In this example, the given httpx2 client is used as a contextmanager; ensuring it is "closed" when finished.
async with aio_httpx_client:
    futures = []
    for domain, tld in [('google', 'com'), ('google', 'buzz')]:
        task = whodap.aio_lookup_domain(domain, tld, httpx_client=aio_httpx_client)
        futures.append(task)
    await asyncio.gather(*futures)

# 2) Pass the httpx_client into the DNSClient classmethod: `new_client` or `new_aio_client`
aio_dns_client = await whodap.DNSClient.new_aio_client(aio_httpx_client)
result = await aio_dns_client.aio_lookup('google', 'buzz')
await aio_httpx_client.aclose()

# 3) Pass the httpx_client into the DNSClient contextmanagers: `new_client_context` or `new_aio_client_context`
# This method ensures the underlying httpx_client is closed when exiting the "with" block.
async with whodap.DNSClient.new_aio_client_context(aio_httpx_client) as dns_client:
    for domain, tld in [('google', 'com'), ('google', 'buzz')]:
        response = await dns_client.aio_lookup(domain, tld)
  • Using the to_whois_dict method and RDAPConformanceException
import logging

from whodap import lookup_domain
from whodap.errors import RDAPConformanceException

logger = logging.getLogger(__name__)

# strict = False (default)
rdap_response = lookup_domain("example", "com")
whois_format = rdap_response.to_whois_dict()
logger.info(f"whois={whois_format}")
# Given a valid RDAP response, the `to_whois_dict` method will attempt to
# convert the RDAP format into a flattened dictionary of WHOIS key/values

# strict = True
try:
    # Unfortunately, there are instances in which the RDAP protocol is not
    # properly implemented by the registrar. By default, the `to_whois_dict`
    # will still attempt to parse the into the WHOIS dictionary. However,
    # there is no guarantee that the information will be correct or non-null.
    # If your applications rely on accurate information, the `strict=True`
    # parameter will raise an `RDAPConformanceException` when encountering
    # invalid or incorrectly formatted RDAP responses.
    rdap_response = lookup_domain("example", "com")
    whois_format = rdap_response.to_whois_dict(strict=True)
except RDAPConformanceException:
    logger.exception("RDAP response is incorrectly formatted.")

Contributions

  • Interested in contributing?
  • Have any questions or comments?
  • Anything that you'd like to see?
  • Anything that doesn't look right?

Please post a question or comment.

Roadmap

[alpha] 0.1.X Release:

  • Support for RDAP "domain" queries
  • Support for RDAP "ipv4" and "ipv6" queries
  • Support for RDAP ASN queries
  • Abstract the HTTP Client (httpx2 is the defacto client for now)
  • Add parser utils/helpers for IPv4, IPv6, and ASN Responses (if someone shows interest)
  • Add RDAP response validation support leveraging ICANN's tool

RDAP Resources:

Metadata

Release files for whodap 0.2.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for whodap 0.2.1
File Size Uploaded
whodap-0.2.1.tar.gz 25.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for whodap 0.2.1
File Interpreter ABI Platform
whodap-0.2.1-py3-none-any.whl Python 3 none any Details

Total release size: 43.1 kB

Release files / whodap-0.2.1.tar.gz

Download URL whodap-0.2.1.tar.gz
Size 25.0 kB
Tags Source
SHA-256 checksum
How to use checksums
80e7fe5e1ded6c9175cf160ba5366a579ef9bd7d4251a59c904a20b519064d06
BLAKE2b-256 checksum
How to use checksums
6f2c21024e9a0d03a95a9cd2e1888328c9868df5b275cf3b773a2327759ae80d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.13

Release files / whodap-0.2.1-py3-none-any.whl

Download URL whodap-0.2.1-py3-none-any.whl
Size 18.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b9eba2c007603b5061037059fab149931988b89fff3bcacc1525af7eb373482a
BLAKE2b-256 checksum
How to use checksums
eae3b45e17637cc982d306cc6887451b3359debea2d76bbf0babc468b1c35e06
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.13

Release history Release notifications | RSS feed

This release

0.2.1 This release

2 release files

0.2.0

2 release files

0.1.16

2 release files

0.1.15

2 release files

0.1.14

2 release files

0.1.13

2 release files

0.1.12

2 release files

0.1.9

2 release files

0.1.8

2 release files

0.1.7

2 release files

0.1.6

2 release files

0.1.5

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page