Feat: Perfect privacy support (#606)
This commit is contained in:
3
.github/labels.yml
vendored
3
.github/labels.yml
vendored
@@ -39,6 +39,9 @@
|
||||
- name: ":cloud: NordVPN"
|
||||
color: "cfe8d4"
|
||||
description: ""
|
||||
- name: ":cloud: Perfect Privacy"
|
||||
color: "cfe8d4"
|
||||
description: ""
|
||||
- name: ":cloud: PIA"
|
||||
color: "cfe8d4"
|
||||
description: ""
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Gluetun VPN client
|
||||
|
||||
*Lightweight swiss-knife-like VPN client to tunnel to Cyberghost, ExpressVPN, FastestVPN,
|
||||
HideMyAss, IPVanish, IVPN, Mullvad, NordVPN, Privado, Private Internet Access, PrivateVPN,
|
||||
HideMyAss, IPVanish, IVPN, Mullvad, NordVPN, Perfect Privacy, Privado, Private Internet Access, PrivateVPN,
|
||||
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*
|
||||
|
||||
@@ -62,7 +62,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**, **ExpressVPN**, **FastestVPN**, **HideMyAss**, **IPVanish**, **IVPN**, **Mullvad**, **NordVPN**, **Privado**, **Private Internet Access**, **PrivateVPN**, **ProtonVPN**, **PureVPN**, **Surfshark**, **TorGuard**, **VPNUnlimited**, **Vyprvpn**, **WeVPN**, **Windscribe** servers
|
||||
- Supports: **Cyberghost**, **ExpressVPN**, **FastestVPN**, **HideMyAss**, **IPVanish**, **IVPN**, **Mullvad**, **NordVPN**, **Perfect Privacy**, **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**
|
||||
|
||||
@@ -26,7 +26,7 @@ var (
|
||||
func (c *CLI) FormatServers(args []string) error {
|
||||
var format, output string
|
||||
var cyberghost, expressvpn, fastestvpn, hideMyAss, ipvanish, ivpn, mullvad,
|
||||
nordvpn, pia, privado, privatevpn, protonvpn, purevpn, surfshark,
|
||||
nordvpn, perfectPrivacy, pia, privado, privatevpn, protonvpn, purevpn, surfshark,
|
||||
torguard, vpnUnlimited, vyprvpn, wevpn, windscribe bool
|
||||
flagSet := flag.NewFlagSet("markdown", flag.ExitOnError)
|
||||
flagSet.StringVar(&format, "format", "markdown", "Format to use which can be: 'markdown'")
|
||||
@@ -39,6 +39,7 @@ func (c *CLI) FormatServers(args []string) error {
|
||||
flagSet.BoolVar(&ivpn, "ivpn", false, "Format IVPN servers")
|
||||
flagSet.BoolVar(&mullvad, "mullvad", false, "Format Mullvad servers")
|
||||
flagSet.BoolVar(&nordvpn, "nordvpn", false, "Format Nordvpn servers")
|
||||
flagSet.BoolVar(&perfectPrivacy, "perfectprivacy", false, "Format Perfect Privacy servers")
|
||||
flagSet.BoolVar(&pia, "pia", false, "Format Private Internet Access servers")
|
||||
flagSet.BoolVar(&privado, "privado", false, "Format Privado servers")
|
||||
flagSet.BoolVar(&privatevpn, "privatevpn", false, "Format Private VPN servers")
|
||||
@@ -83,6 +84,8 @@ func (c *CLI) FormatServers(args []string) error {
|
||||
formatted = currentServers.Mullvad.ToMarkdown()
|
||||
case nordvpn:
|
||||
formatted = currentServers.Nordvpn.ToMarkdown()
|
||||
case perfectPrivacy:
|
||||
formatted = currentServers.Perfectprivacy.ToMarkdown()
|
||||
case pia:
|
||||
formatted = currentServers.Pia.ToMarkdown()
|
||||
case privado:
|
||||
|
||||
@@ -51,6 +51,7 @@ func (c *CLI) Update(ctx context.Context, args []string, logger UpdaterLogger) e
|
||||
flagSet.BoolVar(&options.Ivpn, "ivpn", false, "Update IVPN servers")
|
||||
flagSet.BoolVar(&options.Mullvad, "mullvad", false, "Update Mullvad servers")
|
||||
flagSet.BoolVar(&options.Nordvpn, "nordvpn", false, "Update Nordvpn servers")
|
||||
flagSet.BoolVar(&options.Perfectprivacy, "perfectprivacy", false, "Update Perfect Privacy servers")
|
||||
flagSet.BoolVar(&options.PIA, "pia", false, "Update Private Internet Access post-summer 2020 servers")
|
||||
flagSet.BoolVar(&options.Privado, "privado", false, "Update Privado servers")
|
||||
flagSet.BoolVar(&options.Privatevpn, "privatevpn", false, "Update Private VPN servers")
|
||||
|
||||
43
internal/configuration/perfectprivacy.go
Normal file
43
internal/configuration/perfectprivacy.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package configuration
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
)
|
||||
|
||||
func (settings *Provider) readPerfectPrivacy(r reader) (err error) {
|
||||
settings.Name = constants.Perfectprivacy
|
||||
servers := r.servers.GetPerfectprivacy()
|
||||
|
||||
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.PerfectprivacyCityChoices(servers))
|
||||
if err != nil {
|
||||
return fmt.Errorf("environment variable CITY: %w", err)
|
||||
}
|
||||
|
||||
return settings.ServerSelection.OpenVPN.readPerfectPrivacy(r)
|
||||
}
|
||||
|
||||
func (settings *OpenVPNSelection) readPerfectPrivacy(r reader) (err error) {
|
||||
settings.TCP, err = readOpenVPNProtocol(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
portValidation := openvpnPortValidation{
|
||||
tcp: settings.TCP,
|
||||
allowedTCP: []uint16{44, 443, 4433},
|
||||
allowedUDP: []uint16{44, 443, 4433},
|
||||
}
|
||||
settings.CustomPort, err = readOpenVPNCustomPort(r, portValidation)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -67,6 +67,8 @@ func (settings *Provider) read(r reader, vpnType string) error {
|
||||
err = settings.readMullvad(r)
|
||||
case constants.Nordvpn:
|
||||
err = settings.readNordvpn(r)
|
||||
case constants.Perfectprivacy:
|
||||
err = settings.readPerfectPrivacy(r)
|
||||
case constants.Privado:
|
||||
err = settings.readPrivado(r)
|
||||
case constants.PrivateInternetAccess:
|
||||
@@ -108,7 +110,7 @@ func (settings *Provider) readVPNServiceProvider(r reader, vpnType string) (err
|
||||
constants.Custom,
|
||||
"cyberghost", constants.Expressvpn, "fastestvpn", "hidemyass", "ipvanish",
|
||||
"ivpn", "mullvad", "nordvpn",
|
||||
"privado", "pia", "private internet access", "privatevpn", "protonvpn",
|
||||
constants.Perfectprivacy, "privado", "pia", "private internet access", "privatevpn", "protonvpn",
|
||||
"purevpn", "surfshark", "torguard", constants.VPNUnlimited, "vyprvpn",
|
||||
constants.Wevpn, "windscribe"}
|
||||
case constants.Wireguard:
|
||||
|
||||
@@ -168,6 +168,21 @@ func Test_Provider_lines(t *testing.T) {
|
||||
" |--Protocol: udp",
|
||||
},
|
||||
},
|
||||
"perfectprivacy": {
|
||||
settings: Provider{
|
||||
Name: constants.Perfectprivacy,
|
||||
ServerSelection: ServerSelection{
|
||||
VPN: constants.OpenVPN,
|
||||
Cities: []string{"a", "b"},
|
||||
},
|
||||
},
|
||||
lines: []string{
|
||||
"|--Perfect Privacy settings:",
|
||||
" |--Cities: a, b",
|
||||
" |--OpenVPN selection:",
|
||||
" |--Protocol: udp",
|
||||
},
|
||||
},
|
||||
"privado": {
|
||||
settings: Provider{
|
||||
Name: constants.Privado,
|
||||
|
||||
@@ -17,7 +17,8 @@ type ServerSelection struct { //nolint:maligned
|
||||
|
||||
// Expressvpn, Fastestvpn, HideMyAss, IPVanish, IVPN, Mullvad, PrivateVPN, Protonvpn, PureVPN, VPNUnlimited
|
||||
Countries []string `json:"countries"`
|
||||
// Expressvpn, HideMyAss, IPVanish, IVPN, Mullvad, PrivateVPN, Protonvpn, PureVPN, VPNUnlimited, WeVPN, Windscribe
|
||||
// Expressvpn, HideMyAss, IPVanish, IVPN, Mullvad, Perfectprivacy, PrivateVPN, Protonvpn,
|
||||
// PureVPN, VPNUnlimited, WeVPN, Windscribe
|
||||
Cities []string `json:"cities"`
|
||||
// Expressvpn, Fastestvpn, HideMyAss, IPVanish, IVPN, PrivateVPN, Windscribe, Privado, Protonvpn, VPNUnlimited, WeVPN
|
||||
Hostnames []string `json:"hostnames"`
|
||||
|
||||
@@ -9,27 +9,28 @@ import (
|
||||
)
|
||||
|
||||
type Updater struct {
|
||||
Period time.Duration `json:"period"`
|
||||
DNSAddress string `json:"dns_address"`
|
||||
Cyberghost bool `json:"cyberghost"`
|
||||
Expressvpn bool `json:"expressvpn"`
|
||||
Fastestvpn bool `json:"fastestvpn"`
|
||||
HideMyAss bool `json:"hidemyass"`
|
||||
Ipvanish bool `json:"ipvanish"`
|
||||
Ivpn bool `json:"ivpn"`
|
||||
Mullvad bool `json:"mullvad"`
|
||||
Nordvpn bool `json:"nordvpn"`
|
||||
PIA bool `json:"pia"`
|
||||
Privado bool `json:"privado"`
|
||||
Privatevpn bool `json:"privatevpn"`
|
||||
Protonvpn bool `json:"protonvpn"`
|
||||
Purevpn bool `json:"purevpn"`
|
||||
Surfshark bool `json:"surfshark"`
|
||||
Torguard bool `json:"torguard"`
|
||||
VPNUnlimited bool `json:"vpnunlimited"`
|
||||
Vyprvpn bool `json:"vyprvpn"`
|
||||
Wevpn bool `json:"wevpn"`
|
||||
Windscribe bool `json:"windscribe"`
|
||||
Period time.Duration `json:"period"`
|
||||
DNSAddress string `json:"dns_address"`
|
||||
Cyberghost bool `json:"cyberghost"`
|
||||
Expressvpn bool `json:"expressvpn"`
|
||||
Fastestvpn bool `json:"fastestvpn"`
|
||||
HideMyAss bool `json:"hidemyass"`
|
||||
Ipvanish bool `json:"ipvanish"`
|
||||
Ivpn bool `json:"ivpn"`
|
||||
Mullvad bool `json:"mullvad"`
|
||||
Nordvpn bool `json:"nordvpn"`
|
||||
Perfectprivacy bool `json:"perfectprivacy"`
|
||||
PIA bool `json:"pia"`
|
||||
Privado bool `json:"privado"`
|
||||
Privatevpn bool `json:"privatevpn"`
|
||||
Protonvpn bool `json:"protonvpn"`
|
||||
Purevpn bool `json:"purevpn"`
|
||||
Surfshark bool `json:"surfshark"`
|
||||
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:"-"`
|
||||
}
|
||||
@@ -57,6 +58,7 @@ func (settings *Updater) EnableAll() {
|
||||
settings.Ivpn = true
|
||||
settings.Mullvad = true
|
||||
settings.Nordvpn = true
|
||||
settings.Perfectprivacy = true
|
||||
settings.Privado = true
|
||||
settings.PIA = true
|
||||
settings.Privado = true
|
||||
|
||||
21
internal/constants/perfectprivacy.go
Normal file
21
internal/constants/perfectprivacy.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package constants
|
||||
|
||||
import (
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
)
|
||||
|
||||
//nolint:lll
|
||||
const (
|
||||
PerfectprivacyCA = "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"
|
||||
PerfectprivacyCert = "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"
|
||||
PerfectprivacyPrivateKey = "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"
|
||||
PerfectprivacyTLSCryptOpenvpnStaticKeyV1 = "d10a8e2641f5834f6c5e04a6ee9a798553d338fa2836ef2a91057c1f6174a3a12b36f16d1110b20e42ae94d3bd579213e9c3770be6c74804348dddba876945a5a3ab7660f9436f85f331641f6efc81315f0d12b2766a9f15c10a53cf9ba32dc80f03b5f15a6cc6987bda795dbe83443ec81f3d5e161cd47fab6b1f125b3adeee1eae33370d018594e0ff6b25b815228d27371b32c82a95f4929d3abb5fa36e57bf1f42353542568fbb8233f4645f05820275f79570cb8bbcf8010fc5d20f07d031a8227d45daf7349e34158c91a3d4e5add19cfa02f683f87609f6525fa0594016d11abf2de649f83ad54edd3e74e032e34b1bca685b8499916826d9aee11c13"
|
||||
)
|
||||
|
||||
func PerfectprivacyCityChoices(servers []models.PerfectprivacyServer) (choices []string) {
|
||||
choices = make([]string, len(servers))
|
||||
for i := range servers {
|
||||
choices[i] = servers[i].City
|
||||
}
|
||||
return makeUnique(choices)
|
||||
}
|
||||
@@ -25,6 +25,8 @@ const (
|
||||
Mullvad = "mullvad"
|
||||
// Nordvpn is a VPN provider.
|
||||
Nordvpn = "nordvpn"
|
||||
// Perfectprivacy is a VPN provider.
|
||||
Perfectprivacy = "perfect privacy"
|
||||
// Privado is a VPN provider.
|
||||
Privado = "privado"
|
||||
// PrivateInternetAccess is a VPN provider.
|
||||
|
||||
@@ -14,6 +14,7 @@ func (a AllServers) GetCopy() (servers AllServers) {
|
||||
servers.Ivpn.Servers = a.GetIvpn()
|
||||
servers.Mullvad.Servers = a.GetMullvad()
|
||||
servers.Nordvpn.Servers = a.GetNordvpn()
|
||||
servers.Perfectprivacy.Servers = a.GetPerfectprivacy()
|
||||
servers.Privado.Servers = a.GetPrivado()
|
||||
servers.Pia.Servers = a.GetPia()
|
||||
servers.Privatevpn.Servers = a.GetPrivatevpn()
|
||||
@@ -124,6 +125,18 @@ func (a *AllServers) GetNordvpn() (servers []NordvpnServer) {
|
||||
return servers
|
||||
}
|
||||
|
||||
func (a *AllServers) GetPerfectprivacy() (servers []PerfectprivacyServer) {
|
||||
if a.Perfectprivacy.Servers == nil {
|
||||
return nil
|
||||
}
|
||||
servers = make([]PerfectprivacyServer, len(a.Perfectprivacy.Servers))
|
||||
for i, serverToCopy := range a.Perfectprivacy.Servers {
|
||||
servers[i] = serverToCopy
|
||||
servers[i].IPs = copyIPs(serverToCopy.IPs)
|
||||
}
|
||||
return servers
|
||||
}
|
||||
|
||||
func (a *AllServers) GetPia() (servers []PIAServer) {
|
||||
if a.Pia.Servers == nil {
|
||||
return nil
|
||||
|
||||
@@ -51,6 +51,11 @@ func Test_AllServers_GetCopy(t *testing.T) {
|
||||
IP: net.IP{1, 2, 3, 4},
|
||||
}},
|
||||
},
|
||||
Perfectprivacy: PerfectprivacyServers{
|
||||
Servers: []PerfectprivacyServer{{
|
||||
IPs: []net.IP{{1, 2, 3, 4}},
|
||||
}},
|
||||
},
|
||||
Privado: PrivadoServers{
|
||||
Servers: []PrivadoServer{{
|
||||
IP: net.IP{1, 2, 3, 4},
|
||||
|
||||
@@ -141,6 +141,19 @@ func (s *PrivadoServer) ToMarkdown() (markdown string) {
|
||||
s.Country, s.Region, s.City, s.Hostname)
|
||||
}
|
||||
|
||||
func (s *PerfectprivacyServers) ToMarkdown() (markdown string) {
|
||||
markdown = markdownTableHeading("City", "TCP", "UDP")
|
||||
for _, server := range s.Servers {
|
||||
markdown += server.ToMarkdown() + "\n"
|
||||
}
|
||||
return markdown
|
||||
}
|
||||
|
||||
func (s *PerfectprivacyServer) ToMarkdown() (markdown string) {
|
||||
return fmt.Sprintf("| %s | %s | %s |",
|
||||
s.City, boolToMarkdown(s.TCP), boolToMarkdown(s.UDP))
|
||||
}
|
||||
|
||||
func (s *PiaServers) ToMarkdown() (markdown string) {
|
||||
markdown = markdownTableHeading("Region", "Hostname", "TCP", "UDP")
|
||||
for _, server := range s.Servers {
|
||||
|
||||
@@ -81,6 +81,13 @@ type NordvpnServer struct { //nolint:maligned
|
||||
UDP bool `json:"udp"`
|
||||
}
|
||||
|
||||
type PerfectprivacyServer struct {
|
||||
City string `json:"city"` // primary key
|
||||
IPs []net.IP `json:"ips"`
|
||||
TCP bool `json:"tcp"`
|
||||
UDP bool `json:"udp"`
|
||||
}
|
||||
|
||||
type PrivadoServer struct {
|
||||
Country string `json:"country"`
|
||||
Region string `json:"region"`
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
package models
|
||||
|
||||
type AllServers struct {
|
||||
Version uint16 `json:"version"` // used for migration of the top level scheme
|
||||
Cyberghost CyberghostServers `json:"cyberghost"`
|
||||
Expressvpn ExpressvpnServers `json:"expressvpn"`
|
||||
Fastestvpn FastestvpnServers `json:"fastestvpn"`
|
||||
HideMyAss HideMyAssServers `json:"hidemyass"`
|
||||
Ipvanish IpvanishServers `json:"ipvanish"`
|
||||
Ivpn IvpnServers `json:"ivpn"`
|
||||
Mullvad MullvadServers `json:"mullvad"`
|
||||
Nordvpn NordvpnServers `json:"nordvpn"`
|
||||
Privado PrivadoServers `json:"privado"`
|
||||
Pia PiaServers `json:"pia"`
|
||||
Privatevpn PrivatevpnServers `json:"privatevpn"`
|
||||
Protonvpn ProtonvpnServers `json:"protonvpn"`
|
||||
Purevpn PurevpnServers `json:"purevpn"`
|
||||
Surfshark SurfsharkServers `json:"surfshark"`
|
||||
Torguard TorguardServers `json:"torguard"`
|
||||
VPNUnlimited VPNUnlimitedServers `json:"vpnunlimited"`
|
||||
Vyprvpn VyprvpnServers `json:"vyprvpn"`
|
||||
Wevpn WevpnServers `json:"wevpn"`
|
||||
Windscribe WindscribeServers `json:"windscribe"`
|
||||
Version uint16 `json:"version"` // used for migration of the top level scheme
|
||||
Cyberghost CyberghostServers `json:"cyberghost"`
|
||||
Expressvpn ExpressvpnServers `json:"expressvpn"`
|
||||
Fastestvpn FastestvpnServers `json:"fastestvpn"`
|
||||
HideMyAss HideMyAssServers `json:"hidemyass"`
|
||||
Ipvanish IpvanishServers `json:"ipvanish"`
|
||||
Ivpn IvpnServers `json:"ivpn"`
|
||||
Mullvad MullvadServers `json:"mullvad"`
|
||||
Perfectprivacy PerfectprivacyServers `json:"perfectprivacy"`
|
||||
Nordvpn NordvpnServers `json:"nordvpn"`
|
||||
Privado PrivadoServers `json:"privado"`
|
||||
Pia PiaServers `json:"pia"`
|
||||
Privatevpn PrivatevpnServers `json:"privatevpn"`
|
||||
Protonvpn ProtonvpnServers `json:"protonvpn"`
|
||||
Purevpn PurevpnServers `json:"purevpn"`
|
||||
Surfshark SurfsharkServers `json:"surfshark"`
|
||||
Torguard TorguardServers `json:"torguard"`
|
||||
VPNUnlimited VPNUnlimitedServers `json:"vpnunlimited"`
|
||||
Vyprvpn VyprvpnServers `json:"vyprvpn"`
|
||||
Wevpn WevpnServers `json:"wevpn"`
|
||||
Windscribe WindscribeServers `json:"windscribe"`
|
||||
}
|
||||
|
||||
func (a *AllServers) Count() int {
|
||||
@@ -32,6 +33,7 @@ func (a *AllServers) Count() int {
|
||||
len(a.Ivpn.Servers) +
|
||||
len(a.Mullvad.Servers) +
|
||||
len(a.Nordvpn.Servers) +
|
||||
len(a.Perfectprivacy.Servers) +
|
||||
len(a.Privado.Servers) +
|
||||
len(a.Pia.Servers) +
|
||||
len(a.Privatevpn.Servers) +
|
||||
@@ -85,6 +87,11 @@ type NordvpnServers struct {
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
Servers []NordvpnServer `json:"servers"`
|
||||
}
|
||||
type PerfectprivacyServers struct {
|
||||
Version uint16 `json:"version"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
Servers []PerfectprivacyServer `json:"servers"`
|
||||
}
|
||||
type PrivadoServers struct {
|
||||
Version uint16 `json:"version"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
|
||||
@@ -11,8 +11,6 @@ var (
|
||||
)
|
||||
|
||||
func extractPEM(b []byte, name string) (encodedData string, err error) {
|
||||
name = strings.ToUpper(name) // certificate -> CERTIFICATE
|
||||
|
||||
pemBlock, _ := pem.Decode(b)
|
||||
if pemBlock == nil {
|
||||
return "", errPEMDecode
|
||||
|
||||
@@ -25,7 +25,7 @@ func Test_extractPEM(t *testing.T) {
|
||||
err: errors.New("cannot decode PEM encoded block"),
|
||||
},
|
||||
"valid data": {
|
||||
name: "certificate",
|
||||
name: "CERTIFICATE",
|
||||
b: []byte(validCertPEM),
|
||||
encodedData: validCertData,
|
||||
},
|
||||
|
||||
37
internal/provider/perfectprivacy/connection.go
Normal file
37
internal/provider/perfectprivacy/connection.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package perfectprivacy
|
||||
|
||||
import (
|
||||
"github.com/qdm12/gluetun/internal/configuration"
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
"github.com/qdm12/gluetun/internal/provider/utils"
|
||||
)
|
||||
|
||||
func (p *Perfectprivacy) GetConnection(selection configuration.ServerSelection) (
|
||||
connection models.Connection, err error) {
|
||||
const defaultPort uint16 = 443
|
||||
port := defaultPort
|
||||
if selection.OpenVPN.CustomPort > 0 {
|
||||
port = selection.OpenVPN.CustomPort
|
||||
}
|
||||
protocol := utils.GetProtocol(selection)
|
||||
|
||||
servers, err := p.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, p.randSource)
|
||||
}
|
||||
26
internal/provider/perfectprivacy/filter.go
Normal file
26
internal/provider/perfectprivacy/filter.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package perfectprivacy
|
||||
|
||||
import (
|
||||
"github.com/qdm12/gluetun/internal/configuration"
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
"github.com/qdm12/gluetun/internal/provider/utils"
|
||||
)
|
||||
|
||||
func (p *Perfectprivacy) filterServers(selection configuration.ServerSelection) (
|
||||
servers []models.PerfectprivacyServer, err error) {
|
||||
for _, server := range p.servers {
|
||||
switch {
|
||||
case
|
||||
utils.FilterByPossibilities(server.City, selection.Cities),
|
||||
utils.FilterByProtocol(selection, server.TCP, server.UDP):
|
||||
default:
|
||||
servers = append(servers, server)
|
||||
}
|
||||
}
|
||||
|
||||
if len(servers) == 0 {
|
||||
return nil, utils.NoServerFoundError(selection)
|
||||
}
|
||||
|
||||
return servers, nil
|
||||
}
|
||||
89
internal/provider/perfectprivacy/openvpnconf.go
Normal file
89
internal/provider/perfectprivacy/openvpnconf.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package perfectprivacy
|
||||
|
||||
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 (p *Perfectprivacy) BuildConf(connection models.Connection,
|
||||
settings configuration.OpenVPN) (lines []string, err error) {
|
||||
if settings.Cipher == "" {
|
||||
// TODO add AES 256 GCM
|
||||
settings.Cipher = constants.AES256cbc
|
||||
}
|
||||
|
||||
if settings.Auth == "" {
|
||||
settings.Auth = constants.SHA512
|
||||
}
|
||||
|
||||
if settings.MSSFix == 0 {
|
||||
settings.MSSFix = 1450
|
||||
}
|
||||
|
||||
lines = []string{
|
||||
"client",
|
||||
"nobind",
|
||||
"tls-exit",
|
||||
"dev " + settings.Interface,
|
||||
"verb " + strconv.Itoa(settings.Verbosity),
|
||||
|
||||
// Perfect Privacy specific
|
||||
"ping 5",
|
||||
"tun-mtu 1500",
|
||||
"tun-mtu-extra 32",
|
||||
"mssfix " + strconv.Itoa(int(settings.MSSFix)),
|
||||
"reneg-sec 3600",
|
||||
"key-direction 1",
|
||||
"tls-cipher TLS_CHACHA20_POLY1305_SHA256:TLS-DHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-RSA-WITH-AES-256-CBC-SHA:TLS-DHE-RSA-WITH-AES-128-GCM-SHA256:TLS-DHE-RSA-WITH-AES-128-CBC-SHA:TLS_AES_256_GCM_SHA384:TLS-RSA-WITH-AES-256-CBC-SHA", //nolint:lll
|
||||
"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(),
|
||||
}
|
||||
|
||||
lines = append(lines, utils.CipherLines(settings.Cipher, settings.Version)...)
|
||||
|
||||
if connection.Protocol == constants.UDP {
|
||||
lines = append(lines, "explicit-exit-notify")
|
||||
}
|
||||
|
||||
if !settings.Root {
|
||||
lines = append(lines, "user "+settings.ProcUser)
|
||||
lines = append(lines, "persist-tun")
|
||||
lines = append(lines, "persist-key")
|
||||
}
|
||||
|
||||
if !settings.IPv6 {
|
||||
lines = append(lines, `pull-filter ignore "route-ipv6"`)
|
||||
lines = append(lines, `pull-filter ignore "ifconfig-ipv6"`)
|
||||
// Perfect Privacy specific IPv6
|
||||
lines = append(lines, "redirect-gateway def1")
|
||||
lines = append(lines, `pull-filter ignore "redirect-gateway def1 ipv6"`)
|
||||
}
|
||||
|
||||
lines = append(lines, utils.WrapOpenvpnCA(
|
||||
constants.PerfectprivacyCA)...)
|
||||
lines = append(lines, utils.WrapOpenvpnCert(
|
||||
constants.PerfectprivacyCert)...)
|
||||
lines = append(lines, utils.WrapOpenvpnKey(
|
||||
constants.PerfectprivacyPrivateKey)...)
|
||||
lines = append(lines, utils.WrapOpenvpnTLSCrypt(
|
||||
constants.PerfectprivacyTLSCryptOpenvpnStaticKeyV1)...)
|
||||
|
||||
lines = append(lines, "")
|
||||
|
||||
return lines, nil
|
||||
}
|
||||
23
internal/provider/perfectprivacy/provider.go
Normal file
23
internal/provider/perfectprivacy/provider.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package perfectprivacy
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
"github.com/qdm12/gluetun/internal/provider/utils"
|
||||
)
|
||||
|
||||
type Perfectprivacy struct {
|
||||
servers []models.PerfectprivacyServer
|
||||
randSource rand.Source
|
||||
utils.NoPortForwarder
|
||||
}
|
||||
|
||||
func New(servers []models.PerfectprivacyServer, randSource rand.Source) *Perfectprivacy {
|
||||
return &Perfectprivacy{
|
||||
servers: servers,
|
||||
randSource: randSource,
|
||||
NoPortForwarder: utils.NewNoPortForwarding(constants.Perfectprivacy),
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"github.com/qdm12/gluetun/internal/provider/ivpn"
|
||||
"github.com/qdm12/gluetun/internal/provider/mullvad"
|
||||
"github.com/qdm12/gluetun/internal/provider/nordvpn"
|
||||
"github.com/qdm12/gluetun/internal/provider/perfectprivacy"
|
||||
"github.com/qdm12/gluetun/internal/provider/privado"
|
||||
"github.com/qdm12/gluetun/internal/provider/privateinternetaccess"
|
||||
"github.com/qdm12/gluetun/internal/provider/privatevpn"
|
||||
@@ -70,6 +71,8 @@ func New(provider string, allServers models.AllServers, timeNow func() time.Time
|
||||
return mullvad.New(allServers.Mullvad.Servers, randSource)
|
||||
case constants.Nordvpn:
|
||||
return nordvpn.New(allServers.Nordvpn.Servers, randSource)
|
||||
case constants.Perfectprivacy:
|
||||
return perfectprivacy.New(allServers.Perfectprivacy.Servers, randSource)
|
||||
case constants.Privado:
|
||||
return privado.New(allServers.Privado.Servers, randSource)
|
||||
case constants.PrivateInternetAccess:
|
||||
|
||||
@@ -88,6 +88,11 @@ func Test_versions(t *testing.T) {
|
||||
version: allServers.Nordvpn.Version,
|
||||
digest: "a8043704",
|
||||
},
|
||||
"Perfect privacy": {
|
||||
model: models.PerfectprivacyServer{},
|
||||
version: allServers.Perfectprivacy.Version,
|
||||
digest: "233f0dd4",
|
||||
},
|
||||
"Privado": {
|
||||
model: models.PrivadoServer{},
|
||||
version: allServers.Privado.Version,
|
||||
|
||||
@@ -28,26 +28,27 @@ func (s *Storage) logTimeDiff(provider string, persistedUnix, hardcodedUnix int6
|
||||
|
||||
func (s *Storage) mergeServers(hardcoded, persisted models.AllServers) models.AllServers {
|
||||
return models.AllServers{
|
||||
Version: hardcoded.Version,
|
||||
Cyberghost: s.mergeCyberghost(hardcoded.Cyberghost, persisted.Cyberghost),
|
||||
Expressvpn: s.mergeExpressvpn(hardcoded.Expressvpn, persisted.Expressvpn),
|
||||
Fastestvpn: s.mergeFastestvpn(hardcoded.Fastestvpn, persisted.Fastestvpn),
|
||||
HideMyAss: s.mergeHideMyAss(hardcoded.HideMyAss, persisted.HideMyAss),
|
||||
Ipvanish: s.mergeIpvanish(hardcoded.Ipvanish, persisted.Ipvanish),
|
||||
Ivpn: s.mergeIvpn(hardcoded.Ivpn, persisted.Ivpn),
|
||||
Mullvad: s.mergeMullvad(hardcoded.Mullvad, persisted.Mullvad),
|
||||
Nordvpn: s.mergeNordVPN(hardcoded.Nordvpn, persisted.Nordvpn),
|
||||
Privado: s.mergePrivado(hardcoded.Privado, persisted.Privado),
|
||||
Pia: s.mergePIA(hardcoded.Pia, persisted.Pia),
|
||||
Privatevpn: s.mergePrivatevpn(hardcoded.Privatevpn, persisted.Privatevpn),
|
||||
Protonvpn: s.mergeProtonvpn(hardcoded.Protonvpn, persisted.Protonvpn),
|
||||
Purevpn: s.mergePureVPN(hardcoded.Purevpn, persisted.Purevpn),
|
||||
Surfshark: s.mergeSurfshark(hardcoded.Surfshark, persisted.Surfshark),
|
||||
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),
|
||||
Version: hardcoded.Version,
|
||||
Cyberghost: s.mergeCyberghost(hardcoded.Cyberghost, persisted.Cyberghost),
|
||||
Expressvpn: s.mergeExpressvpn(hardcoded.Expressvpn, persisted.Expressvpn),
|
||||
Fastestvpn: s.mergeFastestvpn(hardcoded.Fastestvpn, persisted.Fastestvpn),
|
||||
HideMyAss: s.mergeHideMyAss(hardcoded.HideMyAss, persisted.HideMyAss),
|
||||
Ipvanish: s.mergeIpvanish(hardcoded.Ipvanish, persisted.Ipvanish),
|
||||
Ivpn: s.mergeIvpn(hardcoded.Ivpn, persisted.Ivpn),
|
||||
Mullvad: s.mergeMullvad(hardcoded.Mullvad, persisted.Mullvad),
|
||||
Nordvpn: s.mergeNordVPN(hardcoded.Nordvpn, persisted.Nordvpn),
|
||||
Perfectprivacy: s.mergePerfectprivacy(hardcoded.Perfectprivacy, persisted.Perfectprivacy),
|
||||
Privado: s.mergePrivado(hardcoded.Privado, persisted.Privado),
|
||||
Pia: s.mergePIA(hardcoded.Pia, persisted.Pia),
|
||||
Privatevpn: s.mergePrivatevpn(hardcoded.Privatevpn, persisted.Privatevpn),
|
||||
Protonvpn: s.mergeProtonvpn(hardcoded.Protonvpn, persisted.Protonvpn),
|
||||
Purevpn: s.mergePureVPN(hardcoded.Purevpn, persisted.Purevpn),
|
||||
Surfshark: s.mergeSurfshark(hardcoded.Surfshark, persisted.Surfshark),
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,6 +124,15 @@ func (s *Storage) mergeNordVPN(hardcoded, persisted models.NordvpnServers) model
|
||||
return persisted
|
||||
}
|
||||
|
||||
func (s *Storage) mergePerfectprivacy(hardcoded, persisted models.PerfectprivacyServers) models.PerfectprivacyServers {
|
||||
if persisted.Timestamp <= hardcoded.Timestamp {
|
||||
return hardcoded
|
||||
}
|
||||
|
||||
s.logTimeDiff("Perfect Privacy", persisted.Timestamp, hardcoded.Timestamp)
|
||||
return persisted
|
||||
}
|
||||
|
||||
func (s *Storage) mergePrivado(hardcoded, persisted models.PrivadoServers) models.PrivadoServers {
|
||||
if persisted.Timestamp <= hardcoded.Timestamp {
|
||||
return hardcoded
|
||||
|
||||
@@ -126,6 +126,15 @@ func (s *Storage) extractServersFromBytes(b []byte, hardcoded models.AllServers)
|
||||
}
|
||||
}
|
||||
|
||||
if hardcoded.Perfectprivacy.Version != versions.Perfectprivacy.Version {
|
||||
s.logVersionDiff("Perfect Privacy", hardcoded.Perfectprivacy.Version, versions.Perfectprivacy.Version)
|
||||
} else {
|
||||
err = json.Unmarshal(rawMessages.Perfectprivacy, &servers.Perfectprivacy)
|
||||
if err != nil {
|
||||
return servers, fmt.Errorf("%w: %s: %s", errDecodeProvider, "Perfect Privacy", err)
|
||||
}
|
||||
}
|
||||
|
||||
if hardcoded.Privado.Version != versions.Privado.Version {
|
||||
s.logVersionDiff("Privado", hardcoded.Privado.Version, versions.Privado.Version)
|
||||
} else {
|
||||
@@ -231,26 +240,27 @@ func (s *Storage) extractServersFromBytes(b []byte, hardcoded models.AllServers)
|
||||
// allVersions is a subset of models.AllServers structure used to track
|
||||
// versions to avoid unmarshaling errors.
|
||||
type allVersions struct {
|
||||
Version uint16 `json:"version"` // used for migration of the top level scheme
|
||||
Cyberghost serverVersion `json:"cyberghost"`
|
||||
Expressvpn serverVersion `json:"expressvpn"`
|
||||
Fastestvpn serverVersion `json:"fastestvpn"`
|
||||
HideMyAss serverVersion `json:"hidemyass"`
|
||||
Ipvanish serverVersion `json:"ipvanish"`
|
||||
Ivpn serverVersion `json:"ivpn"`
|
||||
Mullvad serverVersion `json:"mullvad"`
|
||||
Nordvpn serverVersion `json:"nordvpn"`
|
||||
Privado serverVersion `json:"privado"`
|
||||
Pia serverVersion `json:"pia"`
|
||||
Privatevpn serverVersion `json:"privatevpn"`
|
||||
Protonvpn serverVersion `json:"protonvpn"`
|
||||
Purevpn serverVersion `json:"purevpn"`
|
||||
Surfshark serverVersion `json:"surfshark"`
|
||||
Torguard serverVersion `json:"torguard"`
|
||||
VPNUnlimited serverVersion `json:"vpnunlimited"`
|
||||
Vyprvpn serverVersion `json:"vyprvpn"`
|
||||
Wevpn serverVersion `json:"wevpn"`
|
||||
Windscribe serverVersion `json:"windscribe"`
|
||||
Version uint16 `json:"version"` // used for migration of the top level scheme
|
||||
Cyberghost serverVersion `json:"cyberghost"`
|
||||
Expressvpn serverVersion `json:"expressvpn"`
|
||||
Fastestvpn serverVersion `json:"fastestvpn"`
|
||||
HideMyAss serverVersion `json:"hidemyass"`
|
||||
Ipvanish serverVersion `json:"ipvanish"`
|
||||
Ivpn serverVersion `json:"ivpn"`
|
||||
Mullvad serverVersion `json:"mullvad"`
|
||||
Nordvpn serverVersion `json:"nordvpn"`
|
||||
Perfectprivacy serverVersion `json:"perfectprivacy"`
|
||||
Privado serverVersion `json:"privado"`
|
||||
Pia serverVersion `json:"pia"`
|
||||
Privatevpn serverVersion `json:"privatevpn"`
|
||||
Protonvpn serverVersion `json:"protonvpn"`
|
||||
Purevpn serverVersion `json:"purevpn"`
|
||||
Surfshark serverVersion `json:"surfshark"`
|
||||
Torguard serverVersion `json:"torguard"`
|
||||
VPNUnlimited serverVersion `json:"vpnunlimited"`
|
||||
Vyprvpn serverVersion `json:"vyprvpn"`
|
||||
Wevpn serverVersion `json:"wevpn"`
|
||||
Windscribe serverVersion `json:"windscribe"`
|
||||
}
|
||||
|
||||
type serverVersion struct {
|
||||
@@ -259,24 +269,25 @@ type serverVersion struct {
|
||||
|
||||
// allJSONRawMessages is to delay decoding of each provider servers.
|
||||
type allJSONRawMessages struct {
|
||||
Version uint16 `json:"version"` // used for migration of the top level scheme
|
||||
Cyberghost json.RawMessage `json:"cyberghost"`
|
||||
Expressvpn json.RawMessage `json:"expressvpn"`
|
||||
Fastestvpn json.RawMessage `json:"fastestvpn"`
|
||||
HideMyAss json.RawMessage `json:"hidemyass"`
|
||||
Ipvanish json.RawMessage `json:"ipvanish"`
|
||||
Ivpn json.RawMessage `json:"ivpn"`
|
||||
Mullvad json.RawMessage `json:"mullvad"`
|
||||
Nordvpn json.RawMessage `json:"nordvpn"`
|
||||
Privado json.RawMessage `json:"privado"`
|
||||
Pia json.RawMessage `json:"pia"`
|
||||
Privatevpn json.RawMessage `json:"privatevpn"`
|
||||
Protonvpn json.RawMessage `json:"protonvpn"`
|
||||
Purevpn json.RawMessage `json:"purevpn"`
|
||||
Surfshark json.RawMessage `json:"surfshark"`
|
||||
Torguard json.RawMessage `json:"torguard"`
|
||||
VPNUnlimited json.RawMessage `json:"vpnunlimited"`
|
||||
Vyprvpn json.RawMessage `json:"vyprvpn"`
|
||||
Wevpn json.RawMessage `json:"wevpn"`
|
||||
Windscribe json.RawMessage `json:"windscribe"`
|
||||
Version uint16 `json:"version"` // used for migration of the top level scheme
|
||||
Cyberghost json.RawMessage `json:"cyberghost"`
|
||||
Expressvpn json.RawMessage `json:"expressvpn"`
|
||||
Fastestvpn json.RawMessage `json:"fastestvpn"`
|
||||
HideMyAss json.RawMessage `json:"hidemyass"`
|
||||
Ipvanish json.RawMessage `json:"ipvanish"`
|
||||
Ivpn json.RawMessage `json:"ivpn"`
|
||||
Mullvad json.RawMessage `json:"mullvad"`
|
||||
Nordvpn json.RawMessage `json:"nordvpn"`
|
||||
Perfectprivacy json.RawMessage `json:"perfectprivacy"`
|
||||
Privado json.RawMessage `json:"privado"`
|
||||
Pia json.RawMessage `json:"pia"`
|
||||
Privatevpn json.RawMessage `json:"privatevpn"`
|
||||
Protonvpn json.RawMessage `json:"protonvpn"`
|
||||
Purevpn json.RawMessage `json:"purevpn"`
|
||||
Surfshark json.RawMessage `json:"surfshark"`
|
||||
Torguard json.RawMessage `json:"torguard"`
|
||||
VPNUnlimited json.RawMessage `json:"vpnunlimited"`
|
||||
Vyprvpn json.RawMessage `json:"vyprvpn"`
|
||||
Wevpn json.RawMessage `json:"wevpn"`
|
||||
Windscribe json.RawMessage `json:"windscribe"`
|
||||
}
|
||||
|
||||
@@ -30,25 +30,26 @@ func Test_extractServersFromBytes(t *testing.T) {
|
||||
"different versions": {
|
||||
b: []byte(`{}`),
|
||||
hardcoded: models.AllServers{
|
||||
Cyberghost: models.CyberghostServers{Version: 1},
|
||||
Expressvpn: models.ExpressvpnServers{Version: 1},
|
||||
Fastestvpn: models.FastestvpnServers{Version: 1},
|
||||
HideMyAss: models.HideMyAssServers{Version: 1},
|
||||
Ipvanish: models.IpvanishServers{Version: 1},
|
||||
Ivpn: models.IvpnServers{Version: 1},
|
||||
Mullvad: models.MullvadServers{Version: 1},
|
||||
Nordvpn: models.NordvpnServers{Version: 1},
|
||||
Privado: models.PrivadoServers{Version: 1},
|
||||
Pia: models.PiaServers{Version: 1},
|
||||
Privatevpn: models.PrivatevpnServers{Version: 1},
|
||||
Protonvpn: models.ProtonvpnServers{Version: 1},
|
||||
Purevpn: models.PurevpnServers{Version: 1},
|
||||
Surfshark: models.SurfsharkServers{Version: 1},
|
||||
Torguard: models.TorguardServers{Version: 1},
|
||||
VPNUnlimited: models.VPNUnlimitedServers{Version: 1},
|
||||
Vyprvpn: models.VyprvpnServers{Version: 1},
|
||||
Wevpn: models.WevpnServers{Version: 1},
|
||||
Windscribe: models.WindscribeServers{Version: 1},
|
||||
Cyberghost: models.CyberghostServers{Version: 1},
|
||||
Expressvpn: models.ExpressvpnServers{Version: 1},
|
||||
Fastestvpn: models.FastestvpnServers{Version: 1},
|
||||
HideMyAss: models.HideMyAssServers{Version: 1},
|
||||
Ipvanish: models.IpvanishServers{Version: 1},
|
||||
Ivpn: models.IvpnServers{Version: 1},
|
||||
Mullvad: models.MullvadServers{Version: 1},
|
||||
Nordvpn: models.NordvpnServers{Version: 1},
|
||||
Perfectprivacy: models.PerfectprivacyServers{Version: 1},
|
||||
Privado: models.PrivadoServers{Version: 1},
|
||||
Pia: models.PiaServers{Version: 1},
|
||||
Privatevpn: models.PrivatevpnServers{Version: 1},
|
||||
Protonvpn: models.ProtonvpnServers{Version: 1},
|
||||
Purevpn: models.PurevpnServers{Version: 1},
|
||||
Surfshark: models.SurfsharkServers{Version: 1},
|
||||
Torguard: models.TorguardServers{Version: 1},
|
||||
VPNUnlimited: models.VPNUnlimitedServers{Version: 1},
|
||||
Vyprvpn: models.VyprvpnServers{Version: 1},
|
||||
Wevpn: models.WevpnServers{Version: 1},
|
||||
Windscribe: models.WindscribeServers{Version: 1},
|
||||
},
|
||||
logged: []string{
|
||||
"Cyberghost servers from file discarded because they have version 0 and hardcoded servers have version 1",
|
||||
@@ -59,6 +60,7 @@ func Test_extractServersFromBytes(t *testing.T) {
|
||||
"Ivpn servers from file discarded because they have version 0 and hardcoded servers have version 1",
|
||||
"Mullvad servers from file discarded because they have version 0 and hardcoded servers have version 1",
|
||||
"Nordvpn servers from file discarded because they have version 0 and hardcoded servers have version 1",
|
||||
"Perfect Privacy servers from file discarded because they have version 0 and hardcoded servers have version 1",
|
||||
"Privado servers from file discarded because they have version 0 and hardcoded servers have version 1",
|
||||
"Pia servers from file discarded because they have version 0 and hardcoded servers have version 1",
|
||||
"Privatevpn servers from file discarded because they have version 0 and hardcoded servers have version 1",
|
||||
@@ -82,6 +84,7 @@ func Test_extractServersFromBytes(t *testing.T) {
|
||||
"ivpn": {"version": 1, "timestamp": 1},
|
||||
"mullvad": {"version": 1, "timestamp": 1},
|
||||
"nordvpn": {"version": 1, "timestamp": 1},
|
||||
"perfectprivacy": {"version": 1, "timestamp": 1},
|
||||
"privado": {"version": 1, "timestamp": 1},
|
||||
"pia": {"version": 1, "timestamp": 1},
|
||||
"privatevpn": {"version": 1, "timestamp": 1},
|
||||
@@ -95,46 +98,48 @@ func Test_extractServersFromBytes(t *testing.T) {
|
||||
"windscribe": {"version": 1, "timestamp": 1}
|
||||
}`),
|
||||
hardcoded: models.AllServers{
|
||||
Cyberghost: models.CyberghostServers{Version: 1},
|
||||
Expressvpn: models.ExpressvpnServers{Version: 1},
|
||||
Fastestvpn: models.FastestvpnServers{Version: 1},
|
||||
HideMyAss: models.HideMyAssServers{Version: 1},
|
||||
Ipvanish: models.IpvanishServers{Version: 1},
|
||||
Ivpn: models.IvpnServers{Version: 1},
|
||||
Mullvad: models.MullvadServers{Version: 1},
|
||||
Nordvpn: models.NordvpnServers{Version: 1},
|
||||
Privado: models.PrivadoServers{Version: 1},
|
||||
Pia: models.PiaServers{Version: 1},
|
||||
Privatevpn: models.PrivatevpnServers{Version: 1},
|
||||
Protonvpn: models.ProtonvpnServers{Version: 1},
|
||||
Purevpn: models.PurevpnServers{Version: 1},
|
||||
Surfshark: models.SurfsharkServers{Version: 1},
|
||||
Torguard: models.TorguardServers{Version: 1},
|
||||
VPNUnlimited: models.VPNUnlimitedServers{Version: 1},
|
||||
Vyprvpn: models.VyprvpnServers{Version: 1},
|
||||
Wevpn: models.WevpnServers{Version: 1},
|
||||
Windscribe: models.WindscribeServers{Version: 1},
|
||||
Cyberghost: models.CyberghostServers{Version: 1},
|
||||
Expressvpn: models.ExpressvpnServers{Version: 1},
|
||||
Fastestvpn: models.FastestvpnServers{Version: 1},
|
||||
HideMyAss: models.HideMyAssServers{Version: 1},
|
||||
Ipvanish: models.IpvanishServers{Version: 1},
|
||||
Ivpn: models.IvpnServers{Version: 1},
|
||||
Mullvad: models.MullvadServers{Version: 1},
|
||||
Nordvpn: models.NordvpnServers{Version: 1},
|
||||
Perfectprivacy: models.PerfectprivacyServers{Version: 1},
|
||||
Privado: models.PrivadoServers{Version: 1},
|
||||
Pia: models.PiaServers{Version: 1},
|
||||
Privatevpn: models.PrivatevpnServers{Version: 1},
|
||||
Protonvpn: models.ProtonvpnServers{Version: 1},
|
||||
Purevpn: models.PurevpnServers{Version: 1},
|
||||
Surfshark: models.SurfsharkServers{Version: 1},
|
||||
Torguard: models.TorguardServers{Version: 1},
|
||||
VPNUnlimited: models.VPNUnlimitedServers{Version: 1},
|
||||
Vyprvpn: models.VyprvpnServers{Version: 1},
|
||||
Wevpn: models.WevpnServers{Version: 1},
|
||||
Windscribe: models.WindscribeServers{Version: 1},
|
||||
},
|
||||
persisted: models.AllServers{
|
||||
Cyberghost: models.CyberghostServers{Version: 1, Timestamp: 1},
|
||||
Expressvpn: models.ExpressvpnServers{Version: 1, Timestamp: 1},
|
||||
Fastestvpn: models.FastestvpnServers{Version: 1, Timestamp: 1},
|
||||
HideMyAss: models.HideMyAssServers{Version: 1, Timestamp: 1},
|
||||
Ipvanish: models.IpvanishServers{Version: 1, Timestamp: 1},
|
||||
Ivpn: models.IvpnServers{Version: 1, Timestamp: 1},
|
||||
Mullvad: models.MullvadServers{Version: 1, Timestamp: 1},
|
||||
Nordvpn: models.NordvpnServers{Version: 1, Timestamp: 1},
|
||||
Privado: models.PrivadoServers{Version: 1, Timestamp: 1},
|
||||
Pia: models.PiaServers{Version: 1, Timestamp: 1},
|
||||
Privatevpn: models.PrivatevpnServers{Version: 1, Timestamp: 1},
|
||||
Protonvpn: models.ProtonvpnServers{Version: 1, Timestamp: 1},
|
||||
Purevpn: models.PurevpnServers{Version: 1, Timestamp: 1},
|
||||
Surfshark: models.SurfsharkServers{Version: 1, Timestamp: 1},
|
||||
Torguard: models.TorguardServers{Version: 1, Timestamp: 1},
|
||||
VPNUnlimited: models.VPNUnlimitedServers{Version: 1, Timestamp: 1},
|
||||
Vyprvpn: models.VyprvpnServers{Version: 1, Timestamp: 1},
|
||||
Wevpn: models.WevpnServers{Version: 1, Timestamp: 1},
|
||||
Windscribe: models.WindscribeServers{Version: 1, Timestamp: 1},
|
||||
Cyberghost: models.CyberghostServers{Version: 1, Timestamp: 1},
|
||||
Expressvpn: models.ExpressvpnServers{Version: 1, Timestamp: 1},
|
||||
Fastestvpn: models.FastestvpnServers{Version: 1, Timestamp: 1},
|
||||
HideMyAss: models.HideMyAssServers{Version: 1, Timestamp: 1},
|
||||
Ipvanish: models.IpvanishServers{Version: 1, Timestamp: 1},
|
||||
Ivpn: models.IvpnServers{Version: 1, Timestamp: 1},
|
||||
Mullvad: models.MullvadServers{Version: 1, Timestamp: 1},
|
||||
Nordvpn: models.NordvpnServers{Version: 1, Timestamp: 1},
|
||||
Perfectprivacy: models.PerfectprivacyServers{Version: 1, Timestamp: 1},
|
||||
Privado: models.PrivadoServers{Version: 1, Timestamp: 1},
|
||||
Pia: models.PiaServers{Version: 1, Timestamp: 1},
|
||||
Privatevpn: models.PrivatevpnServers{Version: 1, Timestamp: 1},
|
||||
Protonvpn: models.ProtonvpnServers{Version: 1, Timestamp: 1},
|
||||
Purevpn: models.PurevpnServers{Version: 1, Timestamp: 1},
|
||||
Surfshark: models.SurfsharkServers{Version: 1, Timestamp: 1},
|
||||
Torguard: models.TorguardServers{Version: 1, Timestamp: 1},
|
||||
VPNUnlimited: models.VPNUnlimitedServers{Version: 1, Timestamp: 1},
|
||||
Vyprvpn: models.VyprvpnServers{Version: 1, Timestamp: 1},
|
||||
Wevpn: models.WevpnServers{Version: 1, Timestamp: 1},
|
||||
Windscribe: models.WindscribeServers{Version: 1, Timestamp: 1},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -39835,6 +39835,421 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"perfectprivacy": {
|
||||
"version": 1,
|
||||
"timestamp": 1633357036,
|
||||
"servers": [
|
||||
{
|
||||
"city": "Amsterdam",
|
||||
"ips": [
|
||||
"95.211.95.244",
|
||||
"95.211.95.244",
|
||||
"95.211.95.244",
|
||||
"37.48.94.1",
|
||||
"85.17.64.131",
|
||||
"95.211.95.232",
|
||||
"85.17.28.145"
|
||||
],
|
||||
"tcp": true,
|
||||
"udp": true
|
||||
},
|
||||
{
|
||||
"city": "Basel",
|
||||
"ips": [
|
||||
"82.199.134.162",
|
||||
"82.199.134.162",
|
||||
"82.199.134.162",
|
||||
"80.255.7.66"
|
||||
],
|
||||
"tcp": true,
|
||||
"udp": true
|
||||
},
|
||||
{
|
||||
"city": "Belgrade",
|
||||
"ips": [
|
||||
"152.89.160.98",
|
||||
"152.89.160.98",
|
||||
"152.89.160.98"
|
||||
],
|
||||
"tcp": true,
|
||||
"udp": true
|
||||
},
|
||||
{
|
||||
"city": "Berlin",
|
||||
"ips": [
|
||||
"80.255.7.98",
|
||||
"80.255.7.98",
|
||||
"80.255.7.98"
|
||||
],
|
||||
"tcp": true,
|
||||
"udp": true
|
||||
},
|
||||
{
|
||||
"city": "Bucharest",
|
||||
"ips": [
|
||||
"185.57.82.25",
|
||||
"185.57.82.25",
|
||||
"185.57.82.25"
|
||||
],
|
||||
"tcp": true,
|
||||
"udp": true
|
||||
},
|
||||
{
|
||||
"city": "Calais",
|
||||
"ips": [
|
||||
"149.202.77.77",
|
||||
"149.202.77.77",
|
||||
"149.202.77.77"
|
||||
],
|
||||
"tcp": true,
|
||||
"udp": true
|
||||
},
|
||||
{
|
||||
"city": "Chicago",
|
||||
"ips": [
|
||||
"104.237.193.26",
|
||||
"104.237.193.26",
|
||||
"104.237.193.26"
|
||||
],
|
||||
"tcp": true,
|
||||
"udp": true
|
||||
},
|
||||
{
|
||||
"city": "Copenhagen",
|
||||
"ips": [
|
||||
"185.152.32.66",
|
||||
"185.152.32.66",
|
||||
"185.152.32.66"
|
||||
],
|
||||
"tcp": true,
|
||||
"udp": true
|
||||
},
|
||||
{
|
||||
"city": "Dallas",
|
||||
"ips": [
|
||||
"138.128.136.164",
|
||||
"138.128.136.164",
|
||||
"138.128.136.164"
|
||||
],
|
||||
"tcp": true,
|
||||
"udp": true
|
||||
},
|
||||
{
|
||||
"city": "Erfurt",
|
||||
"ips": [
|
||||
"217.114.218.18",
|
||||
"217.114.218.18",
|
||||
"217.114.218.18"
|
||||
],
|
||||
"tcp": true,
|
||||
"udp": true
|
||||
},
|
||||
{
|
||||
"city": "Frankfurt",
|
||||
"ips": [
|
||||
"178.162.194.30",
|
||||
"178.162.194.30",
|
||||
"178.162.194.30",
|
||||
"37.58.58.239"
|
||||
],
|
||||
"tcp": true,
|
||||
"udp": true
|
||||
},
|
||||
{
|
||||
"city": "Hamburg",
|
||||
"ips": [
|
||||
"80.255.7.114",
|
||||
"80.255.7.114",
|
||||
"80.255.7.114"
|
||||
],
|
||||
"tcp": true,
|
||||
"udp": true
|
||||
},
|
||||
{
|
||||
"city": "Hongkong",
|
||||
"ips": [
|
||||
"209.58.188.129",
|
||||
"209.58.188.129",
|
||||
"209.58.188.129"
|
||||
],
|
||||
"tcp": true,
|
||||
"udp": true
|
||||
},
|
||||
{
|
||||
"city": "Istanbul",
|
||||
"ips": [
|
||||
"185.65.205.18",
|
||||
"185.65.205.18",
|
||||
"185.65.205.18"
|
||||
],
|
||||
"tcp": true,
|
||||
"udp": true
|
||||
},
|
||||
{
|
||||
"city": "Jerusalem",
|
||||
"ips": [
|
||||
"82.81.85.231",
|
||||
"82.81.85.231",
|
||||
"82.81.85.231"
|
||||
],
|
||||
"tcp": true,
|
||||
"udp": true
|
||||
},
|
||||
{
|
||||
"city": "London",
|
||||
"ips": [
|
||||
"82.199.130.34",
|
||||
"82.199.130.34",
|
||||
"82.199.130.34",
|
||||
"5.187.21.98"
|
||||
],
|
||||
"tcp": true,
|
||||
"udp": true
|
||||
},
|
||||
{
|
||||
"city": "LosAngeles",
|
||||
"ips": [
|
||||
"162.245.206.242",
|
||||
"162.245.206.242",
|
||||
"162.245.206.242"
|
||||
],
|
||||
"tcp": true,
|
||||
"udp": true
|
||||
},
|
||||
{
|
||||
"city": "Madrid",
|
||||
"ips": [
|
||||
"185.183.106.146",
|
||||
"185.183.106.146",
|
||||
"185.183.106.146"
|
||||
],
|
||||
"tcp": true,
|
||||
"udp": true
|
||||
},
|
||||
{
|
||||
"city": "Malmoe",
|
||||
"ips": [
|
||||
"194.68.170.51",
|
||||
"194.68.170.51",
|
||||
"194.68.170.51"
|
||||
],
|
||||
"tcp": true,
|
||||
"udp": true
|
||||
},
|
||||
{
|
||||
"city": "Manchester",
|
||||
"ips": [
|
||||
"217.138.196.98",
|
||||
"217.138.196.98",
|
||||
"217.138.196.98"
|
||||
],
|
||||
"tcp": true,
|
||||
"udp": true
|
||||
},
|
||||
{
|
||||
"city": "Miami",
|
||||
"ips": [
|
||||
"38.132.118.66",
|
||||
"38.132.118.66",
|
||||
"38.132.118.66"
|
||||
],
|
||||
"tcp": true,
|
||||
"udp": true
|
||||
},
|
||||
{
|
||||
"city": "Milan",
|
||||
"ips": [
|
||||
"192.145.127.210",
|
||||
"192.145.127.210",
|
||||
"192.145.127.210"
|
||||
],
|
||||
"tcp": true,
|
||||
"udp": true
|
||||
},
|
||||
{
|
||||
"city": "Montreal",
|
||||
"ips": [
|
||||
"167.114.209.103",
|
||||
"167.114.209.103",
|
||||
"167.114.209.103"
|
||||
],
|
||||
"tcp": true,
|
||||
"udp": true
|
||||
},
|
||||
{
|
||||
"city": "Moscow",
|
||||
"ips": [
|
||||
"192.162.100.241",
|
||||
"192.162.100.241",
|
||||
"192.162.100.241",
|
||||
"192.162.100.240"
|
||||
],
|
||||
"tcp": true,
|
||||
"udp": true
|
||||
},
|
||||
{
|
||||
"city": "NewYork",
|
||||
"ips": [
|
||||
"96.9.246.194",
|
||||
"96.9.246.194",
|
||||
"96.9.246.194"
|
||||
],
|
||||
"tcp": true,
|
||||
"udp": true
|
||||
},
|
||||
{
|
||||
"city": "Nuremberg",
|
||||
"ips": [
|
||||
"81.95.5.34",
|
||||
"81.95.5.34",
|
||||
"81.95.5.34",
|
||||
"80.255.10.194"
|
||||
],
|
||||
"tcp": true,
|
||||
"udp": true
|
||||
},
|
||||
{
|
||||
"city": "Oslo",
|
||||
"ips": [
|
||||
"91.205.187.186",
|
||||
"91.205.187.186",
|
||||
"91.205.187.186"
|
||||
],
|
||||
"tcp": true,
|
||||
"udp": true
|
||||
},
|
||||
{
|
||||
"city": "Paris",
|
||||
"ips": [
|
||||
"5.135.143.84",
|
||||
"5.135.143.84",
|
||||
"5.135.143.84"
|
||||
],
|
||||
"tcp": true,
|
||||
"udp": true
|
||||
},
|
||||
{
|
||||
"city": "Prague",
|
||||
"ips": [
|
||||
"195.138.249.2",
|
||||
"195.138.249.2",
|
||||
"195.138.249.2"
|
||||
],
|
||||
"tcp": true,
|
||||
"udp": true
|
||||
},
|
||||
{
|
||||
"city": "Reykjavik",
|
||||
"ips": [
|
||||
"82.221.105.61",
|
||||
"82.221.105.61",
|
||||
"82.221.105.61"
|
||||
],
|
||||
"tcp": true,
|
||||
"udp": true
|
||||
},
|
||||
{
|
||||
"city": "Riga",
|
||||
"ips": [
|
||||
"46.183.221.194",
|
||||
"46.183.221.194",
|
||||
"46.183.221.194"
|
||||
],
|
||||
"tcp": true,
|
||||
"udp": true
|
||||
},
|
||||
{
|
||||
"city": "Rotterdam",
|
||||
"ips": [
|
||||
"31.204.152.102",
|
||||
"31.204.152.102",
|
||||
"31.204.152.102",
|
||||
"31.204.152.189",
|
||||
"31.204.150.106",
|
||||
"31.204.150.138",
|
||||
"31.204.153.106"
|
||||
],
|
||||
"tcp": true,
|
||||
"udp": true
|
||||
},
|
||||
{
|
||||
"city": "Singapore",
|
||||
"ips": [
|
||||
"103.254.153.202",
|
||||
"103.254.153.202",
|
||||
"103.254.153.202",
|
||||
"209.58.162.197"
|
||||
],
|
||||
"tcp": true,
|
||||
"udp": true
|
||||
},
|
||||
{
|
||||
"city": "Steinsel",
|
||||
"ips": [
|
||||
"94.242.243.162",
|
||||
"94.242.243.162",
|
||||
"94.242.243.162",
|
||||
"94.242.243.66"
|
||||
],
|
||||
"tcp": true,
|
||||
"udp": true
|
||||
},
|
||||
{
|
||||
"city": "Stockholm",
|
||||
"ips": [
|
||||
"185.41.240.18",
|
||||
"185.41.240.18",
|
||||
"185.41.240.18",
|
||||
"185.217.1.2"
|
||||
],
|
||||
"tcp": true,
|
||||
"udp": true
|
||||
},
|
||||
{
|
||||
"city": "Strasbourg",
|
||||
"ips": [
|
||||
"37.187.163.66",
|
||||
"37.187.163.66",
|
||||
"37.187.163.66"
|
||||
],
|
||||
"tcp": true,
|
||||
"udp": true
|
||||
},
|
||||
{
|
||||
"city": "Sydney",
|
||||
"ips": [
|
||||
"66.203.112.50",
|
||||
"66.203.112.50",
|
||||
"66.203.112.50",
|
||||
"66.203.112.47"
|
||||
],
|
||||
"tcp": true,
|
||||
"udp": true
|
||||
},
|
||||
{
|
||||
"city": "Tokyo",
|
||||
"ips": [
|
||||
"31.204.145.166",
|
||||
"31.204.145.166",
|
||||
"31.204.145.166"
|
||||
],
|
||||
"tcp": true,
|
||||
"udp": true
|
||||
},
|
||||
{
|
||||
"city": "Zurich",
|
||||
"ips": [
|
||||
"37.120.213.210",
|
||||
"37.120.213.210",
|
||||
"37.120.213.210",
|
||||
"37.120.213.194",
|
||||
"152.89.162.226"
|
||||
],
|
||||
"tcp": true,
|
||||
"udp": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"nordvpn": {
|
||||
"version": 3,
|
||||
"timestamp": 1627008323,
|
||||
|
||||
@@ -22,6 +22,7 @@ func countServers(allServers models.AllServers) int {
|
||||
len(allServers.Ivpn.Servers) +
|
||||
len(allServers.Mullvad.Servers) +
|
||||
len(allServers.Nordvpn.Servers) +
|
||||
len(allServers.Perfectprivacy.Servers) +
|
||||
len(allServers.Privado.Servers) +
|
||||
len(allServers.Pia.Servers) +
|
||||
len(allServers.Privatevpn.Servers) +
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -53,20 +54,38 @@ func ExtractHost(b []byte) (host, warning string, err error) {
|
||||
return hosts[0], warning, nil
|
||||
}
|
||||
|
||||
func ExtractIPs(b []byte) (ips []net.IP, err error) {
|
||||
const rejectIP, rejectDomain = false, true
|
||||
ipStrings := extractRemoteHosts(b, rejectIP, rejectDomain)
|
||||
if len(ipStrings) == 0 {
|
||||
return nil, ErrNoRemoteIP
|
||||
}
|
||||
|
||||
sort.Slice(ipStrings, func(i, j int) bool {
|
||||
return ipStrings[i] < ipStrings[j]
|
||||
})
|
||||
|
||||
ips = make([]net.IP, len(ipStrings))
|
||||
for i := range ipStrings {
|
||||
ips[i] = net.ParseIP(ipStrings[i])
|
||||
}
|
||||
|
||||
return ips, nil
|
||||
}
|
||||
|
||||
func ExtractIP(b []byte) (ip net.IP, warning string, err error) {
|
||||
const (
|
||||
rejectIP = false
|
||||
rejectDomain = true
|
||||
)
|
||||
ips := extractRemoteHosts(b, rejectIP, rejectDomain)
|
||||
if len(ips) == 0 {
|
||||
return nil, "", ErrNoRemoteIP
|
||||
} else if len(ips) > 1 {
|
||||
ips, err := ExtractIPs(b)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
if len(ips) > 1 {
|
||||
warning = fmt.Sprintf(
|
||||
"only using the first IP address %s and discarding %d other hosts",
|
||||
ips[0], len(ips)-1)
|
||||
}
|
||||
return net.ParseIP(ips[0]), warning, nil
|
||||
|
||||
return ips[0], warning, nil
|
||||
}
|
||||
|
||||
func extractRemoteHosts(content []byte, rejectIP, rejectDomain bool) (hosts []string) {
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/qdm12/gluetun/internal/updater/providers/ivpn"
|
||||
"github.com/qdm12/gluetun/internal/updater/providers/mullvad"
|
||||
"github.com/qdm12/gluetun/internal/updater/providers/nordvpn"
|
||||
"github.com/qdm12/gluetun/internal/updater/providers/perfectprivacy"
|
||||
"github.com/qdm12/gluetun/internal/updater/providers/pia"
|
||||
"github.com/qdm12/gluetun/internal/updater/providers/privado"
|
||||
"github.com/qdm12/gluetun/internal/updater/providers/privatevpn"
|
||||
@@ -190,6 +191,27 @@ func (u *updater) updateNordvpn(ctx context.Context) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *updater) updatePerfectprivacy(ctx context.Context) (err error) {
|
||||
minServers := getMinServers(len(u.servers.Perfectprivacy.Servers))
|
||||
servers, warnings, err := perfectprivacy.GetServers(ctx, u.unzipper, minServers)
|
||||
if u.options.CLI {
|
||||
for _, warning := range warnings {
|
||||
u.logger.Warn(constants.Perfectprivacy + ": " + warning)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if reflect.DeepEqual(u.servers.Perfectprivacy.Servers, servers) {
|
||||
return nil
|
||||
}
|
||||
|
||||
u.servers.Perfectprivacy.Timestamp = u.timeNow().Unix()
|
||||
u.servers.Perfectprivacy.Servers = servers
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *updater) updatePIA(ctx context.Context) (err error) {
|
||||
minServers := getMinServers(len(u.servers.Pia.Servers))
|
||||
servers, err := pia.GetServers(ctx, u.client, minServers)
|
||||
|
||||
45
internal/updater/providers/perfectprivacy/citytoserver.go
Normal file
45
internal/updater/providers/perfectprivacy/citytoserver.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package perfectprivacy
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
)
|
||||
|
||||
type cityToServer map[string]models.PerfectprivacyServer
|
||||
|
||||
func (cts cityToServer) add(city string, ips []net.IP) {
|
||||
server, ok := cts[city]
|
||||
if !ok {
|
||||
server.City = city
|
||||
server.IPs = ips
|
||||
server.TCP = true
|
||||
server.UDP = true
|
||||
} else {
|
||||
// Do not insert duplicate IP addresses
|
||||
existingIPs := make(map[string]struct{}, len(server.IPs))
|
||||
for _, ip := range server.IPs {
|
||||
existingIPs[ip.String()] = struct{}{}
|
||||
}
|
||||
|
||||
for _, ip := range ips {
|
||||
ipString := ip.String()
|
||||
_, ok := existingIPs[ipString]
|
||||
if ok {
|
||||
continue
|
||||
}
|
||||
existingIPs[ipString] = struct{}{}
|
||||
server.IPs = append(server.IPs, ip)
|
||||
}
|
||||
}
|
||||
|
||||
cts[city] = server
|
||||
}
|
||||
|
||||
func (cts cityToServer) toServersSlice() (servers []models.PerfectprivacyServer) {
|
||||
servers = make([]models.PerfectprivacyServer, 0, len(cts))
|
||||
for _, server := range cts {
|
||||
servers = append(servers, server)
|
||||
}
|
||||
return servers
|
||||
}
|
||||
20
internal/updater/providers/perfectprivacy/filename.go
Normal file
20
internal/updater/providers/perfectprivacy/filename.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package perfectprivacy
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
func parseFilename(fileName string) (city string) {
|
||||
const suffix = ".conf"
|
||||
s := strings.TrimSuffix(fileName, suffix)
|
||||
|
||||
for i, r := range s {
|
||||
if unicode.IsDigit(r) {
|
||||
s = s[:i]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
73
internal/updater/providers/perfectprivacy/servers.go
Normal file
73
internal/updater/providers/perfectprivacy/servers.go
Normal file
@@ -0,0 +1,73 @@
|
||||
package perfectprivacy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
"github.com/qdm12/gluetun/internal/updater/openvpn"
|
||||
"github.com/qdm12/gluetun/internal/updater/unzip"
|
||||
)
|
||||
|
||||
var ErrNotEnoughServers = errors.New("not enough servers found")
|
||||
|
||||
func GetServers(ctx context.Context, unzipper unzip.Unzipper, minServers int) (
|
||||
servers []models.PerfectprivacyServer, warnings []string, err error) {
|
||||
zipURL := url.URL{
|
||||
Scheme: "https",
|
||||
Host: "www.perfect-privacy.com",
|
||||
Path: "/downloads/openvpn/get",
|
||||
}
|
||||
values := make(url.Values)
|
||||
values.Set("system", "linux")
|
||||
values.Set("scope", "server")
|
||||
values.Set("filetype", "zip")
|
||||
values.Set("protocol", "udp") // all support both TCP and UDP
|
||||
zipURL.RawQuery = values.Encode()
|
||||
|
||||
contents, err := unzipper.FetchAndExtract(ctx, zipURL.String())
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
cts := make(cityToServer)
|
||||
|
||||
for fileName, content := range contents {
|
||||
err := addServerFromOvpn(cts, fileName, content)
|
||||
if err != nil {
|
||||
warnings = append(warnings, fileName+": "+err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if len(cts) < minServers {
|
||||
return nil, warnings, fmt.Errorf("%w: %d and expected at least %d",
|
||||
ErrNotEnoughServers, len(cts), minServers)
|
||||
}
|
||||
|
||||
servers = cts.toServersSlice()
|
||||
|
||||
sortServers(servers)
|
||||
|
||||
return servers, warnings, nil
|
||||
}
|
||||
|
||||
func addServerFromOvpn(cts cityToServer,
|
||||
fileName string, content []byte) (err error) {
|
||||
if !strings.HasSuffix(fileName, ".conf") {
|
||||
return nil // not an OpenVPN file
|
||||
}
|
||||
|
||||
ips, err := openvpn.ExtractIPs(content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
city := parseFilename(fileName)
|
||||
|
||||
cts.add(city, ips)
|
||||
|
||||
return nil
|
||||
}
|
||||
13
internal/updater/providers/perfectprivacy/sort.go
Normal file
13
internal/updater/providers/perfectprivacy/sort.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package perfectprivacy
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
)
|
||||
|
||||
func sortServers(servers []models.PerfectprivacyServer) {
|
||||
sort.Slice(servers, func(i, j int) bool {
|
||||
return servers[i].City < servers[j].City
|
||||
})
|
||||
}
|
||||
@@ -16,7 +16,8 @@ func zipExtractAll(zipBytes []byte) (contents map[string][]byte, err error) {
|
||||
contents = map[string][]byte{}
|
||||
for _, zf := range r.File {
|
||||
fileName := filepath.Base(zf.Name)
|
||||
if !strings.HasSuffix(fileName, ".ovpn") {
|
||||
if !strings.HasSuffix(fileName, ".ovpn") &&
|
||||
!strings.HasSuffix(fileName, ".conf") {
|
||||
continue
|
||||
}
|
||||
f, err := zf.Open()
|
||||
|
||||
@@ -14,8 +14,6 @@ var (
|
||||
|
||||
func (u *unzipper) FetchAndExtract(ctx context.Context, url string) (
|
||||
contents map[string][]byte, err error) {
|
||||
contents = make(map[string][]byte)
|
||||
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -40,13 +38,5 @@ func (u *unzipper) FetchAndExtract(ctx context.Context, url string) (
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newContents, err := zipExtractAll(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for fileName, content := range newContents {
|
||||
contents[fileName] = content
|
||||
}
|
||||
|
||||
return contents, nil
|
||||
return zipExtractAll(b)
|
||||
}
|
||||
|
||||
@@ -132,6 +132,16 @@ func (u *updater) UpdateServers(ctx context.Context) (allServers models.AllServe
|
||||
}
|
||||
}
|
||||
|
||||
if u.options.Perfectprivacy {
|
||||
u.logger.Info("updating " + constants.Perfectprivacy + " servers...")
|
||||
if err := u.updatePerfectprivacy(ctx); err != nil {
|
||||
if ctxErr := ctx.Err(); ctxErr != nil {
|
||||
return allServers, ctxErr
|
||||
}
|
||||
u.logger.Error(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if u.options.Privado {
|
||||
u.logger.Info("updating Privado servers...")
|
||||
if err := u.updatePrivado(ctx); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user