107 lines
2.8 KiB
Go
107 lines
2.8 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"flag"
|
|
"fmt"
|
|
"net"
|
|
"net/http"
|
|
"os"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/qdm12/gluetun/internal/models"
|
|
"github.com/qdm12/golibs/network"
|
|
)
|
|
|
|
func main() {
|
|
os.Exit(_main())
|
|
}
|
|
|
|
func _main() int {
|
|
provider := flag.String("provider", "nordvpn", "VPN provider to map region to IP addresses using their API, can be 'nordvpn'")
|
|
flag.Parse()
|
|
|
|
client := network.NewClient(30 * time.Second) // big file so 30 seconds
|
|
switch *provider {
|
|
case "nordvpn":
|
|
servers, ignoredServers, err := nordvpn(client)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return 1
|
|
}
|
|
for _, server := range servers {
|
|
fmt.Printf(
|
|
"{Region: %q, Number: %d, TCP: %t, UDP: %t, IP: net.IP{%s}},\n",
|
|
server.Region, server.Number, server.TCP, server.UDP, strings.ReplaceAll(server.IP.String(), ".", ", "),
|
|
)
|
|
}
|
|
fmt.Print("\n\n")
|
|
for _, serverName := range ignoredServers {
|
|
fmt.Printf("ignored server %q because it does not support both UDP and TCP\n", serverName)
|
|
}
|
|
default:
|
|
fmt.Printf("Provider %q is not supported\n", *provider)
|
|
return 1
|
|
}
|
|
return 0
|
|
}
|
|
|
|
func nordvpn(client network.Client) (servers []models.NordvpnServer, ignoredServers []string, err error) {
|
|
content, status, err := client.GetContent("https://nordvpn.com/api/server")
|
|
if err != nil {
|
|
return nil, nil, err
|
|
} else if status != http.StatusOK {
|
|
return nil, nil, fmt.Errorf("HTTP status %d from NordVPN API", status)
|
|
}
|
|
response := []struct {
|
|
IPAddress string `json:"ip_address"`
|
|
Name string `json:"name"`
|
|
Country string `json:"country"`
|
|
Features struct {
|
|
UDP bool `json:"openvpn_udp"`
|
|
TCP bool `json:"openvpn_tcp"`
|
|
} `json:"features"`
|
|
}{}
|
|
if err := json.Unmarshal(content, &response); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
for _, element := range response {
|
|
if !element.Features.TCP && !element.Features.UDP {
|
|
ignoredServers = append(ignoredServers, element.Name)
|
|
}
|
|
ip := net.ParseIP(element.IPAddress)
|
|
if ip == nil {
|
|
return nil, nil, fmt.Errorf("IP address %q is not valid for server %q", element.IPAddress, element.Name)
|
|
}
|
|
i := strings.IndexRune(element.Name, '#')
|
|
if i < 0 {
|
|
return nil, nil, fmt.Errorf("No ID in server name %q", element.Name)
|
|
}
|
|
idString := element.Name[i+1:]
|
|
idUint64, err := strconv.ParseUint(idString, 10, 16)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("Bad ID in server name %q", element.Name)
|
|
}
|
|
id := uint16(idUint64)
|
|
server := models.NordvpnServer{
|
|
Region: element.Country,
|
|
Number: id,
|
|
IP: ip,
|
|
TCP: element.Features.TCP,
|
|
UDP: element.Features.UDP,
|
|
}
|
|
servers = append(servers, server)
|
|
}
|
|
sort.Slice(servers, func(i, j int) bool {
|
|
if servers[i].Region == servers[j].Region {
|
|
return servers[i].Number < servers[j].Number
|
|
}
|
|
return servers[i].Region < servers[j].Region
|
|
})
|
|
return servers, ignoredServers, nil
|
|
}
|