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.0

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.0
File Size Uploaded
whodap-0.2.0.tar.gz 24.8 kB Details

Built distribution (wheel)

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

Total release size: 42.8 kB

Release files / whodap-0.2.0.tar.gz

Download URL whodap-0.2.0.tar.gz
Size 24.8 kB
Tags Source
SHA-256 checksum
How to use checksums
44baf2af0641b8134cf616eced19bf42137bead75aa186a92351189ecbe7d6c9
BLAKE2b-256 checksum
How to use checksums
62ed696f43dbee7c7e58cdfde586ddcce32c07e9fa727d7763bebac72d3cd12e
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.0-py3-none-any.whl

Download URL whodap-0.2.0-py3-none-any.whl
Size 17.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
5bc8b112b578a4118529a585eb3d113d2758aa6e5a36646561f4668fa1f7b190
BLAKE2b-256 checksum
How to use checksums
65b7c3c55385689d951ce6f86baa30eb4f4891c98f950e281d5a67228288e7cd
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

0.2.1

2 release files

This release

0.2.0 This release

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