Package: whodap
whodapwhodap| Simple RDAP Utility for PythonSupport for asyncio HTTP requests (httpx)Leverages theSimpleNamespacetype for cleaner RDAP Response traversalKeeps the familiar look of WHOIS via theto_whois_dictmethod for DNS lookupsQuickstartimportasynciofrompprintimportpprintimportwhodap# Looking up a domain nameresponse=whodap.lookup_domain(domain='bitcoin',tld='org')# Equivalent asyncio callloop=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" notationprint(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 dictionarypprint(response.to_dict())# Use `to_whois_dict` for the familiar look of WHOIS outputpprint(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 ClassesObjectDescriptionlookup_domainPerforms an RDAP query for the given Domain and TLDlookup_ipv4Performs an RDAP query for the given IPv4 addresslookup_ipv6Performs an RDAP query for the given IPv6 addresslookup_asnPerforms an RDAP query for the Autonomous System with the given Numberaio_lookup_domainasync counterpart tolookup_domainaio_lookup_ipv4async counterpart tolookup_ipv4aio_lookup_ipv6async counterpart tolookup_ipv6aio_lookup_asnasync counterpart tolookup_asnDNSClientReusable client for RDAP DNS queriesIPv4ClientReusable client for RDAP IPv4 queriesIPv6ClientReusable client for RDAP IPv6 queriesASNClientReusable client for RDAP ASN queriesCommon Usage PatternsUsing the DNSClient:importwhodap# Initialize an instance of DNSClient using classmethods: `new_client` or `new_aio_client`dns_client=whodap.DNSClient.new_client()fordomain,tldin[('google','com'),('google','buzz')]:response=dns_client.lookup(domain,tld)# Equivalent asyncio calldns_client=awaitwhodap.DNSClient.new_aio_client()fordomain,tldin[('google','com'),('google','buzz')]:response=awaitdns_client.aio_lookup(domain,tld)# Use the DNSClient contextmanagers: `new_client_context` or `new_aio_client_context`withwhodap.DNSClient.new_client_context()asdns_client:fordomain,tldin[('google','com'),('google','buzz')]:response=dns_client.lookup(domain,tld)# Equivalent asyncio callasyncwithwhodap.DNSClient.new_aio_client_context()asdns_client:fordomain,tldin[('google','com'),('google','buzz')]:response=awaitdns_client.aio_lookup(domain,tld)Configurablehttpxclient:importasyncioimporthttpximportwhodap# Initialize a custom, pre-configured httpx client ...httpx_client=httpx.Client(proxies=httpx.Proxy('https://user:pw@proxy_url.net'))# ... or an async clientaio_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.asyncwithaio_httpx_client:futures=[]fordomain,tldin[('google','com'),('google','buzz')]:task=whodap.aio_lookup_domain(domain,tld,httpx_client=aio_httpx_client)futures.append(task)awaitasyncio.gather(*futures)# 2) Pass the httpx_client into the DNSClient classmethod: `new_client` or `new_aio_client`aio_dns_client=awaitwhodap.DNSClient.new_aio_client(aio_httpx_client)result=awaitaio_dns_client.aio_lookup('google','buzz')awaitaio_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.asyncwithwhodap.DNSClient.new_aio_client_context(aio_httpx_client)asdns_client:fordomain,tldin[('google','com'),('google','buzz')]:response=awaitdns_client.aio_lookup(domain,tld)Using theto_whois_dictmethod andRDAPConformanceExceptionimportloggingfromwhodapimportlookup_domainfromwhodap.errorsimportRDAPConformanceExceptionlogger=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 = Truetry:# 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)exceptRDAPConformanceException:logger.exception("RDAP response is incorrectly formatted.")ContributionsInterested 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" queriesSupport for RDAP "ipv4" and "ipv6" queriesSupport for RDAP ASN queriesAbstract the HTTP Client (httpxis the defacto client for now)Add parser utils/helpers for IPv4, IPv6, and ASN Responses (if someone shows interest)Add RDAP response validation support leveragingICANN's toolRDAP Resources:rdap.orgRFC 9082