czds

- ICANN Centralized Zone Data Service Tool
git clone git://git.acid.vegas/czds.git
Log | Files | Refs | Archive | README | LICENSE

commit 1b90b6e09993f07e31d30efc1a057373c85a36a7
parent 4c0d8840746c7959d31ca9b1598d67860de2515f
Author: acidvegas <acid.vegas@acid.vegas>
Date: Wed, 13 Dec 2023 17:54:46 -0500

Updated README with statistics & renamed czdz.py to czds.py (oop)

Diffstat:
MREADME.md | 10++++++----
Aczds.py | 105+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dczdz.py | 103-------------------------------------------------------------------------------

3 files changed, 111 insertions(+), 107 deletions(-)

diff --git a/README.md b/README.md
@@ -3,7 +3,11 @@
 The [ICANN Centralized Zone Data Service](https://czds.icann.org) *(CZDS)* allows *approved* users to request and download DNS zone files in bulk, provided they represent a legitimate company or academic institution and their intended use is legal and ethical. Once ICANN approves the request, this tool streamlines the retrieval of extensive domain name system data, facilitating research and security analysis in the realm of internet infrastructure.
 
 ## Zone Information
-Zone files are updated once every 24 hours, specifically from 00:00 UTC to 06:00 UTC. Access to these zones is granted in increments, and the total time for approval across all zones may extend to a month or longer. It is typical for more than 90% of requested zones to receive approval. Access to certain zone files may require additional application forms with the TLD organization. Please be aware that access to certain zones is time-bound, expiring at the beginning of the following year, or up to a decade after the initial approval has been confirmed. At the time of writing this repository, the CZDS offers access to 1,150 zones.
+Zone files are updated once every 24 hours, specifically from 00:00 UTC to 06:00 UTC. Access to these zones is granted in increments, and the total time for approval across all zones may extend to a month or longer. It is typical for more than 90% of requested zones to receive approval. Access to certain zone files may require additional application forms with the TLD organization. Please be aware that access to certain zones is time-bound, expiring at the beginning of the following year, or up to a decade after the initial approval has been confirmed.
+
+At the time of writing this repository, the CZDS offers access to 1,150 zones in total.
+
+1,076 have been approved, 58 are still pending *(after 3 months)*, 10 have been revoked because the TLDs are longer active, and 6 have been denied.
 
 ## Usage
 ### Authentication
@@ -25,9 +29,7 @@ python czds.py [--username <username> --password <password>] [--concurrency <int
 ```
 
 ## Respects
-While ICANN does have an official [czds-api-client-python](https://github.com/icann/czds-api-client-python) repository, I rewrote it from scratch to be more streamline & included a POSIX version for portability.
-
-Either way, big props to ICANN for allowing me to use the CZDS for research purposes!
+While ICANN does have an official [czds-api-client-python](https://github.com/icann/czds-api-client-python) repository, I rewrote it from scratch to be more streamline & included a POSIX version for portability. Either way, big props to ICANN for allowing me to use the CZDS for research purposes!
 
 ___
 
diff --git a/czds.py b/czds.py
@@ -0,0 +1,104 @@
+#!/usr/bin/env python
+# ICANN API for the Centralized Zones Data Service - developed by acidvegas (https://git.acid.vegas/czds)
+
+'''
+References:
+    - https://czds.icann.org
+    - https://czds.icann.org/sites/default/files/czds-api-documentation.pdf
+'''
+
+import argparse
+import concurrent.futures
+import getpass
+import logging
+import os
+
+
+try:
+    import requests
+except ImportError:
+    raise ImportError('Missing dependency: requests (pip install requests)')
+
+
+def authenticate(username: str, password: str) -> str:
+    '''
+    Authenticate with ICANN's API and return the access token.
+    
+    :param username: ICANN Username
+    :param password: ICANN Password
+    '''
+    response = requests.post('https://account-api.icann.org/api/authenticate', json={'username': username, 'password': password})
+    response.raise_for_status()
+    return response.json()['accessToken']
+
+
+def download_zone(url: str, token: str, output_directory: str):
+    '''
+    Download a single zone file.
+    
+    :param url: URL to download
+    :param token: ICANN access token
+    :param output_directory: Directory to save the zone file
+    '''
+    headers = {'Authorization': f'Bearer {token}'}
+    response = requests.get(url, headers=headers)
+    response.raise_for_status()
+    filename = response.headers.get('Content-Disposition').split('filename=')[-1].strip('"')
+    filepath = os.path.join(output_directory, filename)
+    with open(filepath, 'wb') as file:
+        for chunk in response.iter_content(chunk_size=1024):
+            file.write(chunk)
+    return filepath
+
+
+def main(username: str, password: str, concurrency: int):
+    '''
+    Main function to download all zone files.
+
+    :param username: ICANN Username
+    :param password: ICANN Password
+    :param concurrency: Number of concurrent downloads
+    '''
+    token = authenticate(username, password)
+    headers = {'Authorization': f'Bearer {token}'}
+    
+    response = requests.get('https://czds-api.icann.org/czds/downloads/links', headers=headers)
+    response.raise_for_status()
+    zone_links = response.json()
+    
+    output_directory = 'zonefiles'
+    os.makedirs(output_directory, exist_ok=True)
+    
+    with concurrent.futures.ThreadPoolExecutor(max_workers=concurrency) as executor:
+        future_to_url = {executor.submit(download_zone, url, token, output_directory): url for url in zone_links}
+        for future in concurrent.futures.as_completed(future_to_url):
+            url = future_to_url[future]
+            try:
+                filepath = future.result()
+                logging.info(f'Completed downloading {url} to file {filepath}')
+            except Exception as e:
+                logging.error(f'{url} generated an exception: {e}')
+
+
+
+if __name__ == '__main__':
+    parser = argparse.ArgumentParser(description="ICANN Zone Files Downloader")
+    parser.add_argument('-u', '--username', help='ICANN Username')
+    parser.add_argument('-p', '--password', help='ICANN Password')
+    parser.add_argument('-c', '--concurrency', type=int, default=5, help='Number of concurrent downloads')
+    args = parser.parse_args()
+
+    username = args.username or os.getenv('CZDS_USER')
+    password = args.password or os.getenv('CZDS_PASS')
+
+    if not username:
+        username = input('ICANN Username: ')
+    if not password:
+        password = getpass.getpass('ICANN Password: ')
+    
+    try:
+        main(username, password, args.concurrency)
+    except requests.HTTPError as e:
+        logging.error(f'HTTP error occurred: {e.response.status_code} - {e.response.reason}')
+    except Exception as e:
+        logging.error(f'An error occurred: {e}')
+\ No newline at end of file
diff --git a/czdz.py b/czdz.py
@@ -1,102 +0,0 @@
-#!/usr/bin/env python
-# ICANN API for the Centralized Zones Data Service - developed by acidvegas (https://git.acid.vegas/czds)
-
-'''
-References:
-    - https://czds.icann.org
-    - https://czds.icann.org/sites/default/files/czds-api-documentation.pdf
-'''
-
-import argparse
-import concurrent.futures
-import getpass
-import logging
-import os
-
-try:
-    import requests
-except ImportError:
-    raise ImportError('Missing dependency: requests (pip install requests)')
-
-def authenticate(username: str, password: str) -> str:
-    '''
-    Authenticate with ICANN's API and return the access token.
-    
-    :param username: ICANN Username
-    :param password: ICANN Password
-    '''
-    response = requests.post('https://account-api.icann.org/api/authenticate', json={'username': username, 'password': password})
-    response.raise_for_status()
-    return response.json()['accessToken']
-
-
-def download_zone(url: str, token: str, output_directory: str):
-    '''
-    Download a single zone file.
-    
-    :param url: URL to download
-    :param token: ICANN access token
-    :param output_directory: Directory to save the zone file
-    '''
-    headers = {'Authorization': f'Bearer {token}'}
-    response = requests.get(url, headers=headers)
-    response.raise_for_status()
-    filename = response.headers.get('Content-Disposition').split('filename=')[-1].strip('"')
-    filepath = os.path.join(output_directory, filename)
-    with open(filepath, 'wb') as file:
-        for chunk in response.iter_content(chunk_size=1024):
-            file.write(chunk)
-    return filepath
-
-
-def main(username: str, password: str, concurrency: int):
-    '''
-    Main function to download all zone files.
-
-    :param username: ICANN Username
-    :param password: ICANN Password
-    :param concurrency: Number of concurrent downloads
-    '''
-    token = authenticate(username, password)
-    headers = {'Authorization': f'Bearer {token}'}
-    
-    response = requests.get('https://czds-api.icann.org/czds/downloads/links', headers=headers)
-    response.raise_for_status()
-    zone_links = response.json()
-    
-    output_directory = 'zonefiles'
-    os.makedirs(output_directory, exist_ok=True)
-    
-    with concurrent.futures.ThreadPoolExecutor(max_workers=concurrency) as executor:
-        future_to_url = {executor.submit(download_zone, url, token, output_directory): url for url in zone_links}
-        for future in concurrent.futures.as_completed(future_to_url):
-            url = future_to_url[future]
-            try:
-                filepath = future.result()
-                logging.info(f'Completed downloading {url} to file {filepath}')
-            except Exception as e:
-                logging.error(f'{url} generated an exception: {e}')
-
-
-
-if __name__ == '__main__':
-    parser = argparse.ArgumentParser(description="ICANN Zone Files Downloader")
-    parser.add_argument('-u', '--username', help='ICANN Username')
-    parser.add_argument('-p', '--password', help='ICANN Password')
-    parser.add_argument('-c', '--concurrency', type=int, default=5, help='Number of concurrent downloads')
-    args = parser.parse_args()
-
-    username = args.username or os.getenv('CZDS_USER')
-    password = args.password or os.getenv('CZDS_PASS')
-
-    if not username:
-        username = input('ICANN Username: ')
-    if not password:
-        password = getpass.getpass('ICANN Password: ')
-    
-    try:
-        main(username, password, args.concurrency)
-    except requests.HTTPError as e:
-        logging.error(f'HTTP error occurred: {e.response.status_code} - {e.response.reason}')
-    except Exception as e:
-        logging.error(f'An error occurred: {e}')
-\ No newline at end of file