Last active
January 27, 2025 09:07
-
-
Save justsml/3e114ff09d495547f45e3536cbd97c0f to your computer and use it in GitHub Desktop.
Revisions
-
justsml revised this gist
Jan 27, 2025 . 2 changed files with 19 additions and 16 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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 - 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 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=$(get_time) # Initialize counters successful_queries=0 failed_queries=0 # 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=$((end_time - start_time)) if [ $query_status -eq 0 ] && [ ! -z "$resolved_ips" ]; then if [ "$VERBOSE" = "1" ]; then 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=$((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. -
justsml revised this gist
Jan 26, 2025 . No changes.There are no files selected for viewing
-
justsml revised this gist
Jan 26, 2025 . 2 changed files with 299 additions and 69 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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 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="" # 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 [--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} # 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 } # 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 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 printf " β %s [%s] - %d ms\n" "$server_label" "$server_ip" "$duration_ms" echo "fail" > "${tmp_prefix}_${server_ip}" fi } # Loop through each DNS server and run lookups in parallel @@ -135,6 +208,95 @@ done # Wait for any remaining jobs to complete wait # 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 This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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) ``` -
justsml created this gist
Jan 25, 2025 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal 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!"