package main

import (
	"bufio"
	"context"
	"fmt"
	"net"
	"os"
	"sort"
	"time"
)

var dnsServers = []string{
	"114.114.114.114",
	"119.29.29.29",
	"223.5.5.5",
	"223.6.6.6",
	"180.76.76.76",
	"101.226.4.6",
	"123.125.81.6",
	"101.226.4.6",
	"101.226.4.6",
	"112.124.47.27",
	"114.215.126.16",
	"8.8.8.8",
	"8.8.4.4",
	"208.67.222.222",
	"208.67.220.220",
	"101.101.101.101",
}

type DnsResult struct {
	Server string
	Time   float64
}

func resolve(domain string, dnsServer string, results chan<- DnsResult) {
	defer func() {
		if r := recover(); r != nil {
			fmt.Println("Recovered from panic in resolve:", r)
		}
	}()

	r := net.Resolver{
		PreferGo: true,
		Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
			d := net.Dialer{
				Timeout: time.Millisecond * 5000,
			}
			return d.DialContext(ctx, network, dnsServer+":53")
		},
	}

	minTime := float64(1<<63 - 1) // max float64 value
	for i := 0; i < 10; i++ {
		start := time.Now()
		_, err := r.LookupHost(context.Background(), domain)
		elapsed := time.Since(start)

		if err != nil {
			fmt.Printf("DNS resolution for %s at %s failed: %v\n", domain, dnsServer, err)
			return
		} else {
			ms := elapsed.Seconds() * 1000
			if ms < minTime {
				minTime = ms
			}
		}
	}

	results <- DnsResult{Server: dnsServer, Time: minTime}
}

func main() {
	if _, err := os.Stat("dns-servers.txt"); err == nil {
		file, err := os.Open("dns-servers.txt")
		if err != nil {
			fmt.Println("Error opening dns-servers.txt:", err)
			os.Exit(1)
		}
		defer file.Close()

		scanner := bufio.NewScanner(file)
		dnsServers = nil
		for scanner.Scan() {
			dnsServers = append(dnsServers, scanner.Text())
		}

		if err := scanner.Err(); err != nil {
			fmt.Println("Error reading dns-servers.txt:", err)
			os.Exit(1)
		}
	}

	seen := make(map[string]bool)
	var uniqueDnsServers []string
	for _, server := range dnsServers {
		if !seen[server] {
			seen[server] = true
			uniqueDnsServers = append(uniqueDnsServers, server)
		}
	}
	dnsServers = uniqueDnsServers

	for {
		fmt.Print("Enter a domain: ")
		var domain string
		fmt.Scanln(&domain)

		results := make(chan DnsResult, len(dnsServers))

		for _, dnsServer := range dnsServers {
			go resolve(domain, dnsServer, results)
		}

		var dnsResults []DnsResult
		for range dnsServers {
			dnsResults = append(dnsResults, <-results)
		}

		close(results)

		sort.Slice(dnsResults, func(i, j int) bool {
			return dnsResults[i].Time < dnsResults[j].Time
		})

		for _, result := range dnsResults {
			fmt.Printf("DNS resolution for %s at %s took %.2f ms\n", domain, result.Server, result.Time)
		}
	}
}
