Created
August 21, 2025 14:09
-
-
Save alamin-mahamud/b5c48400e6e1b7a8950001300db8fccd to your computer and use it in GitHub Desktop.
test-dns.sh
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 characters
| #!/bin/bash | |
| # | |
| # DNS Fallback Resolution Test Script with Error Handling | |
| # Handles communication errors and timeouts properly | |
| # | |
| # Configuration | |
| declare -A SAFETY_MODES | |
| SAFETY_MODES["LOW"]="203.190.10.114 203.190.10.115" | |
| SAFETY_MODES["MEDIUM"]="203.190.10.116 203.190.10.117" | |
| SAFETY_MODES["HIGH"]="203.190.10.118 203.190.10.119" | |
| # Test domains | |
| TEST_DOMAINS=( | |
| "google.com:legitimate" | |
| "github.com:legitimate" | |
| "wikipedia.org:legitimate" | |
| "bet365.com:gambling" | |
| "pornhub.com:adult" | |
| "thepiratebay.org:torrent" | |
| "malware-test.com:malicious" | |
| ) | |
| # Colors | |
| RED='\033[0;31m' | |
| GREEN='\033[0;32m' | |
| YELLOW='\033[1;33m' | |
| BLUE='\033[0;34m' | |
| PURPLE='\033[0;35m' | |
| CYAN='\033[0;36m' | |
| NC='\033[0m' | |
| # Statistics | |
| declare -A stats | |
| stats["total"]=0 | |
| stats["success"]=0 | |
| stats["blocked"]=0 | |
| stats["failed"]=0 | |
| stats["fallback_used"]=0 | |
| stats["comm_errors"]=0 | |
| stats["timeouts"]=0 | |
| # Error types | |
| ERROR_TIMEOUT="TIMEOUT" | |
| ERROR_NETWORK="NETWORK_ERROR" | |
| ERROR_REFUSED="CONNECTION_REFUSED" | |
| ERROR_UNREACHABLE="UNREACHABLE" | |
| # DNS query timeout (seconds) | |
| DNS_TIMEOUT=2 | |
| # Enhanced DNS resolution with detailed error handling | |
| resolve_dns_with_errors() { | |
| local dns_server=$1 | |
| local domain=$2 | |
| local timeout_val=${3:-$DNS_TIMEOUT} | |
| # Create a temporary file for error capture | |
| local error_file=$(mktemp) | |
| local result_file=$(mktemp) | |
| # Run dig with timeout and capture both stdout and stderr | |
| timeout $timeout_val dig @"$dns_server" "$domain" +short +tries=1 +time=$timeout_val 2>"$error_file" >"$result_file" | |
| local exit_code=$? | |
| local result=$(head -1 "$result_file" 2>/dev/null) | |
| local error_msg=$(cat "$error_file" 2>/dev/null) | |
| # Clean up temp files | |
| rm -f "$error_file" "$result_file" | |
| # Analyze the exit code and error message | |
| local status="" | |
| local error_type="" | |
| if [[ $exit_code -eq 124 ]]; then | |
| # Timeout command exit code | |
| status="ERROR" | |
| error_type="$ERROR_TIMEOUT" | |
| elif [[ $exit_code -ne 0 ]]; then | |
| # Other errors | |
| if [[ "$error_msg" =~ "connection refused" ]]; then | |
| status="ERROR" | |
| error_type="$ERROR_REFUSED" | |
| elif [[ "$error_msg" =~ "network unreachable" ]] || [[ "$error_msg" =~ "host unreachable" ]]; then | |
| status="ERROR" | |
| error_type="$ERROR_UNREACHABLE" | |
| elif [[ "$error_msg" =~ "communications error" ]]; then | |
| status="ERROR" | |
| error_type="$ERROR_NETWORK" | |
| else | |
| status="ERROR" | |
| error_type="UNKNOWN_ERROR" | |
| fi | |
| elif [[ -z "$result" ]]; then | |
| # Empty result could mean blocked or NXDOMAIN | |
| # Try to check if it's a valid empty response vs error | |
| if timeout $timeout_val dig @"$dns_server" "$domain" +short +tries=1 | grep -q "NXDOMAIN"; then | |
| status="NXDOMAIN" | |
| result="NXDOMAIN" | |
| else | |
| status="BLOCKED" | |
| result="BLOCKED" | |
| fi | |
| else | |
| # Success | |
| status="SUCCESS" | |
| fi | |
| echo "${status}|${result}|${error_type}" | |
| } | |
| # DNS resolution with fallback and error handling | |
| resolve_with_fallback() { | |
| local mode=$1 | |
| local domain=$2 | |
| local ips=(${SAFETY_MODES[$mode]}) | |
| local result="" | |
| local used_ip="" | |
| local fallback_used=0 | |
| local error_details="" | |
| local all_errors="" | |
| for i in "${!ips[@]}"; do | |
| local ip="${ips[$i]}" | |
| local dns_response=$(resolve_dns_with_errors "$ip" "$domain") | |
| IFS='|' read -r status result error_type <<< "$dns_response" | |
| if [[ "$status" == "SUCCESS" ]]; then | |
| used_ip=$ip | |
| if [[ $i -gt 0 ]]; then | |
| fallback_used=1 | |
| ((stats["fallback_used"]++)) | |
| fi | |
| break | |
| elif [[ "$status" == "BLOCKED" ]] || [[ "$status" == "NXDOMAIN" ]]; then | |
| used_ip=$ip | |
| break | |
| else | |
| # Communication error - try fallback | |
| all_errors="${all_errors}${ip}:${error_type} " | |
| ((stats["comm_errors"]++)) | |
| if [[ "$error_type" == "$ERROR_TIMEOUT" ]]; then | |
| ((stats["timeouts"]++)) | |
| fi | |
| # Log the error | |
| echo -e "${CYAN}[DEBUG]${NC} ${ip} failed: ${error_type}, trying fallback..." >&2 | |
| fi | |
| done | |
| if [[ -z "$used_ip" ]]; then | |
| result="FAILED" | |
| used_ip="all_failed" | |
| error_details="$all_errors" | |
| fi | |
| echo "$result|$used_ip|$fallback_used|$error_details" | |
| } | |
| # Print test result with error details | |
| print_result() { | |
| local test_num=$1 | |
| local mode=$2 | |
| local domain=$3 | |
| local category=$4 | |
| local result=$5 | |
| local used_ip=$6 | |
| local fallback=$7 | |
| local error_details=$8 | |
| printf "[Test %3d] Mode: %-6s Domain: %-25s " "$test_num" "$mode" "$domain" | |
| if [[ "$result" == "FAILED" ]]; then | |
| echo -e "${RED}FAILED${NC} - All IPs failed" | |
| if [[ -n "$error_details" ]]; then | |
| echo -e " ${CYAN}Error details: ${error_details}${NC}" | |
| fi | |
| ((stats["failed"]++)) | |
| elif [[ "$result" == "BLOCKED" ]]; then | |
| echo -e "${YELLOW}BLOCKED${NC} by $used_ip" | |
| ((stats["blocked"]++)) | |
| elif [[ "$result" == "NXDOMAIN" ]]; then | |
| echo -e "${YELLOW}NXDOMAIN${NC} from $used_ip" | |
| ((stats["failed"]++)) | |
| else | |
| if [[ $fallback -eq 1 ]]; then | |
| echo -e "${GREEN}RESOLVED${NC} via ${YELLOW}fallback${NC} $used_ip → $result" | |
| else | |
| echo -e "${GREEN}RESOLVED${NC} via $used_ip → $result" | |
| fi | |
| ((stats["success"]++)) | |
| fi | |
| ((stats["total"]++)) | |
| } | |
| # Health check function | |
| health_check() { | |
| echo -e "\n${BLUE}=== DNS Server Health Check ===${NC}" | |
| for mode in "LOW" "MEDIUM" "HIGH"; do | |
| echo -e "\n${YELLOW}$mode Mode Servers:${NC}" | |
| local ips=(${SAFETY_MODES[$mode]}) | |
| for ip in "${ips[@]}"; do | |
| echo -n " $ip: " | |
| # Quick health check with 1 second timeout | |
| local response=$(resolve_dns_with_errors "$ip" "google.com" 1) | |
| IFS='|' read -r status result error_type <<< "$response" | |
| if [[ "$status" == "SUCCESS" ]]; then | |
| echo -e "${GREEN}HEALTHY${NC} (Response: $result)" | |
| elif [[ "$status" == "ERROR" ]]; then | |
| echo -e "${RED}UNHEALTHY${NC} ($error_type)" | |
| else | |
| echo -e "${YELLOW}UNKNOWN${NC}" | |
| fi | |
| done | |
| done | |
| echo "" | |
| } | |
| # Continuous monitoring mode | |
| monitor_mode() { | |
| echo -e "${PURPLE}=== Continuous DNS Monitoring ===${NC}" | |
| echo "Press Ctrl+C to stop" | |
| echo "" | |
| local test_count=0 | |
| while true; do | |
| clear | |
| echo -e "${PURPLE}DNS Monitor - $(date)${NC}" | |
| echo "========================================" | |
| # Show server status | |
| for mode in "LOW" "MEDIUM" "HIGH"; do | |
| local ips=(${SAFETY_MODES[$mode]}) | |
| echo -e "\n${YELLOW}$mode Mode:${NC}" | |
| for domain in "google.com" "bet365.com"; do | |
| local result_data=$(resolve_with_fallback "$mode" "$domain") | |
| IFS='|' read -r result used_ip fallback error_details <<< "$result_data" | |
| if [[ "$result" == "FAILED" ]]; then | |
| echo -e " $domain: ${RED}✗ FAILED${NC} - $error_details" | |
| elif [[ "$result" == "BLOCKED" ]]; then | |
| echo -e " $domain: ${YELLOW}⚠ BLOCKED${NC}" | |
| else | |
| local fallback_indicator="" | |
| [[ $fallback -eq 1 ]] && fallback_indicator="${YELLOW}[F]${NC}" | |
| echo -e " $domain: ${GREEN}✓${NC} $result via $used_ip $fallback_indicator" | |
| fi | |
| done | |
| done | |
| # Statistics | |
| echo -e "\n${BLUE}Statistics:${NC}" | |
| echo "Total Queries: ${stats["total"]}" | |
| echo "Communication Errors: ${stats["comm_errors"]} (Timeouts: ${stats["timeouts"]})" | |
| echo "Fallbacks Used: ${stats["fallback_used"]}" | |
| #sleep 5 | |
| ((test_count++)) | |
| done | |
| } | |
| # Run single test | |
| run_test() { | |
| local test_num=$1 | |
| local mode=$2 | |
| local domain=$3 | |
| local category=$4 | |
| local result_data=$(resolve_with_fallback "$mode" "$domain") | |
| IFS='|' read -r result used_ip fallback error_details <<< "$result_data" | |
| print_result "$test_num" "$mode" "$domain" "$category" "$result" "$used_ip" "$fallback" "$error_details" | |
| } | |
| # Main test loop | |
| run_tests() { | |
| local test_count=0 | |
| local target_tests=100 | |
| echo -e "${PURPLE}Running $target_tests DNS tests with fallback...${NC}\n" | |
| while [[ $test_count -lt $target_tests ]]; do | |
| for mode in "LOW" "MEDIUM" "HIGH"; do | |
| # Pick a random domain | |
| local domain_entry=${TEST_DOMAINS[$RANDOM % ${#TEST_DOMAINS[@]}]} | |
| IFS=':' read -r domain category <<< "$domain_entry" | |
| ((test_count++)) | |
| run_test "$test_count" "$mode" "$domain" "$category" | |
| [[ $test_count -ge $target_tests ]] && break 2 | |
| # Small delay between tests | |
| # sleep 0.1 | |
| done | |
| done | |
| } | |
| # Stress test mode | |
| stress_test() { | |
| echo -e "${RED}=== Stress Test Mode ===${NC}" | |
| echo "This will simulate various failure conditions" | |
| echo "" | |
| # Test 1: Unreachable IP | |
| echo -e "${YELLOW}Test 1: Unreachable IP${NC}" | |
| local bad_ip="192.168.255.255" | |
| echo "Testing with unreachable IP: $bad_ip" | |
| local response=$(resolve_dns_with_errors "$bad_ip" "google.com" 2) | |
| IFS='|' read -r status result error_type <<< "$response" | |
| echo "Result: Status=$status, Error=$error_type" | |
| echo "" | |
| # Test 2: All servers down simulation | |
| echo -e "${YELLOW}Test 2: All servers down${NC}" | |
| local saved_low="${SAFETY_MODES["LOW"]}" | |
| SAFETY_MODES["LOW"]="192.168.255.255 192.168.255.254" | |
| local result_data=$(resolve_with_fallback "LOW" "google.com") | |
| IFS='|' read -r result used_ip fallback error_details <<< "$result_data" | |
| echo "Result: $result" | |
| echo "Errors: $error_details" | |
| SAFETY_MODES["LOW"]="$saved_low" | |
| } | |
| # Print statistics | |
| print_stats() { | |
| echo -e "\n${PURPLE}========================================" | |
| echo "Test Statistics" | |
| echo "========================================${NC}" | |
| echo "Total Tests: ${stats["total"]}" | |
| echo -e "${GREEN}Successful: ${stats["success"]}${NC}" | |
| echo -e "${YELLOW}Blocked: ${stats["blocked"]}${NC}" | |
| echo -e "${RED}Failed: ${stats["failed"]}${NC}" | |
| echo "Fallback Used: ${stats["fallback_used"]}" | |
| echo -e "${CYAN}Communication Errors: ${stats["comm_errors"]}${NC}" | |
| echo " - Timeouts: ${stats["timeouts"]}" | |
| if [[ ${stats["total"]} -gt 0 ]]; then | |
| local success_rate=$(( (stats["success"] * 100) / stats["total"] )) | |
| local fallback_rate=$(( (stats["fallback_used"] * 100) / stats["total"] )) | |
| local error_rate=$(( (stats["comm_errors"] * 100) / stats["total"] )) | |
| echo "" | |
| echo "Success Rate: ${success_rate}%" | |
| echo "Fallback Rate: ${fallback_rate}%" | |
| echo "Error Rate: ${error_rate}%" | |
| fi | |
| } | |
| # Main function | |
| main() { | |
| case "${1:-}" in | |
| "health") | |
| health_check | |
| ;; | |
| "monitor") | |
| monitor_mode | |
| ;; | |
| "stress") | |
| stress_test | |
| ;; | |
| "quick") | |
| health_check | |
| echo -e "${BLUE}Running quick test...${NC}" | |
| for i in {1..10}; do | |
| run_test "$i" "LOW" "google.com" "legitimate" | |
| done | |
| print_stats | |
| ;; | |
| *) | |
| health_check | |
| run_tests | |
| print_stats | |
| ;; | |
| esac | |
| } | |
| # Check dependencies | |
| if ! command -v dig >/dev/null 2>&1; then | |
| echo "${RED}Error: 'dig' command not found. Please install dnsutils/bind-utils.${NC}" | |
| exit 1 | |
| fi | |
| # Run main | |
| main "$@" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment