Compare commits
12 Commits
2b16955b18
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| bf9ec0cda0 | |||
| 410cc7874d | |||
| 64101b1dfd | |||
| 7652472953 | |||
| 463a0a6cd5 | |||
| df3c6c6a0e | |||
| 0fe71520c5 | |||
| 93e478755e | |||
| 7f736db2cc | |||
| 4dd5e871f8 | |||
| 20d4f1bdc7 | |||
| de48e9ae24 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1 +1 @@
|
||||
cmd/mullvadclosest/mullvadclosest
|
||||
bin
|
||||
|
||||
14
LICENSE
Normal file
14
LICENSE
Normal file
@@ -0,0 +1,14 @@
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
Version 2, December 2004
|
||||
|
||||
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim or modified
|
||||
copies of this license document, and changing it is allowed as long
|
||||
as the name is changed.
|
||||
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
12
Makefile
12
Makefile
@@ -2,3 +2,15 @@ default: build
|
||||
|
||||
build:
|
||||
@go build -o bin/ ./...
|
||||
|
||||
run:
|
||||
sudo go run ./cmd/mullvadclosest/*.go
|
||||
|
||||
test:
|
||||
go test -v ./...
|
||||
|
||||
fmt:
|
||||
go fmt ./...
|
||||
|
||||
lint:
|
||||
golangci-lint run
|
||||
|
||||
156
README.md
Normal file
156
README.md
Normal file
@@ -0,0 +1,156 @@
|
||||
# mullvadclosest
|
||||
|
||||
A small golang program that checks your latency to all of the known mullvad
|
||||
relays.
|
||||
|
||||
This will use your local relay database as installed by the mullvad client
|
||||
if it finds it. Otherwise, it will use a bundled copy which may be a bit
|
||||
out of date. If that happens, contact me and I'll update it.
|
||||
|
||||
This will show you the top 25 lowest latency relays (VPN servers) to you,
|
||||
and the 5 highest for comparison.
|
||||
|
||||
# Errata
|
||||
|
||||
The go ICMP ping library I'm using doesn't work on OSX if you're not root, so the
|
||||
program requires it. Oh well.
|
||||
|
||||
# Usage
|
||||
|
||||
Make sure you're **disconnected** from the VPN when you run this. It needs
|
||||
to be able to ping the relays directly, and if you're connected to the VPN
|
||||
you'll get the latency through the VPN server to which you're connected,
|
||||
which is not what you want.
|
||||
|
||||
Run:
|
||||
|
||||
```
|
||||
GOPROXY=direct go run sneak.berlin/go/mullvadclosest/cmd/mullvadclosest@4dd5e871f830ff62bb30c2290623ad9d8c4d4554
|
||||
```
|
||||
|
||||
The GOPROXY=direct means to download the code directly from my servers, not
|
||||
the Google-operated golang module proxy. Given that you're running this
|
||||
program as root from a random place on the internet, downloading it directly
|
||||
from me avoids the case where Google can serve you arbitrary code that runs
|
||||
on your machine.
|
||||
|
||||
The full git commit hash is provided to ensure that you're running the
|
||||
specific version of the code that I've tested and that you're expecting to
|
||||
run. Don't do "git run <whatever>@latest" because it's just granting random
|
||||
people code exec on your machine. (Same goes for docker images that use
|
||||
_any_ tag that uses `:` instead of a specific hash identified by `@`.)
|
||||
|
||||
# Example output
|
||||
|
||||
Run from the southwestern US:
|
||||
|
||||
```
|
||||
root@bastion:~# GOPROXY=direct go run sneak.berlin/go/mullvadclosest/cmd/mullvadclosest@latest
|
||||
Checking liveness 100% |███████████████████████████████████████████████████████████████████████████████████████████████| (684/684, 78 it/s)
|
||||
Measuring latency 100% |███████████████████████████████████████████████████████████████████████████████████████████████| (673/673, 7 it/s)
|
||||
Total relays: 684
|
||||
Live relays: 673
|
||||
Dead relays: 11
|
||||
Min latency: 11.240837ms (Relay(hostname=us-lax-wg-203, ip=169.150.203.28, country=USA))
|
||||
Max latency: 351.145942ms (Relay(hostname=sg-sin-wg-002, ip=138.199.60.15, country=Singapore))
|
||||
Median latency: 152.235439ms (Relay(hostname=de-fra-wg-106, ip=146.70.117.34, country=Germany))
|
||||
Mean latency: 122.094918ms
|
||||
Stddev latency: 63.354381ms
|
||||
|
||||
Country City Hostname Latency
|
||||
USA Los Angeles, CA us-lax-wg-203 11.240837ms
|
||||
USA Los Angeles, CA us-lax-wg-303 11.414964ms
|
||||
USA Los Angeles, CA us-lax-ovpn-201 11.575832ms
|
||||
USA Los Angeles, CA us-lax-wg-404 12.160305ms
|
||||
USA Los Angeles, CA us-lax-wg-102 12.189879ms
|
||||
USA Los Angeles, CA us-lax-ovpn-402 12.37345ms
|
||||
USA Los Angeles, CA us-lax-wg-403 12.724075ms
|
||||
USA Los Angeles, CA us-lax-wg-401 12.802629ms
|
||||
USA Los Angeles, CA us-lax-ovpn-202 13.044014ms
|
||||
USA Los Angeles, CA us-lax-wg-202 13.129124ms
|
||||
USA Los Angeles, CA us-lax-wg-406 13.655117ms
|
||||
USA Los Angeles, CA us-lax-wg-302 13.804244ms
|
||||
USA Los Angeles, CA us-lax-wg-501 13.854577ms
|
||||
USA Los Angeles, CA us-lax-wg-502 14.668269ms
|
||||
USA Los Angeles, CA us-lax-br-401 15.198631ms
|
||||
USA Los Angeles, CA us-lax-wg-405 15.47022ms
|
||||
USA Los Angeles, CA us-lax-ovpn-102 15.764457ms
|
||||
USA Los Angeles, CA us-lax-wg-503 15.918306ms
|
||||
USA Los Angeles, CA us-lax-ovpn-401 16.264245ms
|
||||
USA Los Angeles, CA us-lax-wg-103 16.35017ms
|
||||
USA Los Angeles, CA us-lax-wg-201 16.469779ms
|
||||
USA Los Angeles, CA us-lax-ovpn-101 16.655777ms
|
||||
USA Los Angeles, CA us-lax-ovpn-403 17.289434ms
|
||||
USA Los Angeles, CA us-lax-wg-402 18.19131ms
|
||||
USA Los Angeles, CA us-lax-wg-101 18.290308ms
|
||||
|
||||
South Africa Johannesburg za-jnb-wg-001 288.186466ms
|
||||
Singapore Singapore sg-sin-wg-001 342.030852ms
|
||||
Singapore Singapore sg-sin-wg-003 346.614045ms
|
||||
Singapore Singapore sg-sin-wg-002 351.145942ms
|
||||
```
|
||||
|
||||
Run from an LTE connection in Türkiye:
|
||||
|
||||
```
|
||||
akrotiri:~$ GOPROXY=direct sudo go run sneak.berlin/go/mullvadclosest/cmd/mullvadclosest@latest
|
||||
Checking liveness 100% |███████████████████████████████████████████████████████████████████████████████████████████| (684/684, 25 it/s)
|
||||
Measuring latency 100% |███████████████████████████████████████████████████████████████████████████████████████████| (602/602, 5 it/s)
|
||||
Total relays: 684
|
||||
Live relays: 602
|
||||
Dead relays: 82
|
||||
Min latency: 87.088ms (Relay(hostname=nl-ams-ovpn-003, ip=185.65.134.73, country=Netherlands))
|
||||
Max latency: 560.733ms (Relay(hostname=th-bkk-wg-001, ip=156.59.50.194, country=Thailand))
|
||||
Median latency: 155.925ms (Relay(hostname=gr-ath-wg-101, ip=149.102.246.2, country=Greece))
|
||||
Mean latency: 175.519705ms
|
||||
Stddev latency: 84.258738ms
|
||||
|
||||
Country City Hostname Latency
|
||||
Netherlands Amsterdam nl-ams-ovpn-003 87.088ms
|
||||
Netherlands Amsterdam nl-ams-wg-006 87.523ms
|
||||
Netherlands Amsterdam nl-ams-wg-101 89.472ms
|
||||
Netherlands Amsterdam nl-ams-wg-201 91.061ms
|
||||
Netherlands Amsterdam nl-ams-ovpn-005 91.272ms
|
||||
Netherlands Amsterdam nl-ams-wg-004 91.527ms
|
||||
Belgium Brussels be-bru-wg-102 92.082ms
|
||||
Netherlands Amsterdam nl-ams-ovpn-004 93.036ms
|
||||
Belgium Brussels be-bru-wg-101 93.22ms
|
||||
Germany Frankfurt de-fra-wg-004 94.146ms
|
||||
Netherlands Amsterdam nl-ams-ovpn-001 94.465ms
|
||||
Netherlands Amsterdam nl-ams-wg-007 94.578ms
|
||||
Germany Frankfurt de-fra-ovpn-003 94.681ms
|
||||
Germany Dusseldorf de-dus-wg-002 95.224ms
|
||||
Netherlands Amsterdam nl-ams-wg-001 95.555ms
|
||||
UK London gb-lon-wg-202 96.297ms
|
||||
UK London gb-lon-wg-201 96.468ms
|
||||
Germany Frankfurt de-fra-ovpn-006 96.544ms
|
||||
Germany Frankfurt de-fra-wg-006 96.58ms
|
||||
UK London gb-lon-ovpn-002 96.775ms
|
||||
UK London gb-lon-wg-203 96.822ms
|
||||
Germany Dusseldorf de-dus-wg-001 96.865ms
|
||||
Belgium Brussels be-bru-ovpn-101 96.869ms
|
||||
UK London gb-lon-ovpn-301 96.922ms
|
||||
UK London gb-lon-ovpn-005 97.231ms
|
||||
|
||||
Australia Perth au-per-wg-302 438.455ms
|
||||
Singapore Singapore sg-sin-ovpn-102 444.711ms
|
||||
Indonesia Jakarta id-jpu-wg-002 504.1ms
|
||||
Thailand Bangkok th-bkk-wg-001 560.733ms
|
||||
```
|
||||
|
||||
# Author
|
||||
|
||||
sneak <[sneak@sneak.berlin](mailto:sneak@sneak.berlin)>
|
||||
|
||||
# License
|
||||
|
||||
[WTFPL](./LICENSE)
|
||||
|
||||
Do with it what you will. There is no warranty, express or implied,
|
||||
including but not limited to merchantability or fitness for a particular
|
||||
purpose. Use at your own risk. Maybe clone it and read the code before
|
||||
you run random shit from some weirdo from the internet as root.
|
||||
|
||||
# Source
|
||||
|
||||
https://sneak.berlin/go/mullvadclosest
|
||||
@@ -2,8 +2,9 @@ package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"math"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
@@ -15,6 +16,9 @@ import (
|
||||
"github.com/schollz/progressbar/v3"
|
||||
)
|
||||
|
||||
// Package-level variable for debug flag
|
||||
var debugMode bool
|
||||
|
||||
// RelayLatency holds a relay and its associated latency
|
||||
type RelayLatency struct {
|
||||
Relay Relay
|
||||
@@ -46,7 +50,7 @@ func livenessWorker(id int, jobs <-chan Relay, results chan<- RelayLatency, wg *
|
||||
if err == nil && isLive {
|
||||
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,
|
||||
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.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 {
|
||||
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.City,
|
||||
rl.Relay.Hostname,
|
||||
rl.Relay.Ipv4AddrIn,
|
||||
rl.Latency)
|
||||
}
|
||||
if i == 24 {
|
||||
@@ -203,7 +210,7 @@ func CheckMullvadExitIP() (bool, error) {
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to read response body: %v", err)
|
||||
}
|
||||
@@ -217,6 +224,14 @@ func CheckMullvadExitIP() (bool, error) {
|
||||
}
|
||||
|
||||
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()
|
||||
if err != nil {
|
||||
panic("Failed to get current user")
|
||||
@@ -231,8 +246,9 @@ func main() {
|
||||
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("Use -force flag to bypass this check.")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ package main
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
@@ -46,11 +45,21 @@ func (r Relay) CheckLiveness() (bool, time.Duration, error) {
|
||||
pinger.Count = 1
|
||||
pinger.Timeout = 3 * time.Second // Increased timeout for the single ping
|
||||
pinger.SetPrivileged(true)
|
||||
pinger.Run()
|
||||
if err := pinger.Run(); err != nil {
|
||||
return false, 0, err
|
||||
}
|
||||
stats := pinger.Statistics()
|
||||
if stats.PacketsRecv == 0 {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -64,8 +73,28 @@ func (r Relay) MeasureLatency() (time.Duration, error) {
|
||||
pinger.Interval = 1 * time.Second // Adding interval between pings
|
||||
pinger.Timeout = 10 * time.Second // Increased overall timeout
|
||||
pinger.SetPrivileged(true)
|
||||
pinger.Run()
|
||||
if err := pinger.Run(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
@@ -110,7 +139,7 @@ func ParseRelayData() ([]Relay, error) {
|
||||
return nil, fmt.Errorf("failed to read embedded file: %v", err)
|
||||
}
|
||||
} else {
|
||||
fileData, err = ioutil.ReadFile(filePath)
|
||||
fileData, err = os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read file: %v", err)
|
||||
}
|
||||
|
||||
4
embed.go
4
embed.go
@@ -4,7 +4,7 @@ import (
|
||||
"embed"
|
||||
)
|
||||
|
||||
//go:embed relays.2024-06-04.json
|
||||
//go:embed relays.2025-07-14.json
|
||||
var fs embed.FS
|
||||
|
||||
func GetEmbeddedFile(name string) ([]byte, error) {
|
||||
@@ -12,5 +12,5 @@ func GetEmbeddedFile(name string) ([]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
|
||||
|
||||
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 (
|
||||
github.com/go-ping/ping v1.1.0 // indirect
|
||||
github.com/google/uuid v1.2.0 // indirect
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/schollz/progressbar/v3 v3.14.3 // indirect
|
||||
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4 // indirect
|
||||
golang.org/x/net v0.42.0 // indirect
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||
golang.org/x/sys v0.20.0 // indirect
|
||||
golang.org/x/term v0.20.0 // indirect
|
||||
golang.org/x/sys v0.34.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.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
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/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/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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
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/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
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/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=
|
||||
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.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/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-20210315160823-c6e025ad8005 h1:pDMpM2zh2MT0kHy037cKlSby2nEhD50SYqwQk76Nm40=
|
||||
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.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||
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.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
|
||||
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/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
|
||||
16336
relays.2024-06-04.json
16336
relays.2024-06-04.json
File diff suppressed because it is too large
Load Diff
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
Reference in New Issue
Block a user