From 86e9e1f8a16edf779267b508a1a4aa9ac6e4d395 Mon Sep 17 00:00:00 2001 From: Ben Sarmiento Date: Thu, 11 Jan 2024 01:26:43 +0100 Subject: [PATCH] ipv4 and ipv6 network test --- cmd/zurg/main.go | 7 ++- internal/commands.go | 4 +- pkg/realdebrid/network.go | 110 +++++++++++++++++++++++++++++++------- 3 files changed, 97 insertions(+), 24 deletions(-) diff --git a/cmd/zurg/main.go b/cmd/zurg/main.go index b13c87d..9208208 100644 --- a/cmd/zurg/main.go +++ b/cmd/zurg/main.go @@ -9,7 +9,8 @@ import ( ) func main() { - var configPath string // Variable to hold the config path + var configPath string // Variable to hold the config path + var netTestType string // Variable to hold the network test type var rootCmd = &cobra.Command{ Use: "zurg", @@ -33,9 +34,11 @@ func main() { Use: "network-test", Short: "Run a network test", Run: func(cmd *cobra.Command, args []string) { - internal.NetworkTest() + internal.NetworkTest(netTestType) // Pass the network test type to NetworkTest }, } + // Adding a flag for network test type with default value "both" + networkTestCmd.Flags().StringVarP(&netTestType, "type", "t", "both", "network test type (ipv4, ipv6, both)") var clearDownloadsCmd = &cobra.Command{ Use: "clear-downloads", diff --git a/internal/commands.go b/internal/commands.go index 5250df3..fc22675 100644 --- a/internal/commands.go +++ b/internal/commands.go @@ -16,8 +16,8 @@ func ShowVersion() { version.GetBuiltAt(), version.GetGitCommit(), version.GetVersion()) } -func NetworkTest() { - realdebrid.RunTest() +func NetworkTest(netTestType string) { + realdebrid.RunTest(netTestType) } func ClearDownloads() { diff --git a/pkg/realdebrid/network.go b/pkg/realdebrid/network.go index 725c0ca..2bf2fa5 100644 --- a/pkg/realdebrid/network.go +++ b/pkg/realdebrid/network.go @@ -1,7 +1,9 @@ package realdebrid import ( + "bufio" "fmt" + "net/http" "os/exec" "sort" "strconv" @@ -17,37 +19,105 @@ type IPInfo struct { } func traceroute(ip string) (int, time.Duration, error) { + // Try executing traceroute cmd := exec.Command("traceroute", "-n", "-q", "1", "-w", "1", ip) out, err := cmd.CombinedOutput() - if err != nil { - return 0, 0, err + + if err == nil { + // Traceroute executed successfully + output := string(out) + lines := strings.Split(output, "\n") + hopCount := len(lines) - 1 + var latency time.Duration + + if hopCount > 0 { + lastLine := lines[hopCount-1] + fmt.Print(".") + parts := strings.Fields(lastLine) + if len(parts) >= 3 { + latencyValue, parseErr := strconv.ParseFloat(parts[2], 64) + if parseErr == nil { + latency = time.Duration(latencyValue * float64(time.Millisecond)) + } + } + } + return hopCount, latency, nil } - output := string(out) - lines := strings.Split(output, "\n") + // Traceroute not successful, measure latency using ping + pingCmd := exec.Command("ping", "-c", "1", ip) + pingOut, pingErr := pingCmd.CombinedOutput() - hopCount := len(lines) - 1 + if pingErr != nil { + return 0, 0, pingErr + } - var latency time.Duration - if hopCount > 0 { - lastLine := lines[hopCount-1] - fmt.Printf("IP: %s, last hop: %s\n", ip, lastLine) - parts := strings.Fields(lastLine) - if len(parts) >= 3 { - latencyValue, parseErr := strconv.ParseFloat(parts[2], 64) - if parseErr == nil { - latency = time.Duration(latencyValue * float64(time.Millisecond)) + fmt.Print(".") + pingOutput := string(pingOut) + pingLines := strings.Split(pingOutput, "\n") + for _, line := range pingLines { + if strings.Contains(line, "time=") { + parts := strings.Split(line, " ") + for _, part := range parts { + if strings.Contains(part, "time=") { + timeStr := strings.Split(part, "=")[1] + timeStr = strings.TrimSuffix(timeStr, " ms") + latencyValue, parseErr := strconv.ParseFloat(timeStr, 64) + if parseErr == nil { + latency := time.Duration(latencyValue * float64(time.Millisecond)) + return -1, latency, nil + } + } } } } - return hopCount, latency, nil + return 0, 0, fmt.Errorf("failed to measure latency") } -func RunTest() { - fmt.Println("Running network test...") +func fetchIPs(url string) ([]string, error) { + resp, err := http.Get(url) + if err != nil { + return nil, err + } + defer resp.Body.Close() - ips := []string{"20.download.real-debrid.cloud", "20.download.real-debrid.com", "21.download.real-debrid.cloud", "21.download.real-debrid.com", "22.download.real-debrid.cloud", "22.download.real-debrid.com", "23.download.real-debrid.cloud", "23.download.real-debrid.com", "30.download.real-debrid.cloud", "30.download.real-debrid.com", "31.download.real-debrid.cloud", "31.download.real-debrid.com", "32.download.real-debrid.cloud", "32.download.real-debrid.com", "34.download.real-debrid.cloud", "34.download.real-debrid.com", "40.download.real-debrid.cloud", "40.download.real-debrid.com", "41.download.real-debrid.cloud", "41.download.real-debrid.com", "42.download.real-debrid.cloud", "42.download.real-debrid.com", "43.download.real-debrid.cloud", "43.download.real-debrid.com", "44.download.real-debrid.cloud", "44.download.real-debrid.com", "45.download.real-debrid.cloud", "45.download.real-debrid.com", "50.download.real-debrid.cloud", "50.download.real-debrid.com", "51.download.real-debrid.cloud", "51.download.real-debrid.com", "52.download.real-debrid.cloud", "52.download.real-debrid.com", "53.download.real-debrid.cloud", "53.download.real-debrid.com", "54.download.real-debrid.cloud", "54.download.real-debrid.com", "55.download.real-debrid.cloud", "55.download.real-debrid.com", "56.download.real-debrid.cloud", "56.download.real-debrid.com", "57.download.real-debrid.cloud", "57.download.real-debrid.com", "58.download.real-debrid.cloud", "58.download.real-debrid.com", "59.download.real-debrid.cloud", "59.download.real-debrid.com", "60.download.real-debrid.cloud", "60.download.real-debrid.com", "61.download.real-debrid.cloud", "61.download.real-debrid.com", "62.download.real-debrid.cloud", "62.download.real-debrid.com", "63.download.real-debrid.cloud", "63.download.real-debrid.com", "64.download.real-debrid.cloud", "64.download.real-debrid.com", "65.download.real-debrid.cloud", "65.download.real-debrid.com", "66.download.real-debrid.cloud", "66.download.real-debrid.com", "67.download.real-debrid.cloud", "67.download.real-debrid.com", "68.download.real-debrid.cloud", "68.download.real-debrid.com", "69.download.real-debrid.cloud", "69.download.real-debrid.com", "70.download.real-debrid.cloud", "70.download.real-debrid.com", "71.download.real-debrid.cloud", "71.download.real-debrid.com", "72.download.real-debrid.cloud", "72.download.real-debrid.com", "73.download.real-debrid.cloud", "73.download.real-debrid.com", "74.download.real-debrid.cloud", "74.download.real-debrid.com", "75.download.real-debrid.cloud", "75.download.real-debrid.com", "76.download.real-debrid.cloud", "76.download.real-debrid.com", "77.download.real-debrid.cloud", "77.download.real-debrid.com", "78.download.real-debrid.cloud", "78.download.real-debrid.com", "79.download.real-debrid.cloud", "79.download.real-debrid.com", "80.download.real-debrid.com", "ams1.download.real-debrid.com", "hkg1.download.real-debrid.com", "jkt1.download.real-debrid.com", "lax1.download.real-debrid.com", "lax2.download.real-debrid.com", "lax3.download.real-debrid.com", "lax4.download.real-debrid.com", "lax5.download.real-debrid.com", "lon1.download.real-debrid.com", "mum1.download.real-debrid.com", "sgp1.download.real-debrid.com", "tlv1.download.real-debrid.com", "tyo1.download.real-debrid.com"} + var ips []string + scanner := bufio.NewScanner(resp.Body) + for scanner.Scan() { + ips = append(ips, scanner.Text()) + } + if err := scanner.Err(); err != nil { + return nil, err + } + return ips, nil +} + +func RunTest(netTestType string) { + fmt.Print("Running network test...") + + ipv4URL := "https://gist.githubusercontent.com/yowmamasita/d0c1c7353500d0928cb5242484e8ed06/raw/ipv4.txt" + ipv6URL := "https://gist.githubusercontent.com/yowmamasita/d0c1c7353500d0928cb5242484e8ed06/raw/ipv6.txt" + + var ips []string + + if netTestType == "ipv4" || netTestType == "both" { + ipv4IPs, err := fetchIPs(ipv4URL) + if err != nil { + fmt.Println("Error fetching IPv4 IPs:", err) + return + } + ips = append(ips, ipv4IPs...) + } + + if netTestType == "ipv6" || netTestType == "both" { + ipv6IPs, err := fetchIPs(ipv6URL) + if err != nil { + fmt.Println("Error fetching IPv6 IPs:", err) + return + } + ips = append(ips, ipv6IPs...) + } var wg sync.WaitGroup infoChan := make(chan IPInfo, len(ips)) @@ -60,7 +130,7 @@ func RunTest() { defer wg.Done() hops, latency, err := traceroute(ip) if err != nil { - fmt.Println("Error performing traceroute:", err) + fmt.Printf("Error performing traceroute for %s:%s\n", ip, err) } else { infoChan <- IPInfo{Address: ip, Hops: hops, Latency: latency} } @@ -71,7 +141,7 @@ func RunTest() { wg.Wait() close(semaphore) close(infoChan) - fmt.Printf("Network test complete.\n\n") + fmt.Printf("complete!\n\n") var ipInfos []IPInfo for info := range infoChan {