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"
|
- name: ":cloud: NordVPN"
|
||||||
color: "cfe8d4"
|
color: "cfe8d4"
|
||||||
description: ""
|
description: ""
|
||||||
|
- name: ":cloud: Perfect Privacy"
|
||||||
|
color: "cfe8d4"
|
||||||
|
description: ""
|
||||||
- name: ":cloud: PIA"
|
- name: ":cloud: PIA"
|
||||||
color: "cfe8d4"
|
color: "cfe8d4"
|
||||||
description: ""
|
description: ""
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Gluetun VPN client
|
# Gluetun VPN client
|
||||||
|
|
||||||
*Lightweight swiss-knife-like VPN client to tunnel to Cyberghost, ExpressVPN, FastestVPN,
|
*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
|
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*
|
||||||
|
|
||||||
@@ -62,7 +62,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**, **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 OpenVPN for all providers listed
|
||||||
- Supports Wireguard
|
- Supports Wireguard
|
||||||
- For **Mullvad**, **Ivpn** and **Windscribe**
|
- For **Mullvad**, **Ivpn** and **Windscribe**
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ var (
|
|||||||
func (c *CLI) FormatServers(args []string) error {
|
func (c *CLI) FormatServers(args []string) error {
|
||||||
var format, output string
|
var format, output string
|
||||||
var cyberghost, expressvpn, fastestvpn, hideMyAss, ipvanish, ivpn, mullvad,
|
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
|
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'")
|
||||||
@@ -39,6 +39,7 @@ func (c *CLI) FormatServers(args []string) error {
|
|||||||
flagSet.BoolVar(&ivpn, "ivpn", false, "Format IVPN servers")
|
flagSet.BoolVar(&ivpn, "ivpn", false, "Format IVPN servers")
|
||||||
flagSet.BoolVar(&mullvad, "mullvad", false, "Format Mullvad servers")
|
flagSet.BoolVar(&mullvad, "mullvad", false, "Format Mullvad servers")
|
||||||
flagSet.BoolVar(&nordvpn, "nordvpn", false, "Format Nordvpn 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(&pia, "pia", false, "Format Private Internet Access servers")
|
||||||
flagSet.BoolVar(&privado, "privado", false, "Format Privado servers")
|
flagSet.BoolVar(&privado, "privado", false, "Format Privado servers")
|
||||||
flagSet.BoolVar(&privatevpn, "privatevpn", false, "Format Private VPN 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()
|
formatted = currentServers.Mullvad.ToMarkdown()
|
||||||
case nordvpn:
|
case nordvpn:
|
||||||
formatted = currentServers.Nordvpn.ToMarkdown()
|
formatted = currentServers.Nordvpn.ToMarkdown()
|
||||||
|
case perfectPrivacy:
|
||||||
|
formatted = currentServers.Perfectprivacy.ToMarkdown()
|
||||||
case pia:
|
case pia:
|
||||||
formatted = currentServers.Pia.ToMarkdown()
|
formatted = currentServers.Pia.ToMarkdown()
|
||||||
case privado:
|
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.Ivpn, "ivpn", false, "Update IVPN servers")
|
||||||
flagSet.BoolVar(&options.Mullvad, "mullvad", false, "Update Mullvad servers")
|
flagSet.BoolVar(&options.Mullvad, "mullvad", false, "Update Mullvad servers")
|
||||||
flagSet.BoolVar(&options.Nordvpn, "nordvpn", false, "Update Nordvpn 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.PIA, "pia", false, "Update Private Internet Access post-summer 2020 servers")
|
||||||
flagSet.BoolVar(&options.Privado, "privado", false, "Update Privado servers")
|
flagSet.BoolVar(&options.Privado, "privado", false, "Update Privado servers")
|
||||||
flagSet.BoolVar(&options.Privatevpn, "privatevpn", false, "Update Private VPN 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)
|
err = settings.readMullvad(r)
|
||||||
case constants.Nordvpn:
|
case constants.Nordvpn:
|
||||||
err = settings.readNordvpn(r)
|
err = settings.readNordvpn(r)
|
||||||
|
case constants.Perfectprivacy:
|
||||||
|
err = settings.readPerfectPrivacy(r)
|
||||||
case constants.Privado:
|
case constants.Privado:
|
||||||
err = settings.readPrivado(r)
|
err = settings.readPrivado(r)
|
||||||
case constants.PrivateInternetAccess:
|
case constants.PrivateInternetAccess:
|
||||||
@@ -108,7 +110,7 @@ func (settings *Provider) readVPNServiceProvider(r reader, vpnType string) (err
|
|||||||
constants.Custom,
|
constants.Custom,
|
||||||
"cyberghost", constants.Expressvpn, "fastestvpn", "hidemyass", "ipvanish",
|
"cyberghost", constants.Expressvpn, "fastestvpn", "hidemyass", "ipvanish",
|
||||||
"ivpn", "mullvad", "nordvpn",
|
"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",
|
"purevpn", "surfshark", "torguard", constants.VPNUnlimited, "vyprvpn",
|
||||||
constants.Wevpn, "windscribe"}
|
constants.Wevpn, "windscribe"}
|
||||||
case constants.Wireguard:
|
case constants.Wireguard:
|
||||||
|
|||||||
@@ -168,6 +168,21 @@ func Test_Provider_lines(t *testing.T) {
|
|||||||
" |--Protocol: udp",
|
" |--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": {
|
"privado": {
|
||||||
settings: Provider{
|
settings: Provider{
|
||||||
Name: constants.Privado,
|
Name: constants.Privado,
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ type ServerSelection struct { //nolint:maligned
|
|||||||
|
|
||||||
// Expressvpn, Fastestvpn, HideMyAss, IPVanish, IVPN, Mullvad, PrivateVPN, Protonvpn, PureVPN, VPNUnlimited
|
// Expressvpn, Fastestvpn, HideMyAss, IPVanish, IVPN, Mullvad, PrivateVPN, Protonvpn, PureVPN, VPNUnlimited
|
||||||
Countries []string `json:"countries"`
|
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"`
|
Cities []string `json:"cities"`
|
||||||
// Expressvpn, Fastestvpn, HideMyAss, IPVanish, IVPN, PrivateVPN, Windscribe, Privado, Protonvpn, VPNUnlimited, WeVPN
|
// Expressvpn, Fastestvpn, HideMyAss, IPVanish, IVPN, PrivateVPN, Windscribe, Privado, Protonvpn, VPNUnlimited, WeVPN
|
||||||
Hostnames []string `json:"hostnames"`
|
Hostnames []string `json:"hostnames"`
|
||||||
|
|||||||
@@ -9,27 +9,28 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Updater struct {
|
type Updater struct {
|
||||||
Period time.Duration `json:"period"`
|
Period time.Duration `json:"period"`
|
||||||
DNSAddress string `json:"dns_address"`
|
DNSAddress string `json:"dns_address"`
|
||||||
Cyberghost bool `json:"cyberghost"`
|
Cyberghost bool `json:"cyberghost"`
|
||||||
Expressvpn bool `json:"expressvpn"`
|
Expressvpn bool `json:"expressvpn"`
|
||||||
Fastestvpn bool `json:"fastestvpn"`
|
Fastestvpn bool `json:"fastestvpn"`
|
||||||
HideMyAss bool `json:"hidemyass"`
|
HideMyAss bool `json:"hidemyass"`
|
||||||
Ipvanish bool `json:"ipvanish"`
|
Ipvanish bool `json:"ipvanish"`
|
||||||
Ivpn bool `json:"ivpn"`
|
Ivpn bool `json:"ivpn"`
|
||||||
Mullvad bool `json:"mullvad"`
|
Mullvad bool `json:"mullvad"`
|
||||||
Nordvpn bool `json:"nordvpn"`
|
Nordvpn bool `json:"nordvpn"`
|
||||||
PIA bool `json:"pia"`
|
Perfectprivacy bool `json:"perfectprivacy"`
|
||||||
Privado bool `json:"privado"`
|
PIA bool `json:"pia"`
|
||||||
Privatevpn bool `json:"privatevpn"`
|
Privado bool `json:"privado"`
|
||||||
Protonvpn bool `json:"protonvpn"`
|
Privatevpn bool `json:"privatevpn"`
|
||||||
Purevpn bool `json:"purevpn"`
|
Protonvpn bool `json:"protonvpn"`
|
||||||
Surfshark bool `json:"surfshark"`
|
Purevpn bool `json:"purevpn"`
|
||||||
Torguard bool `json:"torguard"`
|
Surfshark bool `json:"surfshark"`
|
||||||
VPNUnlimited bool `json:"vpnunlimited"`
|
Torguard bool `json:"torguard"`
|
||||||
Vyprvpn bool `json:"vyprvpn"`
|
VPNUnlimited bool `json:"vpnunlimited"`
|
||||||
Wevpn bool `json:"wevpn"`
|
Vyprvpn bool `json:"vyprvpn"`
|
||||||
Windscribe bool `json:"windscribe"`
|
Wevpn bool `json:"wevpn"`
|
||||||
|
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:"-"`
|
||||||
}
|
}
|
||||||
@@ -57,6 +58,7 @@ func (settings *Updater) EnableAll() {
|
|||||||
settings.Ivpn = true
|
settings.Ivpn = true
|
||||||
settings.Mullvad = true
|
settings.Mullvad = true
|
||||||
settings.Nordvpn = true
|
settings.Nordvpn = true
|
||||||
|
settings.Perfectprivacy = true
|
||||||
settings.Privado = true
|
settings.Privado = true
|
||||||
settings.PIA = true
|
settings.PIA = true
|
||||||
settings.Privado = 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"
|
Mullvad = "mullvad"
|
||||||
// Nordvpn is a VPN provider.
|
// Nordvpn is a VPN provider.
|
||||||
Nordvpn = "nordvpn"
|
Nordvpn = "nordvpn"
|
||||||
|
// Perfectprivacy is a VPN provider.
|
||||||
|
Perfectprivacy = "perfect privacy"
|
||||||
// Privado is a VPN provider.
|
// Privado is a VPN provider.
|
||||||
Privado = "privado"
|
Privado = "privado"
|
||||||
// PrivateInternetAccess is a VPN provider.
|
// PrivateInternetAccess is a VPN provider.
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ func (a AllServers) GetCopy() (servers AllServers) {
|
|||||||
servers.Ivpn.Servers = a.GetIvpn()
|
servers.Ivpn.Servers = a.GetIvpn()
|
||||||
servers.Mullvad.Servers = a.GetMullvad()
|
servers.Mullvad.Servers = a.GetMullvad()
|
||||||
servers.Nordvpn.Servers = a.GetNordvpn()
|
servers.Nordvpn.Servers = a.GetNordvpn()
|
||||||
|
servers.Perfectprivacy.Servers = a.GetPerfectprivacy()
|
||||||
servers.Privado.Servers = a.GetPrivado()
|
servers.Privado.Servers = a.GetPrivado()
|
||||||
servers.Pia.Servers = a.GetPia()
|
servers.Pia.Servers = a.GetPia()
|
||||||
servers.Privatevpn.Servers = a.GetPrivatevpn()
|
servers.Privatevpn.Servers = a.GetPrivatevpn()
|
||||||
@@ -124,6 +125,18 @@ func (a *AllServers) GetNordvpn() (servers []NordvpnServer) {
|
|||||||
return servers
|
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) {
|
func (a *AllServers) GetPia() (servers []PIAServer) {
|
||||||
if a.Pia.Servers == nil {
|
if a.Pia.Servers == nil {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -51,6 +51,11 @@ func Test_AllServers_GetCopy(t *testing.T) {
|
|||||||
IP: net.IP{1, 2, 3, 4},
|
IP: net.IP{1, 2, 3, 4},
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
|
Perfectprivacy: PerfectprivacyServers{
|
||||||
|
Servers: []PerfectprivacyServer{{
|
||||||
|
IPs: []net.IP{{1, 2, 3, 4}},
|
||||||
|
}},
|
||||||
|
},
|
||||||
Privado: PrivadoServers{
|
Privado: PrivadoServers{
|
||||||
Servers: []PrivadoServer{{
|
Servers: []PrivadoServer{{
|
||||||
IP: net.IP{1, 2, 3, 4},
|
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)
|
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) {
|
func (s *PiaServers) ToMarkdown() (markdown string) {
|
||||||
markdown = markdownTableHeading("Region", "Hostname", "TCP", "UDP")
|
markdown = markdownTableHeading("Region", "Hostname", "TCP", "UDP")
|
||||||
for _, server := range s.Servers {
|
for _, server := range s.Servers {
|
||||||
|
|||||||
@@ -81,6 +81,13 @@ type NordvpnServer struct { //nolint:maligned
|
|||||||
UDP bool `json:"udp"`
|
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 {
|
type PrivadoServer struct {
|
||||||
Country string `json:"country"`
|
Country string `json:"country"`
|
||||||
Region string `json:"region"`
|
Region string `json:"region"`
|
||||||
|
|||||||
@@ -1,26 +1,27 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
type AllServers struct {
|
type AllServers struct {
|
||||||
Version uint16 `json:"version"` // used for migration of the top level scheme
|
Version uint16 `json:"version"` // used for migration of the top level scheme
|
||||||
Cyberghost CyberghostServers `json:"cyberghost"`
|
Cyberghost CyberghostServers `json:"cyberghost"`
|
||||||
Expressvpn ExpressvpnServers `json:"expressvpn"`
|
Expressvpn ExpressvpnServers `json:"expressvpn"`
|
||||||
Fastestvpn FastestvpnServers `json:"fastestvpn"`
|
Fastestvpn FastestvpnServers `json:"fastestvpn"`
|
||||||
HideMyAss HideMyAssServers `json:"hidemyass"`
|
HideMyAss HideMyAssServers `json:"hidemyass"`
|
||||||
Ipvanish IpvanishServers `json:"ipvanish"`
|
Ipvanish IpvanishServers `json:"ipvanish"`
|
||||||
Ivpn IvpnServers `json:"ivpn"`
|
Ivpn IvpnServers `json:"ivpn"`
|
||||||
Mullvad MullvadServers `json:"mullvad"`
|
Mullvad MullvadServers `json:"mullvad"`
|
||||||
Nordvpn NordvpnServers `json:"nordvpn"`
|
Perfectprivacy PerfectprivacyServers `json:"perfectprivacy"`
|
||||||
Privado PrivadoServers `json:"privado"`
|
Nordvpn NordvpnServers `json:"nordvpn"`
|
||||||
Pia PiaServers `json:"pia"`
|
Privado PrivadoServers `json:"privado"`
|
||||||
Privatevpn PrivatevpnServers `json:"privatevpn"`
|
Pia PiaServers `json:"pia"`
|
||||||
Protonvpn ProtonvpnServers `json:"protonvpn"`
|
Privatevpn PrivatevpnServers `json:"privatevpn"`
|
||||||
Purevpn PurevpnServers `json:"purevpn"`
|
Protonvpn ProtonvpnServers `json:"protonvpn"`
|
||||||
Surfshark SurfsharkServers `json:"surfshark"`
|
Purevpn PurevpnServers `json:"purevpn"`
|
||||||
Torguard TorguardServers `json:"torguard"`
|
Surfshark SurfsharkServers `json:"surfshark"`
|
||||||
VPNUnlimited VPNUnlimitedServers `json:"vpnunlimited"`
|
Torguard TorguardServers `json:"torguard"`
|
||||||
Vyprvpn VyprvpnServers `json:"vyprvpn"`
|
VPNUnlimited VPNUnlimitedServers `json:"vpnunlimited"`
|
||||||
Wevpn WevpnServers `json:"wevpn"`
|
Vyprvpn VyprvpnServers `json:"vyprvpn"`
|
||||||
Windscribe WindscribeServers `json:"windscribe"`
|
Wevpn WevpnServers `json:"wevpn"`
|
||||||
|
Windscribe WindscribeServers `json:"windscribe"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *AllServers) Count() int {
|
func (a *AllServers) Count() int {
|
||||||
@@ -32,6 +33,7 @@ func (a *AllServers) Count() int {
|
|||||||
len(a.Ivpn.Servers) +
|
len(a.Ivpn.Servers) +
|
||||||
len(a.Mullvad.Servers) +
|
len(a.Mullvad.Servers) +
|
||||||
len(a.Nordvpn.Servers) +
|
len(a.Nordvpn.Servers) +
|
||||||
|
len(a.Perfectprivacy.Servers) +
|
||||||
len(a.Privado.Servers) +
|
len(a.Privado.Servers) +
|
||||||
len(a.Pia.Servers) +
|
len(a.Pia.Servers) +
|
||||||
len(a.Privatevpn.Servers) +
|
len(a.Privatevpn.Servers) +
|
||||||
@@ -85,6 +87,11 @@ type NordvpnServers struct {
|
|||||||
Timestamp int64 `json:"timestamp"`
|
Timestamp int64 `json:"timestamp"`
|
||||||
Servers []NordvpnServer `json:"servers"`
|
Servers []NordvpnServer `json:"servers"`
|
||||||
}
|
}
|
||||||
|
type PerfectprivacyServers struct {
|
||||||
|
Version uint16 `json:"version"`
|
||||||
|
Timestamp int64 `json:"timestamp"`
|
||||||
|
Servers []PerfectprivacyServer `json:"servers"`
|
||||||
|
}
|
||||||
type PrivadoServers struct {
|
type PrivadoServers struct {
|
||||||
Version uint16 `json:"version"`
|
Version uint16 `json:"version"`
|
||||||
Timestamp int64 `json:"timestamp"`
|
Timestamp int64 `json:"timestamp"`
|
||||||
|
|||||||
@@ -11,8 +11,6 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func extractPEM(b []byte, name string) (encodedData string, err error) {
|
func extractPEM(b []byte, name string) (encodedData string, err error) {
|
||||||
name = strings.ToUpper(name) // certificate -> CERTIFICATE
|
|
||||||
|
|
||||||
pemBlock, _ := pem.Decode(b)
|
pemBlock, _ := pem.Decode(b)
|
||||||
if pemBlock == nil {
|
if pemBlock == nil {
|
||||||
return "", errPEMDecode
|
return "", errPEMDecode
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ func Test_extractPEM(t *testing.T) {
|
|||||||
err: errors.New("cannot decode PEM encoded block"),
|
err: errors.New("cannot decode PEM encoded block"),
|
||||||
},
|
},
|
||||||
"valid data": {
|
"valid data": {
|
||||||
name: "certificate",
|
name: "CERTIFICATE",
|
||||||
b: []byte(validCertPEM),
|
b: []byte(validCertPEM),
|
||||||
encodedData: validCertData,
|
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/ivpn"
|
||||||
"github.com/qdm12/gluetun/internal/provider/mullvad"
|
"github.com/qdm12/gluetun/internal/provider/mullvad"
|
||||||
"github.com/qdm12/gluetun/internal/provider/nordvpn"
|
"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/privado"
|
||||||
"github.com/qdm12/gluetun/internal/provider/privateinternetaccess"
|
"github.com/qdm12/gluetun/internal/provider/privateinternetaccess"
|
||||||
"github.com/qdm12/gluetun/internal/provider/privatevpn"
|
"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)
|
return mullvad.New(allServers.Mullvad.Servers, randSource)
|
||||||
case constants.Nordvpn:
|
case constants.Nordvpn:
|
||||||
return nordvpn.New(allServers.Nordvpn.Servers, randSource)
|
return nordvpn.New(allServers.Nordvpn.Servers, randSource)
|
||||||
|
case constants.Perfectprivacy:
|
||||||
|
return perfectprivacy.New(allServers.Perfectprivacy.Servers, randSource)
|
||||||
case constants.Privado:
|
case constants.Privado:
|
||||||
return privado.New(allServers.Privado.Servers, randSource)
|
return privado.New(allServers.Privado.Servers, randSource)
|
||||||
case constants.PrivateInternetAccess:
|
case constants.PrivateInternetAccess:
|
||||||
|
|||||||
@@ -88,6 +88,11 @@ func Test_versions(t *testing.T) {
|
|||||||
version: allServers.Nordvpn.Version,
|
version: allServers.Nordvpn.Version,
|
||||||
digest: "a8043704",
|
digest: "a8043704",
|
||||||
},
|
},
|
||||||
|
"Perfect privacy": {
|
||||||
|
model: models.PerfectprivacyServer{},
|
||||||
|
version: allServers.Perfectprivacy.Version,
|
||||||
|
digest: "233f0dd4",
|
||||||
|
},
|
||||||
"Privado": {
|
"Privado": {
|
||||||
model: models.PrivadoServer{},
|
model: models.PrivadoServer{},
|
||||||
version: allServers.Privado.Version,
|
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 {
|
func (s *Storage) mergeServers(hardcoded, persisted models.AllServers) models.AllServers {
|
||||||
return models.AllServers{
|
return models.AllServers{
|
||||||
Version: hardcoded.Version,
|
Version: hardcoded.Version,
|
||||||
Cyberghost: s.mergeCyberghost(hardcoded.Cyberghost, persisted.Cyberghost),
|
Cyberghost: s.mergeCyberghost(hardcoded.Cyberghost, persisted.Cyberghost),
|
||||||
Expressvpn: s.mergeExpressvpn(hardcoded.Expressvpn, persisted.Expressvpn),
|
Expressvpn: s.mergeExpressvpn(hardcoded.Expressvpn, persisted.Expressvpn),
|
||||||
Fastestvpn: s.mergeFastestvpn(hardcoded.Fastestvpn, persisted.Fastestvpn),
|
Fastestvpn: s.mergeFastestvpn(hardcoded.Fastestvpn, persisted.Fastestvpn),
|
||||||
HideMyAss: s.mergeHideMyAss(hardcoded.HideMyAss, persisted.HideMyAss),
|
HideMyAss: s.mergeHideMyAss(hardcoded.HideMyAss, persisted.HideMyAss),
|
||||||
Ipvanish: s.mergeIpvanish(hardcoded.Ipvanish, persisted.Ipvanish),
|
Ipvanish: s.mergeIpvanish(hardcoded.Ipvanish, persisted.Ipvanish),
|
||||||
Ivpn: s.mergeIvpn(hardcoded.Ivpn, persisted.Ivpn),
|
Ivpn: s.mergeIvpn(hardcoded.Ivpn, persisted.Ivpn),
|
||||||
Mullvad: s.mergeMullvad(hardcoded.Mullvad, persisted.Mullvad),
|
Mullvad: s.mergeMullvad(hardcoded.Mullvad, persisted.Mullvad),
|
||||||
Nordvpn: s.mergeNordVPN(hardcoded.Nordvpn, persisted.Nordvpn),
|
Nordvpn: s.mergeNordVPN(hardcoded.Nordvpn, persisted.Nordvpn),
|
||||||
Privado: s.mergePrivado(hardcoded.Privado, persisted.Privado),
|
Perfectprivacy: s.mergePerfectprivacy(hardcoded.Perfectprivacy, persisted.Perfectprivacy),
|
||||||
Pia: s.mergePIA(hardcoded.Pia, persisted.Pia),
|
Privado: s.mergePrivado(hardcoded.Privado, persisted.Privado),
|
||||||
Privatevpn: s.mergePrivatevpn(hardcoded.Privatevpn, persisted.Privatevpn),
|
Pia: s.mergePIA(hardcoded.Pia, persisted.Pia),
|
||||||
Protonvpn: s.mergeProtonvpn(hardcoded.Protonvpn, persisted.Protonvpn),
|
Privatevpn: s.mergePrivatevpn(hardcoded.Privatevpn, persisted.Privatevpn),
|
||||||
Purevpn: s.mergePureVPN(hardcoded.Purevpn, persisted.Purevpn),
|
Protonvpn: s.mergeProtonvpn(hardcoded.Protonvpn, persisted.Protonvpn),
|
||||||
Surfshark: s.mergeSurfshark(hardcoded.Surfshark, persisted.Surfshark),
|
Purevpn: s.mergePureVPN(hardcoded.Purevpn, persisted.Purevpn),
|
||||||
Torguard: s.mergeTorguard(hardcoded.Torguard, persisted.Torguard),
|
Surfshark: s.mergeSurfshark(hardcoded.Surfshark, persisted.Surfshark),
|
||||||
VPNUnlimited: s.mergeVPNUnlimited(hardcoded.VPNUnlimited, persisted.VPNUnlimited),
|
Torguard: s.mergeTorguard(hardcoded.Torguard, persisted.Torguard),
|
||||||
Vyprvpn: s.mergeVyprvpn(hardcoded.Vyprvpn, persisted.Vyprvpn),
|
VPNUnlimited: s.mergeVPNUnlimited(hardcoded.VPNUnlimited, persisted.VPNUnlimited),
|
||||||
Wevpn: s.mergeWevpn(hardcoded.Wevpn, persisted.Wevpn),
|
Vyprvpn: s.mergeVyprvpn(hardcoded.Vyprvpn, persisted.Vyprvpn),
|
||||||
Windscribe: s.mergeWindscribe(hardcoded.Windscribe, persisted.Windscribe),
|
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
|
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 {
|
func (s *Storage) mergePrivado(hardcoded, persisted models.PrivadoServers) models.PrivadoServers {
|
||||||
if persisted.Timestamp <= hardcoded.Timestamp {
|
if persisted.Timestamp <= hardcoded.Timestamp {
|
||||||
return hardcoded
|
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 {
|
if hardcoded.Privado.Version != versions.Privado.Version {
|
||||||
s.logVersionDiff("Privado", hardcoded.Privado.Version, versions.Privado.Version)
|
s.logVersionDiff("Privado", hardcoded.Privado.Version, versions.Privado.Version)
|
||||||
} else {
|
} 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
|
// allVersions is a subset of models.AllServers structure used to track
|
||||||
// versions to avoid unmarshaling errors.
|
// versions to avoid unmarshaling errors.
|
||||||
type allVersions struct {
|
type allVersions struct {
|
||||||
Version uint16 `json:"version"` // used for migration of the top level scheme
|
Version uint16 `json:"version"` // used for migration of the top level scheme
|
||||||
Cyberghost serverVersion `json:"cyberghost"`
|
Cyberghost serverVersion `json:"cyberghost"`
|
||||||
Expressvpn serverVersion `json:"expressvpn"`
|
Expressvpn serverVersion `json:"expressvpn"`
|
||||||
Fastestvpn serverVersion `json:"fastestvpn"`
|
Fastestvpn serverVersion `json:"fastestvpn"`
|
||||||
HideMyAss serverVersion `json:"hidemyass"`
|
HideMyAss serverVersion `json:"hidemyass"`
|
||||||
Ipvanish serverVersion `json:"ipvanish"`
|
Ipvanish serverVersion `json:"ipvanish"`
|
||||||
Ivpn serverVersion `json:"ivpn"`
|
Ivpn serverVersion `json:"ivpn"`
|
||||||
Mullvad serverVersion `json:"mullvad"`
|
Mullvad serverVersion `json:"mullvad"`
|
||||||
Nordvpn serverVersion `json:"nordvpn"`
|
Nordvpn serverVersion `json:"nordvpn"`
|
||||||
Privado serverVersion `json:"privado"`
|
Perfectprivacy serverVersion `json:"perfectprivacy"`
|
||||||
Pia serverVersion `json:"pia"`
|
Privado serverVersion `json:"privado"`
|
||||||
Privatevpn serverVersion `json:"privatevpn"`
|
Pia serverVersion `json:"pia"`
|
||||||
Protonvpn serverVersion `json:"protonvpn"`
|
Privatevpn serverVersion `json:"privatevpn"`
|
||||||
Purevpn serverVersion `json:"purevpn"`
|
Protonvpn serverVersion `json:"protonvpn"`
|
||||||
Surfshark serverVersion `json:"surfshark"`
|
Purevpn serverVersion `json:"purevpn"`
|
||||||
Torguard serverVersion `json:"torguard"`
|
Surfshark serverVersion `json:"surfshark"`
|
||||||
VPNUnlimited serverVersion `json:"vpnunlimited"`
|
Torguard serverVersion `json:"torguard"`
|
||||||
Vyprvpn serverVersion `json:"vyprvpn"`
|
VPNUnlimited serverVersion `json:"vpnunlimited"`
|
||||||
Wevpn serverVersion `json:"wevpn"`
|
Vyprvpn serverVersion `json:"vyprvpn"`
|
||||||
Windscribe serverVersion `json:"windscribe"`
|
Wevpn serverVersion `json:"wevpn"`
|
||||||
|
Windscribe serverVersion `json:"windscribe"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type serverVersion struct {
|
type serverVersion struct {
|
||||||
@@ -259,24 +269,25 @@ type serverVersion struct {
|
|||||||
|
|
||||||
// allJSONRawMessages is to delay decoding of each provider servers.
|
// allJSONRawMessages is to delay decoding of each provider servers.
|
||||||
type allJSONRawMessages struct {
|
type allJSONRawMessages struct {
|
||||||
Version uint16 `json:"version"` // used for migration of the top level scheme
|
Version uint16 `json:"version"` // used for migration of the top level scheme
|
||||||
Cyberghost json.RawMessage `json:"cyberghost"`
|
Cyberghost json.RawMessage `json:"cyberghost"`
|
||||||
Expressvpn json.RawMessage `json:"expressvpn"`
|
Expressvpn json.RawMessage `json:"expressvpn"`
|
||||||
Fastestvpn json.RawMessage `json:"fastestvpn"`
|
Fastestvpn json.RawMessage `json:"fastestvpn"`
|
||||||
HideMyAss json.RawMessage `json:"hidemyass"`
|
HideMyAss json.RawMessage `json:"hidemyass"`
|
||||||
Ipvanish json.RawMessage `json:"ipvanish"`
|
Ipvanish json.RawMessage `json:"ipvanish"`
|
||||||
Ivpn json.RawMessage `json:"ivpn"`
|
Ivpn json.RawMessage `json:"ivpn"`
|
||||||
Mullvad json.RawMessage `json:"mullvad"`
|
Mullvad json.RawMessage `json:"mullvad"`
|
||||||
Nordvpn json.RawMessage `json:"nordvpn"`
|
Nordvpn json.RawMessage `json:"nordvpn"`
|
||||||
Privado json.RawMessage `json:"privado"`
|
Perfectprivacy json.RawMessage `json:"perfectprivacy"`
|
||||||
Pia json.RawMessage `json:"pia"`
|
Privado json.RawMessage `json:"privado"`
|
||||||
Privatevpn json.RawMessage `json:"privatevpn"`
|
Pia json.RawMessage `json:"pia"`
|
||||||
Protonvpn json.RawMessage `json:"protonvpn"`
|
Privatevpn json.RawMessage `json:"privatevpn"`
|
||||||
Purevpn json.RawMessage `json:"purevpn"`
|
Protonvpn json.RawMessage `json:"protonvpn"`
|
||||||
Surfshark json.RawMessage `json:"surfshark"`
|
Purevpn json.RawMessage `json:"purevpn"`
|
||||||
Torguard json.RawMessage `json:"torguard"`
|
Surfshark json.RawMessage `json:"surfshark"`
|
||||||
VPNUnlimited json.RawMessage `json:"vpnunlimited"`
|
Torguard json.RawMessage `json:"torguard"`
|
||||||
Vyprvpn json.RawMessage `json:"vyprvpn"`
|
VPNUnlimited json.RawMessage `json:"vpnunlimited"`
|
||||||
Wevpn json.RawMessage `json:"wevpn"`
|
Vyprvpn json.RawMessage `json:"vyprvpn"`
|
||||||
Windscribe json.RawMessage `json:"windscribe"`
|
Wevpn json.RawMessage `json:"wevpn"`
|
||||||
|
Windscribe json.RawMessage `json:"windscribe"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,25 +30,26 @@ func Test_extractServersFromBytes(t *testing.T) {
|
|||||||
"different versions": {
|
"different versions": {
|
||||||
b: []byte(`{}`),
|
b: []byte(`{}`),
|
||||||
hardcoded: models.AllServers{
|
hardcoded: models.AllServers{
|
||||||
Cyberghost: models.CyberghostServers{Version: 1},
|
Cyberghost: models.CyberghostServers{Version: 1},
|
||||||
Expressvpn: models.ExpressvpnServers{Version: 1},
|
Expressvpn: models.ExpressvpnServers{Version: 1},
|
||||||
Fastestvpn: models.FastestvpnServers{Version: 1},
|
Fastestvpn: models.FastestvpnServers{Version: 1},
|
||||||
HideMyAss: models.HideMyAssServers{Version: 1},
|
HideMyAss: models.HideMyAssServers{Version: 1},
|
||||||
Ipvanish: models.IpvanishServers{Version: 1},
|
Ipvanish: models.IpvanishServers{Version: 1},
|
||||||
Ivpn: models.IvpnServers{Version: 1},
|
Ivpn: models.IvpnServers{Version: 1},
|
||||||
Mullvad: models.MullvadServers{Version: 1},
|
Mullvad: models.MullvadServers{Version: 1},
|
||||||
Nordvpn: models.NordvpnServers{Version: 1},
|
Nordvpn: models.NordvpnServers{Version: 1},
|
||||||
Privado: models.PrivadoServers{Version: 1},
|
Perfectprivacy: models.PerfectprivacyServers{Version: 1},
|
||||||
Pia: models.PiaServers{Version: 1},
|
Privado: models.PrivadoServers{Version: 1},
|
||||||
Privatevpn: models.PrivatevpnServers{Version: 1},
|
Pia: models.PiaServers{Version: 1},
|
||||||
Protonvpn: models.ProtonvpnServers{Version: 1},
|
Privatevpn: models.PrivatevpnServers{Version: 1},
|
||||||
Purevpn: models.PurevpnServers{Version: 1},
|
Protonvpn: models.ProtonvpnServers{Version: 1},
|
||||||
Surfshark: models.SurfsharkServers{Version: 1},
|
Purevpn: models.PurevpnServers{Version: 1},
|
||||||
Torguard: models.TorguardServers{Version: 1},
|
Surfshark: models.SurfsharkServers{Version: 1},
|
||||||
VPNUnlimited: models.VPNUnlimitedServers{Version: 1},
|
Torguard: models.TorguardServers{Version: 1},
|
||||||
Vyprvpn: models.VyprvpnServers{Version: 1},
|
VPNUnlimited: models.VPNUnlimitedServers{Version: 1},
|
||||||
Wevpn: models.WevpnServers{Version: 1},
|
Vyprvpn: models.VyprvpnServers{Version: 1},
|
||||||
Windscribe: models.WindscribeServers{Version: 1},
|
Wevpn: models.WevpnServers{Version: 1},
|
||||||
|
Windscribe: models.WindscribeServers{Version: 1},
|
||||||
},
|
},
|
||||||
logged: []string{
|
logged: []string{
|
||||||
"Cyberghost servers from file discarded because they have version 0 and hardcoded servers have version 1",
|
"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",
|
"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",
|
"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",
|
"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",
|
"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",
|
"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",
|
"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},
|
"ivpn": {"version": 1, "timestamp": 1},
|
||||||
"mullvad": {"version": 1, "timestamp": 1},
|
"mullvad": {"version": 1, "timestamp": 1},
|
||||||
"nordvpn": {"version": 1, "timestamp": 1},
|
"nordvpn": {"version": 1, "timestamp": 1},
|
||||||
|
"perfectprivacy": {"version": 1, "timestamp": 1},
|
||||||
"privado": {"version": 1, "timestamp": 1},
|
"privado": {"version": 1, "timestamp": 1},
|
||||||
"pia": {"version": 1, "timestamp": 1},
|
"pia": {"version": 1, "timestamp": 1},
|
||||||
"privatevpn": {"version": 1, "timestamp": 1},
|
"privatevpn": {"version": 1, "timestamp": 1},
|
||||||
@@ -95,46 +98,48 @@ func Test_extractServersFromBytes(t *testing.T) {
|
|||||||
"windscribe": {"version": 1, "timestamp": 1}
|
"windscribe": {"version": 1, "timestamp": 1}
|
||||||
}`),
|
}`),
|
||||||
hardcoded: models.AllServers{
|
hardcoded: models.AllServers{
|
||||||
Cyberghost: models.CyberghostServers{Version: 1},
|
Cyberghost: models.CyberghostServers{Version: 1},
|
||||||
Expressvpn: models.ExpressvpnServers{Version: 1},
|
Expressvpn: models.ExpressvpnServers{Version: 1},
|
||||||
Fastestvpn: models.FastestvpnServers{Version: 1},
|
Fastestvpn: models.FastestvpnServers{Version: 1},
|
||||||
HideMyAss: models.HideMyAssServers{Version: 1},
|
HideMyAss: models.HideMyAssServers{Version: 1},
|
||||||
Ipvanish: models.IpvanishServers{Version: 1},
|
Ipvanish: models.IpvanishServers{Version: 1},
|
||||||
Ivpn: models.IvpnServers{Version: 1},
|
Ivpn: models.IvpnServers{Version: 1},
|
||||||
Mullvad: models.MullvadServers{Version: 1},
|
Mullvad: models.MullvadServers{Version: 1},
|
||||||
Nordvpn: models.NordvpnServers{Version: 1},
|
Nordvpn: models.NordvpnServers{Version: 1},
|
||||||
Privado: models.PrivadoServers{Version: 1},
|
Perfectprivacy: models.PerfectprivacyServers{Version: 1},
|
||||||
Pia: models.PiaServers{Version: 1},
|
Privado: models.PrivadoServers{Version: 1},
|
||||||
Privatevpn: models.PrivatevpnServers{Version: 1},
|
Pia: models.PiaServers{Version: 1},
|
||||||
Protonvpn: models.ProtonvpnServers{Version: 1},
|
Privatevpn: models.PrivatevpnServers{Version: 1},
|
||||||
Purevpn: models.PurevpnServers{Version: 1},
|
Protonvpn: models.ProtonvpnServers{Version: 1},
|
||||||
Surfshark: models.SurfsharkServers{Version: 1},
|
Purevpn: models.PurevpnServers{Version: 1},
|
||||||
Torguard: models.TorguardServers{Version: 1},
|
Surfshark: models.SurfsharkServers{Version: 1},
|
||||||
VPNUnlimited: models.VPNUnlimitedServers{Version: 1},
|
Torguard: models.TorguardServers{Version: 1},
|
||||||
Vyprvpn: models.VyprvpnServers{Version: 1},
|
VPNUnlimited: models.VPNUnlimitedServers{Version: 1},
|
||||||
Wevpn: models.WevpnServers{Version: 1},
|
Vyprvpn: models.VyprvpnServers{Version: 1},
|
||||||
Windscribe: models.WindscribeServers{Version: 1},
|
Wevpn: models.WevpnServers{Version: 1},
|
||||||
|
Windscribe: models.WindscribeServers{Version: 1},
|
||||||
},
|
},
|
||||||
persisted: models.AllServers{
|
persisted: models.AllServers{
|
||||||
Cyberghost: models.CyberghostServers{Version: 1, Timestamp: 1},
|
Cyberghost: models.CyberghostServers{Version: 1, Timestamp: 1},
|
||||||
Expressvpn: models.ExpressvpnServers{Version: 1, Timestamp: 1},
|
Expressvpn: models.ExpressvpnServers{Version: 1, Timestamp: 1},
|
||||||
Fastestvpn: models.FastestvpnServers{Version: 1, Timestamp: 1},
|
Fastestvpn: models.FastestvpnServers{Version: 1, Timestamp: 1},
|
||||||
HideMyAss: models.HideMyAssServers{Version: 1, Timestamp: 1},
|
HideMyAss: models.HideMyAssServers{Version: 1, Timestamp: 1},
|
||||||
Ipvanish: models.IpvanishServers{Version: 1, Timestamp: 1},
|
Ipvanish: models.IpvanishServers{Version: 1, Timestamp: 1},
|
||||||
Ivpn: models.IvpnServers{Version: 1, Timestamp: 1},
|
Ivpn: models.IvpnServers{Version: 1, Timestamp: 1},
|
||||||
Mullvad: models.MullvadServers{Version: 1, Timestamp: 1},
|
Mullvad: models.MullvadServers{Version: 1, Timestamp: 1},
|
||||||
Nordvpn: models.NordvpnServers{Version: 1, Timestamp: 1},
|
Nordvpn: models.NordvpnServers{Version: 1, Timestamp: 1},
|
||||||
Privado: models.PrivadoServers{Version: 1, Timestamp: 1},
|
Perfectprivacy: models.PerfectprivacyServers{Version: 1, Timestamp: 1},
|
||||||
Pia: models.PiaServers{Version: 1, Timestamp: 1},
|
Privado: models.PrivadoServers{Version: 1, Timestamp: 1},
|
||||||
Privatevpn: models.PrivatevpnServers{Version: 1, Timestamp: 1},
|
Pia: models.PiaServers{Version: 1, Timestamp: 1},
|
||||||
Protonvpn: models.ProtonvpnServers{Version: 1, Timestamp: 1},
|
Privatevpn: models.PrivatevpnServers{Version: 1, Timestamp: 1},
|
||||||
Purevpn: models.PurevpnServers{Version: 1, Timestamp: 1},
|
Protonvpn: models.ProtonvpnServers{Version: 1, Timestamp: 1},
|
||||||
Surfshark: models.SurfsharkServers{Version: 1, Timestamp: 1},
|
Purevpn: models.PurevpnServers{Version: 1, Timestamp: 1},
|
||||||
Torguard: models.TorguardServers{Version: 1, Timestamp: 1},
|
Surfshark: models.SurfsharkServers{Version: 1, Timestamp: 1},
|
||||||
VPNUnlimited: models.VPNUnlimitedServers{Version: 1, Timestamp: 1},
|
Torguard: models.TorguardServers{Version: 1, Timestamp: 1},
|
||||||
Vyprvpn: models.VyprvpnServers{Version: 1, Timestamp: 1},
|
VPNUnlimited: models.VPNUnlimitedServers{Version: 1, Timestamp: 1},
|
||||||
Wevpn: models.WevpnServers{Version: 1, Timestamp: 1},
|
Vyprvpn: models.VyprvpnServers{Version: 1, Timestamp: 1},
|
||||||
Windscribe: models.WindscribeServers{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": {
|
"nordvpn": {
|
||||||
"version": 3,
|
"version": 3,
|
||||||
"timestamp": 1627008323,
|
"timestamp": 1627008323,
|
||||||
@@ -111526,4 +111941,4 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ func countServers(allServers models.AllServers) int {
|
|||||||
len(allServers.Ivpn.Servers) +
|
len(allServers.Ivpn.Servers) +
|
||||||
len(allServers.Mullvad.Servers) +
|
len(allServers.Mullvad.Servers) +
|
||||||
len(allServers.Nordvpn.Servers) +
|
len(allServers.Nordvpn.Servers) +
|
||||||
|
len(allServers.Perfectprivacy.Servers) +
|
||||||
len(allServers.Privado.Servers) +
|
len(allServers.Privado.Servers) +
|
||||||
len(allServers.Pia.Servers) +
|
len(allServers.Pia.Servers) +
|
||||||
len(allServers.Privatevpn.Servers) +
|
len(allServers.Privatevpn.Servers) +
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -53,20 +54,38 @@ func ExtractHost(b []byte) (host, warning string, err error) {
|
|||||||
return hosts[0], warning, nil
|
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) {
|
func ExtractIP(b []byte) (ip net.IP, warning string, err error) {
|
||||||
const (
|
ips, err := ExtractIPs(b)
|
||||||
rejectIP = false
|
if err != nil {
|
||||||
rejectDomain = true
|
return nil, "", err
|
||||||
)
|
}
|
||||||
ips := extractRemoteHosts(b, rejectIP, rejectDomain)
|
|
||||||
if len(ips) == 0 {
|
if len(ips) > 1 {
|
||||||
return nil, "", ErrNoRemoteIP
|
|
||||||
} else if len(ips) > 1 {
|
|
||||||
warning = fmt.Sprintf(
|
warning = fmt.Sprintf(
|
||||||
"only using the first IP address %s and discarding %d other hosts",
|
"only using the first IP address %s and discarding %d other hosts",
|
||||||
ips[0], len(ips)-1)
|
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) {
|
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/ivpn"
|
||||||
"github.com/qdm12/gluetun/internal/updater/providers/mullvad"
|
"github.com/qdm12/gluetun/internal/updater/providers/mullvad"
|
||||||
"github.com/qdm12/gluetun/internal/updater/providers/nordvpn"
|
"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/pia"
|
||||||
"github.com/qdm12/gluetun/internal/updater/providers/privado"
|
"github.com/qdm12/gluetun/internal/updater/providers/privado"
|
||||||
"github.com/qdm12/gluetun/internal/updater/providers/privatevpn"
|
"github.com/qdm12/gluetun/internal/updater/providers/privatevpn"
|
||||||
@@ -190,6 +191,27 @@ func (u *updater) updateNordvpn(ctx context.Context) (err error) {
|
|||||||
return nil
|
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) {
|
func (u *updater) updatePIA(ctx context.Context) (err error) {
|
||||||
minServers := getMinServers(len(u.servers.Pia.Servers))
|
minServers := getMinServers(len(u.servers.Pia.Servers))
|
||||||
servers, err := pia.GetServers(ctx, u.client, minServers)
|
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{}
|
contents = map[string][]byte{}
|
||||||
for _, zf := range r.File {
|
for _, zf := range r.File {
|
||||||
fileName := filepath.Base(zf.Name)
|
fileName := filepath.Base(zf.Name)
|
||||||
if !strings.HasSuffix(fileName, ".ovpn") {
|
if !strings.HasSuffix(fileName, ".ovpn") &&
|
||||||
|
!strings.HasSuffix(fileName, ".conf") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
f, err := zf.Open()
|
f, err := zf.Open()
|
||||||
|
|||||||
@@ -14,8 +14,6 @@ var (
|
|||||||
|
|
||||||
func (u *unzipper) FetchAndExtract(ctx context.Context, url string) (
|
func (u *unzipper) FetchAndExtract(ctx context.Context, url string) (
|
||||||
contents map[string][]byte, err error) {
|
contents map[string][]byte, err error) {
|
||||||
contents = make(map[string][]byte)
|
|
||||||
|
|
||||||
request, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
request, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -40,13 +38,5 @@ func (u *unzipper) FetchAndExtract(ctx context.Context, url string) (
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
newContents, err := zipExtractAll(b)
|
return zipExtractAll(b)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
for fileName, content := range newContents {
|
|
||||||
contents[fileName] = content
|
|
||||||
}
|
|
||||||
|
|
||||||
return contents, nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
if u.options.Privado {
|
||||||
u.logger.Info("updating Privado servers...")
|
u.logger.Info("updating Privado servers...")
|
||||||
if err := u.updatePrivado(ctx); err != nil {
|
if err := u.updatePrivado(ctx); err != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user