From 61dcd34e426ce2f5e9593940f76c4d997230a785 Mon Sep 17 00:00:00 2001 From: Joe Obarzanek Date: Thu, 8 Feb 2024 18:59:37 -0500 Subject: [PATCH 1/8] Update setup configs --- .github/workflows/build-and-test.yml | 6 +++--- setup.cfg | 5 +++-- setup.py | 9 +++++---- tox.ini | 7 +++---- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 03bf9b6..984ba71 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -13,7 +13,7 @@ jobs: - ubuntu-latest - macos-latest - windows-latest - python-version: ['3.7', '3.8', '3.9', '3.10', '3.11'] + python-version: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12'] steps: - uses: actions/checkout@v3 @@ -28,12 +28,12 @@ jobs: - name: Test with tox run: tox -vv - name: Upload coverage.xml - if: ${{ matrix.platform == 'ubuntu-latest' && matrix.python-version == '3.10' }} + if: ${{ matrix.platform == 'ubuntu-latest' && matrix.python-version == '3.12' }} uses: actions/upload-artifact@v3 with: name: tox-gh-actions-coverage path: coverage.xml if-no-files-found: error - name: Upload coverage.xml to codecov - if: ${{ matrix.platform == 'ubuntu-latest' && matrix.python-version == '3.10' }} + if: ${{ matrix.platform == 'ubuntu-latest' && matrix.python-version == '3.12' }} uses: codecov/codecov-action@v2 \ No newline at end of file diff --git a/setup.cfg b/setup.cfg index c010626..667390c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -4,7 +4,7 @@ name = asyncwhois version = attrs: asyncwhois.__version__ author = Joseph Obarzanek author_email = pogzyb@umich.edu -description = Python utility for querying and parsing WHOIS information for Domains, IPv4s, IPv6s, and AS numbers +description = Python WHOIS and RDAP utility for querying and parsing information about Domains, IPv4s, IPv6s, and AS Numbers long_description = file: README.md long_description_content_type = text/markdown url = https://github.com/pogzyb/asyncwhois @@ -22,12 +22,13 @@ classifiers = Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.11 + Programming Language :: Python :: 3.12 Programming Language :: Python :: 3 :: Only [options] package_dir = asyncwhois packages = find: -python_requires = >=3.6 +python_requires = >=3.7 [options.packages.find] where = asyncwhois \ No newline at end of file diff --git a/setup.py b/setup.py index da6c9a4..c0961a6 100644 --- a/setup.py +++ b/setup.py @@ -27,8 +27,8 @@ def get_version(location: str) -> str: license="MIT", install_requires=[ "python-socks[asyncio]>=2.0.2", - "tldextract>=2.2.0", - "whodap>=0.1.4", + "tldextract>=3.2.0", + "whodap>=0.1.10", ], classifiers=[ "Environment :: Web Environment", @@ -36,11 +36,12 @@ def get_version(location: str) -> str: "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3 :: Only", "Topic :: Internet :: WWW/HTTP", "Topic :: Software Development", @@ -50,5 +51,5 @@ def get_version(location: str) -> str: url="https://github.com/pogzyb/asyncwhois", packages=["asyncwhois"], package_dir={"asyncwhois": "asyncwhois"}, - python_requires=">=3.6", + python_requires=">=3.7", ) diff --git a/tox.ini b/tox.ini index 62fda13..c6d44ed 100644 --- a/tox.ini +++ b/tox.ini @@ -1,16 +1,15 @@ [tox] -envlist = py36, py37, py38, py39, py310 -minversion = 3.6.0 +envlist = py37, py38, py39, py310, py311, py312 +minversion = 3.7.0 isolated_build = true [gh-actions] python = - 3.6: py36 - 3.7: py37 3.8: py38 3.9: py39 3.10: py310 3.11: py311 + 3.12: py312 [testenv] deps = From b1d0cb9fc66326d84b05a389ea3cae744f8c3af2 Mon Sep 17 00:00:00 2001 From: Joe Obarzanek Date: Thu, 8 Feb 2024 19:02:43 -0500 Subject: [PATCH 2/8] Add reusable client A new Client parent class with child classes - DomainClient, NumberClient, and ASNClient - that better decouple the functionality of the query and parser classes but implement both under the Client base class. --- asyncwhois/__init__.py | 459 ++++++++++++++++++++++++++++++++++++---- asyncwhois/client.py | 208 ++++++++++++++++++ asyncwhois/parse_rir.py | 21 +- asyncwhois/parse_tld.py | 154 +++++++------- asyncwhois/query.py | 201 ++++++++---------- 5 files changed, 802 insertions(+), 241 deletions(-) create mode 100644 asyncwhois/client.py diff --git a/asyncwhois/__init__.py b/asyncwhois/__init__.py index a722b02..352a91f 100644 --- a/asyncwhois/__init__.py +++ b/asyncwhois/__init__.py @@ -1,9 +1,25 @@ +import ipaddress from ipaddress import IPv4Address, IPv6Address from typing import Any, Optional, Union +from dataclasses import dataclass +from warnings import warn -from .pywhois import DomainLookup, NumberLookup, ASNLookup +import whodap +from tldextract.tldextract import TLDExtract + +from .client import ( + ASNClient, + DomainClient, + NumberClient, + convert_to_ip, +) +from .errors import NotFoundError, GeneralError, QueryError, WhoIsError __all__ = [ + "aio_rdap", + "aio_whois", + "whois", + "rdap", "aio_whois_domain", "aio_whois_ipv4", "aio_whois_ipv6", @@ -18,80 +34,355 @@ "whois_domain", "whois_ipv4", "whois_ipv6", + "ASNClient", + "DomainClient", + "NumberClient", + "NotFoundError", + "WhoIsError", + "GeneralError", + "QueryError", ] __version__ = "1.0.10" +def whois( + search_term: Union[str, ipaddress.IPv4Address, ipaddress.IPv6Address], + authoritative_only: bool = False, + ignore_not_found: bool = False, + proxy_url: str = None, + timeout: int = 10, + tldextract_obj: TLDExtract = None, +) -> tuple[str, dict]: + """ + Performs a WHOIS query for the given `search_term`. If `search_term` is or can be cast to an + instance of `ipaddress.IPv4Address` or `ipaddress.IPv6Address` then an IP search is performed + otherwise a DNS search is performed. + + :param search_term: Any domain, URL, IPv4, or IPv6 + :param authoritative_only: If False (default), asyncwhois returns the entire WHOIS query chain, + otherwise if True, only the authoritative response is returned. + :param ignore_not_found: If False (default), the `NotFoundError` exception is raised if the query output + contains "no such domain" language. If True, asyncwhois will not raise `NotFoundError` exceptions. + :param proxy_url: Optional SOCKS4 or SOCKS5 proxy url (e.g. 'socks5://host:port') + :param timeout: Connection timeout. Default is 10 seconds. + :param tldextract_obj: An optional preconfigured instance of `tldextract.TLDExtract` (used for parsing URLs) + :returns: a tuple containing the WHOIS query text and a dictionary of key values parsed from the text + """ + if isinstance(search_term, (ipaddress.IPv6Address, ipaddress.IPv6Address)): + return NumberClient( + authoritative_only=authoritative_only, + proxy_url=proxy_url, + timeout=timeout, + ).whois(search_term) + elif isinstance(search_term, str): + try: + search_term = convert_to_ip(search_term) + return NumberClient( + authoritative_only=authoritative_only, + proxy_url=proxy_url, + timeout=timeout, + ).whois(search_term) + except (ipaddress.AddressValueError, ValueError): + return DomainClient( + authoritative_only=authoritative_only, + ignore_not_found=ignore_not_found, + proxy_url=proxy_url, + timeout=timeout, + tldextract_obj=tldextract_obj, + ).whois(search_term) + else: + return "", {} + + +def rdap( + search_term: Union[int, str, ipaddress.IPv4Address, ipaddress.IPv6Address], + authoritative_only: bool = False, + whodap_client: Union[ + whodap.DNSClient, whodap.IPv4Client, whodap.IPv6Client, whodap.ASNClient + ] = None, + tldextract_obj: Optional[TLDExtract] = None, +) -> tuple[str, dict]: + """ + Performs an RDAP query for the given `search_term`. If `search_term` is or can be cast to an + instance of `ipaddress.IPv4Address` or `ipaddress.IPv6Address` then an IP search is performed + otherwise a DNS search is performed. + + :param search_term: Any domain, URL, IPv4, or IPv6 + :param authoritative_only: If False (default), asyncwhois returns the entire WHOIS query chain, + otherwise if True, only the authoritative response is returned. + :param whodap_client: An optional preconfigured instance of either an ASN, DNS, IPv4, or IPv6 `whodap` client + :param tldextract_obj: An optional preconfigured instance of `tldextract.TLDExtract` (used for parsing URLs) + :returns: a tuple containing the WHOIS query text and a dictionary of key values parsed from the text + """ + if isinstance(search_term, (ipaddress.IPv6Address, ipaddress.IPv6Address)): + return NumberClient( + authoritative_only=authoritative_only, + whodap_client=whodap_client, + ).rdap(search_term) + elif isinstance(search_term, str): + try: + search_term = convert_to_ip(search_term) + return NumberClient( + authoritative_only=authoritative_only, + whodap_client=whodap_client, + ).rdap(search_term) + except (ipaddress.AddressValueError, ValueError): + return DomainClient( + authoritative_only=authoritative_only, + whodap_client=whodap_client, + tldextract_obj=tldextract_obj, + ).rdap(search_term) + elif isinstance(search_term, int): + return ASNClient(whodap_client=whodap_client).rdap(search_term) + else: + return "", {} + + +async def aio_whois( + search_term: str, + authoritative_only: bool = False, + ignore_not_found: bool = False, + proxy_url: str = None, + timeout: int = 10, + tldextract_obj: TLDExtract = None, +) -> tuple[str, dict]: + """ + Performs a WHOIS query for the given `search_term`. If `search_term` is or can be cast to an + instance of `ipaddress.IPv4Address` or `ipaddress.IPv6Address` then an IP search is performed + otherwise a DNS search is performed. + + :param search_term: Any domain, URL, IPv4, or IPv6 + :param authoritative_only: If False (default), asyncwhois returns the entire WHOIS query chain, + otherwise if True, only the authoritative response is returned. + :param ignore_not_found: If False (default), the `NotFoundError` exception is raised if the query output + contains "no such domain" language. If True, asyncwhois will not raise `NotFoundError` exceptions. + :param proxy_url: Optional SOCKS4 or SOCKS5 proxy url (e.g. 'socks5://host:port') + :param timeout: Connection timeout. Default is 10 seconds. + :param tldextract_obj: An optional preconfigured instance of `tldextract.TLDExtract` (used for parsing URLs) + :returns: a tuple containing the WHOIS query text and a dictionary of key values parsed from the text + """ + if isinstance(search_term, (ipaddress.IPv6Address, ipaddress.IPv6Address)): + return await NumberClient( + authoritative_only=authoritative_only, + proxy_url=proxy_url, + timeout=timeout, + ).aio_whois(search_term) + elif isinstance(search_term, str): + try: + search_term = convert_to_ip(search_term) + return await NumberClient( + authoritative_only=authoritative_only, + proxy_url=proxy_url, + timeout=timeout, + ).aio_whois(search_term) + except (ipaddress.AddressValueError, ValueError): + return await DomainClient( + authoritative_only=authoritative_only, + ignore_not_found=ignore_not_found, + proxy_url=proxy_url, + timeout=timeout, + tldextract_obj=tldextract_obj, + ).aio_whois(search_term) + else: + return "", {} + + +async def aio_rdap( + search_term: Union[int, str, ipaddress.IPv4Address, ipaddress.IPv6Address], + authoritative_only: bool = False, + whodap_client: Union[ + whodap.DNSClient, whodap.IPv4Client, whodap.IPv6Client, whodap.ASNClient + ] = None, + tldextract_obj: Optional[TLDExtract] = None, +) -> tuple[str, dict]: + """ + Performs an RDAP query for the given `search_term`. If `search_term` is or can be cast to an + instance of `ipaddress.IPv4Address` or `ipaddress.IPv6Address` then an IP search is performed + otherwise a DNS search is performed. + + :param search_term: Any domain, URL, IPv4, or IPv6 + :param authoritative_only: If False (default), asyncwhois returns the entire WHOIS query chain, + otherwise if True, only the authoritative response is returned. + :param whodap_client: An optional preconfigured instance of either an ASN, DNS, IPv4, or IPv6 `whodap` client + :param tldextract_obj: An optional preconfigured instance of `tldextract.TLDExtract` (used for parsing URLs) + :returns: a tuple containing the WHOIS query text and a dictionary of key values parsed from the text + """ + if isinstance(search_term, (ipaddress.IPv6Address, ipaddress.IPv6Address)): + return await NumberClient( + authoritative_only=authoritative_only, + whodap_client=whodap_client, + ).aio_rdap(search_term) + elif isinstance(search_term, str): + try: + search_term = convert_to_ip(search_term) + return await NumberClient( + authoritative_only=authoritative_only, + whodap_client=whodap_client, + ).aio_rdap(search_term) + except (ipaddress.AddressValueError, ValueError): + return await DomainClient( + authoritative_only=authoritative_only, + whodap_client=whodap_client, + tldextract_obj=tldextract_obj, + ).aio_rdap(search_term) + elif isinstance(search_term, int): + return await ASNClient(whodap_client=whodap_client).aio_rdap(search_term) + else: + return "", {} + + +# ==================== +# TODO: ALL the code below will be removed in a future release; it is here for backwards compatibility only + + +@dataclass +class DomainLookup: + query_output: str + parser_output: dict + + +@dataclass +class NumberLookup: + query_output: str + parser_output: dict + + +@dataclass +class ASNLookup: + query_output: str + parser_output: dict + + def whois_domain( domain: str, authoritative_only: bool = False, + ignore_not_found: bool = False, proxy_url: str = None, timeout: int = 10, + tldextract_obj: TLDExtract = None, ) -> DomainLookup: """ Performs domain lookups with WHOIS. Finds the authoritative WHOIS server and parses the response from the server. :param domain: Any domain or URL (e.g. 'wikipedia.org' or 'https://en.wikipedia.org/wiki/WHOIS') - :param authoritative_only: If False, returns the entire WHOIS query chain + :param authoritative_only: If False (default), asyncwhois returns the entire WHOIS query chain in `query_output`; If True only the authoritative response is included. + :param ignore_not_found: If False (default), the `NotFoundError` exception is raised if the `query_output` + contains "no such domain" language. If True, asyncwhois will not raise `NotFoundError` exceptions. :param proxy_url: Optional SOCKS4 or SOCKS5 proxy url (e.g. 'socks5://host:port') :param timeout: Connection timeout. Default is 10 seconds. + :param tldextract_obj: An optional preconfigured instance of `tldextract.tldextract.TLDExtract` :return: instance of DomainLookup """ - result = DomainLookup.whois_domain(domain, authoritative_only, proxy_url, timeout) - return result + warn( + "`asyncwhois.whois_domain` is deprecated. Please use `asyncwhois.whois` instead.", + DeprecationWarning, + stacklevel=2, + ) + query_output, parser_output = DomainClient( + authoritative_only=authoritative_only, + ignore_not_found=ignore_not_found, + proxy_url=proxy_url, + timeout=timeout, + tldextract_obj=tldextract_obj, + ).whois(domain) + return DomainLookup(query_output, parser_output) async def aio_whois_domain( domain: str, authoritative_only: bool = False, + ignore_not_found: bool = False, proxy_url: str = None, timeout: int = 10, + tldextract_obj: TLDExtract = None, ) -> DomainLookup: """ Performs asynchronous domain lookups with WHOIS. Finds the authoritative WHOIS server and parses the response from the server. :param domain: Any domain or URL (e.g. 'wikipedia.org' or 'https://en.wikipedia.org/wiki/WHOIS') - :param authoritative_only: If False, returns the entire WHOIS query chain + :param authoritative_only: If False (default), asyncwhois returns the entire WHOIS query chain in `query_output`; If True only the authoritative response is included. + :param ignore_not_found: If False (default), the `NotFoundError` exception is raised if the `query_output` + contains "no such domain" language. If True, asyncwhois will not raise `NotFoundError` exceptions. :param proxy_url: Optional SOCKS4 or SOCKS5 proxy url (e.g. 'socks5://host:port') :param timeout: Connection timeout. Default is 10 seconds. + :param tldextract_obj: An optional preconfigured instance of `tldextract.tldextract.TLDExtract` :return: instance of DomainLookup """ - result = await DomainLookup.aio_whois_domain( - domain, authoritative_only, proxy_url, timeout + warn( + "`asyncwhois.aio_whois_domain` is deprecated. Please use `asyncwhois.aio_whois` instead.", + DeprecationWarning, + stacklevel=2, ) - return result + query_output, parser_output = await DomainClient( + authoritative_only=authoritative_only, + ignore_not_found=ignore_not_found, + proxy_url=proxy_url, + timeout=timeout, + tldextract_obj=tldextract_obj, + ).aio_whois(domain) + return DomainLookup(query_output, parser_output) -def rdap_domain(domain: str, httpx_client: Optional[Any] = None) -> DomainLookup: +def rdap_domain( + domain: str, + httpx_client: Optional[Any] = None, + tldextract_obj: Optional[TLDExtract] = None, +) -> DomainLookup: """ Performs an RDAP query for the given domain. Finds the authoritative RDAP server and parses the response from that server. :param domain: Any domain name or URL (e.g. 'wikipedia.org' or 'https://en.wikipedia.org/wiki/WHOIS') - :param httpx_client: Optional preconfigured `httpx.Client` + :param httpx_client: Optional preconfigured instance of `httpx.AsyncClient` + :param tldextract_obj: Optional preconfigured instance of `tldextract.tldextract.TLDExtract` :return: instance of DomainLookup """ - result = DomainLookup.rdap_domain(domain, httpx_client) - return result + warn( + "`asyncwhois.rdap_domain` is deprecated. Please use `asyncwhois.aio_rdap` instead.", + DeprecationWarning, + stacklevel=2, + ) + whodap_client = None + if httpx_client is not None: + whodap_client = whodap.DNSClient.new_client(httpx_client=httpx_client) + query_output, parser_output = DomainClient( + whodap_client=whodap_client, + tldextract_obj=tldextract_obj, + ).rdap(domain) + return DomainLookup(query_output, parser_output) async def aio_rdap_domain( - domain: str, httpx_client: Optional[Any] = None + domain: str, + httpx_client: Optional[Any] = None, + tldextract_obj: Optional[TLDExtract] = None, ) -> DomainLookup: """ Performs an async RDAP query for the given domain name. :param domain: Any domain or URL (e.g. 'wikipedia.org' or 'https://en.wikipedia.org/wiki/WHOIS') - :param httpx_client: Optional preconfigured `httpx.AsyncClient` + :param httpx_client: Optional preconfigured instance of `httpx.AsyncClient` + :param tldextract_obj: Optional preconfigured instance of `tldextract.tldextract.TLDExtract` :return: instance of DomainLookup """ - result = await DomainLookup.aio_rdap_domain(domain, httpx_client) - return result + warn( + "`asyncwhois.aio_rdap_domain` is deprecated. Please use `asyncwhois.aio_rdap` instead.", + DeprecationWarning, + stacklevel=2, + ) + whodap_client = None + if httpx_client is not None: + whodap_client = whodap.DNSClient.new_aio_client(httpx_client=httpx_client) + query_output, parser_output = await DomainClient( + whodap_client=whodap_client, + tldextract_obj=tldextract_obj, + ).aio_rdap(domain) + return DomainLookup(query_output, parser_output) def whois_ipv4( @@ -111,8 +402,17 @@ def whois_ipv4( :param timeout: Connection timeout. Default is 10 seconds. :return: instance of DomainLookup """ - result = NumberLookup.whois_ipv4(ipv4, authoritative_only, proxy_url, timeout) - return result + warn( + "`asyncwhois.whois_ipv4` is deprecated. Please use `asyncwhois.whois` instead.", + DeprecationWarning, + stacklevel=2, + ) + query_output, parser_output = NumberClient( + authoritative_only=authoritative_only, + proxy_url=proxy_url, + timeout=timeout, + ).whois(ipv4) + return NumberLookup(query_output, parser_output) async def aio_whois_ipv4( @@ -132,10 +432,17 @@ async def aio_whois_ipv4( :param timeout: Connection timeout. Default is 10 seconds. :return: instance of NumberLookup """ - result = await NumberLookup.aio_whois_ipv4( - ipv4, authoritative_only, proxy_url, timeout + warn( + "`asyncwhois.aio_whois_ipv4` is deprecated. Please use `asyncwhois.aio_whois` instead.", + DeprecationWarning, + stacklevel=2, ) - return result + query_output, parser_output = await NumberClient( + authoritative_only=authoritative_only, + proxy_url=proxy_url, + timeout=timeout, + ).aio_whois(ipv4) + return NumberLookup(query_output, parser_output) def rdap_ipv4( @@ -148,8 +455,18 @@ def rdap_ipv4( :param httpx_client: Optional preconfigured `httpx.Client` :return: instance of NumberLookup """ - result = NumberLookup.rdap_ipv4(ipv4, httpx_client) - return result + warn( + "`asyncwhois.rdap_ipv4` is deprecated. Please use `asyncwhois.rdap` instead.", + DeprecationWarning, + stacklevel=2, + ) + whodap_client = None + if httpx_client is not None: + whodap_client = whodap.IPv4Client.new_client(httpx_client=httpx_client) + query_output, parser_output = NumberClient( + whodap_client=whodap_client, + ).rdap(ipv4) + return NumberLookup(query_output, parser_output) async def aio_rdap_ipv4( @@ -162,8 +479,18 @@ async def aio_rdap_ipv4( :param httpx_client: Optional preconfigured `httpx.AsyncClient` :return: instance of NumberLookup """ - result = await NumberLookup.aio_rdap_ipv4(ipv4, httpx_client) - return result + warn( + "`asyncwhois.aio_rdap_ipv4` is deprecated. Please use `asyncwhois.aio_rdap` instead.", + DeprecationWarning, + stacklevel=2, + ) + whodap_client = None + if httpx_client is not None: + whodap_client = whodap.IPv4Client.new_client(httpx_client=httpx_client) + query_output, parser_output = NumberClient( + whodap_client=whodap_client, + ).aio_rdap(ipv4) + return NumberLookup(query_output, parser_output) def whois_ipv6( @@ -183,8 +510,17 @@ def whois_ipv6( :param timeout: Connection timeout. Default is 10 seconds. :return: instance of NumberLookup """ - result = NumberLookup.whois_ipv6(ipv6, authoritative_only, proxy_url, timeout) - return result + warn( + "`asyncwhois.whois_ipv6` is deprecated. Please use `asyncwhois.whois` instead.", + DeprecationWarning, + stacklevel=2, + ) + query_output, parser_output = NumberClient( + authoritative_only=authoritative_only, + proxy_url=proxy_url, + timeout=timeout, + ).whois(ipv6) + return NumberLookup(query_output, parser_output) async def aio_whois_ipv6( @@ -204,10 +540,17 @@ async def aio_whois_ipv6( :param timeout: Connection timeout. Default is 10 seconds. :return: instance of NumberLookup """ - result = await NumberLookup.aio_whois_ipv6( - ipv6, authoritative_only, proxy_url, timeout + warn( + "`asyncwhois.aio_whois_ipv6` is deprecated. Please use `asyncwhois.aio_whois` instead.", + DeprecationWarning, + stacklevel=2, ) - return result + query_output, parser_output = await NumberClient( + authoritative_only=authoritative_only, + proxy_url=proxy_url, + timeout=timeout, + ).aio_whois(ipv6) + return NumberLookup(query_output, parser_output) def rdap_ipv6( @@ -220,8 +563,18 @@ def rdap_ipv6( :param httpx_client: Optional preconfigured `httpx.Client` :return: instance of NumberLookup """ - result = NumberLookup.rdap_ipv6(ipv6, httpx_client) - return result + warn( + "`asyncwhois.rdap_ipv6` is deprecated. Please use `asyncwhois.rdap` instead.", + DeprecationWarning, + stacklevel=2, + ) + whodap_client = None + if httpx_client is not None: + whodap_client = whodap.IPv6Client.new_client(httpx_client=httpx_client) + query_output, parser_output = NumberClient( + whodap_client=whodap_client, + ).rdap(ipv6) + return NumberLookup(query_output, parser_output) async def aio_rdap_ipv6( @@ -234,8 +587,18 @@ async def aio_rdap_ipv6( :param httpx_client: Optional preconfigured `httpx.AsyncClient` :return: instance of NumberLookup """ - result = await NumberLookup.aio_rdap_ipv6(ipv6, httpx_client) - return result + warn( + "`asyncwhois.aio_rdap_ipv6` is deprecated. Please use `asyncwhois.aio_rdap` instead.", + DeprecationWarning, + stacklevel=2, + ) + whodap_client = None + if httpx_client is not None: + whodap_client = whodap.IPv6Client.new_aio_client(httpx_client=httpx_client) + query_output, parser_output = NumberClient( + whodap_client=whodap_client, + ).aio_rdap(ipv6) + return NumberLookup(query_output, parser_output) def rdap_asn(asn: int, httpx_client: Optional[Any] = None) -> ASNLookup: @@ -246,8 +609,18 @@ def rdap_asn(asn: int, httpx_client: Optional[Any] = None) -> ASNLookup: :param httpx_client: Optional preconfigured `httpx.Client` :return: instance of ASNLookup """ - result = ASNLookup.rdap_asn(asn, httpx_client) - return result + warn( + "`asyncwhois.rdap_asn` is deprecated. Please use `asyncwhois.rdap` instead.", + DeprecationWarning, + stacklevel=2, + ) + whodap_client = None + if httpx_client is not None: + whodap_client = whodap.IPv6Client.new_client(httpx_client=httpx_client) + query_output, parser_output = ASNClient( + whodap_client=whodap_client, + ).rdap(asn) + return ASNLookup(query_output, parser_output) async def aio_rdap_asn(asn: int, httpx_client: Optional[Any] = None) -> ASNLookup: @@ -258,5 +631,15 @@ async def aio_rdap_asn(asn: int, httpx_client: Optional[Any] = None) -> ASNLooku :param httpx_client: Optional preconfigured `httpx.AsyncClient` :return: instance of ASNLookup """ - result = await ASNLookup.aio_rdap_asn(asn, httpx_client) - return result + warn( + "`asyncwhois.aio_rdap_asn` is deprecated. Please use `asyncwhois.aio_rdap` instead.", + DeprecationWarning, + stacklevel=2, + ) + whodap_client = None + if httpx_client is not None: + whodap_client = whodap.IPv6Client.new_aio_client(httpx_client=httpx_client) + query_output, parser_output = ASNClient( + whodap_client=whodap_client, + ).aio_rdap(asn) + return ASNLookup(query_output, parser_output) diff --git a/asyncwhois/client.py b/asyncwhois/client.py new file mode 100644 index 0000000..6986bd2 --- /dev/null +++ b/asyncwhois/client.py @@ -0,0 +1,208 @@ +import ipaddress +from typing import Union, Any + +from tldextract.tldextract import extract, TLDExtract +import whodap + +from .parse import convert_whodap_keys, IPBaseKeys, TLDBaseKeys +from .parse_rir import NumberParser +from .parse_tld import DomainParser +from .query import DomainQuery, NumberQuery + + +def convert_to_ip(ip: str): + try: + ip_obj = ipaddress.ip_address(ip) + return ip_obj + except ipaddress.AddressValueError as e: + raise e + + +class Client: + def __init__(self, whodap_client): + self.whodap_client = whodap_client + + def init_whodap_client(self, ipv4: bool = True): + if isinstance(self, DomainClient): + self.whodap_client = whodap.DNSClient.new_client() + elif isinstance(self, NumberClient): + if ipv4: + self.whodap_client = whodap.IPv4Client.new_client() + else: + self.whodap_client = whodap.IPv6Client.new_client() + elif isinstance(self, ASNClient): + self.whodap_client = whodap.ASNClient.new_client() + + async def init_async_whodap_client(self, ipv4: bool = True): + if isinstance(self, DomainClient): + self.whodap_client = await whodap.DNSClient.new_aio_client() + elif isinstance(self, NumberClient): + if ipv4: + self.whodap_client = await whodap.IPv4Client.new_aio_client() + else: + self.whodap_client = await whodap.IPv6Client.new_aio_client() + elif isinstance(self, ASNClient): + self.whodap_client = await whodap.ASNClient.new_aio_client() + + +class DomainClient(Client): + def __init__( + self, + authoritative_only: bool = False, + ignore_not_found: bool = False, + proxy_url: str = None, + whodap_client: whodap.DNSClient = None, + timeout: int = 10, + tldextract_obj: TLDExtract = None, + ): + super().__init__(whodap_client) + self.authoritative_only = authoritative_only + self.ignore_not_found = ignore_not_found + self.proxy_url = proxy_url + self.timeout = timeout + self.tldextract_obj = tldextract_obj + self.query_obj = DomainQuery(proxy_url=proxy_url, timeout=timeout) + self.parse_obj = DomainParser(ignore_not_found=ignore_not_found) + + def _get_domain_components(self, domain: str) -> tuple[str, str, str]: + ext = ( + extract(domain) + if self.tldextract_obj is None + else self.tldextract_obj(domain) + ) + suffix = ext.suffix.split(".")[-1] + return ext.registered_domain, ext.domain, suffix + + def rdap(self, domain: str) -> tuple[str, dict]: + if self.whodap_client is None: + self.init_whodap_client() + _, domain, tld = self._get_domain_components(domain) + rdap_output = self.whodap_client.lookup(domain, tld) + query_string = rdap_output.to_json() + parsed_dict = convert_whodap_keys(rdap_output.to_whois_dict()) + return query_string, parsed_dict + + def whois(self, domain: str) -> tuple[str, dict[TLDBaseKeys, Any]]: + registered_domain, domain, tld = self._get_domain_components(domain) + query_chain: list[str] = self.query_obj.run(registered_domain) + authoritative_answer = query_chain[-1] + parsed_dict: dict[TLDBaseKeys, Any] = self.parse_obj.parse( + authoritative_answer, tld + ) + query_string = ( + authoritative_answer if self.authoritative_only else "\n".join(query_chain) + ) + return query_string, parsed_dict + + async def aio_rdap(self, domain: str) -> tuple[str, dict]: + if self.whodap_client is None: + await self.init_async_whodap_client() + _, domain, tld = self._get_domain_components(domain) + rdap_output = await self.whodap_client.aio_lookup(domain, tld) + query_string = rdap_output.to_json() + parsed_dict = convert_whodap_keys(rdap_output.to_whois_dict()) + return query_string, parsed_dict + + async def aio_whois(self, domain: str) -> tuple[str, dict[TLDBaseKeys, Any]]: + registered_domain, domain, tld = self._get_domain_components(domain) + query_chain: list[str] = await self.query_obj.aio_run(registered_domain) + authoritative_answer = query_chain[-1] + parsed_dict: dict[TLDBaseKeys, Any] = self.parse_obj.parse( + authoritative_answer, tld + ) + query_string = ( + authoritative_answer if self.authoritative_only else "\n".join(query_chain) + ) + return query_string, parsed_dict + + +class NumberClient(Client): + def __init__( + self, + authoritative_only: bool = False, + proxy_url: str = None, + whodap_client: Union[whodap.IPv4Client, whodap.IPv6Client] = None, + timeout: int = 10, + ): + super().__init__(whodap_client) + self.authoritative_only = authoritative_only + self.proxy_url = proxy_url + self.timeout = timeout + self.whodap_client = whodap_client + self.query_obj = NumberQuery(proxy_url=proxy_url, timeout=timeout) + self.parse_obj = NumberParser() + + def rdap( + self, ip: Union[ipaddress.IPv4Address, ipaddress.IPv6Address, str] + ) -> tuple[str, dict]: + if not isinstance(ip, (ipaddress.IPv4Address, ipaddress.IPv6Address)): + ip = convert_to_ip(ip) + if self.whodap_client is None: + self.init_whodap_client(ipv4=(ip.version == 4)) + query_string = self.whodap_client.lookup(ip).to_json() + return query_string, {} # no parsed output available + + def whois( + self, ip: Union[ipaddress.IPv4Address, ipaddress.IPv6Address, str] + ) -> tuple[str, dict[IPBaseKeys, Any]]: + if not isinstance(ip, (ipaddress.IPv4Address, ipaddress.IPv6Address)): + ip = convert_to_ip(ip) + query_chain: list[str] = self.query_obj.run(ip) + authoritative_answer = query_chain[-1] + parsed_dict: dict[IPBaseKeys, Any] = self.parse_obj.parse( + authoritative_answer, ip + ) + query_string = ( + authoritative_answer if self.authoritative_only else "\n".join(query_chain) + ) + return query_string, parsed_dict + + async def aio_rdap( + self, ip: Union[ipaddress.IPv4Address, ipaddress.IPv6Address, str] + ) -> tuple[str, dict]: + if not isinstance(ip, (ipaddress.IPv4Address, ipaddress.IPv6Address)): + ip = convert_to_ip(ip) + if self.whodap_client is None: + await self.init_async_whodap_client(ipv4=(ip.version == 4)) + query_resp = await self.whodap_client.aio_lookup(ip) + query_string = query_resp.to_json() + return query_string, {} # no parsed output available + + async def aio_whois( + self, ip: Union[ipaddress.IPv4Address, ipaddress.IPv6Address, str] + ) -> tuple[str, dict[IPBaseKeys, Any]]: + if not isinstance(ip, (ipaddress.IPv4Address, ipaddress.IPv6Address)): + ip = convert_to_ip(ip) + query_chain: list[str] = await self.query_obj.aio_run(ip) + authoritative_answer = query_chain[-1] + parsed_dict: dict[IPBaseKeys, Any] = self.parse_obj.parse( + authoritative_answer, ip + ) + query_string = ( + authoritative_answer if self.authoritative_only else "\n".join(query_chain) + ) + return query_string, parsed_dict + + +class ASNClient(Client): + def __init__( + self, + whodap_client: whodap.ASNClient = None, + timeout: int = 10, + ): + super().__init__(whodap_client) + self.timeout = timeout + + def rdap(self, asn: int) -> tuple[str, dict]: + if self.whodap_client is None: + self.init_whodap_client() + query_resp = self.whodap_client.lookup(asn) + query_string = query_resp.to_json() + return query_string, {} + + async def aio_rdap(self, asn: int) -> tuple[str, dict]: + if self.whodap_client is None: + await self.init_async_whodap_client() + query_resp = await self.whodap_client.aio_lookup(asn) + query_string = query_resp.to_json() + return query_string, {} diff --git a/asyncwhois/parse_rir.py b/asyncwhois/parse_rir.py index 7b5a10b..6e7ca77 100644 --- a/asyncwhois/parse_rir.py +++ b/asyncwhois/parse_rir.py @@ -1,7 +1,9 @@ +import ipaddress import re -from typing import Dict, Any +from typing import Dict, Any, Union from .parse import BaseParser, IPBaseKeys +from .servers import IPv4Allocations class RIRParser(BaseParser): @@ -56,12 +58,17 @@ class RIRParser(BaseParser): class NumberParser: - def __init__(self, rir_server: str): - self.parser_output: Dict[IPBaseKeys, Any] = {} - self._parser = self._init_parser(rir_server) - - def parse(self, blob: str) -> None: - self.parser_output = self._parser.parse(blob) + def __init__(self): + self.servers = IPv4Allocations() + + def parse( + self, + blob: str, + ip: Union[ipaddress.IPv4Address], + ) -> dict[IPBaseKeys, Any]: + _, server = self.servers.get_servers(ip) + parser = self._init_parser(server) + return parser.parse(blob) @staticmethod def _init_parser(rir_server: str) -> RIRParser: diff --git a/asyncwhois/parse_tld.py b/asyncwhois/parse_tld.py index 20d3786..4548038 100644 --- a/asyncwhois/parse_tld.py +++ b/asyncwhois/parse_tld.py @@ -6,72 +6,6 @@ from .errors import NotFoundError -class TLDParser(BaseParser): - base_expressions = { - TLDBaseKeys.DOMAIN_NAME: r"Domain Name: *(.+)", - TLDBaseKeys.CREATED: r"Creation Date: *(.+)", - TLDBaseKeys.UPDATED: r"Updated Date: *(.+)", - TLDBaseKeys.EXPIRES: r"Expir\w+\sDate: *(.+)", - TLDBaseKeys.REGISTRAR: r"Registrar: *(.+)", - TLDBaseKeys.REGISTRAR_IANA_ID: r"Registrar IANA ID: *(.+)", - TLDBaseKeys.REGISTRAR_URL: r"Registrar URL: *(.+)", - TLDBaseKeys.REGISTRAR_ABUSE_EMAIL: r"Registrar Abuse Contact Email: *(.+)", - TLDBaseKeys.REGISTRAR_ABUSE_PHONE: r"Registrar Abuse Contact Phone: *(.+)", - TLDBaseKeys.REGISTRANT_NAME: r"Registrant Name: *(.+)", - TLDBaseKeys.REGISTRANT_ORGANIZATION: r"Registrant Organization: *(.+)", - TLDBaseKeys.REGISTRANT_ADDRESS: r"Registrant Street: *(.+)", - TLDBaseKeys.REGISTRANT_CITY: r"Registrant City: *(.+)", - TLDBaseKeys.REGISTRANT_STATE: r"Registrant State/Province: *(.+)", - TLDBaseKeys.REGISTRANT_ZIPCODE: r"Registrant Postal Code: *(.+)", - TLDBaseKeys.REGISTRANT_COUNTRY: r"Registrant Country: *(.+)", - TLDBaseKeys.REGISTRANT_EMAIL: r"Registrant Email: *(.+)", - TLDBaseKeys.REGISTRANT_PHONE: r"Registrant Phone: *(.+)", - TLDBaseKeys.REGISTRANT_FAX: r"Registrant Fax: *(.+)", - TLDBaseKeys.DNSSEC: r"DNSSEC: *([\S]+)", - TLDBaseKeys.STATUS: r"Status: *(.+)", - TLDBaseKeys.NAME_SERVERS: r"Name server: *(.+)", - TLDBaseKeys.ADMIN_NAME: r"Admin Name: (.+)", - TLDBaseKeys.ADMIN_ID: r"Admin ID: (.+)", - TLDBaseKeys.ADMIN_ORGANIZATION: r"Admin Organization: (.+)", - TLDBaseKeys.ADMIN_CITY: r"Admin City: (.*)", - TLDBaseKeys.ADMIN_ADDRESS: r"Admin Street: (.*)", - TLDBaseKeys.ADMIN_STATE: r"Admin State/Province: (.*)", - TLDBaseKeys.ADMIN_ZIPCODE: r"Admin Postal Code: (.*)", - TLDBaseKeys.ADMIN_COUNTRY: r"Admin Country: (.+)", - TLDBaseKeys.ADMIN_PHONE: r"Admin Phone: (.+)", - TLDBaseKeys.ADMIN_FAX: r"Admin Fax: (.+)", - TLDBaseKeys.ADMIN_EMAIL: r"Admin Email: (.+)", - TLDBaseKeys.BILLING_NAME: r"Billing Name: (.+)", - TLDBaseKeys.BILLING_ID: r"Billing ID: (.+)", - TLDBaseKeys.BILLING_ORGANIZATION: r"Billing Organization: (.+)", - TLDBaseKeys.BILLING_CITY: r"Billing City: (.*)", - TLDBaseKeys.BILLING_ADDRESS: r"Billing Street: (.*)", - TLDBaseKeys.BILLING_STATE: r"Billing State/Province: (.*)", - TLDBaseKeys.BILLING_ZIPCODE: r"Billing Postal Code: (.*)", - TLDBaseKeys.BILLING_COUNTRY: r"Billing Country: (.+)", - TLDBaseKeys.BILLING_PHONE: r"Billing Phone: (.+)", - TLDBaseKeys.BILLING_FAX: r"Billing Fax: (.+)", - TLDBaseKeys.BILLING_EMAIL: r"Billing Email: (.+)", - TLDBaseKeys.TECH_NAME: r"Tech Name: (.+)", - TLDBaseKeys.TECH_ID: r"Tech ID: (.+)", - TLDBaseKeys.TECH_ORGANIZATION: r"Tech Organization: (.+)", - TLDBaseKeys.TECH_CITY: r"Tech City: (.*)", - TLDBaseKeys.TECH_ADDRESS: r"Tech Street: (.*)", - TLDBaseKeys.TECH_STATE: r"Tech State/Province: (.*)", - TLDBaseKeys.TECH_ZIPCODE: r"Tech Postal Code: (.*)", - TLDBaseKeys.TECH_COUNTRY: r"Tech Country: (.+)", - TLDBaseKeys.TECH_PHONE: r"Tech Phone: (.+)", - TLDBaseKeys.TECH_FAX: r"Tech Fax: (.+)", - TLDBaseKeys.TECH_EMAIL: r"Tech Email: (.+)", - } - - multiple_match_keys = (TLDBaseKeys.NAME_SERVERS, TLDBaseKeys.STATUS) - date_keys = (TLDBaseKeys.CREATED, TLDBaseKeys.UPDATED, TLDBaseKeys.EXPIRES) - - def __init__(self): - self.reg_expressions = self.base_expressions.copy() - - class DomainParser: _no_match_checks = [ "no match", @@ -85,21 +19,19 @@ class DomainParser: "domain you requested is not known", ] - def __init__(self, tld: str): - self.parser_output = {} - self._parser = self._init_parser(tld) + def __init__(self, ignore_not_found: bool = False): + self.ignore_not_found = ignore_not_found - def parse(self, blob: str) -> None: - """ - Parses `blob` (whois query output) using a parser class. - Saves the results into the `parser_output` attribute. - """ - if any([n in blob.lower() for n in self._no_match_checks]): - raise NotFoundError(f"Domain not found!") - self.parser_output = self._parser.parse(blob) + def parse(self, blob: str, tld: str) -> dict[TLDBaseKeys, Any]: + if not self.ignore_not_found and any( + [n in blob.lower() for n in self._no_match_checks] + ): + raise NotFoundError("Domain not found!") + parser = self._init_parser(tld) + return parser.parse(blob) @staticmethod - def _init_parser(tld: str) -> TLDParser: + def _init_parser(tld: str) -> "TLDParser": """ Retrieves the parser instance which can most accurately extract key/value pairs from the whois server output for the given `tld`. @@ -248,6 +180,72 @@ def _init_parser(tld: str) -> TLDParser: return TLDParser() +class TLDParser(BaseParser): + base_expressions = { + TLDBaseKeys.DOMAIN_NAME: r"Domain Name: *(.+)", + TLDBaseKeys.CREATED: r"Creation Date: *(.+)", + TLDBaseKeys.UPDATED: r"Updated Date: *(.+)", + TLDBaseKeys.EXPIRES: r"Expir\w+\sDate: *(.+)", + TLDBaseKeys.REGISTRAR: r"Registrar: *(.+)", + TLDBaseKeys.REGISTRAR_IANA_ID: r"Registrar IANA ID: *(.+)", + TLDBaseKeys.REGISTRAR_URL: r"Registrar URL: *(.+)", + TLDBaseKeys.REGISTRAR_ABUSE_EMAIL: r"Registrar Abuse Contact Email: *(.+)", + TLDBaseKeys.REGISTRAR_ABUSE_PHONE: r"Registrar Abuse Contact Phone: *(.+)", + TLDBaseKeys.REGISTRANT_NAME: r"Registrant Name: *(.+)", + TLDBaseKeys.REGISTRANT_ORGANIZATION: r"Registrant Organization: *(.+)", + TLDBaseKeys.REGISTRANT_ADDRESS: r"Registrant Street: *(.+)", + TLDBaseKeys.REGISTRANT_CITY: r"Registrant City: *(.+)", + TLDBaseKeys.REGISTRANT_STATE: r"Registrant State/Province: *(.+)", + TLDBaseKeys.REGISTRANT_ZIPCODE: r"Registrant Postal Code: *(.+)", + TLDBaseKeys.REGISTRANT_COUNTRY: r"Registrant Country: *(.+)", + TLDBaseKeys.REGISTRANT_EMAIL: r"Registrant Email: *(.+)", + TLDBaseKeys.REGISTRANT_PHONE: r"Registrant Phone: *(.+)", + TLDBaseKeys.REGISTRANT_FAX: r"Registrant Fax: *(.+)", + TLDBaseKeys.DNSSEC: r"DNSSEC: *([\S]+)", + TLDBaseKeys.STATUS: r"Status: *(.+)", + TLDBaseKeys.NAME_SERVERS: r"Name server: *(.+)", + TLDBaseKeys.ADMIN_NAME: r"Admin Name: (.+)", + TLDBaseKeys.ADMIN_ID: r"Admin ID: (.+)", + TLDBaseKeys.ADMIN_ORGANIZATION: r"Admin Organization: (.+)", + TLDBaseKeys.ADMIN_CITY: r"Admin City: (.*)", + TLDBaseKeys.ADMIN_ADDRESS: r"Admin Street: (.*)", + TLDBaseKeys.ADMIN_STATE: r"Admin State/Province: (.*)", + TLDBaseKeys.ADMIN_ZIPCODE: r"Admin Postal Code: (.*)", + TLDBaseKeys.ADMIN_COUNTRY: r"Admin Country: (.+)", + TLDBaseKeys.ADMIN_PHONE: r"Admin Phone: (.+)", + TLDBaseKeys.ADMIN_FAX: r"Admin Fax: (.+)", + TLDBaseKeys.ADMIN_EMAIL: r"Admin Email: (.+)", + TLDBaseKeys.BILLING_NAME: r"Billing Name: (.+)", + TLDBaseKeys.BILLING_ID: r"Billing ID: (.+)", + TLDBaseKeys.BILLING_ORGANIZATION: r"Billing Organization: (.+)", + TLDBaseKeys.BILLING_CITY: r"Billing City: (.*)", + TLDBaseKeys.BILLING_ADDRESS: r"Billing Street: (.*)", + TLDBaseKeys.BILLING_STATE: r"Billing State/Province: (.*)", + TLDBaseKeys.BILLING_ZIPCODE: r"Billing Postal Code: (.*)", + TLDBaseKeys.BILLING_COUNTRY: r"Billing Country: (.+)", + TLDBaseKeys.BILLING_PHONE: r"Billing Phone: (.+)", + TLDBaseKeys.BILLING_FAX: r"Billing Fax: (.+)", + TLDBaseKeys.BILLING_EMAIL: r"Billing Email: (.+)", + TLDBaseKeys.TECH_NAME: r"Tech Name: (.+)", + TLDBaseKeys.TECH_ID: r"Tech ID: (.+)", + TLDBaseKeys.TECH_ORGANIZATION: r"Tech Organization: (.+)", + TLDBaseKeys.TECH_CITY: r"Tech City: (.*)", + TLDBaseKeys.TECH_ADDRESS: r"Tech Street: (.*)", + TLDBaseKeys.TECH_STATE: r"Tech State/Province: (.*)", + TLDBaseKeys.TECH_ZIPCODE: r"Tech Postal Code: (.*)", + TLDBaseKeys.TECH_COUNTRY: r"Tech Country: (.+)", + TLDBaseKeys.TECH_PHONE: r"Tech Phone: (.+)", + TLDBaseKeys.TECH_FAX: r"Tech Fax: (.+)", + TLDBaseKeys.TECH_EMAIL: r"Tech Email: (.+)", + } + + multiple_match_keys = (TLDBaseKeys.NAME_SERVERS, TLDBaseKeys.STATUS) + date_keys = (TLDBaseKeys.CREATED, TLDBaseKeys.UPDATED, TLDBaseKeys.EXPIRES) + + def __init__(self): + self.reg_expressions = self.base_expressions.copy() + + # ============================== # Custom Query Output Parsers # ============================== diff --git a/asyncwhois/query.py b/asyncwhois/query.py index 00eda63..46d13be 100644 --- a/asyncwhois/query.py +++ b/asyncwhois/query.py @@ -1,11 +1,11 @@ import asyncio +import ipaddress import re import socket import sys -from typing import Tuple, Generator +from typing import Tuple, Generator, Union from contextlib import contextmanager -# different installs for async contextmanager based on python version if sys.version_info < (3, 7): from async_generator import asynccontextmanager else: @@ -14,6 +14,8 @@ from python_socks.sync import Proxy from python_socks.async_.asyncio import Proxy as AsyncProxy +from .servers import IPv4Allocations, CountryCodeTLD, GenericTLD, SponsoredTLD + BLOCKSIZE = 1024 @@ -23,20 +25,9 @@ class Query: refer_regex = r"refer: *(.+)" whois_server_regex = r".+ whois server: *(.+)" - def __init__( - self, - server: str = None, - authoritative_only: bool = True, - proxy_url: str = None, - timeout: int = 10, - ): - self.server = server - self.authoritative_only = authoritative_only + def __init__(self, proxy_url: str = None, timeout: int = 10): self.proxy_url = proxy_url self.timeout = timeout - self.authoritative_server = "" - self.query_output = "" - self.query_chain = "" @staticmethod def _find_match(regex: str, blob: str) -> str: @@ -116,21 +107,36 @@ async def _aio_send_and_recv( result += received.decode("utf-8", errors="ignore") return result - def _do_query(self, server: str, data: str, regex: str) -> str: + def run(self, search_term: str, server: str = None) -> list[str]: + data = search_term + "\r\n" + if not server: + server_regex = self.refer_regex + server = self.iana_server + else: + server_regex = self.whois_server_regex + return self._do_query(server, data, server_regex, []) + + async def aio_run(self, search_term: str, server: str = None) -> list[str]: + data = search_term + "\r\n" + if not server: + server_regex = self.refer_regex + server = self.iana_server + else: + server_regex = self.whois_server_regex + return await self._aio_do_query(server, data, server_regex, []) + + def _do_query( + self, server: str, data: str, regex: str, chain: list[str] + ) -> list[str]: """ Recursively submits WHOIS queries until it reaches the Authoritative Server. - Additionally, if `authoritative_only` is False, all text output from server hops - is saved into `query_chain`. """ - # save authoritative server - self.authoritative_server = server # connect to whois://:43 with self._create_connection((server, self.whois_port), self.proxy_url) as conn: # submit domain and receive raw query output query_output = self._send_and_recv(conn, data) - if not self.authoritative_only: - # concatenate query outputs - self.query_chain += query_output + # save query chain + chain.append(query_output) # parse response for the referred WHOIS server name whois_server = self._find_match(regex, query_output) whois_server = whois_server.lower() @@ -140,17 +146,15 @@ def _do_query(self, server: str, data: str, regex: str) -> str: and not whois_server.startswith("http") ): # recursive call to find more authoritative server - authoritative_output = self._do_query( - whois_server, data, self.whois_server_regex + chain = self._do_query( + whois_server, data, self.whois_server_regex, chain ) - # check for empty responses; only update query_output if - # there was a complete response from the authoritative server - if authoritative_output: - query_output = authoritative_output - # return the WHOIS query output - return query_output + # return the WHOIS query chain + return chain - async def _aio_do_query(self, server: str, data: str, regex: str): + async def _aio_do_query( + self, server: str, data: str, regex: str, chain: list[str] + ) -> list[str]: # connect to whois://:43 async with self._aio_create_connection( (server, self.whois_port), self.proxy_url @@ -161,9 +165,7 @@ async def _aio_do_query(self, server: str, data: str, regex: str): query_output = await asyncio.wait_for( self._aio_send_and_recv(reader, writer, data), self.timeout ) - if not self.authoritative_only: - # concatenate query outputs - self.query_chain += query_output + chain.append(query_output) # parse response for the referred WHOIS server name whois_server = self._find_match(regex, query_output) whois_server = whois_server.lower() @@ -173,113 +175,76 @@ async def _aio_do_query(self, server: str, data: str, regex: str): and not whois_server.startswith("http") ): # recursive call to find the authoritative server - authoritative_output = await self._aio_do_query( - whois_server, data, self.whois_server_regex + chain = await self._aio_do_query( + whois_server, data, self.whois_server_regex, chain ) - # check for empty responses; only update query_output if - # there was a complete response from the authoritative server - if authoritative_output: - query_output = authoritative_output - # return the WHOIS query output - return query_output + # return the WHOIS query chain + return chain class DomainQuery(Query): def __init__( self, - domain: str, server: str = None, - authoritative_only: bool = True, proxy_url: str = None, timeout: int = 10, ): - super().__init__(server, authoritative_only, proxy_url, timeout) - self.domain = domain + super().__init__(proxy_url, timeout) + self.server = server - @classmethod - def new( - cls, - domain: str, - server: str = None, - authoritative_only: bool = True, - proxy_url: str = None, - timeout: int = 10, - ): - _self = cls(domain, server, authoritative_only, proxy_url, timeout) - data = domain + "\r\n" - if not _self.server: - server_regex = _self.refer_regex - server = _self.iana_server - else: - server_regex = _self.whois_server_regex - server = _self.server - _self.query_output = _self._do_query(server, data, server_regex) - return _self + @staticmethod + def _get_server_name(tld: str) -> Union[str, None]: + tld_converted = tld.upper().replace("-", "_") + for servers in [CountryCodeTLD, GenericTLD, SponsoredTLD]: + if hasattr(servers, tld_converted): + server = getattr(servers, tld_converted) + return server + return None - @classmethod - async def new_aio( - cls, - domain: str, - server: str = None, - authoritative_only: bool = True, - proxy_url: str = None, - timeout: int = 10, - ): - _self = cls(domain, server, authoritative_only, proxy_url, timeout) - data = domain + "\r\n" - if not _self.server: - server_regex = _self.refer_regex - server = _self.iana_server - else: - server_regex = _self.whois_server_regex - server = _self.server - _self.query_output = await _self._aio_do_query(server, data, server_regex) - return _self + def run(self, search_term: str, server: str = None) -> list[str]: + if not server: + server = self._get_server_name(search_term) + return super().run(str(search_term), server) + + async def aio_run(self, search_term: str, server: str = None) -> list[str]: + if not server: + server = self._get_server_name(search_term) + return await super().aio_run(str(search_term), server) class NumberQuery(Query): def __init__( self, - ip: str, # ipv4 or ipv6 server: str = None, - authoritative_only: bool = True, proxy_url: str = None, timeout: int = 10, ): - super().__init__(server, authoritative_only, proxy_url, timeout) + super().__init__(proxy_url, timeout) + self.server = server self.whois_server_regex = r"ReferralServer: *whois://(.+)" - self.ip = ip - @classmethod - def new( - cls, - ip: str, # ipv4 or ipv6; validation is handled by caller. + @staticmethod + def _get_server_name(ip: Union[ipaddress.IPv4Address, ipaddress.IPv6Address]): + if isinstance(ip, ipaddress.IPv4Address): + _, server = IPv4Allocations().get_servers(ip) + return server + elif isinstance(ip, ipaddress.IPv6Address): + return None + + def run( + self, + search_term: Union[ipaddress.IPv4Address, ipaddress.IPv6Address], server: str = None, - authoritative_only: bool = False, - proxy_url: str = None, - timeout: int = 10, - ): - _self = cls(ip, server, authoritative_only, proxy_url, timeout) - data = ip + "\r\n" - if ":" in ip: # ipv6 - _self.refer_regex = r"whois: *(.+)" - server = server or _self.iana_server - _self.query_output = _self._do_query(server, data, _self.refer_regex) - return _self + ) -> list[str]: + if not server: + server = self._get_server_name(search_term) + return super().run(str(search_term), server) - @classmethod - async def new_aio( - cls, - ip: str, + async def aio_run( + self, + search_term: Union[ipaddress.IPv4Address, ipaddress.IPv6Address], server: str = None, - authoritative_only: bool = False, - proxy_url: str = None, - timeout: int = 10, - ): - _self = cls(ip, server, authoritative_only, proxy_url, timeout) - data = ip + "\r\n" - if ":" in ip: # ipv6 - _self.refer_regex = r"whois: *(.+)" - server = server or _self.iana_server - _self.query_output = await _self._aio_do_query(server, data, _self.refer_regex) - return _self + ) -> list[str]: + if not server: + server = self._get_server_name(search_term) + return await super().aio_run(str(search_term), server) From 356b2c71417bd59afe17a79d1cad40b395fb915b Mon Sep 17 00:00:00 2001 From: Joe Obarzanek Date: Thu, 8 Feb 2024 19:03:00 -0500 Subject: [PATCH 3/8] Update README --- README.md | 133 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 83 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index 50278d3..071905f 100644 --- a/README.md +++ b/README.md @@ -20,17 +20,16 @@ domain = 'bitcoin.org' # domain could also be a URL; asyncwhois uses tldextract to parse the URL domain = 'https://www.google.com?q=asyncwhois' -# standard call -result = asyncwhois.whois_domain(domain) -# result.query_output # The semi-free text output from the whois server -# result.parser_output # A dictionary of key:values extracted from query_output -# result.tld_extract_result # tldextract result (`tldextract.tldextract.ExtractResult`) +# basic example +query_string, parsed_dict = asyncwhois.whois(domain) +# query_string # The semi-free text output from the whois server +# parsed_dict # A dictionary of key:values extracted from query_output -# asyncio call +# asyncio example loop = asyncio.get_event_loop() -result = loop.run_until_complete(asyncwhois.aio_whois_domain(domain)) +query_string, parsed_dict = loop.run_until_complete(asyncwhois.aio_whois(domain)) -pprint(result.parser_output) +pprint(parsed_dict) """ {created: datetime.datetime(2008, 8, 18, 13, 19, 55), dnssec: 'unsigned', @@ -50,21 +49,51 @@ pprint(result.parser_output) updated: datetime.datetime(2019, 11, 24, 13, 58, 35, 940000)} ... """ + +# support for IPv4, IPv6, and ASNs too +ipv4 = "8.8.8.8" +query_string, parsed_dict = asyncwhois.whois(ipv4) +pprint(parsed_dict) +""" +{abuse_address: None, + abuse_email: 'network-abuse@google.com', + abuse_handle: 'ABUSE5250-ARIN', + abuse_name: 'Abuse', + abuse_phone: '+1-650-253-0000', + abuse_rdap_ref: 'https://rdap.arin.net/registry/entity/ABUSE5250-ARIN', + cidr: '8.8.8.0/24', + net_handle: 'NET-8-8-8-0-2', + net_name: 'GOGL', + net_range: '8.8.8.0 - 8.8.8.255', + net_type: 'Direct Allocation', + org_address: '1600 Amphitheatre Parkway', + org_city: 'Mountain View', + org_country: 'US', + org_id: 'GOGL', + ... +""" ``` #### RDAP -The `whodap` (https://github.com/pogzyb/whodap) project is used behind the scenes to perform RDAP queries. +The [whodap](https://github.com/pogzyb/whodap) project is used behind the scenes to perform RDAP queries. ```python -# RDAP domain query -result = asyncwhois.rdap_domain('https://google.com') - -# Async RDAP domain query -result = loop.run_until_complete(asyncwhois.aio_rdap_domain('https://google.com')) -pprint(result.query_output) # Raw RDAP query output as a dictionary -pprint(result.parser_output) # RDAP query output parsed/flattened into a WHOIS-like dictionary -print(result.tld_extract_result) # tldextract result (`tldextract.tldextract.ExtractResult`) +domain = "https://google.com" +query_string, parsed_dict = asyncwhois.rdap(domain) +# OR with asyncio +query_string, parsed_dict = loop.run_until_complete(asyncwhois.aio_rdap(domain)) + +# Reusable client (caches the RDAP bootstrap server list, so it is faster for doing multiple calls) +client = asyncwhois.DomainClient() +for domain in ["google.com", "bing.ai", "bitcoin.org"]: + query_string, parsed_dict = client.rdap(domain) + # query_string, parsed_dict = await client.aio_rdap(domain) + +# Using a proxy or need to configure something HTTP related? Try reconfiguring the client: +whodap_client = whodap.DNSClient(httpx_client=httpx.Client(proxies="https://proxy:8080")) +# whodap_client = whodap.DNSClient(httpx_client=httpx.AsyncClient(proxies="https://proxy:8080")) +client = asyncwhois.DomainClient(whodap_client=whodap_client) ``` @@ -73,52 +102,56 @@ print(result.tld_extract_result) # tldextract result (`tldextract.tldextract. SOCKS4 and SOCKS5 proxies are supported for WHOIS and RDAP queries. ```python -tor_host = 'localhost' +import whodap + +tor_host = "localhost" tor_port = 9050 -# WHOIS Queries with Proxy -result = asyncwhois.whois_domain( - 'bitcoin.org', proxy_url=f"socks5://{tor_host}:{tor_port}") -# or with auth... -tor_user = 'torpedo' -tor_pw = 'torpw' -result = asyncwhois.whois_ipv4( - '8.8.8.8', proxy_url=f"socks5://{tor_user}:{tor_pw}@{tor_host}:{tor_port}") +# WHOIS +query_string, parsed_dict = asyncwhois.whois( + "8.8.8.8", proxy_url=f"socks5://{tor_host}:{tor_port}" +) -# RDAP Queries with Proxy +# RDAP import httpx - -# EXTERNAL DEPENDENCY for SOCKS Proxies. -from httpx_socks import SyncProxyTransport, AsyncProxyTransport +from httpx_socks import SyncProxyTransport, AsyncProxyTransport # EXTERNAL DEPENDENCY for SOCKS Proxies transport = SyncProxyTransport.from_url(f"socks5://{tor_host}:{tor_port}") -client = httpx.Client(transport=transport) -result = asyncwhois.rdap_ipv6('2001:4860:4860::8888', httpx_client=client) +httpx_client = httpx.Client(transport=transport) +whodap_client = whodap.IPv6Client(httpx_client=httpx_client) +query_string, parsed_dict = asyncwhois.rdap('2001:4860:4860::8888', whodap_client=whodap_client) transport = AsyncProxyTransport.from_url(f"socks5://{tor_user}:{tor_pw}@{tor_host}:{tor_port}") -async with httpx.AsyncClient(transport=transport) as client: - result = await asyncwhois.aio_rdap_domain('bitcoin.org', httpx_client=client) +async with httpx.AsyncClient(transport=transport) as httpx_client: + whodap_client = await whodap.DNSClient.new_aio_client(httpx_client=httpx_client) + query_string, parsed_dict = await asyncwhois.aio_rdap('bitcoin.org', whodap_client=whodap_client) ``` #### Exported Functions -| Function | Description | -| ----------- | ----------- | -| `whois_domain` | WHOIS lookup for domain names | -| `whois_ipv4` | WHOIS lookup for ipv4 addresses | -| `whois_ipv6` | WHOIS lookup for ipv6 addresses | -| `rdap_domain` | RDAP lookup for domain names | -| `rdap_ipv4` | RDAP lookup for ipv4 addresses | -| `rdap_ipv6` | RDAP lookup for ipv6 addresses | -| `rdap_asn` | RDAP lookup for Autonomous System Numbers | -| `aio_whois_domain` | async counterpart to `whois_domain` | -| `aio_whois_ipv4` | async counterpart to `whois_ipv4` | -| `aio_whois_ipv6` | async counterpart to `whois_ipv6` | -| `aio_rdap_domain` | async counterpart to `rdap_domain` | -| `aio_rdap_ipv4` | async counterpart to `rdap_ipv4` | -| `aio_rdap_ipv6` | async counterpart to `rdap_ipv6` | -| `aio_rdap_asn` | async counterpart to `rdap_asn` | +| Function/Object | Description | +|--------------------|--------------------------------------------------------| +| `DomainClient` | Reusable client for WHOIS or RDAP domain queries | +| `NumberClient` | Reusable client for WHOIS or RDAP ipv4/ipv6 queries | +| `ASNClient` | Reusable client for RDAP asn queries | +| `whois` | WHOIS entrypoint for domain, ipv4, or ipv6 queries | +| `rdap` | RDAP entrypoint for domain, ipv4, ipv6, or asn queries | +| `aio_whois` | async counterpart to `whois` | +| `aio_rdap` | async counterpart to `rdap` | +| `whois_ipv4` | [DEPRECATED] WHOIS lookup for ipv4 addresses | +| `whois_ipv6` | [DEPRECATED] WHOIS lookup for ipv6 addresses | +| `rdap_domain` | [DEPRECATED] RDAP lookup for domain names | +| `rdap_ipv4` | [DEPRECATED] RDAP lookup for ipv4 addresses | +| `rdap_ipv6` | [DEPRECATED] RDAP lookup for ipv6 addresses | +| `rdap_asn` | [DEPRECATED] RDAP lookup for Autonomous System Numbers | +| `aio_whois_domain` | [DEPRECATED] async counterpart to `whois_domain` | +| `aio_whois_ipv4` | [DEPRECATED] async counterpart to `whois_ipv4` | +| `aio_whois_ipv6` | [DEPRECATED] async counterpart to `whois_ipv6` | +| `aio_rdap_domain` | [DEPRECATED] async counterpart to `rdap_domain` | +| `aio_rdap_ipv4` | [DEPRECATED] async counterpart to `rdap_ipv4` | +| `aio_rdap_ipv6` | [DEPRECATED] async counterpart to `rdap_ipv6` | +| `aio_rdap_asn` | [DEPRECATED] async counterpart to `rdap_asn` | #### Contributions From b70460ae43e6d1aa330e19c3e37d435d92f05857 Mon Sep 17 00:00:00 2001 From: Joe Obarzanek Date: Thu, 8 Feb 2024 19:03:36 -0500 Subject: [PATCH 4/8] Update tests and examples --- asyncwhois/pywhois.py | 334 --------- examples/async-web.py | 9 +- examples/authoritative-only.py | 19 + examples/not-found.py | 21 + examples/resuable-clients.py | 45 ++ tests/test_not_found.py | 7 +- tests/test_package_methods.py | 77 +- tests/test_parser_output.py | 1248 +++++++++++++++++--------------- tests/test_pywhois.py | 31 - tests/test_query.py | 17 +- 10 files changed, 799 insertions(+), 1009 deletions(-) delete mode 100644 asyncwhois/pywhois.py create mode 100644 examples/authoritative-only.py create mode 100644 examples/not-found.py create mode 100644 examples/resuable-clients.py delete mode 100644 tests/test_pywhois.py diff --git a/asyncwhois/pywhois.py b/asyncwhois/pywhois.py deleted file mode 100644 index b22e108..0000000 --- a/asyncwhois/pywhois.py +++ /dev/null @@ -1,334 +0,0 @@ -import ipaddress -from typing import Union, Dict, Any, Optional - -import tldextract -import whodap - -from .parse import convert_whodap_keys -from .parse_rir import NumberParser -from .parse_tld import DomainParser -from .query import DomainQuery, NumberQuery -from .servers import CountryCodeTLD, GenericTLD, SponsoredTLD, IPv4Allocations - - -class Lookup: - def __init__(self): - self._query = None - self._parser = None - - @property - def parser_output(self) -> Dict[str, Any]: - if isinstance(self._parser, dict): - return self._parser - elif isinstance(self._parser, (DomainParser, NumberParser)): - return self._parser.parser_output - - @property - def query_output(self) -> str: - if isinstance(self._query, (DomainQuery, NumberQuery)): - if not self._query.authoritative_only: - # return entire query chain - return self._query.query_chain - else: - # return only the authoritative response - return self._query.query_output - else: - return self._query - - -class DomainLookup(Lookup): - def __init__(self): - super().__init__() - self._tld_extract = None - - @staticmethod - def _get_server_name(tld: str) -> Union[str, None]: - tld_converted = tld.upper().replace("-", "_") - for servers in [CountryCodeTLD, GenericTLD, SponsoredTLD]: - if hasattr(servers, tld_converted): - server = getattr(servers, tld_converted) - return server - return None - - def _get_top_level_domain(self, domain: str): - self._tld_extract = tldextract.extract(domain) - top_level_domain = self._tld_extract.suffix - if "." in top_level_domain: - top_level_domain = top_level_domain.split(".")[-1] - return top_level_domain - - @property - def tld_extract_result(self) -> tldextract.tldextract.ExtractResult: - return self._tld_extract - - @classmethod - def whois_domain( - cls, domain: str, authoritative_only: bool, proxy_url: str, timeout: int - ): - _self = cls() - # get TLD - top_level_domain = _self._get_top_level_domain(domain) - # initialize parser based on given TLD - parser = DomainParser(top_level_domain) - # get the WHOIS server associated with this TLD - server = _self._get_server_name(top_level_domain) - # submit the WHOIS query to the server - registered_domain = _self.tld_extract_result.registered_domain - query = DomainQuery.new( - registered_domain, server, authoritative_only, proxy_url, timeout - ) - # parse the raw text output from the WHOIS server - parser.parse(query.query_output) - _self._query = query - _self._parser = parser - return _self - - @classmethod - async def aio_whois_domain( - cls, domain: str, authoritative_only: bool, proxy_url: str, timeout: int - ): - _self = cls() - # get TLD - top_level_domain = _self._get_top_level_domain(domain) - # initialize parser based on given TLD - parser = DomainParser(top_level_domain) - # get the WHOIS server associated with this TLD - server = _self._get_server_name(top_level_domain) - # submit the WHOIS query to the server - registered_domain = _self.tld_extract_result.registered_domain - query = await DomainQuery.new_aio( - registered_domain, server, authoritative_only, proxy_url, timeout - ) - # parse the raw text output from the WHOIS server - parser.parse(query.query_output) - _self._query = query - _self._parser = parser - return _self - - @classmethod - def rdap_domain(cls, domain: str, httpx_client: Any = None): - """ - Performs an RDAP query using the `whodap` project. - Stores the resulting RDAP output into "query_output" and a WHOIS friendly - key:value pair dictionary into "parser_output". - - :param domain: the domain or URL to search (e.g. 'google.com' or 'https://www.google.com') - :param httpx_client: the underlying httpx client to pass to `whodap.lookup_domain` - :return: instance of DomainLookup - """ - _self = cls() - top_level_domain = _self._get_top_level_domain(domain) - response = whodap.lookup_domain( - domain=_self.tld_extract_result.domain, - tld=top_level_domain, - httpx_client=httpx_client, - ) - _self._query = response.to_dict() - whois_dict = response.to_whois_dict() - _self._parser = convert_whodap_keys(whois_dict) - return _self - - @classmethod - async def aio_rdap_domain(cls, domain: str, httpx_client: Any = None): - """ - Performs an async RDAP query using the `whodap` project. - Stores the resulting RDAP output into "query_output" and a WHOIS friendly - key:value pair dictionary into "parser_output". - - :param domain: the domain or URL to search (e.g. 'google.com' or 'https://www.google.com') - :param httpx_client: the underlying httpx client to pass to `whodap.aio_lookup_domain` - :return: instance of DomainLookup - """ - _self = cls() - top_level_domain = _self._get_top_level_domain(domain) - response = await whodap.aio_lookup_domain( - domain=_self.tld_extract_result.domain, - tld=top_level_domain, - httpx_client=httpx_client, - ) - _self._query = response.to_dict() - whois_dict = response.to_whois_dict() - _self._parser = convert_whodap_keys(whois_dict) - return _self - - -class NumberLookup(Lookup): - def __init__(self): - super().__init__() - self._ip = None - - @property - def ipv6(self) -> Optional[ipaddress.IPv6Address]: - if isinstance(self._ip, ipaddress.IPv6Address): - return self._ip - - @property - def ipv4(self) -> Optional[ipaddress.IPv4Address]: - if isinstance(self._ip, ipaddress.IPv4Address): - return self._ip - - @staticmethod - def convert_to_ip(ip: str): - try: - ip_obj = ipaddress.ip_address(ip) - return ip_obj - except ipaddress.AddressValueError: - raise - - @classmethod - def whois_ipv4( - cls, - ipv4: Union[str, ipaddress.IPv4Address], - authoritative_only: bool, - proxy_url: str, - timeout: int, - ): - _self = cls() - if not isinstance(ipv4, ipaddress.IPv4Address): - _self._ip = _self.convert_to_ip(ipv4) - else: - _self._ip = ipv4 - _, server = IPv4Allocations().get_servers(_self._ip) - ip_string = str(_self._ip) - # run the query - query = NumberQuery.new( - ip_string, server, authoritative_only, proxy_url, timeout - ) - # parse the raw text output from the WHOIS server - parser = NumberParser(query.authoritative_server) - parser.parse(query.query_output) - _self._query = query - _self._parser = parser - return _self - - @classmethod - async def aio_whois_ipv4( - cls, - ipv4: Union[str, ipaddress.IPv4Address], - authoritative_only: bool, - proxy_url: str, - timeout: int, - ): - _self = cls() - if not isinstance(ipv4, ipaddress.IPv4Address): - _self._ip = _self.convert_to_ip(ipv4) - else: - _self._ip = ipv4 - _, server = IPv4Allocations().get_servers(_self._ip) - ip_string = str(_self._ip) - # run the query - query = await NumberQuery.new_aio( - ip_string, server, authoritative_only, proxy_url, timeout - ) - # parse the raw text output from the WHOIS server - parser = NumberParser(query.authoritative_server) - parser.parse(query.query_output) - _self._query = query - _self._parser = parser - return _self - - @classmethod - def rdap_ipv4( - cls, ipv4: Union[str, ipaddress.IPv4Address], httpx_client: Any = None - ): - _self = cls() - response = whodap.lookup_ipv4(ipv4, httpx_client) - _self._query = response.to_dict() - _self._parser = None - return _self - - @classmethod - async def aio_rdap_ipv4( - cls, ipv4: Union[str, ipaddress.IPv4Address], httpx_client: Any = None - ): - _self = cls() - response = await whodap.aio_lookup_ipv4(ipv4, httpx_client) - _self._query = response.to_dict() - _self._parser = None - return _self - - @classmethod - def whois_ipv6( - cls, - ipv6: Union[str, ipaddress.IPv6Address], - authoritative_only: bool, - proxy_url: str, - timeout: int, - ): - _self = cls() - if not isinstance(ipv6, ipaddress.IPv6Address): - ipv6 = _self.convert_to_ip(ipv6) - ip_string = str(ipv6) - # run the query - query = NumberQuery.new(ip_string, None, authoritative_only, proxy_url, timeout) - # parse the raw text output from the WHOIS server - parser = NumberParser(query.authoritative_server) - parser.parse(query.query_output) - _self._query = query - _self._parser = parser - return _self - - @classmethod - async def aio_whois_ipv6( - cls, - ipv6: Union[str, ipaddress.IPv6Address], - authoritative_only: bool, - proxy_url: str, - timeout: int, - ): - _self = cls() - if not isinstance(ipv6, ipaddress.IPv4Address): - _self._ip = _self.convert_to_ip(ipv6) - ip_string = str(_self._ip) - # run the query - query = await NumberQuery.new_aio( - ip_string, None, authoritative_only, proxy_url, timeout - ) - # parse the raw text output from the WHOIS server - parser = NumberParser(query.authoritative_server) - parser.parse(query.query_output) - _self._query = query - _self._parser = parser - return _self - - @classmethod - def rdap_ipv6( - cls, ipv6: Union[str, ipaddress.IPv6Address], httpx_client: Any = None - ): - _self = cls() - response = whodap.lookup_ipv6(ipv6, httpx_client) - _self._query = response.to_dict() - _self._parser = None - return _self - - @classmethod - async def aio_rdap_ipv6( - cls, ipv6: Union[str, ipaddress.IPv6Address], httpx_client: Any = None - ): - _self = cls() - response = await whodap.aio_lookup_ipv6(ipv6, httpx_client) - _self._query = response.to_dict() - _self._parser = None - return _self - - -class ASNLookup(Lookup): - def __init__(self): - super().__init__() - self.asn: int = None - - @classmethod - def rdap_asn(cls, asn: int, httpx_client: Any = None): - _self = cls() - response = whodap.lookup_asn(asn, httpx_client) - _self._query = response.to_dict() - _self._parser = None # parser support not implemented - return _self - - @classmethod - async def aio_rdap_asn(cls, asn: int, httpx_client: Any = None): - _self = cls() - response = await whodap.aio_lookup_asn(asn, httpx_client) - _self._query = response.to_dict() - _self._parser = None - return _self diff --git a/examples/async-web.py b/examples/async-web.py index 09f97f8..8a3bf47 100644 --- a/examples/async-web.py +++ b/examples/async-web.py @@ -1,19 +1,20 @@ # https://docs.aiohttp.org/en/stable/web_quickstart.html +import httpx from aiohttp import web import asyncwhois async def whois_handler(request): - domain = request.match_info.get('domain', 'google.com') + domain = request.match_info.get("domain", "google.com") result = await asyncwhois.aio_whois_domain(domain) return web.Response( - text=f'WhoIs Query Parsed:\n{result.parser_output}\nQuery Output:\n{result.query_output}' + text=f"WhoIs Query Parsed:\n{result.parser_output}\nQuery Output:\n{result.query_output}" ) app = web.Application() -app.add_routes([web.get('/whois/{domain}', whois_handler)]) +app.add_routes([web.get("/whois/{domain}", whois_handler)]) web.run_app(app) # @@ -23,4 +24,4 @@ async def whois_handler(request): # localhost:8080/whois/netflix.com # localhost:8080/whois/google.com # localhost:8080/whois/pypi.org -# \ No newline at end of file +# diff --git a/examples/authoritative-only.py b/examples/authoritative-only.py new file mode 100644 index 0000000..246d530 --- /dev/null +++ b/examples/authoritative-only.py @@ -0,0 +1,19 @@ +import asyncwhois + + +def main(): + domain = "google.com" + + # show the entire query-chain answer + result = asyncwhois.whois_domain(domain, authoritative_only=False) # default + print(result.query_output) + + # only show the "authoritative" answer + result = asyncwhois.whois_domain(domain, authoritative_only=True) + print(result.query_output) + + return + + +if __name__ == "__main__": + main() diff --git a/examples/not-found.py b/examples/not-found.py new file mode 100644 index 0000000..81a97d3 --- /dev/null +++ b/examples/not-found.py @@ -0,0 +1,21 @@ +import asyncwhois + + +def main(): + non_existent_domain = "dasrewrdgdfgserw34.com" + + try: + r = asyncwhois.whois_domain(non_existent_domain) + print(r.query_output) + except asyncwhois.NotFoundError: + print("That domain does not exist, so we threw an error.") + + # suppress exception throwing for "not found" domains: + result = asyncwhois.whois_domain(non_existent_domain, ignore_not_found=True) + print(result.query_output) + + return + + +if __name__ == "__main__": + main() diff --git a/examples/resuable-clients.py b/examples/resuable-clients.py new file mode 100644 index 0000000..7c27e72 --- /dev/null +++ b/examples/resuable-clients.py @@ -0,0 +1,45 @@ +from pprint import pprint + +import asyncwhois +import whodap +import httpx + + +def main(): + domain = "axe.pizza" + + # WHOIS + client = asyncwhois.DomainClient(ignore_not_found=True) + + query_string, parsed_dict = client.whois(domain) + pprint(query_string) + pprint(parsed_dict) + + # RDAP + + # Simple client example: + client = asyncwhois.DomainClient() + query_string, parsed_dict = client.rdap(domain) + pprint(query_string) + pprint(parsed_dict) + + # Fully configurable client example: + # Proxy with `httpx` + whodap_client = whodap.DNSClient( + httpx_client=httpx.Client(proxies="https://proxy:8080") + ) + client = asyncwhois.DomainClient(whodap_client=whodap_client) + query_output, parser_output = client.rdap(domain) + + # **EXTERNAL DEPENDENCY** for SOCKS Proxies (`httpx_socks`) + from httpx_socks import SyncProxyTransport + + transport = SyncProxyTransport.from_url("socks5://localhost:9050") + whodap_client = whodap.DNSClient(httpx_client=httpx.Client(transport=transport)) + client = asyncwhois.DomainClient(whodap_client=whodap_client) + query_string, parsed_dict = client.rdap(domain) + return + + +if __name__ == "__main__": + main() diff --git a/tests/test_not_found.py b/tests/test_not_found.py index 1a3aff6..9921dbe 100644 --- a/tests/test_not_found.py +++ b/tests/test_not_found.py @@ -7,13 +7,14 @@ from unittest import IsolatedAsyncioTestCase class TestLookupNotFound(IsolatedAsyncioTestCase): - async def test_not_found_aio(self): - domain = 'some-non-existent-domain123.com' + domain = "some-non-existent-domain123.com" with self.assertRaises(NotFoundError): await asyncwhois.aio_whois_domain(domain) def test_not_found(self): - domain = 'some-non-existent-domain123.com' + domain = "some-non-existent-domain123.com" with self.assertRaises(NotFoundError): asyncwhois.whois_domain(domain) + + asyncwhois.whois_domain(domain, ignore_not_found=True) diff --git a/tests/test_package_methods.py b/tests/test_package_methods.py index 1291989..e01a8cd 100644 --- a/tests/test_package_methods.py +++ b/tests/test_package_methods.py @@ -3,76 +3,51 @@ import unittest.mock as mock import asyncwhois -from asyncwhois.servers import CountryCodeTLD import pytest -test_domain_name = 'amazon.com' -mock_query_data = { - 'parser_output': {'domain_name': test_domain_name}, - 'query_output': 'Domain Name: amazon.com' -} +test_domain_name = "amazon.com" +mock_response = ("Domain Name: amazon.com", {"domain_name": test_domain_name}) if sys.version_info < (3, 8): + @pytest.fixture() def mock_aio_whois_domain(mocker): future = asyncio.Future() - future.set_result(mock.Mock( - query_output=mock_query_data.get('query_output'), - parser_output=mock_query_data.get('parser_output') - )) - mocker.patch('asyncwhois.pywhois.DomainLookup.aio_whois_domain', return_value=future) + future.set_result(mock_response) + mocker.patch("asyncwhois.client.DomainClient.aio_whois", return_value=future) return future + else: + @pytest.fixture() def mock_aio_whois_domain(mocker): - async_mock = mock.AsyncMock( - return_value=mock.Mock( - query_output=mock_query_data.get('query_output'), - parser_output=mock_query_data.get('parser_output') - ) - ) - mocker.patch('asyncwhois.pywhois.DomainLookup.aio_whois_domain', side_effect=async_mock) + async_mock = mock.AsyncMock(return_value=mock_response) + mocker.patch("asyncwhois.client.DomainClient.aio_whois", side_effect=async_mock) return async_mock @pytest.fixture() def mock_whois_domain(mocker): mocker.patch( - 'asyncwhois.pywhois.DomainLookup.whois_domain', - return_value=mock.Mock( - query_output=mock_query_data.get('query_output'), - parser_output=mock_query_data.get('parser_output') - ) - ) - - -@pytest.mark.asyncio -async def test_aio_lookup(mock_aio_whois_domain): - result = await asyncwhois.aio_whois_domain(test_domain_name) - assert f"domain name: {test_domain_name}" in result.query_output.lower(), \ - f"domain name: {test_domain_name} not in {result.query_output.lower()}" - assert result.parser_output.get('domain_name').lower() == test_domain_name - - -def test_lookup(mock_whois_domain): - result = asyncwhois.whois_domain(test_domain_name) - assert f"domain name: {test_domain_name}" in result.query_output.lower(), \ - f"domain name: {test_domain_name} not in {result.query_output.lower()}" - assert result.parser_output.get('domain_name').lower() == test_domain_name - - -def test_input_parameters_for_domain_query(mocker): - spy = mocker.spy(asyncwhois.query.DomainQuery, 'new') - mocker.patch( - 'asyncwhois.query.DomainQuery._do_query', - return_value=mock_query_data.get('query_output') + "asyncwhois.client.DomainClient.whois", + return_value=mock_response, ) - test_subdomain_name = 'example.org.ua' - _ = asyncwhois.whois_domain(test_subdomain_name) - call_args = spy.call_args_list[0][0] - assert call_args[0] == test_subdomain_name - assert call_args[1] == CountryCodeTLD.UA +@pytest.mark.asyncio +async def test_aio_whois(mock_aio_whois_domain): + q, p = await asyncwhois.aio_whois(test_domain_name) + assert ( + f"domain name: {test_domain_name}" in q.lower() + ), f"domain name: {test_domain_name} not in {q.lower()}" + assert p.get("domain_name").lower() == test_domain_name + + +def test_whois(mock_whois_domain): + q, p = asyncwhois.whois(test_domain_name) + assert ( + f"domain name: {test_domain_name}" in q.lower() + ), f"domain name: {test_domain_name} not in {q.lower()}" + assert p.get("domain_name").lower() == test_domain_name diff --git a/tests/test_parser_output.py b/tests/test_parser_output.py index d677ca2..e4fa203 100644 --- a/tests/test_parser_output.py +++ b/tests/test_parser_output.py @@ -5,21 +5,27 @@ class TestTLDParsers(unittest.TestCase): - @staticmethod def get_txt(tld: str): - with open(os.path.join(os.path.abspath(os.path.dirname(__file__)), f"samples/tld_{tld}.txt"), encoding='utf-8') as txt_input: + with open( + os.path.join( + os.path.abspath(os.path.dirname(__file__)), f"samples/tld_{tld}.txt" + ), + encoding="utf-8", + ) as txt_input: query_output = txt_input.read() return query_output + def setUp(self): + self.parser = DomainParser() + def test_parser_com(self): - query_output = self.get_txt('com') - parser = DomainParser('com') - parser.parse(query_output) + query_output = self.get_txt("com") + parser_output = self.parser.parse(query_output, "com") # confirm dates - created_date = parser.parser_output.get("created") - updated_date = parser.parser_output.get("updated") - expires_date = parser.parser_output.get("expires") + created_date = parser_output.get("created") + updated_date = parser_output.get("updated") + expires_date = parser_output.get("expires") self.assertEqual(created_date.year, 1997) self.assertEqual(updated_date.year, 2019) self.assertEqual(expires_date.year, 2028) @@ -30,26 +36,25 @@ def test_parser_com(self): self.assertEqual(updated_date.day, 9) self.assertEqual(expires_date.day, 13) # geo - self.assertEqual(parser.parser_output.get("registrant_state"), "CA") - self.assertEqual(parser.parser_output.get("registrant_country"), "US") + self.assertEqual(parser_output.get("registrant_state"), "CA") + self.assertEqual(parser_output.get("registrant_country"), "US") # registrar - self.assertEqual(parser.parser_output.get("registrar"), "MarkMonitor, Inc.") - self.assertEqual(parser.parser_output.get("registrar_iana_id"), "292") + self.assertEqual(parser_output.get("registrar"), "MarkMonitor, Inc.") + self.assertEqual(parser_output.get("registrar_iana_id"), "292") # registrant - self.assertEqual(parser.parser_output.get("registrant_organization"), "Google LLC") + self.assertEqual(parser_output.get("registrant_organization"), "Google LLC") # misc - self.assertEqual(parser.parser_output.get("dnssec"), "unsigned") - self.assertEqual(len(parser.parser_output.get("name_servers")), 4) - self.assertEqual(len(parser.parser_output.get("status")), 6) + self.assertEqual(parser_output.get("dnssec"), "unsigned") + self.assertEqual(len(parser_output.get("name_servers")), 4) + self.assertEqual(len(parser_output.get("status")), 6) def test_parser_in(self): - query_output = self.get_txt('in') - parser = DomainParser('in') - parser.parse(query_output) + query_output = self.get_txt("in") + parser_output = self.parser.parse(query_output, "in") # confirm dates - created_date = parser.parser_output.get("created") - updated_date = parser.parser_output.get("updated") - expires_date = parser.parser_output.get("expires") + created_date = parser_output.get("created") + updated_date = parser_output.get("updated") + expires_date = parser_output.get("expires") self.assertEqual(created_date.year, 2007) self.assertEqual(updated_date.year, 2019) self.assertEqual(expires_date.year, 2020) @@ -60,23 +65,22 @@ def test_parser_in(self): self.assertEqual(updated_date.day, 1) self.assertEqual(expires_date.day, 1) # geo - self.assertEqual(parser.parser_output.get("registrant_state"), "Rajasthan") - self.assertEqual(parser.parser_output.get("registrant_country"), "IN") - self.assertEqual(parser.parser_output.get("registrant_address"), None) - self.assertEqual(parser.parser_output.get("registrant_zipcode"), None) + self.assertEqual(parser_output.get("registrant_state"), "Rajasthan") + self.assertEqual(parser_output.get("registrant_country"), "IN") + self.assertEqual(parser_output.get("registrant_address"), None) + self.assertEqual(parser_output.get("registrant_zipcode"), None) - self.assertEqual(parser.parser_output.get("dnssec"), "unsigned") - self.assertEqual(len(parser.parser_output.get("name_servers")), 2) - self.assertEqual(len(parser.parser_output.get("status")), 1) + self.assertEqual(parser_output.get("dnssec"), "unsigned") + self.assertEqual(len(parser_output.get("name_servers")), 2) + self.assertEqual(len(parser_output.get("status")), 1) def test_parser_top(self): - query_output = self.get_txt('top') - parser = DomainParser('top') - parser.parse(query_output) + query_output = self.get_txt("top") + parser_output = self.parser.parse(query_output, "top") # confirm dates - created_date = parser.parser_output.get("created") - updated_date = parser.parser_output.get("updated") - expires_date = parser.parser_output.get("expires") + created_date = parser_output.get("created") + updated_date = parser_output.get("updated") + expires_date = parser_output.get("expires") self.assertEqual(created_date.year, 2020) self.assertEqual(updated_date.year, 2020) self.assertEqual(expires_date.year, 2021) @@ -87,27 +91,32 @@ def test_parser_top(self): self.assertEqual(updated_date.day, 22) self.assertEqual(expires_date.day, 25) # geo - self.assertEqual(parser.parser_output.get("registrant_state"), "AZ") - self.assertEqual(parser.parser_output.get("registrant_country"), "US") - self.assertEqual(parser.parser_output.get("registrant_zipcode"), "85016") - self.assertEqual(parser.parser_output.get("registrant_address"), "1928 E. Highland Ave. Ste F104 PMB# 255") + self.assertEqual(parser_output.get("registrant_state"), "AZ") + self.assertEqual(parser_output.get("registrant_country"), "US") + self.assertEqual(parser_output.get("registrant_zipcode"), "85016") + self.assertEqual( + parser_output.get("registrant_address"), + "1928 E. Highland Ave. Ste F104 PMB# 255", + ) # registrar - self.assertEqual(parser.parser_output.get("registrar"), "NameSilo, LLC") + self.assertEqual(parser_output.get("registrar"), "NameSilo, LLC") # registrant - self.assertEqual(parser.parser_output.get("registrant_organization"), "See PrivacyGuardian.org") + self.assertEqual( + parser_output.get("registrant_organization"), + "See PrivacyGuardian.org", + ) # misc - self.assertEqual(parser.parser_output.get("dnssec"), "unsigned") - self.assertEqual(len(parser.parser_output.get("name_servers")), 3) - self.assertEqual(len(parser.parser_output.get("status")), 1) + self.assertEqual(parser_output.get("dnssec"), "unsigned") + self.assertEqual(len(parser_output.get("name_servers")), 3) + self.assertEqual(len(parser_output.get("status")), 1) def test_parser_xyz(self): - query_output = self.get_txt('xyz') - parser = DomainParser('xyz') - parser.parse(query_output) + query_output = self.get_txt("xyz") + parser_output = self.parser.parse(query_output, "xyz") # confirm dates - created_date = parser.parser_output.get("created") - updated_date = parser.parser_output.get("updated") - expires_date = parser.parser_output.get("expires") + created_date = parser_output.get("created") + updated_date = parser_output.get("updated") + expires_date = parser_output.get("expires") self.assertEqual(created_date.year, 2019) self.assertEqual(updated_date.year, 1) self.assertEqual(expires_date.year, 2020) @@ -118,29 +127,30 @@ def test_parser_xyz(self): self.assertEqual(updated_date.day, 1) self.assertEqual(expires_date.day, 15) # geo - self.assertEqual(parser.parser_output.get("registrant_state"), "Panama") - self.assertEqual(parser.parser_output.get("registrant_country"), "PA") - self.assertEqual(parser.parser_output.get("registrant_zipcode"), None) - self.assertEqual(parser.parser_output.get("registrant_address"), "P.O. Box 0823-03411") + self.assertEqual(parser_output.get("registrant_state"), "Panama") + self.assertEqual(parser_output.get("registrant_country"), "PA") + self.assertEqual(parser_output.get("registrant_zipcode"), None) + self.assertEqual(parser_output.get("registrant_address"), "P.O. Box 0823-03411") # registrar - self.assertEqual(parser.parser_output.get("registrar"), "NAMECHEAP INC") + self.assertEqual(parser_output.get("registrar"), "NAMECHEAP INC") # registrant - self.assertEqual(parser.parser_output.get("registrant_organization"), "WhoisGuard, Inc.") - self.assertEqual(parser.parser_output.get("registrant_name"), "WhoisGuard Protected") + self.assertEqual( + parser_output.get("registrant_organization"), "WhoisGuard, Inc." + ) + self.assertEqual(parser_output.get("registrant_name"), "WhoisGuard Protected") # misc - self.assertEqual(parser.parser_output.get("dnssec"), "unsigned") - self.assertEqual(len(parser.parser_output.get("name_servers")), 3) - self.assertEqual(len(parser.parser_output.get("status")), 2) + self.assertEqual(parser_output.get("dnssec"), "unsigned") + self.assertEqual(len(parser_output.get("name_servers")), 3) + self.assertEqual(len(parser_output.get("status")), 2) def test_parser_ir(self): - query_output = self.get_txt('ir') - parser = DomainParser('ir') - parser.parse(query_output) + query_output = self.get_txt("ir") + parser_output = self.parser.parse(query_output, "ir") # confirm dates - created_date = parser.parser_output.get("created") + created_date = parser_output.get("created") self.assertEqual(created_date, None) - updated_date = parser.parser_output.get("updated") - expires_date = parser.parser_output.get("expires") + updated_date = parser_output.get("updated") + expires_date = parser_output.get("expires") self.assertEqual(updated_date.year, 2019) self.assertEqual(expires_date.year, 2020) self.assertEqual(updated_date.month, 11) @@ -148,24 +158,26 @@ def test_parser_ir(self): self.assertEqual(updated_date.day, 7) self.assertEqual(expires_date.day, 22) # geo - self.assertEqual(parser.parser_output.get("registrant_state"), None) - self.assertEqual(parser.parser_output.get("registrant_country"), None) - self.assertEqual(parser.parser_output.get("registrant_zipcode"), None) - self.assertEqual(parser.parser_output.get("registrant_address"), "1600 Amphitheatre Parkway, Mountain View, CA, US") + self.assertEqual(parser_output.get("registrant_state"), None) + self.assertEqual(parser_output.get("registrant_country"), None) + self.assertEqual(parser_output.get("registrant_zipcode"), None) + self.assertEqual( + parser_output.get("registrant_address"), + "1600 Amphitheatre Parkway, Mountain View, CA, US", + ) # registrar - self.assertEqual(parser.parser_output.get("registrar"), None) + self.assertEqual(parser_output.get("registrar"), None) # registrant - self.assertEqual(parser.parser_output.get("registrant_organization"), "Google Inc.") - self.assertEqual(parser.parser_output.get("registrant_name"), "Google Inc.") + self.assertEqual(parser_output.get("registrant_organization"), "Google Inc.") + self.assertEqual(parser_output.get("registrant_name"), "Google Inc.") def test_parser_icu(self): - query_output = self.get_txt('icu') - parser = DomainParser('icu') - parser.parse(query_output) + query_output = self.get_txt("icu") + parser_output = self.parser.parse(query_output, "icu") # confirm dates - created_date = parser.parser_output.get("created") - updated_date = parser.parser_output.get("updated") - expires_date = parser.parser_output.get("expires") + created_date = parser_output.get("created") + updated_date = parser_output.get("updated") + expires_date = parser_output.get("expires") self.assertEqual(created_date.year, 2019) self.assertEqual(updated_date.year, 2019) self.assertEqual(expires_date.year, 2020) @@ -176,28 +188,30 @@ def test_parser_icu(self): self.assertEqual(updated_date.day, 23) self.assertEqual(expires_date.day, 11) # geo - self.assertEqual(parser.parser_output.get("registrant_state"), "Sind(en)") - self.assertEqual(parser.parser_output.get("registrant_city"), "karachi") - self.assertEqual(parser.parser_output.get("registrant_country"), "PK") - self.assertEqual(parser.parser_output.get("registrant_zipcode"), "75640") - self.assertEqual(parser.parser_output.get("registrant_address"), "Manzoor Colony") + self.assertEqual(parser_output.get("registrant_state"), "Sind(en)") + self.assertEqual(parser_output.get("registrant_city"), "karachi") + self.assertEqual(parser_output.get("registrant_country"), "PK") + self.assertEqual(parser_output.get("registrant_zipcode"), "75640") + self.assertEqual(parser_output.get("registrant_address"), "Manzoor Colony") # registrar - self.assertEqual(parser.parser_output.get("registrar"), "PDR Ltd. d/b/a PublicDomainRegistry.com") + self.assertEqual( + parser_output.get("registrar"), + "PDR Ltd. d/b/a PublicDomainRegistry.com", + ) # registrant - self.assertEqual(parser.parser_output.get("registrant_organization"), None) - self.assertEqual(parser.parser_output.get("registrant_name"), None) + self.assertEqual(parser_output.get("registrant_organization"), None) + self.assertEqual(parser_output.get("registrant_name"), None) # misc - self.assertEqual(parser.parser_output.get("dnssec"), "Unsigned") - self.assertEqual(len(parser.parser_output.get("name_servers")), 2) - self.assertEqual(len(parser.parser_output.get("status")), 4) + self.assertEqual(parser_output.get("dnssec"), "Unsigned") + self.assertEqual(len(parser_output.get("name_servers")), 2) + self.assertEqual(len(parser_output.get("status")), 4) def test_parser_ie(self): - query_output = self.get_txt('ie') - parser = DomainParser('ie') - parser.parse(query_output) + query_output = self.get_txt("ie") + parser_output = self.parser.parse(query_output, "ie") # confirm dates - created_date = parser.parser_output.get("created") - expires_date = parser.parser_output.get("expires") + created_date = parser_output.get("created") + expires_date = parser_output.get("expires") self.assertEqual(created_date.year, 2002) self.assertEqual(expires_date.year, 2021) self.assertEqual(created_date.month, 3) @@ -205,22 +219,21 @@ def test_parser_ie(self): self.assertEqual(created_date.day, 21) self.assertEqual(expires_date.day, 21) # registrar - self.assertEqual(parser.parser_output.get("registrar"), "Markmonitor Inc") + self.assertEqual(parser_output.get("registrar"), "Markmonitor Inc") # registrant - self.assertEqual(parser.parser_output.get("registrant_organization"), None) - self.assertEqual(parser.parser_output.get("registrant_name"), "Google, Inc") + self.assertEqual(parser_output.get("registrant_organization"), None) + self.assertEqual(parser_output.get("registrant_name"), "Google, Inc") # misc - self.assertEqual(len(parser.parser_output.get("name_servers")), 3) - self.assertEqual(len(parser.parser_output.get("status")), 1) + self.assertEqual(len(parser_output.get("name_servers")), 3) + self.assertEqual(len(parser_output.get("status")), 1) def test_parser_uk(self): - query_output = self.get_txt('uk') - parser = DomainParser('uk') - parser.parse(query_output) + query_output = self.get_txt("uk") + parser_output = self.parser.parse(query_output, "uk") # confirm dates - created_date = parser.parser_output.get("created") - updated_date = parser.parser_output.get("updated") - expires_date = parser.parser_output.get("expires") + created_date = parser_output.get("created") + updated_date = parser_output.get("updated") + expires_date = parser_output.get("expires") self.assertEqual(created_date.year, 2014) self.assertEqual(updated_date.year, 2020) self.assertEqual(expires_date.year, 2021) @@ -231,21 +244,24 @@ def test_parser_uk(self): self.assertEqual(updated_date.day, 10) self.assertEqual(expires_date.day, 11) # registrar - self.assertEqual(parser.parser_output.get("registrar"), "Markmonitor Inc. t/a MarkMonitor Inc. [Tag = MARKMONITOR]") + self.assertEqual( + parser_output.get("registrar"), + "Markmonitor Inc. t/a MarkMonitor Inc. [Tag = MARKMONITOR]", + ) # registrant - self.assertEqual(parser.parser_output.get("registrant_organization"), None) - self.assertEqual(parser.parser_output.get("registrant_name"), None) + self.assertEqual(parser_output.get("registrant_organization"), None) + self.assertEqual(parser_output.get("registrant_name"), None) # misc - self.assertEqual(len(parser.parser_output.get("name_servers")), 4) - self.assertEqual(len(parser.parser_output.get("status")), 1) + self.assertEqual(len(parser_output.get("name_servers")), 4) + self.assertEqual(len(parser_output.get("status")), 1) def test_parser_cl(self): - query_output = self.get_txt('cl') - parser = DomainParser('cl') - parser.parse(query_output) + query_output = self.get_txt("cl") + tld = "cl" + parser_output = self.parser.parse(query_output, "cl") # confirm dates - created_date = parser.parser_output.get("created") - expires_date = parser.parser_output.get("expires") + created_date = parser_output.get("created") + expires_date = parser_output.get("expires") self.assertEqual(created_date.year, 2002) self.assertEqual(expires_date.year, 2020) self.assertEqual(created_date.month, 10) @@ -253,42 +269,45 @@ def test_parser_cl(self): self.assertEqual(created_date.day, 22) self.assertEqual(expires_date.day, 20) # registrar - self.assertEqual(parser.parser_output.get("registrar"), "MarkMonitor Inc.") + self.assertEqual(parser_output.get("registrar"), "MarkMonitor Inc.") # registrant - self.assertEqual(parser.parser_output.get("registrant_organization"), "Google LLC") - self.assertEqual(parser.parser_output.get("registrant_name"), "Google LLC") + self.assertEqual(parser_output.get("registrant_organization"), "Google LLC") + self.assertEqual(parser_output.get("registrant_name"), "Google LLC") # misc - self.assertEqual(len(parser.parser_output.get("name_servers")), 4) + self.assertEqual(len(parser_output.get("name_servers")), 4) def test_parser_be(self): - query_output = self.get_txt('be') - parser = DomainParser('be') - parser.parse(query_output) + query_output = self.get_txt("be") + tld = "be" + parser_output = self.parser.parse(query_output, tld) # confirm dates - created_date = parser.parser_output.get("created") + created_date = parser_output.get("created") self.assertEqual(created_date.year, 2000) self.assertEqual(created_date.month, 12) self.assertEqual(created_date.day, 12) - self.assertEqual(parser.parser_output.get("registrar"), "MarkMonitor Inc.") + self.assertEqual(parser_output.get("registrar"), "MarkMonitor Inc.") # registrant - self.assertEqual(parser.parser_output.get("registrant_organization"), None) - self.assertEqual(parser.parser_output.get("registrant_name"), "Not shown, please visit www.dnsbelgium.be for webbased whois.") + self.assertEqual(parser_output.get("registrant_organization"), None) + self.assertEqual( + parser_output.get("registrant_name"), + "Not shown, please visit www.dnsbelgium.be for webbased whois.", + ) def test_parser_de(self): - query_output = self.get_txt('de') - parser = DomainParser('de') - parser.parse(query_output) + query_output = self.get_txt("de") + tld = "de" + parser_output = self.parser.parse(query_output, tld) - self.assertEqual(len(parser.parser_output.get('status')), 1) + self.assertEqual(len(parser_output.get("status")), 1) def test_parse_ua(self): - query_output = self.get_txt('ua') - parser = DomainParser('ua') - parser.parse(query_output) + query_output = self.get_txt("ua") + tld = "ua" + parser_output = self.parser.parse(query_output, tld) # confirm dates - created_date = parser.parser_output.get("created") - updated_date = parser.parser_output.get("updated") - expires_date = parser.parser_output.get("expires") + created_date = parser_output.get("created") + updated_date = parser_output.get("updated") + expires_date = parser_output.get("expires") self.assertEqual(created_date.year, 2002) self.assertEqual(updated_date.year, 2022) self.assertEqual(expires_date.year, 2023) @@ -299,31 +318,36 @@ def test_parse_ua(self): self.assertEqual(updated_date.day, 2) self.assertEqual(expires_date.day, 4) # geo - self.assertEqual(parser.parser_output.get("registrant_state"), "CA") - self.assertEqual(parser.parser_output.get("registrant_city"), "Mountain View") - self.assertEqual(parser.parser_output.get("registrant_country"), "US") - self.assertEqual(parser.parser_output.get("registrant_zipcode"), "94043") - self.assertEqual(parser.parser_output.get("registrant_address"), "1600 Amphitheatre Parkway") + self.assertEqual(parser_output.get("registrant_state"), "CA") + self.assertEqual(parser_output.get("registrant_city"), "Mountain View") + self.assertEqual(parser_output.get("registrant_country"), "US") + self.assertEqual(parser_output.get("registrant_zipcode"), "94043") + self.assertEqual( + parser_output.get("registrant_address"), "1600 Amphitheatre Parkway" + ) # registrar - self.assertEqual(parser.parser_output.get("registrar"), "MarkMonitor Inc.") - self.assertEqual(parser.parser_output.get("registrar_url"), "http://markmonitor.com") - self.assertEqual(parser.parser_output.get("registrar_abuse_email"), "abusecomplaints@markmonitor.com") + self.assertEqual(parser_output.get("registrar"), "MarkMonitor Inc.") + self.assertEqual(parser_output.get("registrar_url"), "http://markmonitor.com") + self.assertEqual( + parser_output.get("registrar_abuse_email"), + "abusecomplaints@markmonitor.com", + ) # registrant - self.assertEqual(parser.parser_output.get("registrant_organization"), None) - self.assertEqual(parser.parser_output.get("registrant_name"), "Google LLC") + self.assertEqual(parser_output.get("registrant_organization"), None) + self.assertEqual(parser_output.get("registrant_name"), "Google LLC") # misc - self.assertEqual(parser.parser_output.get("dnssec"), None) - self.assertEqual(len(parser.parser_output.get("name_servers")), 4) - self.assertEqual(len(parser.parser_output.get("status")), 7) + self.assertEqual(parser_output.get("dnssec"), None) + self.assertEqual(len(parser_output.get("name_servers")), 4) + self.assertEqual(len(parser_output.get("status")), 7) def test_parse_ua1(self): - query_output = self.get_txt('ua1') - parser = DomainParser('ua') - parser.parse(query_output) + query_output = self.get_txt("ua1") + tld = "ua" + parser_output = self.parser.parse(query_output, tld) # confirm dates - created_date = parser.parser_output.get("created") - updated_date = parser.parser_output.get("updated") - expires_date = parser.parser_output.get("expires") + created_date = parser_output.get("created") + updated_date = parser_output.get("updated") + expires_date = parser_output.get("expires") self.assertEqual(created_date.year, 2018) self.assertEqual(updated_date.year, 2021) self.assertEqual(expires_date.year, 2023) @@ -334,16 +358,16 @@ def test_parse_ua1(self): self.assertEqual(updated_date.day, 17) self.assertEqual(expires_date.day, 16) # registrar - self.assertEqual(parser.parser_output.get("registrar"), "ua.imena") + self.assertEqual(parser_output.get("registrar"), "ua.imena") def test_parse_us(self): - query_output = self.get_txt('us') - parser = DomainParser('us') - parser.parse(query_output) + query_output = self.get_txt("us") + tld = "us" + parser_output = self.parser.parse(query_output, tld) # confirm dates - created_date = parser.parser_output.get("created") - updated_date = parser.parser_output.get("updated") - expires_date = parser.parser_output.get("expires") + created_date = parser_output.get("created") + updated_date = parser_output.get("updated") + expires_date = parser_output.get("expires") self.assertEqual(created_date.year, 2002) self.assertEqual(updated_date.year, 2020) self.assertEqual(expires_date.year, 2021) @@ -354,29 +378,31 @@ def test_parse_us(self): self.assertEqual(updated_date.day, 22) self.assertEqual(expires_date.day, 18) # geo - self.assertEqual(parser.parser_output.get("registrant_state"), "CA") - self.assertEqual(parser.parser_output.get("registrant_city"), "Mountain View") - self.assertEqual(parser.parser_output.get("registrant_country"), "US") - self.assertEqual(parser.parser_output.get("registrant_zipcode"), "94043") - self.assertEqual(parser.parser_output.get("registrant_address"), "1600 Amphitheatre Parkway") + self.assertEqual(parser_output.get("registrant_state"), "CA") + self.assertEqual(parser_output.get("registrant_city"), "Mountain View") + self.assertEqual(parser_output.get("registrant_country"), "US") + self.assertEqual(parser_output.get("registrant_zipcode"), "94043") + self.assertEqual( + parser_output.get("registrant_address"), "1600 Amphitheatre Parkway" + ) # registrar - self.assertEqual(parser.parser_output.get("registrar"), "MarkMonitor, Inc.") + self.assertEqual(parser_output.get("registrar"), "MarkMonitor, Inc.") # registrant - self.assertEqual(parser.parser_output.get("registrant_organization"), "Google LLC") - self.assertEqual(parser.parser_output.get("registrant_name"), "Google Inc") + self.assertEqual(parser_output.get("registrant_organization"), "Google LLC") + self.assertEqual(parser_output.get("registrant_name"), "Google Inc") # misc - self.assertEqual(parser.parser_output.get("dnssec"), "unsigned") - self.assertEqual(len(parser.parser_output.get("name_servers")), 4) - self.assertEqual(len(parser.parser_output.get("status")), 3) + self.assertEqual(parser_output.get("dnssec"), "unsigned") + self.assertEqual(len(parser_output.get("name_servers")), 4) + self.assertEqual(len(parser_output.get("status")), 3) def test_parse_ar(self): - query_output = self.get_txt('ar') - parser = DomainParser('ar') - parser.parse(query_output) + query_output = self.get_txt("ar") + tld = "ar" + parser_output = self.parser.parse(query_output, tld) # confirm dates - created_date = parser.parser_output.get("created") - updated_date = parser.parser_output.get("updated") - expires_date = parser.parser_output.get("expires") + created_date = parser_output.get("created") + updated_date = parser_output.get("updated") + expires_date = parser_output.get("expires") self.assertEqual(created_date.year, 2013) self.assertEqual(updated_date.year, 2019) self.assertEqual(expires_date.year, 2020) @@ -387,21 +413,21 @@ def test_parse_ar(self): self.assertEqual(updated_date.day, 1) self.assertEqual(expires_date.day, 1) # registrar - self.assertEqual(parser.parser_output.get("registrar"), "nicar") + self.assertEqual(parser_output.get("registrar"), "nicar") # registrant - self.assertEqual(parser.parser_output.get("registrant_name"), "GOOGLE INC.") + self.assertEqual(parser_output.get("registrant_name"), "GOOGLE INC.") # misc - self.assertEqual(len(parser.parser_output.get("name_servers")), 2) - self.assertEqual(len(parser.parser_output.get("status")), 0) + self.assertEqual(len(parser_output.get("name_servers")), 2) + self.assertEqual(len(parser_output.get("status")), 0) def test_parse_no(self): - query_output = self.get_txt('no') - parser = DomainParser('no') - parser.parse(query_output) + query_output = self.get_txt("no") + tld = "no" + parser_output = self.parser.parse(query_output, tld) # confirm dates - created_date = parser.parser_output.get("created") - updated_date = parser.parser_output.get("updated") - expires_date = parser.parser_output.get("expires") + created_date = parser_output.get("created") + updated_date = parser_output.get("updated") + expires_date = parser_output.get("expires") self.assertIsNone(expires_date) self.assertEqual(created_date.year, 2001) self.assertEqual(updated_date.year, 2020) @@ -410,18 +436,18 @@ def test_parse_no(self): self.assertEqual(created_date.day, 26) self.assertEqual(updated_date.day, 27) # registrar - self.assertEqual(parser.parser_output.get("registrar"), "REG466-NORID") + self.assertEqual(parser_output.get("registrar"), "REG466-NORID") # misc - self.assertEqual(len(parser.parser_output.get("name_servers")), 4) + self.assertEqual(len(parser_output.get("name_servers")), 4) def test_parser_ai(self): - query_output = self.get_txt('ai') - parser = DomainParser('ai') - parser.parse(query_output) + query_output = self.get_txt("ai") + tld = "ai" + parser_output = self.parser.parse(query_output, tld) # confirm dates - created_date = parser.parser_output.get("created") - updated_date = parser.parser_output.get("updated") - expires_date = parser.parser_output.get("expires") + created_date = parser_output.get("created") + updated_date = parser_output.get("updated") + expires_date = parser_output.get("expires") self.assertEqual(created_date.year, 2017) self.assertEqual(updated_date.year, 2019) self.assertEqual(expires_date.year, 2021) @@ -432,29 +458,31 @@ def test_parser_ai(self): self.assertEqual(updated_date.day, 24) self.assertEqual(expires_date.day, 25) # geo - self.assertEqual(parser.parser_output.get("registrant_state"), "CA") - self.assertEqual(parser.parser_output.get("registrant_country"), "US") - self.assertEqual(parser.parser_output.get("registrant_city"), "Mountain View") - self.assertEqual(parser.parser_output.get("registrant_zipcode"), "94043") - self.assertEqual(parser.parser_output.get("registrant_address"), "1600 Amphitheatre Parkway") + self.assertEqual(parser_output.get("registrant_state"), "CA") + self.assertEqual(parser_output.get("registrant_country"), "US") + self.assertEqual(parser_output.get("registrant_city"), "Mountain View") + self.assertEqual(parser_output.get("registrant_zipcode"), "94043") + self.assertEqual( + parser_output.get("registrant_address"), "1600 Amphitheatre Parkway" + ) # registrar - self.assertEqual(parser.parser_output.get("registrar"), "Markmonitor") + self.assertEqual(parser_output.get("registrar"), "Markmonitor") # registrant - self.assertEqual(parser.parser_output.get("registrant_organization"), "Google LLC") - self.assertEqual(parser.parser_output.get("registrant_name"), "Domain Administrator") + self.assertEqual(parser_output.get("registrant_organization"), "Google LLC") + self.assertEqual(parser_output.get("registrant_name"), "Domain Administrator") # misc - self.assertEqual(parser.parser_output.get("dnssec"), "unsigned") - self.assertEqual(len(parser.parser_output.get("name_servers")), 4) - self.assertEqual(len(parser.parser_output.get("status")), 3) + self.assertEqual(parser_output.get("dnssec"), "unsigned") + self.assertEqual(len(parser_output.get("name_servers")), 4) + self.assertEqual(len(parser_output.get("status")), 3) def test_parser_me(self): - query_output = self.get_txt('me') - parser = DomainParser('me') - parser.parse(query_output) + query_output = self.get_txt("me") + tld = "me" + parser_output = self.parser.parse(query_output, tld) # confirm dates - created_date = parser.parser_output.get("created") - updated_date = parser.parser_output.get("updated") - expires_date = parser.parser_output.get("expires") + created_date = parser_output.get("created") + updated_date = parser_output.get("updated") + expires_date = parser_output.get("expires") self.assertEqual(created_date.year, 2008) self.assertEqual(updated_date.year, 2020) self.assertEqual(expires_date.year, 2021) @@ -465,25 +493,25 @@ def test_parser_me(self): self.assertEqual(updated_date.day, 12) self.assertEqual(expires_date.day, 13) # geo - self.assertEqual(parser.parser_output.get("registrant_state"), "CA") - self.assertEqual(parser.parser_output.get("registrant_country"), "US") + self.assertEqual(parser_output.get("registrant_state"), "CA") + self.assertEqual(parser_output.get("registrant_country"), "US") # registrar - self.assertEqual(parser.parser_output.get("registrar"), "MarkMonitor Inc.") + self.assertEqual(parser_output.get("registrar"), "MarkMonitor Inc.") # registrant - self.assertEqual(parser.parser_output.get("registrant_organization"), "Google LLC") + self.assertEqual(parser_output.get("registrant_organization"), "Google LLC") # misc - self.assertEqual(parser.parser_output.get("dnssec"), "unsigned") - self.assertEqual(len(parser.parser_output.get("name_servers")), 4) - self.assertEqual(len(parser.parser_output.get("status")), 6) + self.assertEqual(parser_output.get("dnssec"), "unsigned") + self.assertEqual(len(parser_output.get("name_servers")), 4) + self.assertEqual(len(parser_output.get("status")), 6) def test_parser_cc(self): - query_output = self.get_txt('cc') - parser = DomainParser('cc') - parser.parse(query_output) + query_output = self.get_txt("cc") + tld = "cc" + parser_output = self.parser.parse(query_output, tld) # confirm dates - created_date = parser.parser_output.get("created") - updated_date = parser.parser_output.get("updated") - expires_date = parser.parser_output.get("expires") + created_date = parser_output.get("created") + updated_date = parser_output.get("updated") + expires_date = parser_output.get("expires") self.assertEqual(created_date.year, 2002) self.assertEqual(updated_date.year, 2016) self.assertEqual(expires_date.year, 2024) @@ -494,28 +522,28 @@ def test_parser_cc(self): self.assertEqual(updated_date.day, 12) self.assertEqual(expires_date.day, 4) # geo - self.assertEqual(parser.parser_output.get("registrant_state"), "CA") - self.assertEqual(parser.parser_output.get("registrant_city"), "Cupertino") - self.assertEqual(parser.parser_output.get("registrant_country"), "US") - self.assertEqual(parser.parser_output.get("registrant_zipcode"), "95014") - self.assertEqual(parser.parser_output.get("registrant_address"), "1 Infinite Loop") + self.assertEqual(parser_output.get("registrant_state"), "CA") + self.assertEqual(parser_output.get("registrant_city"), "Cupertino") + self.assertEqual(parser_output.get("registrant_country"), "US") + self.assertEqual(parser_output.get("registrant_zipcode"), "95014") + self.assertEqual(parser_output.get("registrant_address"), "1 Infinite Loop") # registrar - self.assertEqual(parser.parser_output.get("registrar"), "CSC CORPORATE DOMAINS, INC.") + self.assertEqual(parser_output.get("registrar"), "CSC CORPORATE DOMAINS, INC.") # registrant - self.assertEqual(parser.parser_output.get("registrant_organization"), "Apple Inc.") - self.assertEqual(parser.parser_output.get("registrant_name"), "Domain Administrator") + self.assertEqual(parser_output.get("registrant_organization"), "Apple Inc.") + self.assertEqual(parser_output.get("registrant_name"), "Domain Administrator") # misc - self.assertEqual(parser.parser_output.get("dnssec"), "unsigned") - self.assertEqual(len(parser.parser_output.get("name_servers")), 4) - self.assertEqual(len(parser.parser_output.get("status")), 1) + self.assertEqual(parser_output.get("dnssec"), "unsigned") + self.assertEqual(len(parser_output.get("name_servers")), 4) + self.assertEqual(len(parser_output.get("status")), 1) def test_parser_ru(self): - query_output = self.get_txt('ru') - parser = DomainParser('ru') - parser.parse(query_output) + query_output = self.get_txt("ru") + tld = "ru" + parser_output = self.parser.parse(query_output, tld) # confirm dates - created_date = parser.parser_output.get("created") - expires_date = parser.parser_output.get("expires") + created_date = parser_output.get("created") + expires_date = parser_output.get("expires") self.assertEqual(created_date.year, 2004) self.assertEqual(expires_date.year, 2021) self.assertEqual(created_date.month, 3) @@ -523,17 +551,17 @@ def test_parser_ru(self): self.assertEqual(created_date.day, 3) self.assertEqual(expires_date.day, 4) # registrant - self.assertEqual(parser.parser_output.get("registrant_organization"), "Google LLC") - self.assertEqual(parser.parser_output.get("admin_email"), "https://www.nic.ru/whois") + self.assertEqual(parser_output.get("registrant_organization"), "Google LLC") + self.assertEqual(parser_output.get("admin_email"), "https://www.nic.ru/whois") def test_parser_edu(self): - query_output = self.get_txt('edu') - parser = DomainParser('edu') - parser.parse(query_output) + query_output = self.get_txt("edu") + tld = "edu" + parser_output = self.parser.parse(query_output, tld) # confirm dates - created_date = parser.parser_output.get("created") - updated_date = parser.parser_output.get("updated") - expires_date = parser.parser_output.get("expires") + created_date = parser_output.get("created") + updated_date = parser_output.get("updated") + expires_date = parser_output.get("expires") self.assertEqual(created_date.year, 1985) self.assertEqual(updated_date.year, 2020) self.assertEqual(expires_date.year, 2021) @@ -544,25 +572,32 @@ def test_parser_edu(self): self.assertEqual(updated_date.day, 26) self.assertEqual(expires_date.day, 31) # geo - self.assertEqual(parser.parser_output.get("registrant_state"), None) - self.assertEqual(parser.parser_output.get("registrant_city"), None) - self.assertEqual(parser.parser_output.get("registrant_country"), "US") - self.assertEqual(parser.parser_output.get("registrant_zipcode"), None) - self.assertEqual(parser.parser_output.get("registrant_address"), - 'ITCS, Arbor Lakes, 4251 Plymouth Road, Ann Arbor, MI 48105-2785') + self.assertEqual(parser_output.get("registrant_state"), None) + self.assertEqual(parser_output.get("registrant_city"), None) + self.assertEqual(parser_output.get("registrant_country"), "US") + self.assertEqual(parser_output.get("registrant_zipcode"), None) + self.assertEqual( + parser_output.get("registrant_address"), + "ITCS, Arbor Lakes, 4251 Plymouth Road, Ann Arbor, MI 48105-2785", + ) # registrant - self.assertEqual(parser.parser_output.get("registrant_organization"), "University of Michigan -- ITD") - self.assertEqual(parser.parser_output.get("registrant_name"), "University of Michigan -- ITD") + self.assertEqual( + parser_output.get("registrant_organization"), + "University of Michigan -- ITD", + ) + self.assertEqual( + parser_output.get("registrant_name"), "University of Michigan -- ITD" + ) # misc - self.assertEqual(len(parser.parser_output.get("name_servers")), 3) + self.assertEqual(len(parser_output.get("name_servers")), 3) def test_parser_info(self): - query_output = self.get_txt('info') - parser = DomainParser('info') - parser.parse(query_output) - created_date = parser.parser_output.get("created") - updated_date = parser.parser_output.get("updated") - expires_date = parser.parser_output.get("expires") + query_output = self.get_txt("info") + tld = "info" + parser_output = self.parser.parse(query_output, tld) + created_date = parser_output.get("created") + updated_date = parser_output.get("updated") + expires_date = parser_output.get("expires") self.assertEqual(created_date.year, 2001) self.assertEqual(updated_date.year, 2020) self.assertEqual(expires_date.year, 2021) @@ -573,22 +608,22 @@ def test_parser_info(self): self.assertEqual(updated_date.day, 29) self.assertEqual(expires_date.day, 31) # geo - self.assertEqual(parser.parser_output.get("registrar"), "MarkMonitor Inc.") - self.assertEqual(parser.parser_output.get("registrant_state"), "CA") - self.assertEqual(parser.parser_output.get("registrant_country"), "US") + self.assertEqual(parser_output.get("registrar"), "MarkMonitor Inc.") + self.assertEqual(parser_output.get("registrant_state"), "CA") + self.assertEqual(parser_output.get("registrant_country"), "US") # registrant - self.assertEqual(parser.parser_output.get("registrant_organization"), "Google LLC") + self.assertEqual(parser_output.get("registrant_organization"), "Google LLC") # misc - self.assertEqual(len(parser.parser_output.get("name_servers")), 4) - self.assertEqual(len(parser.parser_output.get("status")), 6) + self.assertEqual(len(parser_output.get("name_servers")), 4) + self.assertEqual(len(parser_output.get("status")), 6) def test_parser_fi(self): - query_output = self.get_txt('fi') - parser = DomainParser('fi') - parser.parse(query_output) - created_date = parser.parser_output.get("created") - updated_date = parser.parser_output.get("updated") - expires_date = parser.parser_output.get("expires") + query_output = self.get_txt("fi") + tld = "fi" + parser_output = self.parser.parse(query_output, tld) + created_date = parser_output.get("created") + updated_date = parser_output.get("updated") + expires_date = parser_output.get("expires") self.assertEqual(created_date.year, 2006) self.assertEqual(updated_date.year, 2020) self.assertEqual(expires_date.year, 2021) @@ -598,26 +633,30 @@ def test_parser_fi(self): self.assertEqual(created_date.day, 30) self.assertEqual(updated_date.day, 2) self.assertEqual(expires_date.day, 4) - self.assertEqual(parser.parser_output.get("registrar"), "MarkMonitor Inc.") + self.assertEqual(parser_output.get("registrar"), "MarkMonitor Inc.") # geo - self.assertEqual(parser.parser_output.get("registrant_address"), "1600 Amphitheatre Parkway") - self.assertEqual(parser.parser_output.get("registrant_zipcode"), "94043") - self.assertEqual(parser.parser_output.get("registrant_city"), "Mountain View") - self.assertEqual(parser.parser_output.get("registrant_country"), "United States of America") + self.assertEqual( + parser_output.get("registrant_address"), "1600 Amphitheatre Parkway" + ) + self.assertEqual(parser_output.get("registrant_zipcode"), "94043") + self.assertEqual(parser_output.get("registrant_city"), "Mountain View") + self.assertEqual( + parser_output.get("registrant_country"), "United States of America" + ) # registrant - self.assertEqual(parser.parser_output.get("registrant_name"), "Google LLC") + self.assertEqual(parser_output.get("registrant_name"), "Google LLC") # misc - self.assertEqual(len(parser.parser_output.get("name_servers")), 4) - self.assertEqual(len(parser.parser_output.get("status")), 1) - self.assertEqual(parser.parser_output.get("dnssec"), "no") + self.assertEqual(len(parser_output.get("name_servers")), 4) + self.assertEqual(len(parser_output.get("status")), 1) + self.assertEqual(parser_output.get("dnssec"), "no") def test_parser_kz(self): - query_output = self.get_txt('kz') - parser = DomainParser('kz') - parser.parse(query_output) - created_date = parser.parser_output.get("created") - updated_date = parser.parser_output.get("updated") - expires_date = parser.parser_output.get("expires") + query_output = self.get_txt("kz") + tld = "kz" + parser_output = self.parser.parse(query_output, tld) + created_date = parser_output.get("created") + updated_date = parser_output.get("updated") + expires_date = parser_output.get("expires") self.assertEqual(created_date.year, 1999) self.assertEqual(updated_date.year, 2012) self.assertEqual(created_date.month, 6) @@ -625,24 +664,26 @@ def test_parser_kz(self): self.assertEqual(created_date.day, 7) self.assertEqual(updated_date.day, 28) # geo - self.assertEqual(parser.parser_output.get("registrant_address"), "2400 E. Bayshore Pkwy") - self.assertEqual(parser.parser_output.get("registrant_zipcode"), "94043") - self.assertEqual(parser.parser_output.get("registrant_city"), "Mountain View") - self.assertEqual(parser.parser_output.get("registrant_country"), "US") + self.assertEqual( + parser_output.get("registrant_address"), "2400 E. Bayshore Pkwy" + ) + self.assertEqual(parser_output.get("registrant_zipcode"), "94043") + self.assertEqual(parser_output.get("registrant_city"), "Mountain View") + self.assertEqual(parser_output.get("registrant_country"), "US") # registrant - self.assertEqual(parser.parser_output.get("registrant_name"), "Google Inc.") - self.assertEqual(parser.parser_output.get("registrant_organization"), "Google Inc.") + self.assertEqual(parser_output.get("registrant_name"), "Google Inc.") + self.assertEqual(parser_output.get("registrant_organization"), "Google Inc.") # misc - self.assertEqual(len(parser.parser_output.get("name_servers")), 2) - self.assertEqual(len(parser.parser_output.get("status")), 1) - self.assertEqual(parser.parser_output.get("registrar"), "KAZNIC") + self.assertEqual(len(parser_output.get("name_servers")), 2) + self.assertEqual(len(parser_output.get("status")), 1) + self.assertEqual(parser_output.get("registrar"), "KAZNIC") def test_parser_si(self): - query_output = self.get_txt('si') - parser = DomainParser('si') - parser.parse(query_output) - created_date = parser.parser_output.get("created") - expires_date = parser.parser_output.get("expires") + query_output = self.get_txt("si") + tld = "si" + parser_output = self.parser.parse(query_output, tld) + created_date = parser_output.get("created") + expires_date = parser_output.get("expires") self.assertEqual(created_date.year, 2005) self.assertEqual(expires_date.year, 2021) self.assertEqual(created_date.month, 4) @@ -650,32 +691,32 @@ def test_parser_si(self): self.assertEqual(created_date.day, 4) self.assertEqual(expires_date.day, 19) # registrant - self.assertEqual(parser.parser_output.get("registrant_name"), "G830057") + self.assertEqual(parser_output.get("registrant_name"), "G830057") # misc - self.assertEqual(len(parser.parser_output.get("name_servers")), 4) - self.assertEqual(len(parser.parser_output.get("status")), 1) - self.assertEqual(parser.parser_output.get("registrar"), "MarkMonitor") + self.assertEqual(len(parser_output.get("name_servers")), 4) + self.assertEqual(len(parser_output.get("status")), 1) + self.assertEqual(parser_output.get("registrar"), "MarkMonitor") def test_parser_ae(self): - query_output = self.get_txt('ae') - parser = DomainParser('ae') - parser.parse(query_output) + query_output = self.get_txt("ae") + tld = "ae" + parser_output = self.parser.parse(query_output, tld) # registrant - self.assertEqual(parser.parser_output.get("registrant_name"), "Domain Administrator") - self.assertEqual(parser.parser_output.get("registrant_organization"), "Google LLC") + self.assertEqual(parser_output.get("registrant_name"), "Domain Administrator") + self.assertEqual(parser_output.get("registrant_organization"), "Google LLC") # misc - self.assertEqual(len(parser.parser_output.get("name_servers")), 2) - self.assertEqual(len(parser.parser_output.get("status")), 2) - self.assertEqual(parser.parser_output.get("registrar"), "MarkMonitor") + self.assertEqual(len(parser_output.get("name_servers")), 2) + self.assertEqual(len(parser_output.get("status")), 2) + self.assertEqual(parser_output.get("registrar"), "MarkMonitor") def test_parser_ve(self): - query_output = self.get_txt('ve') - parser = DomainParser('ve') - parser.parse(query_output) + query_output = self.get_txt("ve") + tld = "ve" + parser_output = self.parser.parse(query_output, tld) # confirm dates - created_date = parser.parser_output.get("created") - updated_date = parser.parser_output.get("updated") - expires_date = parser.parser_output.get("expires") + created_date = parser_output.get("created") + updated_date = parser_output.get("updated") + expires_date = parser_output.get("expires") self.assertEqual(created_date.year, 2002) self.assertEqual(updated_date.year, 2020) self.assertEqual(expires_date.year, 2021) @@ -686,26 +727,28 @@ def test_parser_ve(self): self.assertEqual(updated_date.day, 24) self.assertEqual(expires_date.day, 6) # geo - self.assertEqual(parser.parser_output.get("registrant_state"), "Ca") - self.assertEqual(parser.parser_output.get("registrant_country"), "US") - self.assertEqual(parser.parser_output.get("registrant_city"), "Mountain View") - self.assertEqual(parser.parser_output.get("registrant_zipcode"), "94043") - self.assertEqual(parser.parser_output.get("registrant_address"), "1600 Amphitheatre Parkway") + self.assertEqual(parser_output.get("registrant_state"), "Ca") + self.assertEqual(parser_output.get("registrant_country"), "US") + self.assertEqual(parser_output.get("registrant_city"), "Mountain View") + self.assertEqual(parser_output.get("registrant_zipcode"), "94043") + self.assertEqual( + parser_output.get("registrant_address"), "1600 Amphitheatre Parkway" + ) # registrar - self.assertEqual(parser.parser_output.get("registrar"), "NIC-VE") + self.assertEqual(parser_output.get("registrar"), "NIC-VE") # registrant - self.assertEqual(parser.parser_output.get("registrant_organization"), "Google Llc") + self.assertEqual(parser_output.get("registrant_organization"), "Google Llc") # misc - self.assertEqual(len(parser.parser_output.get("name_servers")), 4) + self.assertEqual(len(parser_output.get("name_servers")), 4) def test_parser_app(self): - query_output = self.get_txt('app') - parser = DomainParser('app') - parser.parse(query_output) + query_output = self.get_txt("app") + tld = "app" + parser_output = self.parser.parse(query_output, tld) # confirm dates - created_date = parser.parser_output.get("created") - updated_date = parser.parser_output.get("updated") - expires_date = parser.parser_output.get("expires") + created_date = parser_output.get("created") + updated_date = parser_output.get("updated") + expires_date = parser_output.get("expires") self.assertEqual(created_date.year, 2018) self.assertEqual(updated_date.year, 2020) self.assertEqual(expires_date.year, 2021) @@ -716,26 +759,30 @@ def test_parser_app(self): self.assertEqual(updated_date.day, 2) self.assertEqual(expires_date.day, 29) # geo - self.assertEqual(parser.parser_output.get("registrant_state"), "CA") - self.assertEqual(parser.parser_output.get("registrant_country"), "US") - self.assertEqual(parser.parser_output.get("registrant_city"), "REDACTED FOR PRIVACY") - self.assertEqual(parser.parser_output.get("registrant_zipcode"), "REDACTED FOR PRIVACY") - self.assertEqual(parser.parser_output.get("registrant_address"), "REDACTED FOR PRIVACY") + self.assertEqual(parser_output.get("registrant_state"), "CA") + self.assertEqual(parser_output.get("registrant_country"), "US") + self.assertEqual(parser_output.get("registrant_city"), "REDACTED FOR PRIVACY") + self.assertEqual( + parser_output.get("registrant_zipcode"), "REDACTED FOR PRIVACY" + ) + self.assertEqual( + parser_output.get("registrant_address"), "REDACTED FOR PRIVACY" + ) # registrar - self.assertEqual(parser.parser_output.get("registrar"), "MarkMonitor Inc.") + self.assertEqual(parser_output.get("registrar"), "MarkMonitor Inc.") # registrant - self.assertEqual(parser.parser_output.get("registrant_organization"), "Google LLC") + self.assertEqual(parser_output.get("registrant_organization"), "Google LLC") # misc - self.assertEqual(len(parser.parser_output.get("name_servers")), 4) - self.assertEqual(len(parser.parser_output.get("status")), 3) + self.assertEqual(len(parser_output.get("name_servers")), 4) + self.assertEqual(len(parser_output.get("status")), 3) def test_parser_cn(self): - query_output = self.get_txt('cn') - parser = DomainParser('cn') - parser.parse(query_output) + query_output = self.get_txt("cn") + tld = "cn" + parser_output = self.parser.parse(query_output, tld) # confirm dates - created_date = parser.parser_output.get("created") - expires_date = parser.parser_output.get("expires") + created_date = parser_output.get("created") + expires_date = parser_output.get("expires") self.assertEqual(created_date.year, 2003) self.assertEqual(expires_date.year, 2022) self.assertEqual(created_date.month, 3) @@ -743,22 +790,22 @@ def test_parser_cn(self): self.assertEqual(created_date.day, 17) self.assertEqual(expires_date.day, 17) # geo - self.assertEqual(parser.parser_output.get("registrant_name"), "北京谷翔信息技术有限公司") + self.assertEqual(parser_output.get("registrant_name"), "北京谷翔信息技术有限公司") # registrar - self.assertEqual(parser.parser_output.get("registrar"), "厦门易名科技股份有限公司") + self.assertEqual(parser_output.get("registrar"), "厦门易名科技股份有限公司") # registrant # misc - self.assertEqual(len(parser.parser_output.get("name_servers")), 4) - self.assertEqual(len(parser.parser_output.get("status")), 5) - self.assertEqual(parser.parser_output.get("dnssec"), "unsigned") + self.assertEqual(len(parser_output.get("name_servers")), 4) + self.assertEqual(len(parser_output.get("status")), 5) + self.assertEqual(parser_output.get("dnssec"), "unsigned") def test_parser_co(self): - query_output = self.get_txt('co') - parser = DomainParser('co') - parser.parse(query_output) - created_date = parser.parser_output.get("created") - updated_date = parser.parser_output.get("updated") - expires_date = parser.parser_output.get("expires") + query_output = self.get_txt("co") + tld = "co" + parser_output = self.parser.parse(query_output, tld) + created_date = parser_output.get("created") + updated_date = parser_output.get("updated") + expires_date = parser_output.get("expires") self.assertEqual(created_date.year, 2010) self.assertEqual(updated_date.year, 2020) self.assertEqual(expires_date.year, 2021) @@ -769,28 +816,32 @@ def test_parser_co(self): self.assertEqual(updated_date.day, 28) self.assertEqual(expires_date.day, 24) # geo - self.assertEqual(parser.parser_output.get("registrant_address"), "REDACTED FOR PRIVACY") - self.assertEqual(parser.parser_output.get("registrant_zipcode"), "REDACTED FOR PRIVACY") - self.assertEqual(parser.parser_output.get("registrant_city"), "REDACTED FOR PRIVACY") - self.assertEqual(parser.parser_output.get("registrant_country"), "US") - self.assertEqual(parser.parser_output.get("registrant_state"), "CA") + self.assertEqual( + parser_output.get("registrant_address"), "REDACTED FOR PRIVACY" + ) + self.assertEqual( + parser_output.get("registrant_zipcode"), "REDACTED FOR PRIVACY" + ) + self.assertEqual(parser_output.get("registrant_city"), "REDACTED FOR PRIVACY") + self.assertEqual(parser_output.get("registrant_country"), "US") + self.assertEqual(parser_output.get("registrant_state"), "CA") # registrant - self.assertEqual(parser.parser_output.get("registrant_name"), "REDACTED FOR PRIVACY") - self.assertEqual(parser.parser_output.get("registrant_organization"), "Google Inc.") + self.assertEqual(parser_output.get("registrant_name"), "REDACTED FOR PRIVACY") + self.assertEqual(parser_output.get("registrant_organization"), "Google Inc.") # misc - self.assertEqual(len(parser.parser_output.get("name_servers")), 4) - self.assertEqual(len(parser.parser_output.get("status")), 3) - self.assertEqual(parser.parser_output.get("registrar"), "MarkMonitor, Inc.") - self.assertEqual(parser.parser_output.get("dnssec"), "unsigned") + self.assertEqual(len(parser_output.get("name_servers")), 4) + self.assertEqual(len(parser_output.get("status")), 3) + self.assertEqual(parser_output.get("registrar"), "MarkMonitor, Inc.") + self.assertEqual(parser_output.get("dnssec"), "unsigned") def test_parser_pl(self): - query_output = self.get_txt('pl') - parser = DomainParser('pl') - parser.parse(query_output) + query_output = self.get_txt("pl") + tld = "pl" + parser_output = self.parser.parse(query_output, tld) # confirm dates - created_date = parser.parser_output.get("created") - updated_date = parser.parser_output.get("updated") - expires_date = parser.parser_output.get("expires") + created_date = parser_output.get("created") + updated_date = parser_output.get("updated") + expires_date = parser_output.get("expires") self.assertEqual(created_date.year, 2002) self.assertEqual(updated_date.year, 2020) self.assertEqual(expires_date.year, 2023) @@ -801,19 +852,19 @@ def test_parser_pl(self): self.assertEqual(updated_date.day, 17) self.assertEqual(expires_date.day, 14) # registrar - self.assertEqual(parser.parser_output.get("registrar"), "Markmonitor, Inc.") + self.assertEqual(parser_output.get("registrar"), "Markmonitor, Inc.") # misc - self.assertEqual(parser.parser_output.get("dnssec"), "Unsigned") - self.assertEqual(len(parser.parser_output.get("name_servers")), 4) + self.assertEqual(parser_output.get("dnssec"), "Unsigned") + self.assertEqual(len(parser_output.get("name_servers")), 4) def test_parser_online(self): - query_output = self.get_txt('online') - parser = DomainParser('online') - parser.parse(query_output) + query_output = self.get_txt("online") + tld = "online" + parser_output = self.parser.parse(query_output, tld) # confirm dates - created_date = parser.parser_output.get("created") - updated_date = parser.parser_output.get("updated") - expires_date = parser.parser_output.get("expires") + created_date = parser_output.get("created") + updated_date = parser_output.get("updated") + expires_date = parser_output.get("expires") self.assertEqual(created_date.year, 2015) self.assertEqual(updated_date.year, 2020) self.assertEqual(expires_date.year, 2021) @@ -824,23 +875,23 @@ def test_parser_online(self): self.assertEqual(updated_date.day, 25) self.assertEqual(expires_date.day, 19) # registrar - self.assertEqual(parser.parser_output.get("registrar"), "MarkMonitor, Inc (TLDs)") + self.assertEqual(parser_output.get("registrar"), "MarkMonitor, Inc (TLDs)") # registrant - self.assertEqual(parser.parser_output.get("registrant_organization"), "Google LLC") - self.assertEqual(parser.parser_output.get("registrant_country"), "US") - self.assertEqual(parser.parser_output.get("registrant_state"), "CA") + self.assertEqual(parser_output.get("registrant_organization"), "Google LLC") + self.assertEqual(parser_output.get("registrant_country"), "US") + self.assertEqual(parser_output.get("registrant_state"), "CA") # misc - self.assertEqual(parser.parser_output.get("dnssec"), "unsigned") - self.assertEqual(len(parser.parser_output.get("name_servers")), 4) + self.assertEqual(parser_output.get("dnssec"), "unsigned") + self.assertEqual(len(parser_output.get("name_servers")), 4) def test_parser_buzz(self): - query_output = self.get_txt('buzz') - parser = DomainParser('buzz') - parser.parse(query_output) + query_output = self.get_txt("buzz") + tld = "buzz" + parser_output = self.parser.parse(query_output, tld) # confirm dates - created_date = parser.parser_output.get("created") - updated_date = parser.parser_output.get("updated") - expires_date = parser.parser_output.get("expires") + created_date = parser_output.get("created") + updated_date = parser_output.get("updated") + expires_date = parser_output.get("expires") self.assertEqual(created_date.year, 2014) self.assertEqual(updated_date.year, 2020) self.assertEqual(expires_date.year, 2021) @@ -851,22 +902,24 @@ def test_parser_buzz(self): self.assertEqual(updated_date.day, 19) self.assertEqual(expires_date.day, 17) # registrar - self.assertEqual(parser.parser_output.get("registrar"), "MarkMonitor, Inc.") + self.assertEqual(parser_output.get("registrar"), "MarkMonitor, Inc.") # registrant - self.assertEqual(parser.parser_output.get("registrant_organization"), "Google LLC") - self.assertEqual(parser.parser_output.get("registrant_country"), "US") - self.assertEqual(parser.parser_output.get("registrant_state"), "CA") - self.assertEqual(parser.parser_output.get("registrant_address"), "REDACTED FOR PRIVACY") - self.assertEqual(parser.parser_output.get("registrant_city"), "REDACTED FOR PRIVACY") + self.assertEqual(parser_output.get("registrant_organization"), "Google LLC") + self.assertEqual(parser_output.get("registrant_country"), "US") + self.assertEqual(parser_output.get("registrant_state"), "CA") + self.assertEqual( + parser_output.get("registrant_address"), "REDACTED FOR PRIVACY" + ) + self.assertEqual(parser_output.get("registrant_city"), "REDACTED FOR PRIVACY") def test_parser_live(self): - query_output = self.get_txt('live') - parser = DomainParser('live') - parser.parse(query_output) + query_output = self.get_txt("live") + tld = "live" + parser_output = self.parser.parse(query_output, tld) # confirm dates - created_date = parser.parser_output.get("created") - updated_date = parser.parser_output.get("updated") - expires_date = parser.parser_output.get("expires") + created_date = parser_output.get("created") + updated_date = parser_output.get("updated") + expires_date = parser_output.get("expires") self.assertEqual(created_date.year, 2015) self.assertEqual(updated_date.year, 2020) self.assertEqual(expires_date.year, 2021) @@ -877,22 +930,27 @@ def test_parser_live(self): self.assertEqual(updated_date.day, 19) self.assertEqual(expires_date.day, 19) # registrar - self.assertEqual(parser.parser_output.get("registrar"), "Nom-iq Ltd. dba COM LAUDE") + self.assertEqual(parser_output.get("registrar"), "Nom-iq Ltd. dba COM LAUDE") # registrant - self.assertEqual(parser.parser_output.get("registrant_organization"), "Amazon Technologies, Inc.") - self.assertEqual(parser.parser_output.get("registrant_country"), "US") - self.assertEqual(parser.parser_output.get("registrant_state"), "NV") - self.assertEqual(parser.parser_output.get("registrant_address"), "REDACTED FOR PRIVACY") - self.assertEqual(parser.parser_output.get("registrant_city"), "REDACTED FOR PRIVACY") + self.assertEqual( + parser_output.get("registrant_organization"), + "Amazon Technologies, Inc.", + ) + self.assertEqual(parser_output.get("registrant_country"), "US") + self.assertEqual(parser_output.get("registrant_state"), "NV") + self.assertEqual( + parser_output.get("registrant_address"), "REDACTED FOR PRIVACY" + ) + self.assertEqual(parser_output.get("registrant_city"), "REDACTED FOR PRIVACY") def test_parser_cat(self): - query_output = self.get_txt('cat') - parser = DomainParser('cat') - parser.parse(query_output) + query_output = self.get_txt("cat") + tld = "cat" + parser_output = self.parser.parse(query_output, tld) # confirm dates - created_date = parser.parser_output.get("created") - updated_date = parser.parser_output.get("updated") - expires_date = parser.parser_output.get("expires") + created_date = parser_output.get("created") + updated_date = parser_output.get("updated") + expires_date = parser_output.get("expires") self.assertEqual(created_date.year, 2006) self.assertEqual(updated_date.year, 2021) self.assertEqual(expires_date.year, 2022) @@ -903,20 +961,20 @@ def test_parser_cat(self): self.assertEqual(updated_date.day, 15) self.assertEqual(expires_date.day, 14) # registrar - self.assertEqual(parser.parser_output.get("registrar"), "MarkMonitor") + self.assertEqual(parser_output.get("registrar"), "MarkMonitor") # registrant - self.assertEqual(parser.parser_output.get("registrant_organization"), "Google LLC") - self.assertEqual(parser.parser_output.get("registrant_country"), "US") - self.assertEqual(parser.parser_output.get("registrant_state"), "CA") + self.assertEqual(parser_output.get("registrant_organization"), "Google LLC") + self.assertEqual(parser_output.get("registrant_country"), "US") + self.assertEqual(parser_output.get("registrant_state"), "CA") def test_parser_ma(self): - query_output = self.get_txt('ma') - parser = DomainParser('ma') - parser.parse(query_output) + query_output = self.get_txt("ma") + tld = "ma" + parser_output = self.parser.parse(query_output, tld) # confirm dates - created_date = parser.parser_output.get("created") - updated_date = parser.parser_output.get("updated") - expires_date = parser.parser_output.get("expires") + created_date = parser_output.get("created") + updated_date = parser_output.get("updated") + expires_date = parser_output.get("expires") self.assertEqual(created_date.year, 2009) self.assertEqual(updated_date.year, 2020) self.assertEqual(expires_date.year, 2021) @@ -927,20 +985,20 @@ def test_parser_ma(self): self.assertEqual(updated_date.day, 25) self.assertEqual(expires_date.day, 24) # registrar - self.assertEqual(parser.parser_output.get("registrar"), "GENIOUS COMMUNICATION") + self.assertEqual(parser_output.get("registrar"), "GENIOUS COMMUNICATION") # registrant - self.assertEqual(parser.parser_output.get("registrant_name"), "Google LLC") - self.assertEqual(len(parser.parser_output.get("name_servers")), 4) - self.assertEqual(len(parser.parser_output.get("status")), 2) + self.assertEqual(parser_output.get("registrant_name"), "Google LLC") + self.assertEqual(len(parser_output.get("name_servers")), 4) + self.assertEqual(len(parser_output.get("status")), 2) def test_parser_vg(self): - query_output = self.get_txt('vg') - parser = DomainParser('vg') - parser.parse(query_output) + query_output = self.get_txt("vg") + tld = "vg" + parser_output = self.parser.parse(query_output, tld) # confirm dates - created_date = parser.parser_output.get("created") - updated_date = parser.parser_output.get("updated") - expires_date = parser.parser_output.get("expires") + created_date = parser_output.get("created") + updated_date = parser_output.get("updated") + expires_date = parser_output.get("expires") self.assertEqual(created_date.year, 1999) self.assertEqual(updated_date.year, 2020) self.assertEqual(expires_date.year, 2021) @@ -951,17 +1009,17 @@ def test_parser_vg(self): self.assertEqual(updated_date.day, 10) self.assertEqual(expires_date.day, 5) # registrar - self.assertEqual(parser.parser_output.get("registrar"), "MarkMonitor, Inc.") - self.assertEqual(len(parser.parser_output.get("name_servers")), 4) - self.assertEqual(len(parser.parser_output.get("status")), 3) + self.assertEqual(parser_output.get("registrar"), "MarkMonitor, Inc.") + self.assertEqual(len(parser_output.get("name_servers")), 4) + self.assertEqual(len(parser_output.get("status")), 3) def test_parser_tk(self): - query_output = self.get_txt('tk') - parser = DomainParser('tk') - parser.parse(query_output) + query_output = self.get_txt("tk") + tld = "tk" + parser_output = self.parser.parse(query_output, tld) # confirm dates - created_date = parser.parser_output.get("created") - expires_date = parser.parser_output.get("expires") + created_date = parser_output.get("created") + expires_date = parser_output.get("expires") self.assertEqual(created_date.year, 2014) self.assertEqual(expires_date.year, 2021) self.assertEqual(created_date.month, 9) @@ -969,23 +1027,28 @@ def test_parser_tk(self): self.assertEqual(created_date.day, 17) self.assertEqual(expires_date.day, 11) # registrar - self.assertEqual(parser.parser_output.get("registrar"), None) - self.assertEqual(parser.parser_output.get("registrant_organization"), "Amazon Technologies, Inc.") - self.assertEqual(parser.parser_output.get("registrant_name"), "Hostmaster Amazon Legal Dept.") - self.assertEqual(len(parser.parser_output.get("name_servers")), 5) - self.assertEqual(len(parser.parser_output.get("status")), 1) - self.assertEqual(parser.parser_output.get("registrant_country"), "U.S.A.") - self.assertEqual(parser.parser_output.get("registrant_state"), "Nevada") - self.assertEqual(parser.parser_output.get("registrant_address"), "P.O. Box 8102") - self.assertEqual(parser.parser_output.get("registrant_city"), "Reno") + self.assertEqual(parser_output.get("registrar"), None) + self.assertEqual( + parser_output.get("registrant_organization"), + "Amazon Technologies, Inc.", + ) + self.assertEqual( + parser_output.get("registrant_name"), "Hostmaster Amazon Legal Dept." + ) + self.assertEqual(len(parser_output.get("name_servers")), 5) + self.assertEqual(len(parser_output.get("status")), 1) + self.assertEqual(parser_output.get("registrant_country"), "U.S.A.") + self.assertEqual(parser_output.get("registrant_state"), "Nevada") + self.assertEqual(parser_output.get("registrant_address"), "P.O. Box 8102") + self.assertEqual(parser_output.get("registrant_city"), "Reno") def test_parser_nl(self): - query_output = self.get_txt('nl') - parser = DomainParser('nl') - parser.parse(query_output) + query_output = self.get_txt("nl") + tld = "nl" + parser_output = self.parser.parse(query_output, tld) # confirm dates - created_date = parser.parser_output.get("created") - updated_date = parser.parser_output.get("updated") + created_date = parser_output.get("created") + updated_date = parser_output.get("updated") self.assertEqual(created_date.year, 1999) self.assertEqual(updated_date.year, 2015) self.assertEqual(created_date.month, 5) @@ -993,228 +1056,257 @@ def test_parser_nl(self): self.assertEqual(created_date.day, 27) self.assertEqual(updated_date.day, 30) # registrar - self.assertEqual(parser.parser_output.get("registrar"), "MarkMonitor Inc.") - self.assertEqual(parser.parser_output.get("registrar"), "MarkMonitor Inc.") - self.assertEqual(len(parser.parser_output.get("name_servers")), 4) - self.assertEqual(len(parser.parser_output.get("status")), 1) + self.assertEqual(parser_output.get("registrar"), "MarkMonitor Inc.") + self.assertEqual(parser_output.get("registrar"), "MarkMonitor Inc.") + self.assertEqual(len(parser_output.get("name_servers")), 4) + self.assertEqual(len(parser_output.get("status")), 1) def test_parser_gq(self): - query_output = self.get_txt('gq') - parser = DomainParser('gq') - parser.parse(query_output) + query_output = self.get_txt("gq") + tld = "gq" + parser_output = self.parser.parse(query_output, tld) # confirm dates - created_date = parser.parser_output.get("created") + created_date = parser_output.get("created") self.assertEqual(created_date.year, 2014) self.assertEqual(created_date.month, 10) self.assertEqual(created_date.day, 14) # registrar - self.assertEqual(parser.parser_output.get("registrar"), None) - self.assertEqual(parser.parser_output.get("registrant_organization"), "Google Inc") - self.assertEqual(parser.parser_output.get("registrant_name"), "DNS Admin") - self.assertEqual(len(parser.parser_output.get("name_servers")), 4) - self.assertEqual(len(parser.parser_output.get("status")), 1) - self.assertEqual(parser.parser_output.get("registrant_country"), "U.S.A.") - self.assertEqual(parser.parser_output.get("registrant_state"), "California") - self.assertEqual(parser.parser_output.get("registrant_address"), "1600 Amphitheatre Parkway") - self.assertEqual(parser.parser_output.get("registrant_city"), "Mountain View") + self.assertEqual(parser_output.get("registrar"), None) + self.assertEqual(parser_output.get("registrant_organization"), "Google Inc") + self.assertEqual(parser_output.get("registrant_name"), "DNS Admin") + self.assertEqual(len(parser_output.get("name_servers")), 4) + self.assertEqual(len(parser_output.get("status")), 1) + self.assertEqual(parser_output.get("registrant_country"), "U.S.A.") + self.assertEqual(parser_output.get("registrant_state"), "California") + self.assertEqual( + parser_output.get("registrant_address"), "1600 Amphitheatre Parkway" + ) + self.assertEqual(parser_output.get("registrant_city"), "Mountain View") def test_tld_nu(self): - query_output = self.get_txt('nu') - parser = DomainParser('nu') - parser.parse(query_output) + query_output = self.get_txt("nu") + tld = "nu" + parser_output = self.parser.parse(query_output, tld) # confirm dates - created_date = parser.parser_output.get("created") + created_date = parser_output.get("created") self.assertEqual(created_date.year, 2011) self.assertEqual(created_date.month, 4) self.assertEqual(created_date.day, 15) - updated_date = parser.parser_output.get("updated") + updated_date = parser_output.get("updated") self.assertEqual(updated_date.year, 2021) self.assertEqual(updated_date.month, 2) self.assertEqual(updated_date.day, 16) - expired_date = parser.parser_output.get("expires") + expired_date = parser_output.get("expires") self.assertEqual(expired_date.year, 2022) self.assertEqual(expired_date.month, 4) self.assertEqual(expired_date.day, 15) # registrar - self.assertEqual(parser.parser_output.get("registrar"), "Domeneshop AS") - self.assertEqual(parser.parser_output.get("registrant_name"), "DNS1856879") - self.assertEqual(len(parser.parser_output.get("name_servers")), 3) - self.assertEqual(len(parser.parser_output.get("status")), 1) - self.assertEqual(parser.parser_output.get("dnssec"), "signed delegation") + self.assertEqual(parser_output.get("registrar"), "Domeneshop AS") + self.assertEqual(parser_output.get("registrant_name"), "DNS1856879") + self.assertEqual(len(parser_output.get("name_servers")), 3) + self.assertEqual(len(parser_output.get("status")), 1) + self.assertEqual(parser_output.get("dnssec"), "signed delegation") def test_tld_is(self): - query_output = self.get_txt('is') - parser = DomainParser('is') - parser.parse(query_output) + query_output = self.get_txt("is") + tld = "is" + parser_output = self.parser.parse(query_output, tld) # confirm dates - created_date = parser.parser_output.get("created") + created_date = parser_output.get("created") self.assertEqual(created_date.year, 2000) self.assertEqual(created_date.month, 1) self.assertEqual(created_date.day, 18) - expired_date = parser.parser_output.get("expires") + expired_date = parser_output.get("expires") self.assertEqual(expired_date.year, 2022) self.assertEqual(expired_date.month, 1) self.assertEqual(expired_date.day, 18) # registrar - self.assertEqual(parser.parser_output.get("registrant_name"), "Amazon Europe Core S.a.r.l.") - self.assertEqual(parser.parser_output.get("registrant_address"), "38 avenue John F. Kennedy, LU-L-1855 Luxembourg") - self.assertEqual(len(parser.parser_output.get("name_servers")), 4) - self.assertEqual(parser.parser_output.get("dnssec"), "unsigned delegation") + self.assertEqual( + parser_output.get("registrant_name"), "Amazon Europe Core S.a.r.l." + ) + self.assertEqual( + parser_output.get("registrant_address"), + "38 avenue John F. Kennedy, LU-L-1855 Luxembourg", + ) + self.assertEqual(len(parser_output.get("name_servers")), 4) + self.assertEqual(parser_output.get("dnssec"), "unsigned delegation") def test_tld_cr(self): - query_output = self.get_txt('cr') - parser = DomainParser('cr') - parser.parse(query_output) + query_output = self.get_txt("cr") + tld = "cr" + parser_output = self.parser.parse(query_output, tld) # confirm dates - created_date = parser.parser_output.get("created") + created_date = parser_output.get("created") self.assertEqual(created_date.year, 2008) self.assertEqual(created_date.month, 3) self.assertEqual(created_date.day, 23) - updated_date = parser.parser_output.get("updated") + updated_date = parser_output.get("updated") self.assertEqual(updated_date.year, 2021) self.assertEqual(updated_date.month, 2) self.assertEqual(updated_date.day, 5) - expired_date = parser.parser_output.get("expires") + expired_date = parser_output.get("expires") self.assertEqual(expired_date.year, 2022) self.assertEqual(expired_date.month, 3) self.assertEqual(expired_date.day, 24) # registrar - self.assertEqual(parser.parser_output.get("registrar"), "COMLAUDE") - self.assertEqual(parser.parser_output.get("registrant_name"), "Amazon Technologies, Inc.") - self.assertEqual(parser.parser_output.get("registrant_organization"), "Amazon Technologies, Inc.") - self.assertEqual(parser.parser_output.get("registrant_address"), "P.O. Box 8102, Reno, 89507, Nevada, US") - self.assertEqual(len(parser.parser_output.get("name_servers")), 9) + self.assertEqual(parser_output.get("registrar"), "COMLAUDE") + self.assertEqual( + parser_output.get("registrant_name"), "Amazon Technologies, Inc." + ) + self.assertEqual( + parser_output.get("registrant_organization"), + "Amazon Technologies, Inc.", + ) + self.assertEqual( + parser_output.get("registrant_address"), + "P.O. Box 8102, Reno, 89507, Nevada, US", + ) + self.assertEqual(len(parser_output.get("name_servers")), 9) def test_tld_cz(self): - query_output = self.get_txt('cz') - parser = DomainParser('cz') - parser.parse(query_output) + query_output = self.get_txt("cz") + tld = "cz" + parser_output = self.parser.parse(query_output, tld) # confirm dates - created_date = parser.parser_output.get("created") + created_date = parser_output.get("created") self.assertEqual(created_date.year, 1997) self.assertEqual(created_date.month, 9) self.assertEqual(created_date.day, 19) - expired_date = parser.parser_output.get("expires") + expired_date = parser_output.get("expires") self.assertEqual(expired_date.year, 2021) self.assertEqual(expired_date.month, 10) self.assertEqual(expired_date.day, 28) - updated_date = parser.parser_output.get("updated") + updated_date = parser_output.get("updated") self.assertEqual(updated_date.year, 2017) self.assertEqual(updated_date.month, 1) self.assertEqual(updated_date.day, 12) # registrar - self.assertEqual(parser.parser_output.get("registrar"), "REG-NOMIQ") - self.assertEqual(parser.parser_output.get("registrant_name"), "Legal Department") - self.assertEqual(parser.parser_output.get("registrant_organization"), "Amazon Europe Holding Technologies SCS") - self.assertEqual(parser.parser_output.get("registrant_address"), "65, boulevard Grande-Duchesse Charlotte, Luxembourg City, 1331, LU") - self.assertEqual(len(parser.parser_output.get("name_servers")), 6) + self.assertEqual(parser_output.get("registrar"), "REG-NOMIQ") + self.assertEqual(parser_output.get("registrant_name"), "Legal Department") + self.assertEqual( + parser_output.get("registrant_organization"), + "Amazon Europe Holding Technologies SCS", + ) + self.assertEqual( + parser_output.get("registrant_address"), + "65, boulevard Grande-Duchesse Charlotte, Luxembourg City, 1331, LU", + ) + self.assertEqual(len(parser_output.get("name_servers")), 6) def test_tld_gg(self): - query_output = self.get_txt('gg') - parser = DomainParser('gg') - parser.parse(query_output) + query_output = self.get_txt("gg") + tld = "gg" + parser_output = self.parser.parse(query_output, tld) # confirm dates - created_date = parser.parser_output.get("created") + created_date = parser_output.get("created") self.assertEqual(created_date.year, 2003) self.assertEqual(created_date.month, 4) self.assertEqual(created_date.day, 30) # registrant - self.assertEqual(parser.parser_output.get("registrant_name"), "Google LLC") + self.assertEqual(parser_output.get("registrant_name"), "Google LLC") # registrar - self.assertEqual(parser.parser_output.get("registrar"), "MarkMonitor Inc. (http://www.markmonitor.com)") + self.assertEqual( + parser_output.get("registrar"), + "MarkMonitor Inc. (http://www.markmonitor.com)", + ) # name servers and status - self.assertEqual(len(parser.parser_output.get("name_servers")), 4) - self.assertEqual(len(parser.parser_output.get("status")), 4) + self.assertEqual(len(parser_output.get("name_servers")), 4) + self.assertEqual(len(parser_output.get("status")), 4) def test_tld_ge(self): - query_output = self.get_txt('ge') - parser = DomainParser('ge') - parser.parse(query_output) + query_output = self.get_txt("ge") + tld = "ge" + parser_output = self.parser.parse(query_output, tld) # confirm dates - created_date = parser.parser_output.get("created") + created_date = parser_output.get("created") self.assertEqual(created_date.year, 2006) self.assertEqual(created_date.month, 7) self.assertEqual(created_date.day, 28) - expired_date = parser.parser_output.get("expires") + expired_date = parser_output.get("expires") self.assertEqual(expired_date.year, 2021) self.assertEqual(expired_date.month, 7) self.assertEqual(expired_date.day, 29) # registrant - self.assertEqual(parser.parser_output.get("registrant_name"), "Google LLC") + self.assertEqual(parser_output.get("registrant_name"), "Google LLC") # registrar - self.assertEqual(parser.parser_output.get("registrar"), "proservice ltd") + self.assertEqual(parser_output.get("registrar"), "proservice ltd") # misc - self.assertEqual(len(parser.parser_output.get("name_servers")), 4) - self.assertEqual(len(parser.parser_output.get("status")), 1) + self.assertEqual(len(parser_output.get("name_servers")), 4) + self.assertEqual(len(parser_output.get("status")), 1) def test_tld_jp(self): - query_output = self.get_txt('jp') - parser = DomainParser('jp') - parser.parse(query_output) + query_output = self.get_txt("jp") + tld = "jp" + parser_output = self.parser.parse(query_output, tld) # confirm dates - created_date = parser.parser_output.get("created") + created_date = parser_output.get("created") self.assertEqual(created_date.year, 2010) self.assertEqual(created_date.month, 9) self.assertEqual(created_date.day, 22) # - expires_date = parser.parser_output.get("expires") + expires_date = parser_output.get("expires") self.assertEqual(expires_date.year, 2021) self.assertEqual(expires_date.month, 9) self.assertEqual(expires_date.day, 30) # registrant - self.assertEqual(parser.parser_output.get("registrant_name"), "Amazon, Inc.") + self.assertEqual(parser_output.get("registrant_name"), "Amazon, Inc.") # address - self.assertEqual(parser.parser_output.get("registrant_address"), - "Meguro-ku, Arco Tower Annex, 8-1, Shimomeguro 1-chome") + self.assertEqual( + parser_output.get("registrant_address"), + "Meguro-ku, Arco Tower Annex, 8-1, Shimomeguro 1-chome", + ) # name servers - self.assertEqual(len(parser.parser_output.get("name_servers")), 8) + self.assertEqual(len(parser_output.get("name_servers")), 8) def test_tld_ax(self): - query_output = self.get_txt('ax') - parser = DomainParser('ax') - parser.parse(query_output) + query_output = self.get_txt("ax") + tld = "ax" + parser_output = self.parser.parse(query_output, tld) # confirm dates - created_date = parser.parser_output.get("created") + created_date = parser_output.get("created") self.assertEqual(created_date.year, 2016) self.assertEqual(created_date.month, 9) self.assertEqual(created_date.day, 8) - expired_date = parser.parser_output.get("expires") + expired_date = parser_output.get("expires") self.assertEqual(expired_date.year, 2021) self.assertEqual(expired_date.month, 9) self.assertEqual(expired_date.day, 8) - updated_date = parser.parser_output.get("updated") + updated_date = parser_output.get("updated") self.assertEqual(updated_date.year, 2020) self.assertEqual(updated_date.month, 9) self.assertEqual(updated_date.day, 5) # registrant - self.assertEqual(parser.parser_output.get("registrant_name"), "xTom GmbH") - self.assertEqual(parser.parser_output.get("registrant_country"), "Tyskland") - self.assertEqual(parser.parser_output.get("registrant_address"), "Kreuzstr.60, 40210, Duesseldorf") + self.assertEqual(parser_output.get("registrant_name"), "xTom GmbH") + self.assertEqual(parser_output.get("registrant_country"), "Tyskland") + self.assertEqual( + parser_output.get("registrant_address"), + "Kreuzstr.60, 40210, Duesseldorf", + ) # registrar - self.assertEqual(parser.parser_output.get("registrar"), "xTom") - self.assertEqual(parser.parser_output.get("registrar_url"), "https://xtom.com/") + self.assertEqual(parser_output.get("registrar"), "xTom") + self.assertEqual(parser_output.get("registrar_url"), "https://xtom.com/") # misc - self.assertEqual(len(parser.parser_output.get("name_servers")), 2) - self.assertEqual(len(parser.parser_output.get("status")), 1) - self.assertEqual(parser.parser_output.get("domain_name"), "google.ax") + self.assertEqual(len(parser_output.get("name_servers")), 2) + self.assertEqual(len(parser_output.get("status")), 1) + self.assertEqual(parser_output.get("domain_name"), "google.ax") def test_tld_aw(self): - query_output = self.get_txt('aw') - parser = DomainParser('aw') - parser.parse(query_output) + query_output = self.get_txt("aw") + tld = "aw" + parser_output = self.parser.parse(query_output, tld) # confirm dates - created_date = parser.parser_output.get("created") + created_date = parser_output.get("created") self.assertEqual(created_date.year, 2017) self.assertEqual(created_date.month, 9) self.assertEqual(created_date.day, 13) - updated_date = parser.parser_output.get("updated") + updated_date = parser_output.get("updated") self.assertEqual(updated_date.year, 2018) self.assertEqual(updated_date.month, 5) self.assertEqual(updated_date.day, 21) # registrar - self.assertEqual(parser.parser_output.get("registrar"), "SETAR N.V.") + self.assertEqual(parser_output.get("registrar"), "SETAR N.V.") # misc - self.assertEqual(parser.parser_output.get("dnssec"), "no") - self.assertEqual(len(parser.parser_output.get("name_servers")), 4) - self.assertEqual(len(parser.parser_output.get("status")), 1) - self.assertEqual(parser.parser_output.get("domain_name"), "google.aw") + self.assertEqual(parser_output.get("dnssec"), "no") + self.assertEqual(len(parser_output.get("name_servers")), 4) + self.assertEqual(len(parser_output.get("status")), 1) + self.assertEqual(parser_output.get("domain_name"), "google.aw") diff --git a/tests/test_pywhois.py b/tests/test_pywhois.py deleted file mode 100644 index 66d9bc5..0000000 --- a/tests/test_pywhois.py +++ /dev/null @@ -1,31 +0,0 @@ -import unittest - -from asyncwhois.pywhois import DomainLookup - - -class TestDomainLookup(unittest.TestCase): - - def setUp(self) -> None: - self.fake_domain = "some-domain-somewhere-out-there.co.uk" - self.fake_query_result = "Domain Name: some-domain-somewhere-out-there.co.uk" - - def test__get_server_name(self): - pyw = DomainLookup() - generic_tld = 'com' - whois_server = pyw._get_server_name(generic_tld) - self.assertEqual(whois_server, 'whois.verisign-grs.com') - generic_tld_unicode = 'xn--4gbrim' - whois_server = pyw._get_server_name(generic_tld_unicode) - self.assertEqual(whois_server, 'whois.afilias-srs.net') - country_tld = 'us' - whois_server = pyw._get_server_name(country_tld) - self.assertEqual(whois_server, 'whois.nic.us') - sponsored_tld = 'aero' - whois_server = pyw._get_server_name(sponsored_tld) - self.assertEqual(whois_server, 'whois.aero') - - def test__get_top_level_domain(self): - pyw = DomainLookup() - assert pyw._get_top_level_domain('https://www.google.co.uk') == 'uk' - assert pyw._get_top_level_domain('https://www.wikipedia.org') == 'org' - assert pyw._get_top_level_domain('https://www.coral.ai') == 'ai' diff --git a/tests/test_query.py b/tests/test_query.py index 26420fc..9d1332c 100644 --- a/tests/test_query.py +++ b/tests/test_query.py @@ -2,28 +2,29 @@ import unittest.mock as mock import asyncwhois.query -from asyncwhois.errors import QueryError class TestWhoIsQuery(unittest.TestCase): - - @mock.patch('asyncwhois.query.socket') + @mock.patch("asyncwhois.query.socket") def test_whois_query_create_connection(self, mock_socket_lib): # test connect - test_address_tuple_param = ('0.0.0.0', 69) + test_address_tuple_param = ("0.0.0.0", 69) test_timeout_param = 10 with asyncwhois.query.Query()._create_connection(test_address_tuple_param) as _: ... mock_socket_lib.create_connection.assert_called() - mock_socket_lib.create_connection.assert_called_with(test_address_tuple_param, - test_timeout_param) + mock_socket_lib.create_connection.assert_called_with( + test_address_tuple_param, test_timeout_param + ) - @mock.patch('asyncwhois.query.socket.socket') + @mock.patch("asyncwhois.query.socket.socket") def test_whois_query_send_and_recv(self, mock_socket_instance): test_data_send_string = "a-domain-to-send" test_data_recv_bytes = b"" # empty so _send_and_recv does not infinite loop mock_socket_instance.recv.return_value = test_data_recv_bytes - asyncwhois.query.Query._send_and_recv(mock_socket_instance, test_data_send_string) + asyncwhois.query.Query._send_and_recv( + mock_socket_instance, test_data_send_string + ) mock_socket_instance.recv.assert_called() mock_socket_instance.sendall.assert_called() mock_socket_instance.sendall.assert_called_with(test_data_send_string.encode()) From f6a7748611d1d8c471b83c65a9afbd2769cdaf02 Mon Sep 17 00:00:00 2001 From: Joe Obarzanek Date: Thu, 8 Feb 2024 19:14:20 -0500 Subject: [PATCH 5/8] Remove py37 and py38 support --- .github/workflows/build-and-test.yml | 2 +- setup.cfg | 4 +--- setup.py | 4 +--- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 984ba71..190ecf2 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -13,7 +13,7 @@ jobs: - ubuntu-latest - macos-latest - windows-latest - python-version: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12'] + python-version: ['3.9', '3.10', '3.11', '3.12'] steps: - uses: actions/checkout@v3 diff --git a/setup.cfg b/setup.cfg index 667390c..1268669 100644 --- a/setup.cfg +++ b/setup.cfg @@ -17,8 +17,6 @@ classifiers = Topic :: Security License :: OSI Approved :: MIT License Programming Language :: Python :: 3 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.11 @@ -28,7 +26,7 @@ classifiers = [options] package_dir = asyncwhois packages = find: -python_requires = >=3.7 +python_requires = >=3.9 [options.packages.find] where = asyncwhois \ No newline at end of file diff --git a/setup.py b/setup.py index c0961a6..ffc90ae 100644 --- a/setup.py +++ b/setup.py @@ -36,8 +36,6 @@ def get_version(location: str) -> str: "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", @@ -51,5 +49,5 @@ def get_version(location: str) -> str: url="https://github.com/pogzyb/asyncwhois", packages=["asyncwhois"], package_dir={"asyncwhois": "asyncwhois"}, - python_requires=">=3.7", + python_requires=">=3.9", ) From 0ac7cca8a0f7387cd7e6fe78ae3c9b51c3fcb943 Mon Sep 17 00:00:00 2001 From: Joe Obarzanek Date: Thu, 8 Feb 2024 20:40:04 -0500 Subject: [PATCH 6/8] Fix handling of ipv6 --- asyncwhois/client.py | 12 ++++++------ asyncwhois/query.py | 10 ++++++++-- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/asyncwhois/client.py b/asyncwhois/client.py index 6986bd2..5750df7 100644 --- a/asyncwhois/client.py +++ b/asyncwhois/client.py @@ -149,9 +149,9 @@ def whois( ip = convert_to_ip(ip) query_chain: list[str] = self.query_obj.run(ip) authoritative_answer = query_chain[-1] - parsed_dict: dict[IPBaseKeys, Any] = self.parse_obj.parse( - authoritative_answer, ip - ) + parsed_dict: dict[IPBaseKeys, Any] = {} + if isinstance(ip, ipaddress.IPv4Address): + parsed_dict = self.parse_obj.parse(authoritative_answer, ip) query_string = ( authoritative_answer if self.authoritative_only else "\n".join(query_chain) ) @@ -175,9 +175,9 @@ async def aio_whois( ip = convert_to_ip(ip) query_chain: list[str] = await self.query_obj.aio_run(ip) authoritative_answer = query_chain[-1] - parsed_dict: dict[IPBaseKeys, Any] = self.parse_obj.parse( - authoritative_answer, ip - ) + parsed_dict: dict[IPBaseKeys, Any] = {} + if isinstance(ip, ipaddress.IPv4Address): + parsed_dict = self.parse_obj.parse(authoritative_answer, ip) query_string = ( authoritative_answer if self.authoritative_only else "\n".join(query_chain) ) diff --git a/asyncwhois/query.py b/asyncwhois/query.py index 46d13be..61906c8 100644 --- a/asyncwhois/query.py +++ b/asyncwhois/query.py @@ -110,7 +110,10 @@ async def _aio_send_and_recv( def run(self, search_term: str, server: str = None) -> list[str]: data = search_term + "\r\n" if not server: - server_regex = self.refer_regex + if ":" in data: # ipv6 + server_regex = r"whois: *(.+)" + else: + server_regex = self.refer_regex server = self.iana_server else: server_regex = self.whois_server_regex @@ -119,7 +122,10 @@ def run(self, search_term: str, server: str = None) -> list[str]: async def aio_run(self, search_term: str, server: str = None) -> list[str]: data = search_term + "\r\n" if not server: - server_regex = self.refer_regex + if ":" in data: # ipv6 + server_regex = r"whois: *(.+)" + else: + server_regex = self.refer_regex server = self.iana_server else: server_regex = self.whois_server_regex From 7680c6dc8883a9788cab00512e04976d9eb3b6e0 Mon Sep 17 00:00:00 2001 From: Joe Obarzanek Date: Fri, 9 Feb 2024 07:57:00 -0500 Subject: [PATCH 7/8] Update BLOCKSIZE --- asyncwhois/query.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/asyncwhois/query.py b/asyncwhois/query.py index 61906c8..bae8807 100644 --- a/asyncwhois/query.py +++ b/asyncwhois/query.py @@ -2,21 +2,15 @@ import ipaddress import re import socket -import sys from typing import Tuple, Generator, Union -from contextlib import contextmanager - -if sys.version_info < (3, 7): - from async_generator import asynccontextmanager -else: - from contextlib import asynccontextmanager +from contextlib import contextmanager, asynccontextmanager from python_socks.sync import Proxy from python_socks.async_.asyncio import Proxy as AsyncProxy from .servers import IPv4Allocations, CountryCodeTLD, GenericTLD, SponsoredTLD -BLOCKSIZE = 1024 +BLOCKSIZE = 1500 class Query: @@ -108,8 +102,12 @@ async def _aio_send_and_recv( return result def run(self, search_term: str, server: str = None) -> list[str]: + """ + Submits the `search_term` to the WHOIS server and returns a list of query responses. + """ data = search_term + "\r\n" if not server: + # TODO: think about moving this to subclass if ":" in data: # ipv6 server_regex = r"whois: *(.+)" else: From ae381574ff33361b9626b5fc8e358a0148be6527 Mon Sep 17 00:00:00 2001 From: Joe Obarzanek Date: Fri, 9 Feb 2024 13:28:47 -0500 Subject: [PATCH 8/8] Update client examples --- README.md | 8 +++++--- examples/resuable-clients.py | 6 ++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 071905f..868bb56 100644 --- a/README.md +++ b/README.md @@ -86,13 +86,15 @@ query_string, parsed_dict = loop.run_until_complete(asyncwhois.aio_rdap(domain)) # Reusable client (caches the RDAP bootstrap server list, so it is faster for doing multiple calls) client = asyncwhois.DomainClient() -for domain in ["google.com", "bing.ai", "bitcoin.org"]: +for domain in ["google.com", "tesla.coffee", "bitcoin.org"]: query_string, parsed_dict = client.rdap(domain) # query_string, parsed_dict = await client.aio_rdap(domain) # Using a proxy or need to configure something HTTP related? Try reconfiguring the client: -whodap_client = whodap.DNSClient(httpx_client=httpx.Client(proxies="https://proxy:8080")) -# whodap_client = whodap.DNSClient(httpx_client=httpx.AsyncClient(proxies="https://proxy:8080")) +whodap_client = whodap.DNSClient.new_client( + httpx_client=httpx.Client(proxies="https://proxy:8080") +) +# whodap_client = await whodap.DNSClient.new_aio_client(httpx_client=httpx.AsyncClient(proxies="https://proxy:8080")) client = asyncwhois.DomainClient(whodap_client=whodap_client) ``` diff --git a/examples/resuable-clients.py b/examples/resuable-clients.py index 7c27e72..b278a9d 100644 --- a/examples/resuable-clients.py +++ b/examples/resuable-clients.py @@ -25,7 +25,7 @@ def main(): # Fully configurable client example: # Proxy with `httpx` - whodap_client = whodap.DNSClient( + whodap_client = whodap.DNSClient.new_client( httpx_client=httpx.Client(proxies="https://proxy:8080") ) client = asyncwhois.DomainClient(whodap_client=whodap_client) @@ -35,7 +35,9 @@ def main(): from httpx_socks import SyncProxyTransport transport = SyncProxyTransport.from_url("socks5://localhost:9050") - whodap_client = whodap.DNSClient(httpx_client=httpx.Client(transport=transport)) + whodap_client = whodap.DNSClient.new_client( + httpx_client=httpx.Client(transport=transport) + ) client = asyncwhois.DomainClient(whodap_client=whodap_client) query_string, parsed_dict = client.rdap(domain) return