diff --git a/internal/app.go b/internal/app.go index 0f9c42f..8a7c5f3 100644 --- a/internal/app.go +++ b/internal/app.go @@ -57,8 +57,8 @@ func MainApp(configPath string) { proxyURL = os.Getenv("PROXY") } - repoClient4 := http.NewHTTPClient("", 0, 1, false, []string{}, proxyURL, nil, log.Named("network_test")) - repoClient6 := http.NewHTTPClient("", 0, 1, true, []string{}, proxyURL, nil, log.Named("network_test")) + repoClient4 := http.NewHTTPClient("", 0, 2, false, []string{}, proxyURL, nil, log.Named("network_test")) + repoClient6 := http.NewHTTPClient("", 0, 2, true, []string{}, proxyURL, nil, log.Named("network_test")) repo := http.NewIPRepository(repoClient4, repoClient6, "", log.Named("network_test")) var hosts []string diff --git a/pkg/http/client.go b/pkg/http/client.go index ee4b68a..d5a87e3 100644 --- a/pkg/http/client.go +++ b/pkg/http/client.go @@ -7,6 +7,7 @@ import ( "fmt" "io" "math" + "math/rand" "net" "net/http" "net/url" @@ -156,7 +157,7 @@ func (r *HTTPClient) Do(req *http.Request) (*http.Response, error) { } if len(r.hosts) > 0 { - r.ensureReachableHost(req) + r.ensureReachableDownloadServer(req) } if r.rateLimiter != nil { @@ -199,14 +200,14 @@ func (r *HTTPClient) Do(req *http.Request) (*http.Response, error) { return resp, err } -// ensureReachableHost ensures that the request is sent to a reachable host +// ensureReachableDownloadServer ensures that the request is sent to a reachable host // if not, it will replace the host with a reachable one -func (r *HTTPClient) ensureReachableHost(req *http.Request) { +func (r *HTTPClient) ensureReachableDownloadServer(req *http.Request) { // skip if not a download server if !strings.Contains(req.Host, ".download.real-debrid.") { return } - // skip CDN servers + // skip if CDN servers if req.Host[0] >= 'a' && req.Host[0] <= 'z' { return } @@ -221,19 +222,17 @@ func (r *HTTPClient) ensureReachableHost(req *http.Request) { } else if strings.HasSuffix(req.Host, ".cloud") { newHost = strings.Replace(req.Host, ".cloud", ".com", 1) } - // check if newHost is reachable if r.CheckIfHostIsReachable(newHost) { req.Host = newHost - req.URL.Host = req.Host - return + } else { + // just pick a random host + req.Host = r.hosts[rand.Intn(len(r.hosts))] } - // // just pick a random host - // req.Host = r.hosts[rand.Intn(len(r.hosts))] - // req.URL.Host = req.Host + req.URL.Host = req.Host // just retain the original host if not in the list of reachable hosts - r.log.Debugf("Host %s is not found on the list of reachable hosts", req.Host) + // r.log.Debugf("Host %s is not found on the list of reachable hosts", req.Host) } // CheckIfHostIsReachable checks if the given host is passed in the list of reachable hosts diff --git a/pkg/http/ip.go b/pkg/http/ip.go index eb6b72a..363c201 100644 --- a/pkg/http/ip.go +++ b/pkg/http/ip.go @@ -11,9 +11,12 @@ import ( "net/url" "os" "sort" + "strings" + "sync" "time" "github.com/debridmediamanager/zurg/pkg/logutil" + "github.com/panjf2000/ants/v2" ) type IPRepository struct { @@ -89,6 +92,7 @@ func (r *IPRepository) GetHosts(numberOfHosts int, ipv6 bool) []string { kvList = append(kvList, kv{k, v}) } + // Sort by latency from lowest to highest sort.Slice(kvList, func(i, j int) bool { return kvList[i].Value < kvList[j].Value }) @@ -105,98 +109,78 @@ func (r *IPRepository) GetHosts(numberOfHosts int, ipv6 bool) []string { } func (r *IPRepository) runLatencyTest() { - limit := 99 - start := 0 - for { - lastDomainsWorked := false - for i := start; i <= limit; i++ { - // EU servers - ipv4Domain := fmt.Sprintf("%d-4.download.real-debrid.com", i) - ipv6Domain := fmt.Sprintf("%d-6.download.real-debrid.com", i) + var wg sync.WaitGroup + ipv6Enabled := false + if r.canConnectToIPv6() { + r.log.Info("Your network supports IPv6") + ipv6Enabled = true + } else { + r.log.Info("Your network does not support IPv6") + } - ips, err := net.LookupIP(ipv4Domain) - if err != nil || len(ips) == 0 { - continue + p, _ := ants.NewPoolWithFunc(8, func(h interface{}) { + defer wg.Done() + host := h.(string) + + ips, err := net.LookupIP(host) + if err == nil && len(ips) > 0 { + latency, err := r.testDomainLatency(r.ipv4client, host) + if err == nil { + r.ipv4latencyMap[host] = latency + r.log.Debugf("Latency from ipv4 %s: %.5f seconds", host, latency) } - latency, err := r.testDomainLatency(r.ipv4client, ipv4Domain) - if err == nil { - r.ipv4latencyMap[ipv4Domain] = latency - r.log.Debugf("Latency from ipv4 %s: %.5f seconds", ipv4Domain, latency) - if i >= limit-2 { - lastDomainsWorked = true + if ipv6Enabled { + latency, err = r.testDomainLatency(r.ipv6client, host) + if err == nil { + r.ipv6latencyMap[host] = latency + r.log.Debugf("Latency from ipv6 %s: %.5f seconds", host, latency) } } - latency, err = r.testDomainLatency(r.ipv6client, ipv6Domain) - if err == nil { - r.ipv6latencyMap[ipv6Domain] = latency - r.log.Debugf("Latency from ipv6 %s: %.5f seconds", ipv6Domain, latency) - if i >= limit-2 { - lastDomainsWorked = true + if strings.HasSuffix(host, ".download.real-debrid.com") { + ipv4Host := strings.Replace(host, ".download.real-debrid.com", "-4.download.real-debrid.com", 1) + latency, err := r.testDomainLatency(r.ipv4client, ipv4Host) + if err == nil { + r.ipv4latencyMap[ipv4Host] = latency + r.log.Debugf("Latency from ipv4 %s: %.5f seconds", ipv4Host, latency) } - } - // EU servers (old) - ipv4Domain = fmt.Sprintf("%d.download.real-debrid.com", i) - ipv6Domain = fmt.Sprintf("%d.download.real-debrid.com", i) - - ips, err = net.LookupIP(ipv4Domain) - if err != nil || len(ips) == 0 { - continue - } - - latency, err = r.testDomainLatency(r.ipv4client, ipv4Domain) - if err == nil { - r.ipv4latencyMap[ipv4Domain] = latency - r.log.Debugf("Latency from ipv4 %s: %.5f seconds", ipv4Domain, latency) - if i >= limit-2 { - lastDomainsWorked = true - } - } - - latency, err = r.testDomainLatency(r.ipv6client, ipv6Domain) - if err == nil { - r.ipv6latencyMap[ipv6Domain] = latency - r.log.Debugf("Latency from ipv6 %s: %.5f seconds", ipv6Domain, latency) - if i >= limit-2 { - lastDomainsWorked = true - } - } - - // Cloudflare servers - ipv4Domain = fmt.Sprintf("%d.download.real-debrid.cloud", i) - ipv6Domain = fmt.Sprintf("%d.download.real-debrid.cloud", i) - - ips, err = net.LookupIP(ipv4Domain) - if err != nil || len(ips) == 0 { - continue - } - - latency, err = r.testDomainLatency(r.ipv4client, ipv4Domain) - if err == nil { - r.ipv4latencyMap[ipv4Domain] = latency - r.log.Debugf("Latency from ipv4 %s: %.5f seconds", ipv4Domain, latency) - if i >= limit-2 { - lastDomainsWorked = true - } - } - - latency, err = r.testDomainLatency(r.ipv6client, ipv6Domain) - if err == nil { - r.ipv6latencyMap[ipv6Domain] = latency - r.log.Debugf("Latency from ipv6 %s: %.5f seconds", ipv6Domain, latency) - if i >= limit-2 { - lastDomainsWorked = true + if ipv6Enabled { + ipv6Host := strings.Replace(host, ".download.real-debrid.com", "-6.download.real-debrid.com", 1) + latency, err = r.testDomainLatency(r.ipv6client, ipv6Host) + if err == nil { + r.ipv6latencyMap[ipv6Host] = latency + r.log.Debugf("Latency from ipv6 %s: %.5f seconds", ipv6Host, latency) + } } } } - if lastDomainsWorked { - start = limit + 1 - limit += 10 - } else { - break - } + }) + + defer p.Release() + + for i := 1; i <= 99; i++ { + wg.Add(2) + _ = p.Invoke(fmt.Sprintf("%d.download.real-debrid.com", i)) + _ = p.Invoke(fmt.Sprintf("%d.download.real-debrid.cloud", i)) + } + + // for i := 'a'; i <= 'z'; i++ { + // for j := 'a'; j <= 'z'; j++ { + // for k := 'a'; k <= 'z'; k++ { + // wg.Add(2) + // _ = p.Invoke(fmt.Sprintf("%c%c%c.download.real-debrid.com", i, j, k)) + // _ = p.Invoke(fmt.Sprintf("%c%c%c1.download.real-debrid.com", i, j, k)) + // } + // } + // } + + wg.Wait() + + r.log.Infof("Found %d ipv4 hosts", len(r.ipv4latencyMap)) + if ipv6Enabled { + r.log.Infof("Found %d ipv6 hosts", len(r.ipv6latencyMap)) } } @@ -285,3 +269,18 @@ func (r *IPRepository) writeLatencyFile(latencyFile string, data interface{}) { return } } + +func (r *IPRepository) canConnectToIPv6() bool { + address := "[2001:4860:4860::8888]:53" // Google Public DNS IPv6 address on port 53 + timeout := 5 * time.Second // Timeout duration + + conn, err := net.DialTimeout("tcp6", address, timeout) + if err != nil { + fmt.Printf("Failed to connect to IPv6 address %s: %v\n", address, err) + return false + } + defer conn.Close() + + fmt.Printf("Successfully connected to IPv6 address %s\n", address) + return true +}