Skip to main content

Simple RDAP Utility for Python

Project description

whodap

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

whodap | Simple RDAP Utility for Python

  • Support for asyncio HTTP requests (httpx)
  • 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 httpx client:
import asyncio

import httpx
import whodap

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

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

# 1) Pass the httpx client directly into the convenience functions: `lookup_domain` or `aio_lookup_domain`
# Important: In this scenario, you are responsible for closing the httpx client.
# In this example, the given httpx 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 (httpx 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:

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

whodap-0.1.15.tar.gz (23.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

whodap-0.1.15-py3-none-any.whl (16.8 kB view details)

Uploaded Python 3

File details

Details for the file whodap-0.1.15.tar.gz.

File metadata

  • Download URL: whodap-0.1.15.tar.gz
  • Upload date:
  • Size: 23.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for whodap-0.1.15.tar.gz
Algorithm Hash digest
SHA256 6baf31d30330e9b323609035943fc284ab29571dd04847e39bf716f311f9bd6f
MD5 5fbe3546702514713e5abdb43027c7c9
BLAKE2b-256 dfe5dc96e0cbc488c1b7326a6f092a599a763e650e5df939f1a482ad52fa291b

See more details on using hashes here.

File details

Details for the file whodap-0.1.15-py3-none-any.whl.

File metadata

  • Download URL: whodap-0.1.15-py3-none-any.whl
  • Upload date:
  • Size: 16.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for whodap-0.1.15-py3-none-any.whl
Algorithm Hash digest
SHA256 91a9612965e9c06fd18e412245dae19c28ac07cc215651772c7dd81e3f22f7d1
MD5 c9e4db5d9cb660cc4c547920f5a9fb3c
BLAKE2b-256 8a48e27452178526da9f7791ae05e1b3cc02dbcd53086dc63d1652de5486b5d6

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page