Files
gluetun/cmd/mapper/main.go
2020-07-15 18:14:45 -04:00

107 lines
2.8 KiB
Go

package main
import (
"encoding/json"
"flag"
"fmt"
"net"
"net/http"
"os"
"sort"
"strconv"
"strings"
"time"
"github.com/qdm12/golibs/network"
"github.com/qdm12/private-internet-access-docker/internal/models"
)
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
}