Compare commits
	
		
			7 Commits
		
	
	
		
			7f736db2cc
			...
			410cc7874d
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 410cc7874d | |||
| 64101b1dfd | |||
| 7652472953 | |||
| 463a0a6cd5 | |||
| df3c6c6a0e | |||
| 0fe71520c5 | |||
| 93e478755e | 
							
								
								
									
										9
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										9
									
								
								Makefile
									
									
									
									
									
								
							@ -5,3 +5,12 @@ build:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
run:
 | 
					run:
 | 
				
			||||||
	sudo go run ./cmd/mullvadclosest/*.go
 | 
						sudo go run ./cmd/mullvadclosest/*.go
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					test:
 | 
				
			||||||
 | 
						go test -v ./...
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fmt:
 | 
				
			||||||
 | 
						go fmt ./...
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					lint:
 | 
				
			||||||
 | 
						golangci-lint run
 | 
				
			||||||
 | 
				
			|||||||
@ -2,8 +2,9 @@ package main
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"encoding/json"
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"flag"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"io/ioutil"
 | 
						"io"
 | 
				
			||||||
	"math"
 | 
						"math"
 | 
				
			||||||
	"math/rand"
 | 
						"math/rand"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
@ -15,6 +16,9 @@ import (
 | 
				
			|||||||
	"github.com/schollz/progressbar/v3"
 | 
						"github.com/schollz/progressbar/v3"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Package-level variable for debug flag
 | 
				
			||||||
 | 
					var debugMode bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// RelayLatency holds a relay and its associated latency
 | 
					// RelayLatency holds a relay and its associated latency
 | 
				
			||||||
type RelayLatency struct {
 | 
					type RelayLatency struct {
 | 
				
			||||||
	Relay   Relay
 | 
						Relay   Relay
 | 
				
			||||||
@ -23,15 +27,15 @@ type RelayLatency struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// MullvadIPResponse represents the response from the Mullvad IP check API
 | 
					// MullvadIPResponse represents the response from the Mullvad IP check API
 | 
				
			||||||
type MullvadIPResponse struct {
 | 
					type MullvadIPResponse struct {
 | 
				
			||||||
	IP                   string `json:"ip"`
 | 
						IP                    string  `json:"ip"`
 | 
				
			||||||
	Country              string `json:"country"`
 | 
						Country               string  `json:"country"`
 | 
				
			||||||
	City                 string `json:"city"`
 | 
						City                  string  `json:"city"`
 | 
				
			||||||
	Longitude            float64 `json:"longitude"`
 | 
						Longitude             float64 `json:"longitude"`
 | 
				
			||||||
	Latitude             float64 `json:"latitude"`
 | 
						Latitude              float64 `json:"latitude"`
 | 
				
			||||||
	MullvadExitIP        bool   `json:"mullvad_exit_ip"`
 | 
						MullvadExitIP         bool    `json:"mullvad_exit_ip"`
 | 
				
			||||||
	MullvadExitIPHostname string `json:"mullvad_exit_ip_hostname,omitempty"`
 | 
						MullvadExitIPHostname string  `json:"mullvad_exit_ip_hostname,omitempty"`
 | 
				
			||||||
	MullvadServerType    string `json:"mullvad_server_type,omitempty"`
 | 
						MullvadServerType     string  `json:"mullvad_server_type,omitempty"`
 | 
				
			||||||
	Blacklisted          struct {
 | 
						Blacklisted           struct {
 | 
				
			||||||
		Blacklisted bool     `json:"blacklisted"`
 | 
							Blacklisted bool     `json:"blacklisted"`
 | 
				
			||||||
		Results     []string `json:"results"`
 | 
							Results     []string `json:"results"`
 | 
				
			||||||
	} `json:"blacklisted"`
 | 
						} `json:"blacklisted"`
 | 
				
			||||||
@ -46,7 +50,7 @@ func livenessWorker(id int, jobs <-chan Relay, results chan<- RelayLatency, wg *
 | 
				
			|||||||
		if err == nil && isLive {
 | 
							if err == nil && isLive {
 | 
				
			||||||
			results <- RelayLatency{Relay: relay, Latency: latency}
 | 
								results <- RelayLatency{Relay: relay, Latency: latency}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		bar.Add(1)
 | 
							_ = bar.Add(1)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -60,8 +64,10 @@ func latencyWorker(id int, jobs <-chan RelayLatency, results chan<- RelayLatency
 | 
				
			|||||||
				Relay:   relayLatency.Relay,
 | 
									Relay:   relayLatency.Relay,
 | 
				
			||||||
				Latency: latency,
 | 
									Latency: latency,
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
							} else if debugMode {
 | 
				
			||||||
 | 
								fmt.Printf("Debug: Error measuring latency for %s: %v\n", relayLatency.Relay.Hostname, err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		bar.Add(1)
 | 
							_ = bar.Add(1)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -180,13 +186,14 @@ func PrintRelayLatencies(relayLatencies []RelayLatency, totalRelays int, deadRel
 | 
				
			|||||||
		fmt.Println()
 | 
							fmt.Println()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fmt.Printf("%-20s %-20s %-30s %s\n", "Country", "City", "Hostname", "Latency")
 | 
						fmt.Printf("%-20s %-20s %-30s %-15s %s\n", "Country", "City", "Hostname", "IPv4 Address", "Latency")
 | 
				
			||||||
	for i, rl := range relayLatencies {
 | 
						for i, rl := range relayLatencies {
 | 
				
			||||||
		if i < 25 || i >= len(relayLatencies)-4 {
 | 
							if i < 25 || i >= len(relayLatencies)-4 {
 | 
				
			||||||
			fmt.Printf("%-20s %-20s %-30s %v\n",
 | 
								fmt.Printf("%-20s %-20s %-30s %-15s %v\n",
 | 
				
			||||||
				rl.Relay.Location.Country,
 | 
									rl.Relay.Location.Country,
 | 
				
			||||||
				rl.Relay.Location.City,
 | 
									rl.Relay.Location.City,
 | 
				
			||||||
				rl.Relay.Hostname,
 | 
									rl.Relay.Hostname,
 | 
				
			||||||
 | 
									rl.Relay.Ipv4AddrIn,
 | 
				
			||||||
				rl.Latency)
 | 
									rl.Latency)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if i == 24 {
 | 
							if i == 24 {
 | 
				
			||||||
@ -203,7 +210,7 @@ func CheckMullvadExitIP() (bool, error) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	defer resp.Body.Close()
 | 
						defer resp.Body.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	body, err := ioutil.ReadAll(resp.Body)
 | 
						body, err := io.ReadAll(resp.Body)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return false, fmt.Errorf("failed to read response body: %v", err)
 | 
							return false, fmt.Errorf("failed to read response body: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -217,6 +224,14 @@ func CheckMullvadExitIP() (bool, error) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func main() {
 | 
					func main() {
 | 
				
			||||||
 | 
						// Define force flag
 | 
				
			||||||
 | 
						force := flag.Bool("force", false, "Force execution even when connected to VPN")
 | 
				
			||||||
 | 
						debug := flag.Bool("debug", false, "Enable debug output")
 | 
				
			||||||
 | 
						flag.Parse()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Set the global debug mode
 | 
				
			||||||
 | 
						debugMode = *debug
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	currentUser, err := user.Current()
 | 
						currentUser, err := user.Current()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		panic("Failed to get current user")
 | 
							panic("Failed to get current user")
 | 
				
			||||||
@ -231,8 +246,9 @@ func main() {
 | 
				
			|||||||
		panic(fmt.Sprintf("Error checking Mullvad exit IP: %v", err))
 | 
							panic(fmt.Sprintf("Error checking Mullvad exit IP: %v", err))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if isMullvadExitIP {
 | 
						if isMullvadExitIP && !*force {
 | 
				
			||||||
		fmt.Println("This program is designed to test latency between your actual IP and the Mullvad VPN servers. Please disconnect from the VPN and run the program again.")
 | 
							fmt.Println("This program is designed to test latency between your actual IP and the Mullvad VPN servers. Please disconnect from the VPN and run the program again.")
 | 
				
			||||||
 | 
							fmt.Println("Use -force flag to bypass this check.")
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -3,7 +3,6 @@ package main
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"encoding/json"
 | 
						"encoding/json"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"io/ioutil"
 | 
					 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -46,11 +45,21 @@ func (r Relay) CheckLiveness() (bool, time.Duration, error) {
 | 
				
			|||||||
	pinger.Count = 1
 | 
						pinger.Count = 1
 | 
				
			||||||
	pinger.Timeout = 3 * time.Second // Increased timeout for the single ping
 | 
						pinger.Timeout = 3 * time.Second // Increased timeout for the single ping
 | 
				
			||||||
	pinger.SetPrivileged(true)
 | 
						pinger.SetPrivileged(true)
 | 
				
			||||||
	pinger.Run()
 | 
						if err := pinger.Run(); err != nil {
 | 
				
			||||||
 | 
							return false, 0, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	stats := pinger.Statistics()
 | 
						stats := pinger.Statistics()
 | 
				
			||||||
	if stats.PacketsRecv == 0 {
 | 
						if stats.PacketsRecv == 0 {
 | 
				
			||||||
		return false, 0, nil
 | 
							return false, 0, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Validate that we don't have a zero latency for a successful ping
 | 
				
			||||||
 | 
						if stats.AvgRtt == 0 {
 | 
				
			||||||
 | 
							// Return as live but with no initial latency measurement
 | 
				
			||||||
 | 
							// The actual latency will be measured in MeasureLatency
 | 
				
			||||||
 | 
							return true, time.Duration(1), nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return true, stats.AvgRtt, nil
 | 
						return true, stats.AvgRtt, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -64,8 +73,28 @@ func (r Relay) MeasureLatency() (time.Duration, error) {
 | 
				
			|||||||
	pinger.Interval = 1 * time.Second // Adding interval between pings
 | 
						pinger.Interval = 1 * time.Second // Adding interval between pings
 | 
				
			||||||
	pinger.Timeout = 10 * time.Second // Increased overall timeout
 | 
						pinger.Timeout = 10 * time.Second // Increased overall timeout
 | 
				
			||||||
	pinger.SetPrivileged(true)
 | 
						pinger.SetPrivileged(true)
 | 
				
			||||||
	pinger.Run()
 | 
						if err := pinger.Run(); err != nil {
 | 
				
			||||||
 | 
							return 0, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	stats := pinger.Statistics()
 | 
						stats := pinger.Statistics()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Debug output
 | 
				
			||||||
 | 
						if debugMode && (stats.MinRtt == 0 || stats.AvgRtt == 0) {
 | 
				
			||||||
 | 
							fmt.Printf("Debug: %s (%s) - PacketsSent: %d, PacketsRecv: %d, MinRtt: %v, AvgRtt: %v, MaxRtt: %v\n",
 | 
				
			||||||
 | 
								r.Hostname, r.Ipv4AddrIn, stats.PacketsSent, stats.PacketsRecv,
 | 
				
			||||||
 | 
								stats.MinRtt, stats.AvgRtt, stats.MaxRtt)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Validate the statistics - if MinRtt is 0 but we received packets, something is wrong
 | 
				
			||||||
 | 
						if stats.PacketsRecv > 0 && stats.MinRtt == 0 {
 | 
				
			||||||
 | 
							// Use average RTT if available and non-zero
 | 
				
			||||||
 | 
							if stats.AvgRtt > 0 {
 | 
				
			||||||
 | 
								return stats.AvgRtt, nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							// If all RTT values are 0, this is likely a bug - return an error
 | 
				
			||||||
 | 
							return 0, fmt.Errorf("invalid zero latency for relay %s", r.Hostname)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return stats.MinRtt, nil
 | 
						return stats.MinRtt, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -110,7 +139,7 @@ func ParseRelayData() ([]Relay, error) {
 | 
				
			|||||||
			return nil, fmt.Errorf("failed to read embedded file: %v", err)
 | 
								return nil, fmt.Errorf("failed to read embedded file: %v", err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		fileData, err = ioutil.ReadFile(filePath)
 | 
							fileData, err = os.ReadFile(filePath)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, fmt.Errorf("failed to read file: %v", err)
 | 
								return nil, fmt.Errorf("failed to read file: %v", err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										4
									
								
								embed.go
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								embed.go
									
									
									
									
									
								
							@ -4,7 +4,7 @@ import (
 | 
				
			|||||||
	"embed"
 | 
						"embed"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//go:embed relays.2024-06-04.json
 | 
					//go:embed relays.2025-07-14.json
 | 
				
			||||||
var fs embed.FS
 | 
					var fs embed.FS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func GetEmbeddedFile(name string) ([]byte, error) {
 | 
					func GetEmbeddedFile(name string) ([]byte, error) {
 | 
				
			||||||
@ -12,5 +12,5 @@ func GetEmbeddedFile(name string) ([]byte, error) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func GetRelays() ([]byte, error) {
 | 
					func GetRelays() ([]byte, error) {
 | 
				
			||||||
	return GetEmbeddedFile("relays.2024-06-04.json")
 | 
						return GetEmbeddedFile("relays.2025-07-14.json")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										17
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								go.mod
									
									
									
									
									
								
							@ -1,15 +1,20 @@
 | 
				
			|||||||
module sneak.berlin/go/mullvadclosest
 | 
					module sneak.berlin/go/mullvadclosest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
go 1.22.3
 | 
					go 1.23.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					toolchain go1.24.4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					require (
 | 
				
			||||||
 | 
						github.com/go-ping/ping v1.1.0
 | 
				
			||||||
 | 
						github.com/schollz/progressbar/v3 v3.14.3
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
require (
 | 
					require (
 | 
				
			||||||
	github.com/go-ping/ping v1.1.0 // indirect
 | 
					 | 
				
			||||||
	github.com/google/uuid v1.2.0 // indirect
 | 
						github.com/google/uuid v1.2.0 // indirect
 | 
				
			||||||
	github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
 | 
						github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
 | 
				
			||||||
	github.com/rivo/uniseg v0.4.7 // indirect
 | 
						github.com/rivo/uniseg v0.4.7 // indirect
 | 
				
			||||||
	github.com/schollz/progressbar/v3 v3.14.3 // indirect
 | 
						golang.org/x/net v0.42.0 // indirect
 | 
				
			||||||
	golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4 // indirect
 | 
					 | 
				
			||||||
	golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
 | 
						golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
 | 
				
			||||||
	golang.org/x/sys v0.20.0 // indirect
 | 
						golang.org/x/sys v0.34.0 // indirect
 | 
				
			||||||
	golang.org/x/term v0.20.0 // indirect
 | 
						golang.org/x/term v0.33.0 // indirect
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										10
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								go.sum
									
									
									
									
									
								
							@ -1,4 +1,5 @@
 | 
				
			|||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
					github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
				
			||||||
 | 
					github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 | 
				
			||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
					github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
				
			||||||
github.com/go-ping/ping v1.1.0 h1:3MCGhVX4fyEUuhsfwPrsEdQw6xspHkv5zHsiSoDFZYw=
 | 
					github.com/go-ping/ping v1.1.0 h1:3MCGhVX4fyEUuhsfwPrsEdQw6xspHkv5zHsiSoDFZYw=
 | 
				
			||||||
github.com/go-ping/ping v1.1.0/go.mod h1:xIFjORFzTxqIV/tDVGO4eDy/bLuSyawEeojSm3GfRGk=
 | 
					github.com/go-ping/ping v1.1.0/go.mod h1:xIFjORFzTxqIV/tDVGO4eDy/bLuSyawEeojSm3GfRGk=
 | 
				
			||||||
@ -8,25 +9,32 @@ github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1
 | 
				
			|||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
 | 
					github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
 | 
				
			||||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
 | 
					github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
 | 
				
			||||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
 | 
					github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
 | 
				
			||||||
 | 
					github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 | 
				
			||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
					github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
				
			||||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
 | 
					github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
 | 
				
			||||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
 | 
					github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
 | 
				
			||||||
github.com/schollz/progressbar/v3 v3.14.3 h1:oOuWW19ka12wxYU1XblR4n16wF/2Y1dBLMarMo6p4xU=
 | 
					github.com/schollz/progressbar/v3 v3.14.3 h1:oOuWW19ka12wxYU1XblR4n16wF/2Y1dBLMarMo6p4xU=
 | 
				
			||||||
github.com/schollz/progressbar/v3 v3.14.3/go.mod h1:aT3UQ7yGm+2ZjeXPqsjTenwL3ddUiuZ0kfQ/2tHlyNI=
 | 
					github.com/schollz/progressbar/v3 v3.14.3/go.mod h1:aT3UQ7yGm+2ZjeXPqsjTenwL3ddUiuZ0kfQ/2tHlyNI=
 | 
				
			||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 | 
					github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 | 
				
			||||||
 | 
					github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
 | 
				
			||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 | 
					github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 | 
				
			||||||
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4 h1:b0LrWgu8+q7z4J+0Y3Umo5q1dL7NXBkKBWkaVkAq17E=
 | 
					golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4 h1:b0LrWgu8+q7z4J+0Y3Umo5q1dL7NXBkKBWkaVkAq17E=
 | 
				
			||||||
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
 | 
					golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
 | 
				
			||||||
 | 
					golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
 | 
				
			||||||
 | 
					golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
 | 
				
			||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
 | 
					golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
 | 
				
			||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005 h1:pDMpM2zh2MT0kHy037cKlSby2nEhD50SYqwQk76Nm40=
 | 
					 | 
				
			||||||
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
					golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
				
			||||||
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
 | 
					golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
 | 
				
			||||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 | 
					golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 | 
				
			||||||
 | 
					golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
 | 
				
			||||||
 | 
					golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
 | 
				
			||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 | 
					golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 | 
				
			||||||
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
 | 
					golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
 | 
				
			||||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
 | 
					golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
 | 
				
			||||||
 | 
					golang.org/x/term v0.33.0 h1:NuFncQrRcaRvVmgRkvM3j/F00gWIAlcmlB8ACEKmGIg=
 | 
				
			||||||
 | 
					golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
 | 
				
			||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 | 
					golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 | 
				
			||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
					golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										15293
									
								
								relays.2025-07-14.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15293
									
								
								relays.2025-07-14.json
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
		Loading…
	
		Reference in New Issue
	
	Block a user