czds

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

commit 7e9597efb41791d8686147b1dbbb551e85c392d2
Author: acidvegas <acid.vegas@acid.vegas>
Date: Fri, 3 Nov 2023 02:12:13 -0400

Initial commit

Diffstat:
ALICENSE | 15+++++++++++++++
AREADME.md | 31+++++++++++++++++++++++++++++++
Aczds | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aczdz.py | 102+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

4 files changed, 210 insertions(+), 0 deletions(-)

diff --git a/LICENSE b/LICENSE
@@ -0,0 +1,15 @@
+ISC License
+
+Copyright (c) 2023, acidvegas <acid.vegas@acid.vegas>
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/README.md b/README.md
@@ -0,0 +1,30 @@
+# ICANN CZDS
+
+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. 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.
+
+## Usage
+### Authentication
+Credentials may be provided interactively upon execution or via the `CZDS_USER` & `CZDS_PASS` environment variables:
+
+```bash
+export CZDS_USER='your_username'
+export CZDS_PASS='your_password'
+```
+
+### Python version
+```bash
+python czds.py [--username <username> --password <password>] [--concurrency <int>]
+```
+
+### POSIX version
+```bash
+./czds
+```
+
+___
+
+###### Mirrors
+[acid.vegas](https://git.acid.vegas/czds) • [GitHub](https://github.com/acidvegas/czds) • [GitLab](https://gitlab.com/acidvegas/czds) • [SuperNETs](https://git.supernets.org/acidvegas/czds)
+\ No newline at end of file
diff --git a/czds b/czds
@@ -0,0 +1,62 @@
+#!/bin/sh
+# ICANN API for the Centralized Zones Data Service - developed by acidvegas (https://git.acid.vegas/czds)
+
+ # https://czds.icann.org
+ # https://czds.icann.org/sites/default/files/czds-api-documentation.pdf
+
+# Function to authenticate and get access token
+authenticate() {
+    username="$1"
+    password="$2"
+    # Make an authentication request and inline the URL
+    response=$(curl -s -X POST "https://account-api.icann.org/api/authenticate" \
+        -H "Content-Type: application/json" \
+        -H "Accept: application/json" \
+        -d "{\"username\":\"$username\",\"password\":\"$password\"}")
+
+    # Extract and return the access token
+    echo "$response" | grep -o '"accessToken":"[^"]*' | cut -d '"' -f 4
+}
+
+# Function to download a zone file
+download_zone() {
+    url="$1"
+    token="$2"
+    filename=$(basename "$url")
+    filepath="zonefiles/$filename"
+    # Create output directory if it does not exist
+    mkdir -p "zonefiles"
+
+    # Make the GET request and save the response to a file
+    curl -s -o "$filepath" -H "Authorization: Bearer $token" "$url"
+    echo "Downloaded zone file to $filepath"
+}
+
+# Main program starts here
+echo "ICANN Zone Data Service Script"
+
+# Get username and password
+username=${CZDS_USER:-$(read -p "ICANN Username: " user && echo "$user")}
+password=${CZDS_PASS:-$(read -sp "ICANN Password: " pass && echo "$pass" && echo)}
+
+# Authenticate and get token
+echo "Authenticating..."
+token=$(authenticate "$username" "$password")
+
+# Check if authentication was successful
+if [ -z "$token" ]; then
+    echo "Authentication failed."
+    exit 1
+fi
+
+echo "Fetching zone file links..."
+# Fetch zone links with inline URL and download zone files
+zone_links=$(curl -s -H "Authorization: Bearer $token" "https://czds-api.icann.org/czds/downloads/links" | grep -o 'https://[^"]*')
+
+# Download zone files
+for url in $zone_links; do
+    echo "Downloading $url..."
+    download_zone "$url" "$token"
+done
+
+echo "All zone files downloaded."
diff --git a/czdz.py b/czdz.py
@@ -0,0 +1,101 @@
+#!/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 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()
+                print(f'Completed downloading {url} to file {filepath}')
+            except Exception as e:
+                print(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:
+        print(f'HTTP error occurred: {e.response.status_code} - {e.response.reason}')
+    except Exception as e:
+        print(f'An error occurred: {e}')
+\ No newline at end of file