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"
|
- name: ":cloud: Vyprvpn"
|
||||||
color: "cfe8d4"
|
color: "cfe8d4"
|
||||||
description: ""
|
description: ""
|
||||||
|
- name: ":cloud: WeVPN"
|
||||||
|
color: "cfe8d4"
|
||||||
|
description: ""
|
||||||
- name: ":cloud: Windscribe"
|
- name: ":cloud: Windscribe"
|
||||||
color: "cfe8d4"
|
color: "cfe8d4"
|
||||||
description: ""
|
description: ""
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
*Lightweight swiss-knife-like VPN client to tunnel to Cyberghost, FastestVPN,
|
*Lightweight swiss-knife-like VPN client to tunnel to Cyberghost, FastestVPN,
|
||||||
HideMyAss, IPVanish, IVPN, Mullvad, NordVPN, Privado, Private Internet Access, PrivateVPN,
|
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*
|
using Go, OpenVPN or Wireguard, iptables, DNS over TLS, ShadowSocks and an HTTP proxy*
|
||||||
|
|
||||||
**ANNOUNCEMENT**: Wireguard is now supported for all providers supporting it!
|
**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
|
## Features
|
||||||
|
|
||||||
- Based on Alpine 3.14 for a small Docker image of 31MB
|
- 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 OpenVPN for all providers listed
|
||||||
- Supports Wireguard
|
- Supports Wireguard
|
||||||
- For **Mullvad**, **Ivpn** and **Windscribe**
|
- For **Mullvad**, **Ivpn** and **Windscribe**
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ func (c *CLI) FormatServers(args []string) error {
|
|||||||
var format, output string
|
var format, output string
|
||||||
var cyberghost, fastestvpn, hideMyAss, ipvanish, ivpn, mullvad,
|
var cyberghost, fastestvpn, hideMyAss, ipvanish, ivpn, mullvad,
|
||||||
nordvpn, pia, privado, privatevpn, protonvpn, purevpn, surfshark,
|
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 := flag.NewFlagSet("markdown", flag.ExitOnError)
|
||||||
flagSet.StringVar(&format, "format", "markdown", "Format to use which can be: 'markdown'")
|
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")
|
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(&torguard, "torguard", false, "Format Torguard servers")
|
||||||
flagSet.BoolVar(&vpnUnlimited, "vpnunlimited", false, "Format VPN Unlimited servers")
|
flagSet.BoolVar(&vpnUnlimited, "vpnunlimited", false, "Format VPN Unlimited servers")
|
||||||
flagSet.BoolVar(&vyprvpn, "vyprvpn", false, "Format Vyprvpn 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")
|
flagSet.BoolVar(&windscribe, "windscribe", false, "Format Windscribe servers")
|
||||||
if err := flagSet.Parse(args); err != nil {
|
if err := flagSet.Parse(args); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -97,6 +98,8 @@ func (c *CLI) FormatServers(args []string) error {
|
|||||||
formatted = currentServers.VPNUnlimited.ToMarkdown()
|
formatted = currentServers.VPNUnlimited.ToMarkdown()
|
||||||
case vyprvpn:
|
case vyprvpn:
|
||||||
formatted = currentServers.Vyprvpn.ToMarkdown()
|
formatted = currentServers.Vyprvpn.ToMarkdown()
|
||||||
|
case wevpn:
|
||||||
|
formatted = currentServers.Wevpn.ToMarkdown()
|
||||||
case windscribe:
|
case windscribe:
|
||||||
formatted = currentServers.Windscribe.ToMarkdown()
|
formatted = currentServers.Windscribe.ToMarkdown()
|
||||||
default:
|
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.Torguard, "torguard", false, "Update Torguard servers")
|
||||||
flagSet.BoolVar(&options.VPNUnlimited, "vpnunlimited", false, "Update VPN Unlimited servers")
|
flagSet.BoolVar(&options.VPNUnlimited, "vpnunlimited", false, "Update VPN Unlimited servers")
|
||||||
flagSet.BoolVar(&options.Vyprvpn, "vyprvpn", false, "Update Vyprvpn 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")
|
flagSet.BoolVar(&options.Windscribe, "windscribe", false, "Update Windscribe servers")
|
||||||
if err := flagSet.Parse(args); err != nil {
|
if err := flagSet.Parse(args); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -168,6 +168,8 @@ func (settings *OpenVPN) read(r reader, serviceProvider string) (err error) {
|
|||||||
settings.EncPreset, err = getPIAEncryptionPreset(r)
|
settings.EncPreset, err = getPIAEncryptionPreset(r)
|
||||||
case constants.VPNUnlimited:
|
case constants.VPNUnlimited:
|
||||||
err = settings.readVPNUnlimited(r)
|
err = settings.readVPNUnlimited(r)
|
||||||
|
case constants.Wevpn:
|
||||||
|
err = settings.readWevpn(r)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -83,6 +83,8 @@ func (settings *Provider) read(r reader, vpnType string) error {
|
|||||||
err = settings.readVPNUnlimited(r)
|
err = settings.readVPNUnlimited(r)
|
||||||
case constants.Vyprvpn:
|
case constants.Vyprvpn:
|
||||||
err = settings.readVyprvpn(r)
|
err = settings.readVyprvpn(r)
|
||||||
|
case constants.Wevpn:
|
||||||
|
err = settings.readWevpn(r)
|
||||||
case constants.Windscribe:
|
case constants.Windscribe:
|
||||||
err = settings.readWindscribe(r)
|
err = settings.readWindscribe(r)
|
||||||
default:
|
default:
|
||||||
@@ -104,7 +106,8 @@ func (settings *Provider) readVPNServiceProvider(r reader, vpnType string) (err
|
|||||||
constants.Custom,
|
constants.Custom,
|
||||||
"cyberghost", "fastestvpn", "hidemyass", "ipvanish", "ivpn", "mullvad", "nordvpn",
|
"cyberghost", "fastestvpn", "hidemyass", "ipvanish", "ivpn", "mullvad", "nordvpn",
|
||||||
"privado", "pia", "private internet access", "privatevpn", "protonvpn",
|
"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:
|
case constants.Wireguard:
|
||||||
allowedVPNServiceProviders = []string{
|
allowedVPNServiceProviders = []string{
|
||||||
constants.Custom, constants.Ivpn,
|
constants.Custom, constants.Ivpn,
|
||||||
|
|||||||
@@ -322,6 +322,27 @@ func Test_Provider_lines(t *testing.T) {
|
|||||||
" |--Protocol: udp",
|
" |--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": {
|
"windscribe": {
|
||||||
settings: Provider{
|
settings: Provider{
|
||||||
Name: constants.Windscribe,
|
Name: constants.Windscribe,
|
||||||
|
|||||||
@@ -17,9 +17,9 @@ type ServerSelection struct { //nolint:maligned
|
|||||||
|
|
||||||
// Fastestvpn, HideMyAss, IPVanish, IVPN, Mullvad, PrivateVPN, Protonvpn, PureVPN, VPNUnlimited
|
// Fastestvpn, HideMyAss, IPVanish, IVPN, Mullvad, PrivateVPN, Protonvpn, PureVPN, VPNUnlimited
|
||||||
Countries []string `json:"countries"`
|
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"`
|
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"`
|
Hostnames []string `json:"hostnames"`
|
||||||
Names []string `json:"names"` // Protonvpn
|
Names []string `json:"names"` // Protonvpn
|
||||||
|
|
||||||
@@ -100,7 +100,7 @@ func (selection ServerSelection) toLines() (lines []string) {
|
|||||||
type OpenVPNSelection struct {
|
type OpenVPNSelection struct {
|
||||||
ConfFile string `json:"conf_file"` // Custom configuration file path
|
ConfFile string `json:"conf_file"` // Custom configuration file path
|
||||||
TCP bool `json:"tcp"` // UDP if TCP is false
|
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
|
EncPreset string `json:"encryption_preset"` // PIA - needed to get the port number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ type Updater struct {
|
|||||||
Torguard bool `json:"torguard"`
|
Torguard bool `json:"torguard"`
|
||||||
VPNUnlimited bool `json:"vpnunlimited"`
|
VPNUnlimited bool `json:"vpnunlimited"`
|
||||||
Vyprvpn bool `json:"vyprvpn"`
|
Vyprvpn bool `json:"vyprvpn"`
|
||||||
|
Wevpn bool `json:"wevpn"`
|
||||||
Windscribe bool `json:"windscribe"`
|
Windscribe bool `json:"windscribe"`
|
||||||
// The two below should be used in CLI mode only
|
// The two below should be used in CLI mode only
|
||||||
CLI bool `json:"-"`
|
CLI bool `json:"-"`
|
||||||
@@ -65,6 +66,7 @@ func (settings *Updater) EnableAll() {
|
|||||||
settings.Torguard = true
|
settings.Torguard = true
|
||||||
settings.VPNUnlimited = true
|
settings.VPNUnlimited = true
|
||||||
settings.Vyprvpn = true
|
settings.Vyprvpn = true
|
||||||
|
settings.Wevpn = true
|
||||||
settings.Windscribe = 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"
|
VPNUnlimited = "vpn unlimited"
|
||||||
// Vyprvpn is a VPN provider.
|
// Vyprvpn is a VPN provider.
|
||||||
Vyprvpn = "vyprvpn"
|
Vyprvpn = "vyprvpn"
|
||||||
|
// WeVPN is a VPN provider.
|
||||||
|
Wevpn = "wevpn"
|
||||||
// Windscribe is a VPN provider.
|
// Windscribe is a VPN provider.
|
||||||
Windscribe = "windscribe"
|
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
|
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) {
|
func (a *AllServers) GetWindscribe() (servers []WindscribeServer) {
|
||||||
if a.Windscribe.Servers == nil {
|
if a.Windscribe.Servers == nil {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -239,6 +239,19 @@ func (s *VyprvpnServer) ToMarkdown() (markdown string) {
|
|||||||
boolToMarkdown(s.TCP), boolToMarkdown(s.UDP))
|
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) {
|
func (s *WindscribeServers) ToMarkdown() (markdown string) {
|
||||||
markdown = markdownTableHeading("Region", "City", "Hostname", "VPN")
|
markdown = markdownTableHeading("Region", "City", "Hostname", "VPN")
|
||||||
for _, server := range s.Servers {
|
for _, server := range s.Servers {
|
||||||
|
|||||||
@@ -157,6 +157,14 @@ type VyprvpnServer struct {
|
|||||||
IPs []net.IP `json:"ips"`
|
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 {
|
type WindscribeServer struct {
|
||||||
VPN string `json:"vpn"`
|
VPN string `json:"vpn"`
|
||||||
Region string `json:"region"`
|
Region string `json:"region"`
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ type AllServers struct {
|
|||||||
Torguard TorguardServers `json:"torguard"`
|
Torguard TorguardServers `json:"torguard"`
|
||||||
VPNUnlimited VPNUnlimitedServers `json:"vpnunlimited"`
|
VPNUnlimited VPNUnlimitedServers `json:"vpnunlimited"`
|
||||||
Vyprvpn VyprvpnServers `json:"vyprvpn"`
|
Vyprvpn VyprvpnServers `json:"vyprvpn"`
|
||||||
|
Wevpn WevpnServers `json:"wevpn"`
|
||||||
Windscribe WindscribeServers `json:"windscribe"`
|
Windscribe WindscribeServers `json:"windscribe"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,6 +39,7 @@ func (a *AllServers) Count() int {
|
|||||||
len(a.Torguard.Servers) +
|
len(a.Torguard.Servers) +
|
||||||
len(a.VPNUnlimited.Servers) +
|
len(a.VPNUnlimited.Servers) +
|
||||||
len(a.Vyprvpn.Servers) +
|
len(a.Vyprvpn.Servers) +
|
||||||
|
len(a.Wevpn.Servers) +
|
||||||
len(a.Windscribe.Servers)
|
len(a.Windscribe.Servers)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,6 +123,11 @@ type VyprvpnServers struct {
|
|||||||
Timestamp int64 `json:"timestamp"`
|
Timestamp int64 `json:"timestamp"`
|
||||||
Servers []VyprvpnServer `json:"servers"`
|
Servers []VyprvpnServer `json:"servers"`
|
||||||
}
|
}
|
||||||
|
type WevpnServers struct {
|
||||||
|
Version uint16 `json:"version"`
|
||||||
|
Timestamp int64 `json:"timestamp"`
|
||||||
|
Servers []WevpnServer `json:"servers"`
|
||||||
|
}
|
||||||
type WindscribeServers struct {
|
type WindscribeServers struct {
|
||||||
Version uint16 `json:"version"`
|
Version uint16 `json:"version"`
|
||||||
Timestamp int64 `json:"timestamp"`
|
Timestamp int64 `json:"timestamp"`
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import (
|
|||||||
"github.com/qdm12/gluetun/internal/provider/torguard"
|
"github.com/qdm12/gluetun/internal/provider/torguard"
|
||||||
"github.com/qdm12/gluetun/internal/provider/vpnunlimited"
|
"github.com/qdm12/gluetun/internal/provider/vpnunlimited"
|
||||||
"github.com/qdm12/gluetun/internal/provider/vyprvpn"
|
"github.com/qdm12/gluetun/internal/provider/vyprvpn"
|
||||||
|
"github.com/qdm12/gluetun/internal/provider/wevpn"
|
||||||
"github.com/qdm12/gluetun/internal/provider/windscribe"
|
"github.com/qdm12/gluetun/internal/provider/windscribe"
|
||||||
"github.com/qdm12/golibs/logging"
|
"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)
|
return vpnunlimited.New(allServers.VPNUnlimited.Servers, randSource)
|
||||||
case constants.Vyprvpn:
|
case constants.Vyprvpn:
|
||||||
return vyprvpn.New(allServers.Vyprvpn.Servers, randSource)
|
return vyprvpn.New(allServers.Vyprvpn.Servers, randSource)
|
||||||
|
case constants.Wevpn:
|
||||||
|
return wevpn.New(allServers.Wevpn.Servers, randSource)
|
||||||
case constants.Windscribe:
|
case constants.Windscribe:
|
||||||
return windscribe.New(allServers.Windscribe.Servers, randSource)
|
return windscribe.New(allServers.Windscribe.Servers, randSource)
|
||||||
default:
|
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,
|
version: allServers.Vyprvpn.Version,
|
||||||
digest: "58de06d8",
|
digest: "58de06d8",
|
||||||
},
|
},
|
||||||
|
"Wevpn": {
|
||||||
|
model: models.WevpnServer{},
|
||||||
|
version: allServers.Wevpn.Version,
|
||||||
|
digest: "f4daa186",
|
||||||
|
},
|
||||||
"Windscribe": {
|
"Windscribe": {
|
||||||
model: models.WindscribeServer{},
|
model: models.WindscribeServer{},
|
||||||
version: allServers.Windscribe.Version,
|
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),
|
Torguard: s.mergeTorguard(hardcoded.Torguard, persisted.Torguard),
|
||||||
VPNUnlimited: s.mergeVPNUnlimited(hardcoded.VPNUnlimited, persisted.VPNUnlimited),
|
VPNUnlimited: s.mergeVPNUnlimited(hardcoded.VPNUnlimited, persisted.VPNUnlimited),
|
||||||
Vyprvpn: s.mergeVyprvpn(hardcoded.Vyprvpn, persisted.Vyprvpn),
|
Vyprvpn: s.mergeVyprvpn(hardcoded.Vyprvpn, persisted.Vyprvpn),
|
||||||
|
Wevpn: s.mergeWevpn(hardcoded.Wevpn, persisted.Wevpn),
|
||||||
Windscribe: s.mergeWindscribe(hardcoded.Windscribe, persisted.Windscribe),
|
Windscribe: s.mergeWindscribe(hardcoded.Windscribe, persisted.Windscribe),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -279,6 +280,21 @@ func (s *Storage) mergeVyprvpn(hardcoded, persisted models.VyprvpnServers) model
|
|||||||
return persisted
|
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 {
|
func (s *Storage) mergeWindscribe(hardcoded, persisted models.WindscribeServers) models.WindscribeServers {
|
||||||
if persisted.Timestamp <= hardcoded.Timestamp {
|
if persisted.Timestamp <= hardcoded.Timestamp {
|
||||||
return hardcoded
|
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": {
|
"windscribe": {
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"timestamp": 1629420754,
|
"timestamp": 1629420754,
|
||||||
@@ -115123,4 +115750,4 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ func countServers(allServers models.AllServers) int {
|
|||||||
len(allServers.Torguard.Servers) +
|
len(allServers.Torguard.Servers) +
|
||||||
len(allServers.VPNUnlimited.Servers) +
|
len(allServers.VPNUnlimited.Servers) +
|
||||||
len(allServers.Vyprvpn.Servers) +
|
len(allServers.Vyprvpn.Servers) +
|
||||||
|
len(allServers.Wevpn.Servers) +
|
||||||
len(allServers.Windscribe.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/torguard"
|
||||||
"github.com/qdm12/gluetun/internal/updater/providers/vpnunlimited"
|
"github.com/qdm12/gluetun/internal/updater/providers/vpnunlimited"
|
||||||
"github.com/qdm12/gluetun/internal/updater/providers/vyprvpn"
|
"github.com/qdm12/gluetun/internal/updater/providers/vyprvpn"
|
||||||
|
"github.com/qdm12/gluetun/internal/updater/providers/wevpn"
|
||||||
"github.com/qdm12/gluetun/internal/updater/providers/windscribe"
|
"github.com/qdm12/gluetun/internal/updater/providers/windscribe"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -357,6 +358,27 @@ func (u *updater) updateVyprvpn(ctx context.Context) (err error) {
|
|||||||
return nil
|
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) {
|
func (u *updater) updateWindscribe(ctx context.Context) (err error) {
|
||||||
minServers := getMinServers(len(u.servers.Windscribe.Servers))
|
minServers := getMinServers(len(u.servers.Windscribe.Servers))
|
||||||
servers, err := windscribe.GetServers(ctx, u.client, minServers)
|
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 {
|
if u.options.Windscribe {
|
||||||
u.logger.Info("updating Windscribe servers...")
|
u.logger.Info("updating Windscribe servers...")
|
||||||
if err := u.updateWindscribe(ctx); err != nil {
|
if err := u.updateWindscribe(ctx); err != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user