Feat: WeVPN support (#591)
This commit is contained in:
3
.github/labels.yml
vendored
3
.github/labels.yml
vendored
@@ -62,6 +62,9 @@
|
||||
- name: ":cloud: Vyprvpn"
|
||||
color: "cfe8d4"
|
||||
description: ""
|
||||
- name: ":cloud: WeVPN"
|
||||
color: "cfe8d4"
|
||||
description: ""
|
||||
- name: ":cloud: Windscribe"
|
||||
color: "cfe8d4"
|
||||
description: ""
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
*Lightweight swiss-knife-like VPN client to tunnel to Cyberghost, FastestVPN,
|
||||
HideMyAss, IPVanish, IVPN, Mullvad, NordVPN, Privado, Private Internet Access, PrivateVPN,
|
||||
ProtonVPN, PureVPN, Surfshark, TorGuard, VPNUnlimited, VyprVPN and Windscribe VPN servers
|
||||
ProtonVPN, PureVPN, Surfshark, TorGuard, VPNUnlimited, VyprVPN, WeVPN and Windscribe VPN servers
|
||||
using Go, OpenVPN or Wireguard, iptables, DNS over TLS, ShadowSocks and an HTTP proxy*
|
||||
|
||||
**ANNOUNCEMENT**: Wireguard is now supported for all providers supporting it!
|
||||
@@ -60,7 +60,7 @@ using Go, OpenVPN or Wireguard, iptables, DNS over TLS, ShadowSocks and an HTTP
|
||||
## Features
|
||||
|
||||
- Based on Alpine 3.14 for a small Docker image of 31MB
|
||||
- Supports: **Cyberghost**, **FastestVPN**, **HideMyAss**, **IPVanish**, **IVPN**, **Mullvad**, **NordVPN**, **Privado**, **Private Internet Access**, **PrivateVPN**, **ProtonVPN**, **PureVPN**, **Surfshark**, **TorGuard**, **VPNUnlimited**, **Vyprvpn**, **Windscribe** servers
|
||||
- Supports: **Cyberghost**, **FastestVPN**, **HideMyAss**, **IPVanish**, **IVPN**, **Mullvad**, **NordVPN**, **Privado**, **Private Internet Access**, **PrivateVPN**, **ProtonVPN**, **PureVPN**, **Surfshark**, **TorGuard**, **VPNUnlimited**, **Vyprvpn**, **WeVPN**, **Windscribe** servers
|
||||
- Supports OpenVPN for all providers listed
|
||||
- Supports Wireguard
|
||||
- For **Mullvad**, **Ivpn** and **Windscribe**
|
||||
|
||||
@@ -27,7 +27,7 @@ func (c *CLI) FormatServers(args []string) error {
|
||||
var format, output string
|
||||
var cyberghost, fastestvpn, hideMyAss, ipvanish, ivpn, mullvad,
|
||||
nordvpn, pia, privado, privatevpn, protonvpn, purevpn, surfshark,
|
||||
torguard, vpnUnlimited, vyprvpn, windscribe bool
|
||||
torguard, vpnUnlimited, vyprvpn, wevpn, windscribe bool
|
||||
flagSet := flag.NewFlagSet("markdown", flag.ExitOnError)
|
||||
flagSet.StringVar(&format, "format", "markdown", "Format to use which can be: 'markdown'")
|
||||
flagSet.StringVar(&output, "output", "/dev/stdout", "Output file to write the formatted data to")
|
||||
@@ -47,6 +47,7 @@ func (c *CLI) FormatServers(args []string) error {
|
||||
flagSet.BoolVar(&torguard, "torguard", false, "Format Torguard servers")
|
||||
flagSet.BoolVar(&vpnUnlimited, "vpnunlimited", false, "Format VPN Unlimited servers")
|
||||
flagSet.BoolVar(&vyprvpn, "vyprvpn", false, "Format Vyprvpn servers")
|
||||
flagSet.BoolVar(&wevpn, "wevpn", false, "Format WeVPN servers")
|
||||
flagSet.BoolVar(&windscribe, "windscribe", false, "Format Windscribe servers")
|
||||
if err := flagSet.Parse(args); err != nil {
|
||||
return err
|
||||
@@ -97,6 +98,8 @@ func (c *CLI) FormatServers(args []string) error {
|
||||
formatted = currentServers.VPNUnlimited.ToMarkdown()
|
||||
case vyprvpn:
|
||||
formatted = currentServers.Vyprvpn.ToMarkdown()
|
||||
case wevpn:
|
||||
formatted = currentServers.Wevpn.ToMarkdown()
|
||||
case windscribe:
|
||||
formatted = currentServers.Windscribe.ToMarkdown()
|
||||
default:
|
||||
|
||||
@@ -54,6 +54,7 @@ func (c *CLI) Update(ctx context.Context, args []string, logger logging.Logger)
|
||||
flagSet.BoolVar(&options.Torguard, "torguard", false, "Update Torguard servers")
|
||||
flagSet.BoolVar(&options.VPNUnlimited, "vpnunlimited", false, "Update VPN Unlimited servers")
|
||||
flagSet.BoolVar(&options.Vyprvpn, "vyprvpn", false, "Update Vyprvpn servers")
|
||||
flagSet.BoolVar(&options.Wevpn, "wevpn", false, "Update WeVPN servers")
|
||||
flagSet.BoolVar(&options.Windscribe, "windscribe", false, "Update Windscribe servers")
|
||||
if err := flagSet.Parse(args); err != nil {
|
||||
return err
|
||||
|
||||
@@ -168,6 +168,8 @@ func (settings *OpenVPN) read(r reader, serviceProvider string) (err error) {
|
||||
settings.EncPreset, err = getPIAEncryptionPreset(r)
|
||||
case constants.VPNUnlimited:
|
||||
err = settings.readVPNUnlimited(r)
|
||||
case constants.Wevpn:
|
||||
err = settings.readWevpn(r)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -83,6 +83,8 @@ func (settings *Provider) read(r reader, vpnType string) error {
|
||||
err = settings.readVPNUnlimited(r)
|
||||
case constants.Vyprvpn:
|
||||
err = settings.readVyprvpn(r)
|
||||
case constants.Wevpn:
|
||||
err = settings.readWevpn(r)
|
||||
case constants.Windscribe:
|
||||
err = settings.readWindscribe(r)
|
||||
default:
|
||||
@@ -104,7 +106,8 @@ func (settings *Provider) readVPNServiceProvider(r reader, vpnType string) (err
|
||||
constants.Custom,
|
||||
"cyberghost", "fastestvpn", "hidemyass", "ipvanish", "ivpn", "mullvad", "nordvpn",
|
||||
"privado", "pia", "private internet access", "privatevpn", "protonvpn",
|
||||
"purevpn", "surfshark", "torguard", constants.VPNUnlimited, "vyprvpn", "windscribe"}
|
||||
"purevpn", "surfshark", "torguard", constants.VPNUnlimited, "vyprvpn",
|
||||
constants.Wevpn, "windscribe"}
|
||||
case constants.Wireguard:
|
||||
allowedVPNServiceProviders = []string{
|
||||
constants.Custom, constants.Ivpn,
|
||||
|
||||
@@ -322,6 +322,27 @@ func Test_Provider_lines(t *testing.T) {
|
||||
" |--Protocol: udp",
|
||||
},
|
||||
},
|
||||
"wevpn": {
|
||||
settings: Provider{
|
||||
Name: constants.Wevpn,
|
||||
ServerSelection: ServerSelection{
|
||||
VPN: constants.OpenVPN,
|
||||
Cities: []string{"a", "b"},
|
||||
Hostnames: []string{"c", "d"},
|
||||
OpenVPN: OpenVPNSelection{
|
||||
CustomPort: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
lines: []string{
|
||||
"|--Wevpn settings:",
|
||||
" |--Cities: a, b",
|
||||
" |--Hostnames: c, d",
|
||||
" |--OpenVPN selection:",
|
||||
" |--Protocol: udp",
|
||||
" |--Custom port: 1",
|
||||
},
|
||||
},
|
||||
"windscribe": {
|
||||
settings: Provider{
|
||||
Name: constants.Windscribe,
|
||||
|
||||
@@ -17,9 +17,9 @@ type ServerSelection struct { //nolint:maligned
|
||||
|
||||
// Fastestvpn, HideMyAss, IPVanish, IVPN, Mullvad, PrivateVPN, Protonvpn, PureVPN, VPNUnlimited
|
||||
Countries []string `json:"countries"`
|
||||
// HideMyAss, IPVanish, IVPN, Mullvad, PrivateVPN, Protonvpn, PureVPN, VPNUnlimited, Windscribe
|
||||
// HideMyAss, IPVanish, IVPN, Mullvad, PrivateVPN, Protonvpn, PureVPN, VPNUnlimited, WeVPN, Windscribe
|
||||
Cities []string `json:"cities"`
|
||||
// Fastestvpn, HideMyAss, IPVanish, IVPN, PrivateVPN, Windscribe, Privado, Protonvpn, VPNUnlimited
|
||||
// Fastestvpn, HideMyAss, IPVanish, IVPN, PrivateVPN, Windscribe, Privado, Protonvpn, VPNUnlimited, WeVPN
|
||||
Hostnames []string `json:"hostnames"`
|
||||
Names []string `json:"names"` // Protonvpn
|
||||
|
||||
@@ -100,7 +100,7 @@ func (selection ServerSelection) toLines() (lines []string) {
|
||||
type OpenVPNSelection struct {
|
||||
ConfFile string `json:"conf_file"` // Custom configuration file path
|
||||
TCP bool `json:"tcp"` // UDP if TCP is false
|
||||
CustomPort uint16 `json:"custom_port"` // HideMyAss, Mullvad, PIA, ProtonVPN, Windscribe
|
||||
CustomPort uint16 `json:"custom_port"` // HideMyAss, Mullvad, PIA, ProtonVPN, WeVPN, Windscribe
|
||||
EncPreset string `json:"encryption_preset"` // PIA - needed to get the port number
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ type Updater struct {
|
||||
Torguard bool `json:"torguard"`
|
||||
VPNUnlimited bool `json:"vpnunlimited"`
|
||||
Vyprvpn bool `json:"vyprvpn"`
|
||||
Wevpn bool `json:"wevpn"`
|
||||
Windscribe bool `json:"windscribe"`
|
||||
// The two below should be used in CLI mode only
|
||||
CLI bool `json:"-"`
|
||||
@@ -65,6 +66,7 @@ func (settings *Updater) EnableAll() {
|
||||
settings.Torguard = true
|
||||
settings.VPNUnlimited = true
|
||||
settings.Vyprvpn = true
|
||||
settings.Wevpn = true
|
||||
settings.Windscribe = true
|
||||
}
|
||||
|
||||
|
||||
57
internal/configuration/wevpn.go
Normal file
57
internal/configuration/wevpn.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package configuration
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
)
|
||||
|
||||
func (settings *Provider) readWevpn(r reader) (err error) {
|
||||
settings.Name = constants.Wevpn
|
||||
servers := r.servers.GetWevpn()
|
||||
|
||||
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.WevpnCityChoices(servers))
|
||||
if err != nil {
|
||||
return fmt.Errorf("environment variable CITY: %w", err)
|
||||
}
|
||||
|
||||
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.WevpnHostnameChoices(servers))
|
||||
if err != nil {
|
||||
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
|
||||
}
|
||||
|
||||
return settings.ServerSelection.OpenVPN.readWevpn(r)
|
||||
}
|
||||
|
||||
func (settings *OpenVPNSelection) readWevpn(r reader) (err error) {
|
||||
settings.TCP, err = readOpenVPNProtocol(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
validation := openvpnPortValidation{
|
||||
tcp: settings.TCP,
|
||||
allowedTCP: []uint16{53, 1195, 1199, 2018},
|
||||
allowedUDP: []uint16{80, 1194, 1198},
|
||||
}
|
||||
settings.CustomPort, err = readOpenVPNCustomPort(r, validation)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (settings *OpenVPN) readWevpn(r reader) (err error) {
|
||||
settings.ClientKey, err = readClientKey(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -41,6 +41,8 @@ const (
|
||||
VPNUnlimited = "vpn unlimited"
|
||||
// Vyprvpn is a VPN provider.
|
||||
Vyprvpn = "vyprvpn"
|
||||
// WeVPN is a VPN provider.
|
||||
Wevpn = "wevpn"
|
||||
// Windscribe is a VPN provider.
|
||||
Windscribe = "windscribe"
|
||||
)
|
||||
|
||||
26
internal/constants/wevpn.go
Normal file
26
internal/constants/wevpn.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package constants
|
||||
|
||||
import "github.com/qdm12/gluetun/internal/models"
|
||||
|
||||
//nolint:lll
|
||||
const (
|
||||
WevpnCA = "MIIDQjCCAiqgAwIBAgIUPppqnRZfvGGrT4GjXFE4Q29QzgowDQYJKoZIhvcNAQELBQAwEzERMA8GA1UEAwwIQ2hhbmdlTWUwHhcNMTkxMTA1MjMzMzIzWhcNMjkxMTAyMjMzMzIzWjATMREwDwYDVQQDDAhDaGFuZ2VNZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL5DFBJlTqhXukJFWlI8TNW9+HEQCZXhyVFvQhJFF2xIGVNx51XzqxiRANjVJZJrA68kV8az0v2Dxj0SFnRWDR6pOjjdp2CyHFcgHyfv+4MrsreAtkue86bB/1ECPWaoIwtaLnwI6SEmFZl98RlI9v4M/8IE4chOnMrM/F22+2OXI//TduvTcbyOMUiiouIP8UG1FB3J5FyuaW6qPZz2G0efDoaOI+E9LSxE87OoFrII7UqdHlWxRb3nUuPU1Ee4rN/d4tFyP4AvPKfsGhVOwyGG21IdRnbXIuDi0xytkCGOZ4j2bq5zqudnp4Izt6yJgdzZpQQWK3kSHB3qTT/Yzl8CAwEAAaOBjTCBijAdBgNVHQ4EFgQUXYkoo4WbkkvbgLVdGob9RScRf3AwTgYDVR0jBEcwRYAUXYkoo4WbkkvbgLVdGob9RScRf3ChF6QVMBMxETAPBgNVBAMMCENoYW5nZU1lghQ+mmqdFl+8YatPgaNcUThDb1DOCjAMBgNVHRMEBTADAQH/MAsGA1UdDwQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAOr1XmyWBRYfTQPNvZZ+DjCfiRYzLOi2AGefZt/jETqPDF8deVbyL1fLhXZzuX+5Etlsil3PflJjpzc/FSeZRuYRaShtwF3j6I08Eww9rBkaCnsukMUcLtMOvhdAU8dUakcRA2wkQ7Z+TWdMBv5+/6MnX10An1fIz7bAy3btMEOPTEFLo8Bst1SxJtUMaqhUteSOJ1VorpK3CWfOFaXxbJAb4E0+3zt6Vsc8mY5tt6wAi8IqiN4WD79ZdvKxENK4FMkR1kNpBY97mvdf82rzpwiBuJgN5ywmH78Ghj+9T8nI6/UIqJ1y22IRYGv6dMif8fHo5WWhCv3qmCqqY8vwuxw=="
|
||||
WevpnCertificate = "MIIDTDCCAjSgAwIBAgIRAKxt8SMIXezjmHm2KDCAQdIwDQYJKoZIhvcNAQELBQAwEzERMA8GA1UEAwwIQ2hhbmdlTWUwHhcNMTkxMTA1MjMzMzI0WhcNMjkxMTAyMjMzMzI0WjAOMQwwCgYDVQQDDAN0Y3AwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvEwY2erLhMm3Mpsnybm3G6zvGyeblUAaehQVEUs+KM2/5np0Ovx0y8Iz9pIC9ITaWM0B3dM6uBsNEtylZIe4Dd9aFujunSeCFsLRf8i9AbrUombpQ6P4jzYFBxwcEw//UShwa4HZI6JuSYikdpx/dyXdBH2skahwDVc8VUFdBLLSglfKGbuzP9GsdSwQCeBRWgA3dvIzIkQkBwfnt9WQKUfRAe8e5NybaAn8Yuu9sjLkQe6eyV7toxkZTcEXdABG2vtdTEzlAsQilZzIxg3jcdeEgMgRKngng+YNP0rR5nofZ1iDlp+vBj0nuqTTJLHMrRWPIc7bdYFD/f2J49WORAgMBAAGjgZ8wgZwwCQYDVR0TBAIwADAdBgNVHQ4EFgQUmSAFmCo1FAKVq8RQF7jMxMxcMtUwTgYDVR0jBEcwRYAUXYkoo4WbkkvbgLVdGob9RScRf3ChF6QVMBMxETAPBgNVBAMMCENoYW5nZU1lghQ+mmqdFl+8YatPgaNcUThDb1DOCjATBgNVHSUEDDAKBggrBgEFBQcDAjALBgNVHQ8EBAMCB4AwDQYJKoZIhvcNAQELBQADggEBADPqdEgL+0kou8P974QEaNg1XOAXpwP0NNqbkZ/Oj9+Lp96YAhAHOAJig+RWbBktK8zu8oUUGR1qLXAWCmirlXErVuBRnadTEh3A7SOuY02BcsYAtpQ2EU9j5K/LV7nTfagkVdWy7x/av361UD4t9fv1j4YYTh4XLRp7KVXs6AGZ7T1hqPYFMUIoPpFhPzFxH4euJjfazr4SkTR6k6Vhw3pyFd6HP65vcqpzHGxFytSa8HtltBk2DpzIf8yV9TEy+gOXFaaGss0YKQ5OU1ieqZRuLVEGiu17lByYiQGyemIETJbdkyiSg93dDJRxjaTk7c8CEdpipt07ndSIPldMtXA="
|
||||
WevpnOpenvpnStaticKeyV1 = "7be66c0df0b8855e076d9e37b19f9ff3c1735ed537dee6dc786e51bdb8502f878077eeba0420a25e2b04814d22bbdcc0191a4fc396fdba1af6eb090a9d8664f18e70012ee98a2e32c28620a771d13cf3a619c417480c2c312562fffaebfd7ba73f57a28edde6c287365e6ce28291a29728da211cb53e01aa46b92f5f276c61fb46bd810b41219022c8f3d9e699fe9ade6bfcbb937fbbf6f49d741740e71c7c008a9a13c2432608038c6310b4f33588d8d234b3dffcf0823395267d73140d0e9a40e323ca92866c37073bfb072ab9de518bb9f2c65df7e219c2f114afbcf7c6e3c401cb08c3ed2901725b0601d2b5de89245719dd32506d52f149d14156215c1e"
|
||||
)
|
||||
|
||||
func WevpnCityChoices(servers []models.WevpnServer) (choices []string) {
|
||||
choices = make([]string, len(servers))
|
||||
for i := range servers {
|
||||
choices[i] = servers[i].City
|
||||
}
|
||||
return makeUnique(choices)
|
||||
}
|
||||
|
||||
func WevpnHostnameChoices(servers []models.WevpnServer) (choices []string) {
|
||||
choices = make([]string, len(servers))
|
||||
for i := range servers {
|
||||
choices[i] = servers[i].Hostname
|
||||
}
|
||||
return makeUnique(choices)
|
||||
}
|
||||
@@ -220,6 +220,18 @@ func (a *AllServers) GetVyprvpn() (servers []VyprvpnServer) {
|
||||
return servers
|
||||
}
|
||||
|
||||
func (a *AllServers) GetWevpn() (servers []WevpnServer) {
|
||||
if a.Windscribe.Servers == nil {
|
||||
return nil
|
||||
}
|
||||
servers = make([]WevpnServer, len(a.Wevpn.Servers))
|
||||
for i, serverToCopy := range a.Wevpn.Servers {
|
||||
servers[i] = serverToCopy
|
||||
servers[i].IPs = copyIPs(serverToCopy.IPs)
|
||||
}
|
||||
return servers
|
||||
}
|
||||
|
||||
func (a *AllServers) GetWindscribe() (servers []WindscribeServer) {
|
||||
if a.Windscribe.Servers == nil {
|
||||
return nil
|
||||
|
||||
@@ -239,6 +239,19 @@ func (s *VyprvpnServer) ToMarkdown() (markdown string) {
|
||||
boolToMarkdown(s.TCP), boolToMarkdown(s.UDP))
|
||||
}
|
||||
|
||||
func (s *WevpnServers) ToMarkdown() (markdown string) {
|
||||
markdown = markdownTableHeading("City", "Hostname", "TCP", "UDP")
|
||||
for _, server := range s.Servers {
|
||||
markdown += server.ToMarkdown() + "\n"
|
||||
}
|
||||
return markdown
|
||||
}
|
||||
|
||||
func (s *WevpnServer) ToMarkdown() (markdown string) {
|
||||
return fmt.Sprintf("| %s | `%s` | %s | %s |",
|
||||
s.City, s.Hostname, boolToMarkdown(s.TCP), boolToMarkdown(s.UDP))
|
||||
}
|
||||
|
||||
func (s *WindscribeServers) ToMarkdown() (markdown string) {
|
||||
markdown = markdownTableHeading("Region", "City", "Hostname", "VPN")
|
||||
for _, server := range s.Servers {
|
||||
|
||||
@@ -157,6 +157,14 @@ type VyprvpnServer struct {
|
||||
IPs []net.IP `json:"ips"`
|
||||
}
|
||||
|
||||
type WevpnServer struct {
|
||||
City string `json:"city"`
|
||||
Hostname string `json:"hostname"`
|
||||
TCP bool `json:"tcp"`
|
||||
UDP bool `json:"udp"`
|
||||
IPs []net.IP `json:"ips"`
|
||||
}
|
||||
|
||||
type WindscribeServer struct {
|
||||
VPN string `json:"vpn"`
|
||||
Region string `json:"region"`
|
||||
|
||||
@@ -18,6 +18,7 @@ type AllServers struct {
|
||||
Torguard TorguardServers `json:"torguard"`
|
||||
VPNUnlimited VPNUnlimitedServers `json:"vpnunlimited"`
|
||||
Vyprvpn VyprvpnServers `json:"vyprvpn"`
|
||||
Wevpn WevpnServers `json:"wevpn"`
|
||||
Windscribe WindscribeServers `json:"windscribe"`
|
||||
}
|
||||
|
||||
@@ -38,6 +39,7 @@ func (a *AllServers) Count() int {
|
||||
len(a.Torguard.Servers) +
|
||||
len(a.VPNUnlimited.Servers) +
|
||||
len(a.Vyprvpn.Servers) +
|
||||
len(a.Wevpn.Servers) +
|
||||
len(a.Windscribe.Servers)
|
||||
}
|
||||
|
||||
@@ -121,6 +123,11 @@ type VyprvpnServers struct {
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
Servers []VyprvpnServer `json:"servers"`
|
||||
}
|
||||
type WevpnServers struct {
|
||||
Version uint16 `json:"version"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
Servers []WevpnServer `json:"servers"`
|
||||
}
|
||||
type WindscribeServers struct {
|
||||
Version uint16 `json:"version"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
|
||||
@@ -28,6 +28,7 @@ import (
|
||||
"github.com/qdm12/gluetun/internal/provider/torguard"
|
||||
"github.com/qdm12/gluetun/internal/provider/vpnunlimited"
|
||||
"github.com/qdm12/gluetun/internal/provider/vyprvpn"
|
||||
"github.com/qdm12/gluetun/internal/provider/wevpn"
|
||||
"github.com/qdm12/gluetun/internal/provider/windscribe"
|
||||
"github.com/qdm12/golibs/logging"
|
||||
)
|
||||
@@ -85,6 +86,8 @@ func New(provider string, allServers models.AllServers, timeNow func() time.Time
|
||||
return vpnunlimited.New(allServers.VPNUnlimited.Servers, randSource)
|
||||
case constants.Vyprvpn:
|
||||
return vyprvpn.New(allServers.Vyprvpn.Servers, randSource)
|
||||
case constants.Wevpn:
|
||||
return wevpn.New(allServers.Wevpn.Servers, randSource)
|
||||
case constants.Windscribe:
|
||||
return windscribe.New(allServers.Windscribe.Servers, randSource)
|
||||
default:
|
||||
|
||||
43
internal/provider/wevpn/connection.go
Normal file
43
internal/provider/wevpn/connection.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package wevpn
|
||||
|
||||
import (
|
||||
"github.com/qdm12/gluetun/internal/configuration"
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
"github.com/qdm12/gluetun/internal/provider/utils"
|
||||
)
|
||||
|
||||
func (w *Wevpn) GetConnection(selection configuration.ServerSelection) (
|
||||
connection models.Connection, err error) {
|
||||
port := getPort(selection)
|
||||
protocol := utils.GetProtocol(selection)
|
||||
|
||||
servers, err := w.filterServers(selection)
|
||||
if err != nil {
|
||||
return connection, err
|
||||
}
|
||||
|
||||
var connections []models.Connection
|
||||
for _, server := range servers {
|
||||
for _, IP := range server.IPs {
|
||||
connection := models.Connection{
|
||||
Type: selection.VPN,
|
||||
IP: IP,
|
||||
Port: port,
|
||||
Protocol: protocol,
|
||||
}
|
||||
connections = append(connections, connection)
|
||||
}
|
||||
}
|
||||
|
||||
return utils.PickConnection(connections, selection, w.randSource)
|
||||
}
|
||||
|
||||
func getPort(selection configuration.ServerSelection) (port uint16) {
|
||||
const (
|
||||
defaultOpenVPNTCP = 1195
|
||||
defaultOpenVPNUDP = 1194
|
||||
defaultWireguard = 0 // Wireguard not supported
|
||||
)
|
||||
return utils.GetPort(selection, defaultOpenVPNTCP,
|
||||
defaultOpenVPNUDP, defaultWireguard)
|
||||
}
|
||||
96
internal/provider/wevpn/connection_test.go
Normal file
96
internal/provider/wevpn/connection_test.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package wevpn
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/rand"
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration"
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_Wevpn_GetConnection(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testCases := map[string]struct {
|
||||
servers []models.WevpnServer
|
||||
selection configuration.ServerSelection
|
||||
connection models.Connection
|
||||
err error
|
||||
}{
|
||||
"no server available": {
|
||||
selection: configuration.ServerSelection{
|
||||
VPN: constants.OpenVPN,
|
||||
},
|
||||
err: errors.New("no server found: for VPN openvpn; protocol udp"),
|
||||
},
|
||||
"no filter": {
|
||||
servers: []models.WevpnServer{
|
||||
{UDP: true, IPs: []net.IP{net.IPv4(1, 1, 1, 1)}},
|
||||
{UDP: true, IPs: []net.IP{net.IPv4(2, 2, 2, 2)}},
|
||||
{UDP: true, IPs: []net.IP{net.IPv4(3, 3, 3, 3)}},
|
||||
},
|
||||
connection: models.Connection{
|
||||
IP: net.IPv4(1, 1, 1, 1),
|
||||
Port: 1194,
|
||||
Protocol: constants.UDP,
|
||||
},
|
||||
},
|
||||
"target IP": {
|
||||
selection: configuration.ServerSelection{
|
||||
TargetIP: net.IPv4(2, 2, 2, 2),
|
||||
},
|
||||
servers: []models.WevpnServer{
|
||||
{UDP: true, IPs: []net.IP{net.IPv4(1, 1, 1, 1)}},
|
||||
{UDP: true, IPs: []net.IP{net.IPv4(2, 2, 2, 2)}},
|
||||
{UDP: true, IPs: []net.IP{net.IPv4(3, 3, 3, 3)}},
|
||||
},
|
||||
connection: models.Connection{
|
||||
IP: net.IPv4(2, 2, 2, 2),
|
||||
Port: 1194,
|
||||
Protocol: constants.UDP,
|
||||
},
|
||||
},
|
||||
"with filter": {
|
||||
selection: configuration.ServerSelection{
|
||||
Hostnames: []string{"b"},
|
||||
},
|
||||
servers: []models.WevpnServer{
|
||||
{UDP: true, Hostname: "a", IPs: []net.IP{net.IPv4(1, 1, 1, 1)}},
|
||||
{UDP: true, Hostname: "b", IPs: []net.IP{net.IPv4(2, 2, 2, 2)}},
|
||||
{UDP: true, Hostname: "a", IPs: []net.IP{net.IPv4(3, 3, 3, 3)}},
|
||||
},
|
||||
connection: models.Connection{
|
||||
IP: net.IPv4(2, 2, 2, 2),
|
||||
Port: 1194,
|
||||
Protocol: constants.UDP,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for name, testCase := range testCases {
|
||||
testCase := testCase
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
randSource := rand.NewSource(0)
|
||||
|
||||
m := New(testCase.servers, randSource)
|
||||
|
||||
connection, err := m.GetConnection(testCase.selection)
|
||||
|
||||
if testCase.err != nil {
|
||||
require.Error(t, err)
|
||||
assert.Equal(t, testCase.err.Error(), err.Error())
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
assert.Equal(t, testCase.connection, connection)
|
||||
})
|
||||
}
|
||||
}
|
||||
27
internal/provider/wevpn/filter.go
Normal file
27
internal/provider/wevpn/filter.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package wevpn
|
||||
|
||||
import (
|
||||
"github.com/qdm12/gluetun/internal/configuration"
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
"github.com/qdm12/gluetun/internal/provider/utils"
|
||||
)
|
||||
|
||||
func (w *Wevpn) filterServers(selection configuration.ServerSelection) (
|
||||
servers []models.WevpnServer, err error) {
|
||||
for _, server := range w.servers {
|
||||
switch {
|
||||
case
|
||||
utils.FilterByProtocol(selection, server.TCP, server.UDP),
|
||||
utils.FilterByPossibilities(server.City, selection.Cities),
|
||||
utils.FilterByPossibilities(server.Hostname, selection.Hostnames):
|
||||
default:
|
||||
servers = append(servers, server)
|
||||
}
|
||||
}
|
||||
|
||||
if len(servers) == 0 {
|
||||
return nil, utils.NoServerFoundError(selection)
|
||||
}
|
||||
|
||||
return servers, nil
|
||||
}
|
||||
104
internal/provider/wevpn/filter_test.go
Normal file
104
internal/provider/wevpn/filter_test.go
Normal file
@@ -0,0 +1,104 @@
|
||||
package wevpn
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration"
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_Wevpn_filterServers(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testCases := map[string]struct {
|
||||
servers []models.WevpnServer
|
||||
selection configuration.ServerSelection
|
||||
filtered []models.WevpnServer
|
||||
err error
|
||||
}{
|
||||
"no server available": {
|
||||
selection: configuration.ServerSelection{
|
||||
VPN: constants.OpenVPN,
|
||||
},
|
||||
err: errors.New("no server found: for VPN openvpn; protocol udp"),
|
||||
},
|
||||
"no filter": {
|
||||
servers: []models.WevpnServer{
|
||||
{Hostname: "a", UDP: true},
|
||||
{Hostname: "b", UDP: true},
|
||||
{Hostname: "c", UDP: true},
|
||||
},
|
||||
filtered: []models.WevpnServer{
|
||||
{Hostname: "a", UDP: true},
|
||||
{Hostname: "b", UDP: true},
|
||||
{Hostname: "c", UDP: true},
|
||||
},
|
||||
},
|
||||
"filter by protocol": {
|
||||
selection: configuration.ServerSelection{
|
||||
OpenVPN: configuration.OpenVPNSelection{TCP: true},
|
||||
},
|
||||
servers: []models.WevpnServer{
|
||||
{Hostname: "a", UDP: true},
|
||||
{Hostname: "b", TCP: true},
|
||||
{Hostname: "c", UDP: true},
|
||||
},
|
||||
filtered: []models.WevpnServer{
|
||||
{Hostname: "b", TCP: true},
|
||||
},
|
||||
},
|
||||
"filter by city": {
|
||||
selection: configuration.ServerSelection{
|
||||
Cities: []string{"b"},
|
||||
},
|
||||
servers: []models.WevpnServer{
|
||||
{City: "a", UDP: true},
|
||||
{City: "b", UDP: true},
|
||||
{City: "c", UDP: true},
|
||||
},
|
||||
filtered: []models.WevpnServer{
|
||||
{City: "b", UDP: true},
|
||||
},
|
||||
},
|
||||
"filter by hostname": {
|
||||
selection: configuration.ServerSelection{
|
||||
Hostnames: []string{"b"},
|
||||
},
|
||||
servers: []models.WevpnServer{
|
||||
{Hostname: "a", UDP: true},
|
||||
{Hostname: "b", UDP: true},
|
||||
{Hostname: "c", UDP: true},
|
||||
},
|
||||
filtered: []models.WevpnServer{
|
||||
{Hostname: "b", UDP: true},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for name, testCase := range testCases {
|
||||
testCase := testCase
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
randSource := rand.NewSource(0)
|
||||
|
||||
w := New(testCase.servers, randSource)
|
||||
|
||||
servers, err := w.filterServers(testCase.selection)
|
||||
|
||||
if testCase.err != nil {
|
||||
require.Error(t, err)
|
||||
assert.Equal(t, testCase.err.Error(), err.Error())
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
assert.Equal(t, testCase.filtered, servers)
|
||||
})
|
||||
}
|
||||
}
|
||||
84
internal/provider/wevpn/openvpnconf.go
Normal file
84
internal/provider/wevpn/openvpnconf.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package wevpn
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration"
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
"github.com/qdm12/gluetun/internal/provider/utils"
|
||||
)
|
||||
|
||||
func (w *Wevpn) BuildConf(connection models.Connection,
|
||||
settings configuration.OpenVPN) (lines []string, err error) {
|
||||
if settings.Cipher == "" {
|
||||
settings.Cipher = constants.AES256gcm
|
||||
}
|
||||
|
||||
if settings.Auth == "" {
|
||||
settings.Auth = constants.SHA512
|
||||
}
|
||||
|
||||
lines = []string{
|
||||
"client",
|
||||
"nobind",
|
||||
"tls-exit",
|
||||
"dev " + settings.Interface,
|
||||
"verb " + strconv.Itoa(settings.Verbosity),
|
||||
|
||||
// Wevpn specific
|
||||
"ping 30",
|
||||
"remote-cert-tls server",
|
||||
"redirect-gateway def1 bypass-dhcp",
|
||||
"reneg-sec 0",
|
||||
"auth-user-pass " + constants.OpenVPNAuthConf,
|
||||
"auth " + settings.Auth,
|
||||
|
||||
// Added constant values
|
||||
"auth-nocache",
|
||||
"mute-replay-warnings",
|
||||
"pull-filter ignore \"auth-token\"", // prevent auth failed loops
|
||||
"auth-retry nointeract",
|
||||
"suppress-timestamps",
|
||||
|
||||
// Modified variables
|
||||
connection.OpenVPNProtoLine(),
|
||||
connection.OpenVPNRemoteLine(),
|
||||
}
|
||||
|
||||
if connection.Protocol == constants.UDP {
|
||||
lines = append(lines, "explicit-exit-notify")
|
||||
}
|
||||
|
||||
lines = append(lines, utils.CipherLines(settings.Cipher, settings.Version)...)
|
||||
|
||||
if !settings.Root {
|
||||
lines = append(lines, "user "+settings.ProcUser)
|
||||
lines = append(lines, "persist-tun")
|
||||
lines = append(lines, "persist-key")
|
||||
}
|
||||
|
||||
if settings.MSSFix > 0 {
|
||||
lines = append(lines, "mssfix "+strconv.Itoa(int(settings.MSSFix)))
|
||||
}
|
||||
|
||||
if settings.IPv6 {
|
||||
lines = append(lines, "tun-ipv6")
|
||||
} else {
|
||||
lines = append(lines, `pull-filter ignore "route-ipv6"`)
|
||||
lines = append(lines, `pull-filter ignore "ifconfig-ipv6"`)
|
||||
}
|
||||
|
||||
lines = append(lines, utils.WrapOpenvpnKey(
|
||||
settings.ClientKey)...)
|
||||
lines = append(lines, utils.WrapOpenvpnCA(
|
||||
constants.WevpnCA)...)
|
||||
lines = append(lines, utils.WrapOpenvpnCert(
|
||||
constants.WevpnCertificate)...)
|
||||
lines = append(lines, utils.WrapOpenvpnTLSCrypt(
|
||||
constants.WevpnOpenvpnStaticKeyV1)...)
|
||||
|
||||
lines = append(lines, "")
|
||||
|
||||
return lines, nil
|
||||
}
|
||||
23
internal/provider/wevpn/provider.go
Normal file
23
internal/provider/wevpn/provider.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package wevpn
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
"github.com/qdm12/gluetun/internal/provider/utils"
|
||||
)
|
||||
|
||||
type Wevpn struct {
|
||||
servers []models.WevpnServer
|
||||
randSource rand.Source
|
||||
utils.NoPortForwarder
|
||||
}
|
||||
|
||||
func New(servers []models.WevpnServer, randSource rand.Source) *Wevpn {
|
||||
return &Wevpn{
|
||||
servers: servers,
|
||||
randSource: randSource,
|
||||
NoPortForwarder: utils.NewNoPortForwarding(constants.Wevpn),
|
||||
}
|
||||
}
|
||||
@@ -128,6 +128,11 @@ func Test_versions(t *testing.T) {
|
||||
version: allServers.Vyprvpn.Version,
|
||||
digest: "58de06d8",
|
||||
},
|
||||
"Wevpn": {
|
||||
model: models.WevpnServer{},
|
||||
version: allServers.Wevpn.Version,
|
||||
digest: "f4daa186",
|
||||
},
|
||||
"Windscribe": {
|
||||
model: models.WindscribeServer{},
|
||||
version: allServers.Windscribe.Version,
|
||||
|
||||
@@ -49,6 +49,7 @@ func (s *Storage) mergeServers(hardcoded, persisted models.AllServers) models.Al
|
||||
Torguard: s.mergeTorguard(hardcoded.Torguard, persisted.Torguard),
|
||||
VPNUnlimited: s.mergeVPNUnlimited(hardcoded.VPNUnlimited, persisted.VPNUnlimited),
|
||||
Vyprvpn: s.mergeVyprvpn(hardcoded.Vyprvpn, persisted.Vyprvpn),
|
||||
Wevpn: s.mergeWevpn(hardcoded.Wevpn, persisted.Wevpn),
|
||||
Windscribe: s.mergeWindscribe(hardcoded.Windscribe, persisted.Windscribe),
|
||||
}
|
||||
}
|
||||
@@ -279,6 +280,21 @@ func (s *Storage) mergeVyprvpn(hardcoded, persisted models.VyprvpnServers) model
|
||||
return persisted
|
||||
}
|
||||
|
||||
func (s *Storage) mergeWevpn(hardcoded, persisted models.WevpnServers) models.WevpnServers {
|
||||
if persisted.Timestamp <= hardcoded.Timestamp {
|
||||
return hardcoded
|
||||
}
|
||||
|
||||
versionDiff := int(hardcoded.Version) - int(persisted.Version)
|
||||
if versionDiff > 0 {
|
||||
s.logVersionDiff("WeVPN", versionDiff)
|
||||
return hardcoded
|
||||
}
|
||||
|
||||
s.logTimeDiff("WeVPN", persisted.Timestamp, hardcoded.Timestamp)
|
||||
return persisted
|
||||
}
|
||||
|
||||
func (s *Storage) mergeWindscribe(hardcoded, persisted models.WindscribeServers) models.WindscribeServers {
|
||||
if persisted.Timestamp <= hardcoded.Timestamp {
|
||||
return hardcoded
|
||||
|
||||
@@ -107914,6 +107914,633 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"wevpn": {
|
||||
"version": 1,
|
||||
"timestamp": 1632407719,
|
||||
"servers": [
|
||||
{
|
||||
"city": "Amsterdam",
|
||||
"hostname": "amsterdam.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"89.238.177.234"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Athens",
|
||||
"hostname": "athens.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"185.51.134.210"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Atlanta",
|
||||
"hostname": "atlanta.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"104.223.91.146"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Auckland",
|
||||
"hostname": "auckland.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"103.108.94.226"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Belgrade",
|
||||
"hostname": "belgrade.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"37.120.193.170"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Brussels",
|
||||
"hostname": "brussels.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"77.243.191.178"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Bucharest",
|
||||
"hostname": "bucharest.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"86.105.25.162"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Budapest",
|
||||
"hostname": "budapest.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"185.128.26.130"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Buenos Aires",
|
||||
"hostname": "buenosaires.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"131.255.4.140"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Cairo",
|
||||
"hostname": "cairo.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"188.214.122.138"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Chennai",
|
||||
"hostname": "chennai.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"169.38.107.52"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Chicago",
|
||||
"hostname": "chicago.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"69.39.239.57"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Copenhagen",
|
||||
"hostname": "copenhagen.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"37.120.194.82"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Dallas",
|
||||
"hostname": "dallas.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"194.110.112.74"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Denizli",
|
||||
"hostname": "bursa.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"95.173.161.240"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Denver",
|
||||
"hostname": "denver.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"70.39.71.2"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Dubai",
|
||||
"hostname": "dubai.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"217.138.193.42"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Dublin",
|
||||
"hostname": "dublin.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"217.138.222.138"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Frankfurt",
|
||||
"hostname": "frankfurt.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"45.141.152.178"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Hanoi",
|
||||
"hostname": "hanoi.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"188.214.152.194"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Helsinki",
|
||||
"hostname": "helsinki.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"185.212.149.152"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Hong Kong",
|
||||
"hostname": "hongkong.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"84.17.37.55"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Jakarta",
|
||||
"hostname": "jakarta.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"45.133.181.58"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Johannesburg",
|
||||
"hostname": "johannesburg.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"102.165.20.38"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Kiev",
|
||||
"hostname": "kyiv.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"37.19.218.197"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Lagos",
|
||||
"hostname": "lagos.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"102.129.144.142"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Lisbon",
|
||||
"hostname": "lisbon.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"185.90.57.152"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "London",
|
||||
"hostname": "london.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"45.141.154.2"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "London-PF",
|
||||
"hostname": "london-pf.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"77.243.177.78"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Los Angeles",
|
||||
"hostname": "losangeles.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"45.152.182.82"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Los Angeles-PF",
|
||||
"hostname": "losangeles-pf.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"66.55.92.75"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Luxembourg",
|
||||
"hostname": "luxembourg.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"5.253.204.194"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Madrid",
|
||||
"hostname": "madrid.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"45.152.183.242"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Manchester",
|
||||
"hostname": "manchester.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"86.106.136.98"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Manila",
|
||||
"hostname": "manila.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"188.214.125.106"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Melbourne",
|
||||
"hostname": "melbourne.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"116.206.230.130"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Mexico City",
|
||||
"hostname": "mexico.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"169.57.35.97"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Miami",
|
||||
"hostname": "miami.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"96.47.224.2"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Milan",
|
||||
"hostname": "milan.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"217.138.197.138"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Montreal",
|
||||
"hostname": "montreal.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"217.138.200.242"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Moscow",
|
||||
"hostname": "moscow.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"80.93.181.194"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "New Jersey",
|
||||
"hostname": "newjersey.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"23.226.131.146"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "New York",
|
||||
"hostname": "newyork.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"217.138.206.106"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "New York-PF",
|
||||
"hostname": "newyork-pf.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"188.241.179.2"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Oslo",
|
||||
"hostname": "oslo.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"84.247.50.10"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Oulu",
|
||||
"hostname": "oulu.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"188.126.89.174"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Paris",
|
||||
"hostname": "paris.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"37.120.136.234"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Phoenix",
|
||||
"hostname": "phoenix.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"170.130.15.34"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Prague",
|
||||
"hostname": "prague.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"217.138.199.138"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Salt Lake City",
|
||||
"hostname": "saltlakecity.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"107.182.234.233"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "San Jose",
|
||||
"hostname": "sanjose.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"169.62.109.140"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Sao Paulo",
|
||||
"hostname": "saopaulo.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"177.54.152.89"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Seattle",
|
||||
"hostname": "seattle.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"104.140.21.178"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Seoul",
|
||||
"hostname": "seoul.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"169.56.83.206"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Sibu",
|
||||
"hostname": "kualalumpur.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"223.25.246.4"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Singapore",
|
||||
"hostname": "singapore.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"84.17.38.144"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Sofia",
|
||||
"hostname": "sofia.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"217.138.221.42"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "St Petersburg",
|
||||
"hostname": "petersburg.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"82.202.220.242"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Stockholm",
|
||||
"hostname": "stockholm.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"45.83.91.66"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Sydney",
|
||||
"hostname": "sydney.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"46.102.153.106"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Taipei",
|
||||
"hostname": "taipei.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"188.214.106.98"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Tel Aviv",
|
||||
"hostname": "telaviv.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"193.43.72.242"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Tokyo",
|
||||
"hostname": "tokyo.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"84.17.34.8"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Toronto",
|
||||
"hostname": "toronto.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"184.75.208.234"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Vancouver",
|
||||
"hostname": "vancouver.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"71.19.249.109"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Vienna",
|
||||
"hostname": "vienna.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"37.120.212.162"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Warsaw",
|
||||
"hostname": "warsaw.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"217.138.209.122"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Washington DC",
|
||||
"hostname": "washington.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"70.32.0.208"
|
||||
]
|
||||
},
|
||||
{
|
||||
"city": "Zurich",
|
||||
"hostname": "zurich.wevpn.com",
|
||||
"tcp": false,
|
||||
"udp": true,
|
||||
"ips": [
|
||||
"37.120.137.82"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"windscribe": {
|
||||
"version": 1,
|
||||
"timestamp": 1629420754,
|
||||
@@ -115123,4 +115750,4 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ func countServers(allServers models.AllServers) int {
|
||||
len(allServers.Torguard.Servers) +
|
||||
len(allServers.VPNUnlimited.Servers) +
|
||||
len(allServers.Vyprvpn.Servers) +
|
||||
len(allServers.Wevpn.Servers) +
|
||||
len(allServers.Windscribe.Servers)
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"github.com/qdm12/gluetun/internal/updater/providers/torguard"
|
||||
"github.com/qdm12/gluetun/internal/updater/providers/vpnunlimited"
|
||||
"github.com/qdm12/gluetun/internal/updater/providers/vyprvpn"
|
||||
"github.com/qdm12/gluetun/internal/updater/providers/wevpn"
|
||||
"github.com/qdm12/gluetun/internal/updater/providers/windscribe"
|
||||
)
|
||||
|
||||
@@ -357,6 +358,27 @@ func (u *updater) updateVyprvpn(ctx context.Context) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *updater) updateWevpn(ctx context.Context) (err error) {
|
||||
minServers := getMinServers(len(u.servers.Wevpn.Servers))
|
||||
servers, warnings, err := wevpn.GetServers(ctx, u.presolver, minServers)
|
||||
if u.options.CLI {
|
||||
for _, warning := range warnings {
|
||||
u.logger.Warn("WeVPN: " + warning)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if reflect.DeepEqual(u.servers.Wevpn.Servers, servers) {
|
||||
return nil
|
||||
}
|
||||
|
||||
u.servers.Wevpn.Timestamp = u.timeNow().Unix()
|
||||
u.servers.Wevpn.Servers = servers
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *updater) updateWindscribe(ctx context.Context) (err error) {
|
||||
minServers := getMinServers(len(u.servers.Windscribe.Servers))
|
||||
servers, err := windscribe.GetServers(ctx, u.client, minServers)
|
||||
|
||||
76
internal/updater/providers/wevpn/cities.go
Normal file
76
internal/updater/providers/wevpn/cities.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package wevpn
|
||||
|
||||
// getAvailableCities get available cities as listed on the WeVPN website.
|
||||
func getAvailableCities() (cities []string) {
|
||||
return []string{
|
||||
"Cairo",
|
||||
"Chennai",
|
||||
"Denizli",
|
||||
"Dubai",
|
||||
"Johannesburg",
|
||||
"Lagos",
|
||||
"Tel Aviv",
|
||||
"Atlanta",
|
||||
"Buenos Aires",
|
||||
"Chicago",
|
||||
"Dallas",
|
||||
"Denver",
|
||||
"Los Angeles",
|
||||
"Los Angeles-PF",
|
||||
"Mexico City",
|
||||
"Miami",
|
||||
"Montreal",
|
||||
"New Jersey",
|
||||
"New York",
|
||||
"New York-PF",
|
||||
"Phoenix",
|
||||
"Salt Lake City",
|
||||
"San Jose",
|
||||
"Sao Paulo",
|
||||
"Seattle",
|
||||
"Toronto",
|
||||
"Vancouver",
|
||||
"Washington DC",
|
||||
"Auckland",
|
||||
"Hanoi",
|
||||
"Hong Kong",
|
||||
"Jakarta",
|
||||
"Manila",
|
||||
"Melbourne",
|
||||
"Moscow",
|
||||
"Seoul",
|
||||
"Sibu",
|
||||
"Singapore",
|
||||
"St Petersburg",
|
||||
"Sydney",
|
||||
"Taipei",
|
||||
"Tokyo",
|
||||
"Amsterdam",
|
||||
"Athens",
|
||||
"Belgrade",
|
||||
"Brussels",
|
||||
"Bucharest",
|
||||
"Budapest",
|
||||
"Copenhagen",
|
||||
"Dublin",
|
||||
"Frankfurt",
|
||||
"Helsinki",
|
||||
"Kiev",
|
||||
"Lisbon",
|
||||
"London",
|
||||
"London-PF",
|
||||
"Luxembourg",
|
||||
"Madrid",
|
||||
"Manchester",
|
||||
"Milan",
|
||||
"Oslo",
|
||||
"Oulu",
|
||||
"Paris",
|
||||
"Prague",
|
||||
"Sofia",
|
||||
"Stockholm",
|
||||
"Vienna",
|
||||
"Warsaw",
|
||||
"Zurich",
|
||||
}
|
||||
}
|
||||
24
internal/updater/providers/wevpn/hostname.go
Normal file
24
internal/updater/providers/wevpn/hostname.go
Normal file
@@ -0,0 +1,24 @@
|
||||
package wevpn
|
||||
|
||||
import "strings"
|
||||
|
||||
func getHostnameFromCity(city string) (hostname string) {
|
||||
host := strings.ToLower(city)
|
||||
host = strings.ReplaceAll(host, ".", "")
|
||||
host = strings.ReplaceAll(host, " ", "")
|
||||
|
||||
specialCases := map[string]string{
|
||||
"washingtondc": "washington",
|
||||
"mexicocity": "mexico",
|
||||
"denizli": "bursa",
|
||||
"sibu": "kualalumpur",
|
||||
"kiev": "kyiv",
|
||||
"stpetersburg": "petersburg",
|
||||
}
|
||||
if specialHost, ok := specialCases[host]; ok {
|
||||
host = specialHost
|
||||
}
|
||||
|
||||
hostname = host + ".wevpn.com"
|
||||
return hostname
|
||||
}
|
||||
33
internal/updater/providers/wevpn/resolve.go
Normal file
33
internal/updater/providers/wevpn/resolve.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package wevpn
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/updater/resolver"
|
||||
)
|
||||
|
||||
func resolveHosts(ctx context.Context, presolver resolver.Parallel,
|
||||
hosts []string, minServers int) (hostToIPs map[string][]net.IP,
|
||||
warnings []string, err error) {
|
||||
const (
|
||||
maxFailRatio = 0.1
|
||||
maxDuration = 20 * time.Second
|
||||
betweenDuration = time.Second
|
||||
maxNoNew = 2
|
||||
maxFails = 2
|
||||
)
|
||||
settings := resolver.ParallelSettings{
|
||||
MaxFailRatio: maxFailRatio,
|
||||
MinFound: minServers,
|
||||
Repeat: resolver.RepeatSettings{
|
||||
MaxDuration: maxDuration,
|
||||
BetweenDuration: betweenDuration,
|
||||
MaxNoNew: maxNoNew,
|
||||
MaxFails: maxFails,
|
||||
SortIPs: true,
|
||||
},
|
||||
}
|
||||
return presolver.Resolve(ctx, hosts, settings)
|
||||
}
|
||||
57
internal/updater/providers/wevpn/resolve_test.go
Normal file
57
internal/updater/providers/wevpn/resolve_test.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package wevpn
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/qdm12/gluetun/internal/updater/resolver"
|
||||
"github.com/qdm12/gluetun/internal/updater/resolver/mock_resolver"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_resolveHosts(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctrl := gomock.NewController(t)
|
||||
|
||||
ctx := context.Background()
|
||||
presolver := mock_resolver.NewMockParallel(ctrl)
|
||||
hosts := []string{"host1", "host2"}
|
||||
const minServers = 10
|
||||
|
||||
expectedHostToIPs := map[string][]net.IP{
|
||||
"host1": {{1, 2, 3, 4}},
|
||||
"host2": {{2, 3, 4, 5}},
|
||||
}
|
||||
expectedWarnings := []string{"warning1", "warning2"}
|
||||
expectedErr := errors.New("dummy")
|
||||
|
||||
const (
|
||||
maxFailRatio = 0.1
|
||||
maxDuration = 20 * time.Second
|
||||
betweenDuration = time.Second
|
||||
maxNoNew = 2
|
||||
maxFails = 2
|
||||
)
|
||||
expectedSettings := resolver.ParallelSettings{
|
||||
MaxFailRatio: maxFailRatio,
|
||||
MinFound: minServers,
|
||||
Repeat: resolver.RepeatSettings{
|
||||
MaxDuration: maxDuration,
|
||||
BetweenDuration: betweenDuration,
|
||||
MaxNoNew: maxNoNew,
|
||||
MaxFails: maxFails,
|
||||
SortIPs: true,
|
||||
},
|
||||
}
|
||||
presolver.EXPECT().Resolve(ctx, hosts, expectedSettings).
|
||||
Return(expectedHostToIPs, expectedWarnings, expectedErr)
|
||||
|
||||
hostToIPs, warnings, err := resolveHosts(ctx, presolver, hosts, minServers)
|
||||
assert.Equal(t, expectedHostToIPs, hostToIPs)
|
||||
assert.Equal(t, expectedWarnings, warnings)
|
||||
assert.Equal(t, expectedErr, err)
|
||||
}
|
||||
58
internal/updater/providers/wevpn/servers.go
Normal file
58
internal/updater/providers/wevpn/servers.go
Normal file
@@ -0,0 +1,58 @@
|
||||
// package wevpn contains code to obtain the server information
|
||||
// for the WeVPN provider.
|
||||
package wevpn
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
"github.com/qdm12/gluetun/internal/updater/resolver"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrGetZip = errors.New("cannot get OpenVPN ZIP file")
|
||||
ErrGetAPI = errors.New("cannot fetch server information from API")
|
||||
ErrNotEnoughServers = errors.New("not enough servers found")
|
||||
)
|
||||
|
||||
func GetServers(ctx context.Context, presolver resolver.Parallel, minServers int) (
|
||||
servers []models.WevpnServer, warnings []string, err error) {
|
||||
cities := getAvailableCities()
|
||||
servers = make([]models.WevpnServer, 0, len(cities))
|
||||
hostnames := make([]string, len(cities))
|
||||
hostnameToCity := make(map[string]string, len(cities))
|
||||
|
||||
for i, city := range cities {
|
||||
hostname := getHostnameFromCity(city)
|
||||
hostnames[i] = hostname
|
||||
hostnameToCity[hostname] = city
|
||||
}
|
||||
|
||||
hostnameToIPs, newWarnings, err := resolveHosts(ctx, presolver, hostnames, minServers)
|
||||
warnings = append(warnings, newWarnings...)
|
||||
if err != nil {
|
||||
return nil, warnings, err
|
||||
}
|
||||
|
||||
if len(hostnameToIPs) < minServers {
|
||||
return nil, warnings, fmt.Errorf("%w: %d and expected at least %d",
|
||||
ErrNotEnoughServers, len(servers), minServers)
|
||||
}
|
||||
|
||||
for hostname, ips := range hostnameToIPs {
|
||||
city := hostnameToCity[hostname]
|
||||
server := models.WevpnServer{
|
||||
City: city,
|
||||
Hostname: hostname,
|
||||
UDP: true,
|
||||
IPs: ips,
|
||||
}
|
||||
servers = append(servers, server)
|
||||
}
|
||||
|
||||
sortServers(servers)
|
||||
|
||||
return servers, warnings, nil
|
||||
}
|
||||
16
internal/updater/providers/wevpn/sort.go
Normal file
16
internal/updater/providers/wevpn/sort.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package wevpn
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
)
|
||||
|
||||
func sortServers(servers []models.WevpnServer) {
|
||||
sort.Slice(servers, func(i, j int) bool {
|
||||
if servers[i].City == servers[j].City {
|
||||
return servers[i].Hostname < servers[j].Hostname
|
||||
}
|
||||
return servers[i].City < servers[j].City
|
||||
})
|
||||
}
|
||||
40
internal/updater/providers/wevpn/sort_test.go
Normal file
40
internal/updater/providers/wevpn/sort_test.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package wevpn
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_sortServers(t *testing.T) {
|
||||
t.Parallel()
|
||||
testCases := map[string]struct {
|
||||
initialServers []models.WevpnServer
|
||||
sortedServers []models.WevpnServer
|
||||
}{
|
||||
"no server": {},
|
||||
"sorted servers": {
|
||||
initialServers: []models.WevpnServer{
|
||||
{City: "A", Hostname: "A"},
|
||||
{City: "A", Hostname: "B"},
|
||||
{City: "A", Hostname: "A"},
|
||||
{City: "B", Hostname: "A"},
|
||||
},
|
||||
sortedServers: []models.WevpnServer{
|
||||
{City: "A", Hostname: "A"},
|
||||
{City: "A", Hostname: "A"},
|
||||
{City: "A", Hostname: "B"},
|
||||
{City: "B", Hostname: "A"},
|
||||
},
|
||||
},
|
||||
}
|
||||
for name, testCase := range testCases {
|
||||
testCase := testCase
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
sortServers(testCase.initialServers)
|
||||
assert.Equal(t, testCase.sortedServers, testCase.initialServers)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -214,6 +214,16 @@ func (u *updater) UpdateServers(ctx context.Context) (allServers models.AllServe
|
||||
}
|
||||
}
|
||||
|
||||
if u.options.Wevpn {
|
||||
u.logger.Info("updating WeVPN servers...")
|
||||
if err := u.updateWevpn(ctx); err != nil {
|
||||
if ctxErr := ctx.Err(); ctxErr != nil {
|
||||
return allServers, ctxErr
|
||||
}
|
||||
u.logger.Error(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if u.options.Windscribe {
|
||||
u.logger.Info("updating Windscribe servers...")
|
||||
if err := u.updateWindscribe(ctx); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user