Skip to content

Instantly share code, notes, and snippets.

@justsml
Last active January 27, 2025 09:07
Show Gist options
  • Save justsml/3e114ff09d495547f45e3536cbd97c0f to your computer and use it in GitHub Desktop.
Save justsml/3e114ff09d495547f45e3536cbd97c0f to your computer and use it in GitHub Desktop.

Revisions

  1. justsml revised this gist Jan 27, 2025. 2 changed files with 19 additions and 16 deletions.
    35 changes: 19 additions & 16 deletions dns_global_check.sh
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,5 @@
    #!/usr/bin/env bash

    # Run global DNS queries for a domain, in parallel! πŸ¦„
    #
    # This script is useful for testing DNS propagation and performance.
    @@ -103,7 +104,7 @@ if ! load_dns_servers; then
    "94.140.14.15 (AdGuard 2)"
    "85.214.28.183 (Digitalcourage - Germany 1)"
    "135.125.237.69 (OVH - Germany 2)"
    "91.26.116.18 (Deutsche Telekom AG - Germany 3)"
    "91.26.116.18 (Deutsche Telekom - Germany 3)"
    "223.5.5.148 (AliDNS - China 1)"
    "223.5.5.0 (AliDNS - China 2)"
    "223.5.5.84 (AliDNS - China 3)"
    @@ -117,8 +118,8 @@ if ! load_dns_servers; then
    "54.37.30.59 (OVH - France 2)"
    "178.255.79.70 (Telecom Italia - Italy 1)"
    # "89.96.49.60 (Fastweb - Italy 2)"
    "197.155.92.20 (Liquid Telecommunications - Kenya)"
    "197.248.131.203 (Safaricom - Kenya 1)"
    "197.155.92.20 (Liquid Teleco - Kenya 1)"
    # "197.248.131.203 (Safaricom - Kenya 2)"
    # "197.253.8.109 (Mainone - Nigeria 1)"
    # "196.25.1.11 (Telkom SA - South Africa 1)"
    # "196.25.1.9 (Telkom SA - South Africa 2)"
    @@ -134,23 +135,25 @@ if ! load_dns_servers; then
    )
    fi

    # Function to get current time in milliseconds
    get_time() {
    if [[ "$OSTYPE" == "darwin"* ]]; then
    # On macOS, use perl since date doesn't support nanoseconds
    perl -MTime::HiRes -e 'printf("%.0f\n", Time::HiRes::time() * 1000)'
    else
    # On Linux, use date with millisecond precision
    date +%s%3N
    fi
    }

    current_jobs=0
    total_servers=${#SERVERS[@]}
    script_start_time=$(date +%s)
    script_start_time=$(get_time)

    # Initialize counters
    successful_queries=0
    failed_queries=0

    # Function to get current time in seconds with decimal precision
    get_time() {
    if [[ "$OSTYPE" == "darwin"* ]]; then
    date +%s.%N
    else
    date +%s.%N
    fi
    }

    # Function to get safe filename from domain
    get_safe_filename() {
    echo "$1" | tr -c '[:alnum:]' '_'
    @@ -175,11 +178,11 @@ dns_query() {
    resolved_ips=$(dig +tries=1 +short +time="$timeout" @"$server_ip" "$domain" "$record_type" 2>/dev/null)
    query_status=$?
    end_time=$(get_time)
    local duration_ms=$(printf "%.0f" "$(echo "(${end_time} - ${start_time}) * 1000" | bc)")
    local duration_ms=$((end_time - start_time))

    if [ $query_status -eq 0 ] && [ ! -z "$resolved_ips" ]; then
    if [ "$VERBOSE" = "1" ]; then
    printf " βœ… %s @ %s - %d ms\n" "$server_label" "$server_ip" "$duration_ms"
    printf " βœ… %d ms @ %s - %s\n" "$duration_ms" "$server_label" "$server_ip"
    printf " └─ Resolved: %s\n" "$(echo "$resolved_ips" | tr '\n' ' ')"
    fi
    echo "success" > "${tmp_prefix}_${server_ip}"
    @@ -257,7 +260,7 @@ for entry in "${SERVERS[@]}"; do
    done

    end_time=$(get_time)
    duration=$(printf "%.0f" "$(echo "(${end_time} - ${script_start_time}) * 1000" | bc)")
    duration=$((end_time - script_start_time))
    if [ "$VERBOSE" = "1" ]; then
    printf "\nDone! 🏁 🏎️ %s servers in %s ms\n" "$total_servers" "$duration"
    printf "Successful: %d, Failed: %d, Total: %d\n" "$successful_queries" "$failed_queries" "$total_servers"
    File renamed without changes.
  2. justsml revised this gist Jan 26, 2025. No changes.
  3. justsml revised this gist Jan 26, 2025. 2 changed files with 299 additions and 69 deletions.
    300 changes: 231 additions & 69 deletions dns_global_check.sh
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,4 @@
    #!/usr/bin/env bash

    # Run global DNS queries for a domain, in parallel! πŸ¦„
    #
    # This script is useful for testing DNS propagation and performance.
    @@ -13,27 +12,50 @@
    # ./dns_global_check.sh <domain> [<record type>]
    #
    # Examples:
    # ./dns_global_check.sh example.com # defaults to A record
    # ./dns_global_check.sh example.com MX # queries MX records
    # ./dns_global_check.sh danlevy.net # defaults to A record
    # ./dns_global_check.sh danlevy.net MX # queries MX records
    #
    # Author: Dan Levy / https://danlevy.net / @justsml
    #
    # License: Yolo 🫢
    #
    # Note: Public DNS resolvers are subject to frequent changes, blocks or rate-limits on foreign IPs.

    # Initialize parameters
    VERBOSE=0
    DOMAIN=""
    TYPE=""
    TIMEOUT=""
    MAX_CONCURRENT=""


    DOMAIN="$1"
    TYPE="$2"
    TIMEOUT="$3"
    MAX_CONCURRENT="$4"
    # Process all arguments
    while [ $# -gt 0 ]; do
    case "$1" in
    --verbose|-v)
    VERBOSE=1
    ;;
    *)
    if [ -z "$DOMAIN" ]; then
    DOMAIN="$1"
    elif [ -z "$TYPE" ]; then
    TYPE="$1"
    elif [ -z "$TIMEOUT" ]; then
    TIMEOUT="$1"
    elif [ -z "$MAX_CONCURRENT" ]; then
    MAX_CONCURRENT="$1"
    fi
    ;;
    esac
    shift
    done

    if [ -z "$DOMAIN" ]; then
    echo "Usage: $0 <domain> [<record type>] [<timeout>] [<max concurrent>]"
    echo "Example: $0 example.com"
    echo "Example: $0 example.com MX"
    echo "Example: $0 example.com TXT 20 10"
    echo "Usage: $0 [--verbose|-v] <domain> [<record type>] [<timeout>] [<max concurrent>]"
    echo "Example: $0 danlevy.net"
    echo "Example: $0 danlevy.net MX"
    echo "Example: $0 --verbose danlevy.net TXT 20 10"
    echo "Example: $0 danlevy.net A --verbose"
    echo "Example: MAX_CONCURRENT=20 $0 danlevy.net A"
    exit 1
    fi

    @@ -47,50 +69,92 @@ TIMEOUT="${TIMEOUT:-8}"
    # Limit of concurrent lookups
    MAX_CONCURRENT=${MAX_CONCURRENT:-10}

    # List of DNS servers and labels (IP (Label))
    SERVERS=(
    "1.1.1.1 (Cloudflare 1)"
    "1.0.0.1 (Cloudflare 2)"
    "8.8.8.8 (Google DNS 1)"
    "8.8.4.4 (Google DNS 2)"
    "94.140.14.14 (AdGuard 1)"
    "94.140.14.15 (AdGuard 2)"
    "85.214.28.183 (Digitalcourage - Germany 1)"
    "135.125.237.69 (OVH - Germany 2)"
    "91.26.116.18 (Deutsche Telekom AG - Germany 3)"
    "223.5.5.148 (AliDNS - China 1)"
    "223.5.5.0 (AliDNS - China 2)"
    "223.5.5.84 (AliDNS - China 3)"
    "89.234.93.210 (Digiweb - Ireland 1)"
    "80.93.18.196 (Digiweb - Ireland 2)"
    "158.43.192.1 (Verizon - London 1)"
    "195.21.13.234 (GTT - London 2)"
    "195.186.1.111 (Swisscom - Switzerland 1)"
    "81.7.255.3 (Swisscom - Switzerland 2)"
    "170.64.147.31 (OVH - France 1)"
    "54.37.30.59 (OVH - France 2)"
    "178.255.79.70 (Telecom Italia - Italy 1)"
    "89.96.49.60 (Fastweb - Italy 2)"
    "197.155.92.20 (Liquid Telecommunications - Kenya)"
    "197.248.131.203 (Safaricom - Kenya 1)"
    "197.253.8.109 (Mainone - Nigeria 1)"
    "196.25.1.11 (Telkom SA - South Africa 1)"
    "196.25.1.9 (Telkom SA - South Africa 2)"
    "115.99.172.26 (BSNL - India 1)"
    "115.98.145.112 (Hathway - India 2)"
    "202.136.162.11 (M1 - Singapore 1)"
    "202.136.162.12 (M1 - Singapore 2)"
    "172.104.90.123 (Akamai - Japan 1)"
    "153.156.93.5 (NTT - Japan 2)"
    "210.163.158.224 (NTT - Japan 3)"
    "168.126.63.1 (KT - South Korea 1)"
    "168.126.63.2 (KT - South Korea 2)"
    )
    # Function to load DNS servers from file
    load_dns_servers() {
    local custom_file="$HOME/.dns-resolvers.txt"
    if [ -f "$custom_file" ] && [ -r "$custom_file" ]; then
    if [ "$VERBOSE" = "1" ]; then
    printf "Loading DNS servers from %s\n" "$custom_file"
    fi
    # Read custom servers, skip empty lines and comments
    while IFS= read -r line; do
    line=$(echo "$line" | sed 's/#.*//;s/^[[:space:]]*//;s/[[:space:]]*$//')
    if [ ! -z "$line" ]; then
    SERVERS+=("$line")
    fi
    done < "$custom_file"
    return 0
    fi
    return 1
    }

    # Before defining default SERVERS array, try loading custom file
    if ! load_dns_servers; then
    # Default list of DNS servers and labels (IP (Label))
    SERVERS=(
    # IMPORTANT:
    # Public DNS resolvers are subject to frequent changes, blocks or rate-limits on foreign IPs.
    # Feel free to add or remove servers as needed.
    "1.1.1.1 (Cloudflare 1)"
    "1.0.0.1 (Cloudflare 2)"
    "8.8.8.8 (Google DNS 1)"
    "8.8.4.4 (Google DNS 2)"
    "94.140.14.14 (AdGuard 1)"
    "94.140.14.15 (AdGuard 2)"
    "85.214.28.183 (Digitalcourage - Germany 1)"
    "135.125.237.69 (OVH - Germany 2)"
    "91.26.116.18 (Deutsche Telekom AG - Germany 3)"
    "223.5.5.148 (AliDNS - China 1)"
    "223.5.5.0 (AliDNS - China 2)"
    "223.5.5.84 (AliDNS - China 3)"
    "89.234.93.210 (Digiweb - Ireland 1)"
    "80.93.18.196 (Digiweb - Ireland 2)"
    "158.43.192.1 (Verizon - London 1)"
    "195.21.13.234 (GTT - London 2)"
    "195.186.1.111 (Swisscom - Switzerland 1)"
    "81.7.255.3 (Swisscom - Switzerland 2)"
    "170.64.147.31 (OVH - France 1)"
    "54.37.30.59 (OVH - France 2)"
    "178.255.79.70 (Telecom Italia - Italy 1)"
    # "89.96.49.60 (Fastweb - Italy 2)"
    "197.155.92.20 (Liquid Telecommunications - Kenya)"
    "197.248.131.203 (Safaricom - Kenya 1)"
    # "197.253.8.109 (Mainone - Nigeria 1)"
    # "196.25.1.11 (Telkom SA - South Africa 1)"
    # "196.25.1.9 (Telkom SA - South Africa 2)"
    "115.99.172.26 (BSNL - India 1)"
    "115.98.145.112 (Hathway - India 2)"
    "202.136.162.11 (M1 - Singapore 1)"
    "202.136.162.12 (M1 - Singapore 2)"
    "172.104.90.123 (Akamai - Japan 1)"
    "153.156.93.5 (NTT - Japan 2)"
    "210.163.158.224 (NTT - Japan 3)"
    "168.126.63.1 (KT - South Korea 1)"
    "168.126.63.2 (KT - South Korea 2)"
    )
    fi

    current_jobs=0
    total_servers=${#SERVERS[@]}
    script_start_time=$(date +%s)

    # Initialize counters
    successful_queries=0
    failed_queries=0

    # Function to get current time in seconds with decimal precision
    get_time() {
    if [[ "$OSTYPE" == "darwin"* ]]; then
    date +%s.%N
    else
    date +%s.%N
    fi
    }

    script_start_time=$(date +%s%3N)
    # Function to get safe filename from domain
    get_safe_filename() {
    echo "$1" | tr -c '[:alnum:]' '_'
    }

    # Function to perform a DNS lookup
    dns_query() {
    @@ -99,22 +163,31 @@ dns_query() {
    local domain="$3"
    local record_type="$4"
    local timeout="$5"
    local start_time_ms
    local end_time_ms

    start_time_ms=$(date +%s%3N)
    if dig +short +time="$timeout" @"$server_ip" "$domain" "$record_type" >/dev/null 2>&1; then
    (( successful_queries++ ))
    end_time_ms=$(date +%s%3N)
    local duration_ms=$((end_time_ms - start_time_ms))
    printf " βœ… %s @ %s - %d ms " "$server_label" "$server_ip" "$duration_ms"
    local start_time
    local end_time
    local query_status
    local resolved_ips
    local first_resolved
    local safe_domain=$(get_safe_filename "$domain")
    local tmp_prefix="/tmp/dns_check_${safe_domain}_${record_type}_$$"

    start_time=$(get_time)
    resolved_ips=$(dig +tries=1 +short +time="$timeout" @"$server_ip" "$domain" "$record_type" 2>/dev/null)
    query_status=$?
    end_time=$(get_time)
    local duration_ms=$(printf "%.0f" "$(echo "(${end_time} - ${start_time}) * 1000" | bc)")

    if [ $query_status -eq 0 ] && [ ! -z "$resolved_ips" ]; then
    if [ "$VERBOSE" = "1" ]; then
    printf " βœ… %s @ %s - %d ms\n" "$server_label" "$server_ip" "$duration_ms"
    printf " └─ Resolved: %s\n" "$(echo "$resolved_ips" | tr '\n' ' ')"
    fi
    echo "success" > "${tmp_prefix}_${server_ip}"
    echo "$resolved_ips" > "${tmp_prefix}_${server_ip}.resolved"
    else
    (( failed_queries++ ))
    end_time_ms=$(date +%s%3N)
    local duration_ms=$((end_time_ms - start_time_ms))
    printf " ❌ Error#: %s - %s - dig @%s %s %s - %d ms " "$?" "$server_label" "$server_ip" "$domain" "$record_type" "$duration_ms"
    printf " ❌ %s [%s] - %d ms\n" "$server_label" "$server_ip" "$duration_ms"
    echo "fail" > "${tmp_prefix}_${server_ip}"
    fi
    echo
    }

    # Loop through each DNS server and run lookups in parallel
    @@ -135,6 +208,95 @@ done
    # Wait for any remaining jobs to complete
    wait

    duration=$(( $(date +%s%3N) - script_start_time ))
    printf "πŸ•’ Total duration: %s ms\n" "$duration"
    echo "🏁 🏎️ Finished!"
    # After wait, before counting results
    declare -A resolved_values
    declare -A answer_counts
    first_success=""
    consensus_answer=""
    max_count=0

    # Function to normalize results for comparison
    normalize_results() {
    echo "$1" | tr ' ' '\n' | sort | tr '\n' ' ' | sed 's/ $//'
    }

    # Count results
    for entry in "${SERVERS[@]}"; do
    IP=$(echo "$entry" | awk '{print $1}')
    LABEL=$(echo "$entry" | sed 's/^[^ ]* //')
    safe_domain=$(get_safe_filename "$DOMAIN")
    tmp_prefix="/tmp/dns_check_${safe_domain}_${TYPE}_$$"

    if [ -f "${tmp_prefix}_${IP}" ]; then
    if [ "$(cat "${tmp_prefix}_${IP}")" = "success" ]; then
    ((successful_queries++))
    if [ -f "${tmp_prefix}_${IP}.resolved" ]; then
    resolved=$(normalize_results "$(cat "${tmp_prefix}_${IP}.resolved")")
    [ -z "$first_success" ] && first_success="$resolved"

    # Track answer frequencies
    count="${answer_counts[$resolved]:-0}"
    ((count++))
    answer_counts[$resolved]=$count

    # Update consensus if this answer has higher count
    if [ "$count" -gt "$max_count" ]; then
    max_count=$count
    consensus_answer=$resolved
    fi

    if [ "$resolved" != "$first_success" ] && [ "$VERBOSE" != "1" ]; then
    printf " ⚠️ %s returned different result:\n └─ %s\n" "$LABEL" "$resolved"
    fi
    fi
    else
    ((failed_queries++))
    fi
    rm -f "${tmp_prefix}_${IP}" "${tmp_prefix}_${IP}.resolved"
    fi
    done

    end_time=$(get_time)
    duration=$(printf "%.0f" "$(echo "(${end_time} - ${script_start_time}) * 1000" | bc)")
    if [ "$VERBOSE" = "1" ]; then
    printf "\nDone! 🏁 🏎️ %s servers in %s ms\n" "$total_servers" "$duration"
    printf "Successful: %d, Failed: %d, Total: %d\n" "$successful_queries" "$failed_queries" "$total_servers"
    else
    printf "\n%d/%d succeeded in %dms" "$successful_queries" "$total_servers" "$duration"
    [ "$failed_queries" -gt 0 ] && printf " (%d failed)" "$failed_queries"
    echo
    fi

    # Function to print sorted answers
    print_sorted_answers() {
    local -n answers=$1
    local indent="$2"
    # Create array of "count answer" pairs
    local pairs=()
    for answer in "${!answers[@]}"; do
    pairs+=("${answers[$answer]} $answer")
    done
    # Sort numerically in reverse order
    printf '%s\n' "${pairs[@]}" | sort -rn | while read -r count answer; do
    local percent=$(( (count * 100) / total_servers ))
    printf "${indent}%d%% (%d servers): %s\n" "$percent" "$count" "$answer"
    done
    }

    # Before exit, show consensus and all answers
    if [ ! -z "$consensus_answer" ]; then
    if [ "$VERBOSE" = "1" ]; then
    printf "\nπŸ“Š DNS Results:\n"
    print_sorted_answers answer_counts " "
    else
    consensus_percent=$(( (max_count * 100) / total_servers ))
    printf "βœ… Winning Answer: %s (%d%%)\n" "$consensus_answer" "$consensus_percent"
    if [ "${#answer_counts[@]}" -gt 1 ]; then
    printf "πŸ“Š Other answers found:\n"
    print_sorted_answers answer_counts " "
    fi
    fi
    fi

    [ "$failed_queries" -gt 0 ] && exit 1 || exit 0

    68 changes: 68 additions & 0 deletions readme.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,68 @@
    ## Custom DNS Servers Configuration

    You can provide your own list of DNS servers by creating a file at `~/.dns-resolvers.txt`.

    ### Format

    Each line should contain an IP address followed by a label in parentheses:

    ```text
    1.1.1.1 (Cloudflare 1)
    8.8.8.8 (Google DNS)
    208.67.222.222 (OpenDNS)
    ```

    ### Rules

    - Lines starting with # are treated as comments
    - Empty lines are ignored
    - Whitespace at start/end of lines is trimmed
    - Each IP must be follow by a space and the label string: `IP_ADDRESS LABEL`

    ### Example File

    ```text
    # My preferred DNS servers
    1.1.1.1 (Cloudflare Primary)
    1.0.0.1 (Cloudflare Secondary)
    # Google DNS
    8.8.8.8 (Google Primary)
    8.8.4.4 (Google Secondary)
    # Assorted Global Resolvers
    1.1.1.1 (Cloudflare 1)
    1.0.0.1 (Cloudflare 2)
    8.8.8.8 (Google DNS 1)
    8.8.4.4 (Google DNS 2)
    94.140.14.14 (AdGuard 1)
    94.140.14.15 (AdGuard 2)
    85.214.28.183 (Digitalcourage - Germany 1)
    135.125.237.69 (OVH - Germany 2)
    91.26.116.18 (Deutsche Telekom AG - Germany 3)
    223.5.5.148 (AliDNS - China 1)
    223.5.5.0 (AliDNS - China 2)
    223.5.5.84 (AliDNS - China 3)
    89.234.93.210 (Digiweb - Ireland 1)
    80.93.18.196 (Digiweb - Ireland 2)
    158.43.192.1 (Verizon - London 1)
    195.21.13.234 (GTT - London 2)
    195.186.1.111 (Swisscom - Switzerland 1)
    81.7.255.3 (Swisscom - Switzerland 2)
    170.64.147.31 (OVH - France 1)
    54.37.30.59 (OVH - France 2)
    178.255.79.70 (Telecom Italia - Italy 1)
    197.155.92.20 (Liquid Telecommunications - Kenya)
    197.248.131.203 (Safaricom - Kenya 1)
    115.99.172.26 (BSNL - India 1)
    115.98.145.112 (Hathway - India 2)
    202.136.162.11 (M1 - Singapore 1)
    202.136.162.12 (M1 - Singapore 2)
    172.104.90.123 (Akamai - Japan 1)
    153.156.93.5 (NTT - Japan 2)
    210.163.158.224 (NTT - Japan 3)
    168.126.63.1 (KT - South Korea 1)
    168.126.63.2 (KT - South Korea 2)
    ```


  4. justsml created this gist Jan 25, 2025.
    140 changes: 140 additions & 0 deletions dns_global_check.sh
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,140 @@
    #!/usr/bin/env bash

    # Run global DNS queries for a domain, in parallel! πŸ¦„
    #
    # This script is useful for testing DNS propagation and performance.
    #
    # Setup
    # This script requires the 'dig' command to be installed.
    # On macOS, you can install dig with 'brew install bind'.
    # On Ubuntu/Debian, you can install dig with 'sudo apt-get install dnsutils'.
    #
    # Usage:
    # ./dns_global_check.sh <domain> [<record type>]
    #
    # Examples:
    # ./dns_global_check.sh example.com # defaults to A record
    # ./dns_global_check.sh example.com MX # queries MX records
    #
    # Author: Dan Levy / https://danlevy.net / @justsml
    #
    # License: Yolo 🫢
    #
    # Note: Public DNS resolvers are subject to frequent changes, blocks or rate-limits on foreign IPs.



    DOMAIN="$1"
    TYPE="$2"
    TIMEOUT="$3"
    MAX_CONCURRENT="$4"

    if [ -z "$DOMAIN" ]; then
    echo "Usage: $0 <domain> [<record type>] [<timeout>] [<max concurrent>]"
    echo "Example: $0 example.com"
    echo "Example: $0 example.com MX"
    echo "Example: $0 example.com TXT 20 10"
    exit 1
    fi

    # Default record type to 'A' if none provided
    if [ -z "$TYPE" ]; then
    TYPE="A"
    fi

    # Timeout in seconds for each DNS query
    TIMEOUT="${TIMEOUT:-8}"
    # Limit of concurrent lookups
    MAX_CONCURRENT=${MAX_CONCURRENT:-10}

    # List of DNS servers and labels (IP (Label))
    SERVERS=(
    "1.1.1.1 (Cloudflare 1)"
    "1.0.0.1 (Cloudflare 2)"
    "8.8.8.8 (Google DNS 1)"
    "8.8.4.4 (Google DNS 2)"
    "94.140.14.14 (AdGuard 1)"
    "94.140.14.15 (AdGuard 2)"
    "85.214.28.183 (Digitalcourage - Germany 1)"
    "135.125.237.69 (OVH - Germany 2)"
    "91.26.116.18 (Deutsche Telekom AG - Germany 3)"
    "223.5.5.148 (AliDNS - China 1)"
    "223.5.5.0 (AliDNS - China 2)"
    "223.5.5.84 (AliDNS - China 3)"
    "89.234.93.210 (Digiweb - Ireland 1)"
    "80.93.18.196 (Digiweb - Ireland 2)"
    "158.43.192.1 (Verizon - London 1)"
    "195.21.13.234 (GTT - London 2)"
    "195.186.1.111 (Swisscom - Switzerland 1)"
    "81.7.255.3 (Swisscom - Switzerland 2)"
    "170.64.147.31 (OVH - France 1)"
    "54.37.30.59 (OVH - France 2)"
    "178.255.79.70 (Telecom Italia - Italy 1)"
    "89.96.49.60 (Fastweb - Italy 2)"
    "197.155.92.20 (Liquid Telecommunications - Kenya)"
    "197.248.131.203 (Safaricom - Kenya 1)"
    "197.253.8.109 (Mainone - Nigeria 1)"
    "196.25.1.11 (Telkom SA - South Africa 1)"
    "196.25.1.9 (Telkom SA - South Africa 2)"
    "115.99.172.26 (BSNL - India 1)"
    "115.98.145.112 (Hathway - India 2)"
    "202.136.162.11 (M1 - Singapore 1)"
    "202.136.162.12 (M1 - Singapore 2)"
    "172.104.90.123 (Akamai - Japan 1)"
    "153.156.93.5 (NTT - Japan 2)"
    "210.163.158.224 (NTT - Japan 3)"
    "168.126.63.1 (KT - South Korea 1)"
    "168.126.63.2 (KT - South Korea 2)"
    )

    current_jobs=0
    total_servers=${#SERVERS[@]}

    script_start_time=$(date +%s%3N)

    # Function to perform a DNS lookup
    dns_query() {
    local server_ip="$1"
    local server_label="$2"
    local domain="$3"
    local record_type="$4"
    local timeout="$5"
    local start_time_ms
    local end_time_ms

    start_time_ms=$(date +%s%3N)
    if dig +short +time="$timeout" @"$server_ip" "$domain" "$record_type" >/dev/null 2>&1; then
    (( successful_queries++ ))
    end_time_ms=$(date +%s%3N)
    local duration_ms=$((end_time_ms - start_time_ms))
    printf " βœ… %s @ %s - %d ms " "$server_label" "$server_ip" "$duration_ms"
    else
    (( failed_queries++ ))
    end_time_ms=$(date +%s%3N)
    local duration_ms=$((end_time_ms - start_time_ms))
    printf " ❌ Error#: %s - %s - dig @%s %s %s - %d ms " "$?" "$server_label" "$server_ip" "$domain" "$record_type" "$duration_ms"
    fi
    echo
    }

    # Loop through each DNS server and run lookups in parallel
    for entry in "${SERVERS[@]}"; do
    IP=$(echo "$entry" | awk '{print $1}')
    LABEL=$(echo "$entry" | sed 's/^[^ ]* //')

    dns_query "$IP" "$LABEL" "$DOMAIN" "$TYPE" "$TIMEOUT" &

    ((current_jobs++))
    if [ "$current_jobs" -ge "$MAX_CONCURRENT" ]; then
    # Wait until at least one finishes before starting a new one
    wait -n
    ((current_jobs--))
    fi
    done

    # Wait for any remaining jobs to complete
    wait

    duration=$(( $(date +%s%3N) - script_start_time ))
    printf "πŸ•’ Total duration: %s ms\n" "$duration"
    echo "🏁 🏎️ Finished!"