ZDNS

repository·main·Indexed 22 days ago

https://github.com/zmap/zdns

A high-speed DNS resolver and command-line utility written in Go, designed for large-scale DNS measurements. It features a custom recursive resolver, supports iterative resolution from root servers, and provides a wide array of raw DNS and enhanced lookup modules. ZDNS is optimized for high-concurrency lookups using goroutines and offers flexible input methods, including stdin, zone files, and per-domain name server overrides.

Tokens
18.3K
Snippets
49
Records
71
Agent score
78%

What's inside zdns

  1. Configure Local Recursion

    main

    By default, ZDNS operates against a recursive resolver. To perform internal recursion (iterative resolution) starting from root servers, use the --iterative flag.

    • When to use: Use recursive resolvers (like Cloudflare or Google) for smaller scales (millions of lookups). Use --iterative when performing very large-scale scans (tens of thousands of concurrent threads) to avoid rate-limiting or DOS'ing upstream resolvers.
    • Tuning Iterative Mode:
      • --cache-size: Control the local cache size.
      • --iteration-timeout: Timeout for individual iteration steps.
      • --timeout: Total timeout for the entire resolution process.
      • --retries=N: Number of retries per name if a connection fails.
  2. How ZDNS library and CLI work together

    main

    ZDNS consists of two main components:

    1. Recursive Resolver Library (src/zdns): The core engine. It uses a ResolverConfig struct to define configuration options for all lookups. This config is used to create one or more Resolver structs.
      • Note: A Resolver is not thread-safe and should only perform a single lookup at a time. To achieve parallelism, you must use multiple Resolver instances.
    2. CLI Wrapper (src/cli): A command-line interface for performing large-scale DNS measurements.

    Lookups are defined by Modules (src/modules), which determine the behavior of the query.

  3. Configure ZDNS input formats

    main

    ZDNS supports several input methods:

    Basic Input

    • Stdin: Pipe a list of names separated by newlines. echo "google.com\nyahoo.com" | zdns A
    • File: Use the --input-file flag. zdns A --input-file=list_of_domains.txt

    Dig-style Input

    Provide a single domain as a CLI argument for ease of use, similar to dig. zdns A google.com --name-servers=1.1.1.1

    Name Servers per-domain

    Override the global --name-servers by providing domainName,nameServerIP pairs separated by newlines in the input. echo "google.com,1.1.1.1\nfacebook.com,8.8.8.8" | zdns A

    Zone Files

    Use the --zone-file flag to parse zone files. By default, ZDNS extracts only the name. To resolve names referenced in the answer section (like CNAME or NS records), use the --zone-file-include-targets flag.

  4. Profile ZDNS using pprof

    main

    The benchmark automatically starts a pprof server at localhost:6060. You can access profiles (CPU, goroutine, memory, etc.) via http://localhost:6060/debug/pprof/.

    To generate a graphical PNG view of a specific profile instead of plain text, use the pprof tool with the -png flag. Replace goroutine in the command below with the profile type you wish to visualize (e.g., allocs, heap, profile, thread).

    pprof -png http://localhost:6060/debug/pprof/goroutine > pprof.png
  5. Use Per-Module Triggers with the MULTIPLE module

    main

    The MULTIPLE module allows you to map specific input lines to specific modules using a configuration file (.ini).

    Input Format: domain_name,name_server,trigger1,trigger2,... (where name_server can be empty).

    Configuration File Structure:

    1. [Application Options]: Global settings like iterative.
    2. [MODULE_NAME]: Module-specific settings. Each module can only be listed once. Use the trigger key to map it to an input trigger.

    Example command:

    zdns MULTIPLE --multi-config-file="./multiple.ini" --input-file="input.csv"
    ; multiple.ini example
    [Application Options]
    iterative=true
    
    [A]
    trigger = "a-trigger"
    
    [MXLOOKUP]
    ipv4-lookup = true
  6. Adjust Benchmark Scale and Performance

    main

    The benchmark is intended for relative A/B testing of performance enhancements. If the benchmark is running too slowly on your hardware, you can scale it down using two methods:

    1. Reduce Goroutines: Use the --threads=X CLI argument to specify a lower number of goroutines.
    2. Reduce Input Size: Modify the linesOfInput constant in benchmark/main.go to decrease the total number of domains to be resolved.
  7. Optimize ZDNS performance and threading

    main

    ZDNS uses Go routines for massive parallelization. Each routine uses its own network socket.

    Key Performance Considerations:

    • File Descriptors: You must ensure your OS allows enough open files (ulimit -n). If you encounter too many open files, increase the limit.
    • Threads: Control concurrency with --threads. Default is 1,000. Start at 100 and increase until you see name resolution failures.
    • CPU: Limit CPU usage with --go-processes=n or the GOMAXPROCS environment variable.
    • Socket Reuse: By default, ZDNS reuses UDP sockets for performance. To create a fresh socket for every query, use --recycle-sockets=false.
    • Local IP Addresses: If running out of ephemeral ports, use multiple client IPs with --local-addr=A,B,C.
  8. Run ZDNS Benchmarks

    main

    To run the ZDNS benchmark suite, execute the make benchmark command from the zdns root directory.

    By default, the benchmark performs the following:

    • Resolves 7,000 domains from the CrUX dataset.
    • Uses 1,000 goroutines (the ZDNS default).
    • Starts a pprof server at localhost:6060 for profiling.
    make benchmark
  9. AXFR module limitations and requirements

    main

    The AXFR module has specific constraints when used with the ZDNS CLI:

    • No Iterative Resolution: The module does not support iterative resolution. If IterativeResolution is enabled in the CLI configuration, the module will trigger a fatal error.
    • No All-Nameservers Flag: The module does not support the --all-nameservers flag. Attempting to use it will result in an error.
  10. Understand the Answer structure and its hierarchy

    main

    In ZDNS, all specific DNS record types (like CAAAnswer, DNSKEYAnswer, etc.) embed the Answer struct. This provides a consistent base for all parsed records.

    The Base Answer Struct

    Every record contains these fields:

    • TTL (uint32): Time to live.
    • Type (string): The DNS record type (e.g., "A", "MX").
    • Class (string): The DNS class (e.g., "IN").
    • Name (string): The domain name.
    • Answer (string): The primary data payload (e.g., the IP address for an A record or the target for a CNAME).
    type Answer struct {
    	TTL     uint32 `json:"ttl"` 
    	Type    string `json:"type,omitempty"` 
    	Class   string `json:"class,omitempty"` 
    	Name    string `json:"name,omitempty"` 
    	Answer  string `json:"answer,omitempty"` 
    }
  11. Parse domains from DNS zone files

    main

    The ZoneFileInputHandler is used to extract domain names from DNS zone files for querying. It supports two modes of operation via the includeTargets configuration:

    1. Domain Only (Default): Extracts only the primary domain name (the first field in the record).
    2. Include Targets: Extracts both the primary domain name and the target domain specified in the RDATA field for specific record types.

    Supported record types for target extraction include:

    • NS, CNAME, DNAME, PTR, SOA: Extracts the first field after the record type.
    • MX: Extracts the second field after the record type (the exchange).
    • SRV: Extracts the fourth field after the record type (the target).

    All extracted domains are converted to lowercase and trailing dots are removed.