Compare commits

...

38 Commits

Author SHA1 Message Date
Quentin McGaw (desktop)
6b6caa435f Fix: clear IP data when VPN is stopped 2021-09-06 13:28:05 +00:00
Quentin McGaw (desktop)
f9cb71027c Feat: location data at /v1/publicip/ip 2021-09-05 22:54:10 +00:00
Quentin McGaw (desktop)
82ac568ee3 Fix: wireguard cleanup preventing restarts 2021-09-04 22:29:04 +00:00
Quentin McGaw (desktop)
61afdce788 Hotfix: Wireguard WIREGUARD_ADDRESSES setting 2021-08-28 20:59:39 +00:00
Quentin McGaw (desktop)
119cac5a67 Feat: OPENVPN_TARGET_IP overrides IP
- Check target IP matches a server for Wireguard since we need the public key
- Streamline connection picking for all providers
2021-08-28 19:07:44 +00:00
Quentin McGaw (desktop)
c6fedd9214 Feat: support csv addresses in WIREGUARD_ADDRESS 2021-08-28 18:43:23 +00:00
Quentin McGaw (desktop)
da525e039d Fix: update Mullvad annoucement logged 2021-08-28 18:14:28 +00:00
Quentin McGaw (desktop)
29d92fd307 Fix: Surfshark REGION retro-compatibility 2021-08-28 18:14:21 +00:00
Quentin McGaw (desktop)
3863cc439e Maint: internal/storage rework
- No more global variables
- Inject merged servers to configuration package
- Fix #566: configuration parsing to use persisted servers.json
- Move server data files from `internal/constants` to `internal/storage`
2021-08-27 19:10:03 +00:00
Quentin McGaw (desktop)
b1cfc03fc5 Maint: internal/storage remove Windscribe debug logs 2021-08-27 12:10:49 +00:00
Quentin McGaw (desktop)
f706071048 Fix: FIREWALL_VPN_INPUT_PORTS for Wireguard 2021-08-26 19:54:48 +00:00
Quentin McGaw (desktop)
501ae2741b Fix: FIREWALL_OUTBOUND_SUBNETS ip rules 2021-08-26 15:46:19 +00:00
Quentin McGaw (desktop)
5b75635386 Maint: fix rules equality check for nil networks 2021-08-26 14:33:51 +00:00
Quentin McGaw (desktop)
2901db3cf3 Maint: internal/routing IP rules functions
- Take in `src` as `*net.IPNet` instead of `net.IP`
- Take `dst` IP network
- Debug logged `ip rule` dynamically built
- Add unit tests for all IP rules functions
2021-08-26 13:59:43 +00:00
Quentin McGaw (desktop)
6c2a3e36b5 Maint: rename outboundsubnets.go to outbound.go 2021-08-25 19:09:42 +00:00
Quentin McGaw (desktop)
8b125e6e95 Maint: internal/routing/inbound.go file 2021-08-25 19:08:55 +00:00
Quentin McGaw (desktop)
e1cc14e055 Fix: firewall inherits log level from LOG_LEVEL 2021-08-25 17:55:46 +00:00
Quentin McGaw (desktop)
d6659552df Maint: refactor internal/routing
- Split Go files better
- Reduce public API for exported errors
2021-08-25 17:52:05 +00:00
Quentin McGaw (desktop)
67001fa958 Maint: rename files in internal/subnet 2021-08-25 17:27:10 +00:00
Quentin McGaw (desktop)
ffeeae91ab Maint: merge subnet.FindSubnetsToAdd and subnet.FindSubnetsToRemove in subnet.FindSubnetsToChange 2021-08-25 17:25:36 +00:00
Quentin McGaw (desktop)
04fad1b781 Maint: internal/subnet package 2021-08-25 17:22:48 +00:00
Quentin McGaw (desktop)
dcaf952986 Maint: http proxy server constructor returns struct 2021-08-25 17:03:55 +00:00
Quentin McGaw (desktop)
ca3b9e892d Maint: http proxy HTTPS handling simplifications 2021-08-25 17:02:50 +00:00
Quentin McGaw (desktop)
9f12ffc069 Fix: MULTIHOP_ONLY defaults to no 2021-08-24 13:12:40 +00:00
Quentin McGaw (desktop)
0d6800a515 Fix: panic for certain no server found errors 2021-08-23 21:19:53 +00:00
Quentin McGaw (desktop)
b3d8b78205 Maint: only internal/netlink depends on github.com/vishvananda/netlink 2021-08-23 21:12:28 +00:00
Quentin McGaw (desktop)
ee82a85543 Maint: internal/routing uses internal/netlink 2021-08-23 20:56:10 +00:00
Quentin McGaw (desktop)
7907146aaf Maint: rework IPIsPrivate in internal/routing 2021-08-23 20:50:50 +00:00
Quentin McGaw (desktop)
1a677ce4f7 Maint: internal/routing returns *Routine struct 2021-08-23 20:50:32 +00:00
Quentin McGaw (desktop)
f1a6594474 Maint: utils.FilterByProtocol function 2021-08-23 20:16:29 +00:00
Quentin McGaw
f1a82d9d9c Feat: rework Surfshark servers data (#575)
- Feat: `MULTIHOP_ONLY` variable
- Feat: `COUNTRY` variable
- Feat: `CITY` variable
- Feat: `REGION` variable, with retro-compatibility
- Feat: merge servers from API, zip and hardcoded hostnames
- Fix: remove outdated and duplicate servers
- Maint: faster update with fully parallel DNS resolutions
2021-08-23 10:25:00 -07:00
Quentin McGaw (desktop)
8b52af0d03 Maint: common GetPort for OpenVPN+Wireguard providers 2021-08-23 16:13:20 +00:00
Quentin McGaw (desktop)
dbf5c569ea Maint: common GetProtocol for OpenVPN+Wireguard providers 2021-08-23 16:07:47 +00:00
Quentin McGaw (desktop)
06a2d79cb4 Feat: Wireguard support for Ivpn (#584) 2021-08-23 16:01:01 +00:00
Quentin McGaw (desktop)
eb6238ee52 Feat: WIREGUARD_PORT for Mullvad 2021-08-23 16:00:40 +00:00
Quentin McGaw (desktop)
f41fec57ed Feat: IVPN supports TCP and custom port 2021-08-23 13:34:00 +00:00
Quentin McGaw
c348343b22 IVPN server data update code and ISP filter (#578)
- Use IVPN's HTTP API instead of their .zip file
- Unit tests for API and GetServers
- Paves the way for Wireguard
- Update server information for IVPN
- Add `ISP` filter for IVPN
2021-08-22 20:11:56 -07:00
Quentin McGaw
b69dcb62e3 LOG_LEVEL variable (#577) 2021-08-22 18:57:10 -07:00
190 changed files with 8055 additions and 4071 deletions

View File

@@ -68,6 +68,7 @@ LABEL \
org.opencontainers.image.description="VPN swiss-knife like client to tunnel to multiple VPN servers using OpenVPN, IPtables, DNS over TLS, Shadowsocks, an HTTP proxy and Alpine Linux" org.opencontainers.image.description="VPN swiss-knife like client to tunnel to multiple VPN servers using OpenVPN, IPtables, DNS over TLS, Shadowsocks, an HTTP proxy and Alpine Linux"
ENV VPNSP=pia \ ENV VPNSP=pia \
VERSION_INFORMATION=on \ VERSION_INFORMATION=on \
LOG_LEVEL=info \
VPN_TYPE=openvpn \ VPN_TYPE=openvpn \
PROTOCOL=udp \ PROTOCOL=udp \
OPENVPN_VERSION=2.5 \ OPENVPN_VERSION=2.5 \

View File

@@ -58,7 +58,7 @@ using Go, OpenVPN, iptables, DNS over TLS, ShadowSocks and an HTTP proxy*
- Based on Alpine 3.14 for a small Docker image of 31MB - Based on Alpine 3.14 for a small Docker image of 31MB
- Supports: **Cyberghost**, **FastestVPN**, **HideMyAss**, **IPVanish**, **IVPN**, **Mullvad**, **NordVPN**, **Privado**, **Private Internet Access**, **PrivateVPN**, **ProtonVPN**, **PureVPN**, **Surfshark**, **TorGuard**, **VPNUnlimited**, **Vyprvpn**, **Windscribe** servers - Supports: **Cyberghost**, **FastestVPN**, **HideMyAss**, **IPVanish**, **IVPN**, **Mullvad**, **NordVPN**, **Privado**, **Private Internet Access**, **PrivateVPN**, **ProtonVPN**, **PureVPN**, **Surfshark**, **TorGuard**, **VPNUnlimited**, **Vyprvpn**, **Windscribe** servers
- Supports OpenVPN - Supports OpenVPN
- Supports Wireguard for **Mullvad** and **Windscribe** (more in progress, see #134) - Supports Wireguard for **Mullvad**, **Ivpn** and **Windscribe** (more in progress, see #134)
- DNS over TLS baked in with service provider(s) of your choice - DNS over TLS baked in with service provider(s) of your choice
- DNS fine blocking of malicious/ads/surveillance hostnames and IP addresses, with live update every 24 hours - DNS fine blocking of malicious/ads/surveillance hostnames and IP addresses, with live update every 24 hours
- Choose the vpn network protocol, `udp` or `tcp` - Choose the vpn network protocol, `udp` or `tcp`

View File

@@ -135,12 +135,21 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
} }
} }
// TODO run this in a loop or in openvpn to reload from file without restarting
storageLogger := logger.NewChild(logging.Settings{Prefix: "storage: "})
storage, err := storage.New(storageLogger, constants.ServersData)
if err != nil {
return err
}
allServers := storage.GetServers()
var allSettings configuration.Settings var allSettings configuration.Settings
err := allSettings.Read(env, err = allSettings.Read(env, allServers,
logger.NewChild(logging.Settings{Prefix: "configuration: "})) logger.NewChild(logging.Settings{Prefix: "configuration: "}))
if err != nil { if err != nil {
return err return err
} }
logger.PatchLevel(allSettings.Log.Level)
puid, pgid := allSettings.System.PUID, allSettings.System.PGID puid, pgid := allSettings.System.PUID, allSettings.System.PGID
@@ -167,7 +176,7 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
Version: buildInfo.Version, Version: buildInfo.Version,
Commit: buildInfo.Commit, Commit: buildInfo.Commit,
BuildDate: buildInfo.Created, BuildDate: buildInfo.Created,
Announcement: "Wireguard is now supported!", Announcement: "Wireguard is now supported for Mullvad, IVPN and Windscribe!",
AnnounceExp: announcementExp, AnnounceExp: announcementExp,
// Sponsor information // Sponsor information
PaypalUser: "qmcgaw", PaypalUser: "qmcgaw",
@@ -199,15 +208,6 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
return err return err
} }
// TODO run this in a loop or in openvpn to reload from file without restarting
storage := storage.New(
logger.NewChild(logging.Settings{Prefix: "storage: "}),
constants.ServersData)
allServers, err := storage.SyncServers(constants.GetAllServers())
if err != nil {
return err
}
const defaultUsername = "nonrootuser" const defaultUsername = "nonrootuser"
nonRootUsername, err := alpineConf.CreateUser(defaultUsername, puid) nonRootUsername, err := alpineConf.CreateUser(defaultUsername, puid)
if err != nil { if err != nil {
@@ -225,7 +225,7 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
return err return err
} }
firewallLogLevel := logging.LevelInfo firewallLogLevel := allSettings.Log.Level
if allSettings.Firewall.Debug { if allSettings.Firewall.Debug {
firewallLogLevel = logging.LevelDebug firewallLogLevel = logging.LevelDebug
} }
@@ -233,7 +233,7 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
Prefix: "routing: ", Prefix: "routing: ",
Level: firewallLogLevel, Level: firewallLogLevel,
}) })
routingConf := routing.NewRouting(routingLogger) routingConf := routing.New(netLinker, routingLogger)
defaultInterface, defaultGateway, err := routingConf.DefaultRoute() defaultInterface, defaultGateway, err := routingConf.DefaultRoute()
if err != nil { if err != nil {
@@ -293,7 +293,8 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
} }
for _, vpnPort := range allSettings.Firewall.VPNInputPorts { for _, vpnPort := range allSettings.Firewall.VPNInputPorts {
err = firewallConf.SetAllowedPort(ctx, vpnPort, allSettings.VPN.OpenVPN.Interface) vpnIntf := allSettings.VPN.VPNInterface()
err = firewallConf.SetAllowedPort(ctx, vpnPort, vpnIntf)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -18,13 +18,14 @@ type OpenvpnConfigMaker interface {
} }
func (c *CLI) OpenvpnConfig(logger logging.Logger, env params.Interface) error { func (c *CLI) OpenvpnConfig(logger logging.Logger, env params.Interface) error {
var allSettings configuration.Settings storage, err := storage.New(logger, constants.ServersData)
err := allSettings.Read(env, logger)
if err != nil { if err != nil {
return err return err
} }
allServers, err := storage.New(logger, constants.ServersData). allServers := storage.GetServers()
SyncServers(constants.GetAllServers())
var allSettings configuration.Settings
err = allSettings.Read(env, allServers, logger)
if err != nil { if err != nil {
return err return err
} }

View File

@@ -20,7 +20,7 @@ import (
var ( var (
ErrModeUnspecified = errors.New("at least one of -enduser or -maintainers must be specified") ErrModeUnspecified = errors.New("at least one of -enduser or -maintainers must be specified")
ErrSyncServers = errors.New("cannot sync hardcoded and persisted servers") ErrNewStorage = errors.New("cannot create storage")
ErrUpdateServerInformation = errors.New("cannot update server information") ErrUpdateServerInformation = errors.New("cannot update server information")
ErrWriteToFile = errors.New("cannot write updated information to file") ErrWriteToFile = errors.New("cannot write updated information to file")
) )
@@ -68,11 +68,13 @@ func (c *CLI) Update(ctx context.Context, args []string, logger logging.Logger)
const clientTimeout = 10 * time.Second const clientTimeout = 10 * time.Second
httpClient := &http.Client{Timeout: clientTimeout} httpClient := &http.Client{Timeout: clientTimeout}
storage := storage.New(logger, constants.ServersData)
currentServers, err := storage.SyncServers(constants.GetAllServers()) storage, err := storage.New(logger, constants.ServersData)
if err != nil { if err != nil {
return fmt.Errorf("%w: %s", ErrSyncServers, err) return fmt.Errorf("%w: %s", ErrNewStorage, err)
} }
currentServers := storage.GetServers()
updater := updater.New(options, httpClient, currentServers, logger) updater := updater.New(options, httpClient, currentServers, logger)
allServers, err := updater.UpdateServers(ctx) allServers, err := updater.UpdateServers(ctx)
if err != nil { if err != nil {

View File

@@ -8,6 +8,7 @@ import (
func (settings *Provider) readCyberghost(r reader) (err error) { func (settings *Provider) readCyberghost(r reader) (err error) {
settings.Name = constants.Cyberghost settings.Name = constants.Cyberghost
servers := r.servers.GetCyberghost()
settings.ServerSelection.TargetIP, err = readTargetIP(r.env) settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil { if err != nil {
@@ -15,17 +16,18 @@ func (settings *Provider) readCyberghost(r reader) (err error) {
} }
settings.ServerSelection.Groups, err = r.env.CSVInside("CYBERGHOST_GROUP", settings.ServerSelection.Groups, err = r.env.CSVInside("CYBERGHOST_GROUP",
constants.CyberghostGroupChoices()) constants.CyberghostGroupChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable CYBERGHOST_GROUP: %w", err) return fmt.Errorf("environment variable CYBERGHOST_GROUP: %w", err)
} }
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.CyberghostRegionChoices()) settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.CyberghostRegionChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable REGION: %w", err) return fmt.Errorf("environment variable REGION: %w", err)
} }
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.CyberghostHostnameChoices()) settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME",
constants.CyberghostHostnameChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err) return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
} }

View File

@@ -8,18 +8,20 @@ import (
func (settings *Provider) readFastestvpn(r reader) (err error) { func (settings *Provider) readFastestvpn(r reader) (err error) {
settings.Name = constants.Fastestvpn settings.Name = constants.Fastestvpn
servers := r.servers.GetFastestvpn()
settings.ServerSelection.TargetIP, err = readTargetIP(r.env) settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil { if err != nil {
return err return err
} }
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.FastestvpnHostnameChoices()) settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME",
constants.FastestvpnHostnameChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err) return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
} }
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.FastestvpnCountriesChoices()) settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.FastestvpnCountriesChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err) return fmt.Errorf("environment variable COUNTRY: %w", err)
} }

View File

@@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/golibs/logging" "github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/params" "github.com/qdm12/golibs/params"
) )
@@ -33,7 +34,7 @@ func (settings *Health) lines() (lines []string) {
// Read is to be used for the healthcheck query mode. // Read is to be used for the healthcheck query mode.
func (settings *Health) Read(env params.Interface, logger logging.Logger) (err error) { func (settings *Health) Read(env params.Interface, logger logging.Logger) (err error) {
reader := newReader(env, logger) reader := newReader(env, models.AllServers{}, logger) // note: no need for servers data
return settings.read(reader) return settings.read(reader)
} }

View File

@@ -8,28 +8,30 @@ import (
func (settings *Provider) readHideMyAss(r reader) (err error) { func (settings *Provider) readHideMyAss(r reader) (err error) {
settings.Name = constants.HideMyAss settings.Name = constants.HideMyAss
servers := r.servers.GetHideMyAss()
settings.ServerSelection.TargetIP, err = readTargetIP(r.env) settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil { if err != nil {
return err return err
} }
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.HideMyAssCountryChoices()) settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.HideMyAssCountryChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err) return fmt.Errorf("environment variable COUNTRY: %w", err)
} }
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.HideMyAssCountryChoices()) settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.HideMyAssCountryChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable REGION: %w", err) return fmt.Errorf("environment variable REGION: %w", err)
} }
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.HideMyAssCityChoices()) settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.HideMyAssCityChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable CITY: %w", err) return fmt.Errorf("environment variable CITY: %w", err)
} }
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.HideMyAssHostnameChoices()) settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME",
constants.HideMyAssHostnameChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err) return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
} }

View File

@@ -8,23 +8,25 @@ import (
func (settings *Provider) readIpvanish(r reader) (err error) { func (settings *Provider) readIpvanish(r reader) (err error) {
settings.Name = constants.Ipvanish settings.Name = constants.Ipvanish
servers := r.servers.GetIpvanish()
settings.ServerSelection.TargetIP, err = readTargetIP(r.env) settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil { if err != nil {
return err return err
} }
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.IpvanishCountryChoices()) settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.IpvanishCountryChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err) return fmt.Errorf("environment variable COUNTRY: %w", err)
} }
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.IpvanishCityChoices()) settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.IpvanishCityChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable CITY: %w", err) return fmt.Errorf("environment variable CITY: %w", err)
} }
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.IpvanishHostnameChoices()) settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME",
constants.IpvanishHostnameChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err) return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
} }

View File

@@ -7,6 +7,7 @@ import (
"github.com/golang/mock/gomock" "github.com/golang/mock/gomock"
"github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/golibs/params/mock_params" "github.com/qdm12/golibs/params/mock_params"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@@ -119,21 +120,28 @@ func Test_Provider_readIpvanish(t *testing.T) {
t.Parallel() t.Parallel()
ctrl := gomock.NewController(t) ctrl := gomock.NewController(t)
servers := []models.IpvanishServer{{Hostname: "a"}}
allServers := models.AllServers{
Ipvanish: models.IpvanishServers{
Servers: servers,
},
}
env := mock_params.NewMockInterface(ctrl) env := mock_params.NewMockInterface(ctrl)
if testCase.targetIP.call { if testCase.targetIP.call {
env.EXPECT().Get("OPENVPN_TARGET_IP"). env.EXPECT().Get("OPENVPN_TARGET_IP").
Return(testCase.targetIP.value, testCase.targetIP.err) Return(testCase.targetIP.value, testCase.targetIP.err)
} }
if testCase.countries.call { if testCase.countries.call {
env.EXPECT().CSVInside("COUNTRY", constants.IpvanishCountryChoices()). env.EXPECT().CSVInside("COUNTRY", constants.IpvanishCountryChoices(servers)).
Return(testCase.countries.values, testCase.countries.err) Return(testCase.countries.values, testCase.countries.err)
} }
if testCase.cities.call { if testCase.cities.call {
env.EXPECT().CSVInside("CITY", constants.IpvanishCityChoices()). env.EXPECT().CSVInside("CITY", constants.IpvanishCityChoices(servers)).
Return(testCase.cities.values, testCase.cities.err) Return(testCase.cities.values, testCase.cities.err)
} }
if testCase.hostnames.call { if testCase.hostnames.call {
env.EXPECT().CSVInside("SERVER_HOSTNAME", constants.IpvanishHostnameChoices()). env.EXPECT().CSVInside("SERVER_HOSTNAME", constants.IpvanishHostnameChoices(servers)).
Return(testCase.hostnames.values, testCase.hostnames.err) Return(testCase.hostnames.values, testCase.hostnames.err)
} }
if testCase.protocol.call { if testCase.protocol.call {
@@ -141,7 +149,10 @@ func Test_Provider_readIpvanish(t *testing.T) {
Return(testCase.protocol.value, testCase.protocol.err) Return(testCase.protocol.value, testCase.protocol.err)
} }
r := reader{env: env} r := reader{
servers: allServers,
env: env,
}
var settings Provider var settings Provider
err := settings.readIpvanish(r) err := settings.readIpvanish(r)

View File

@@ -4,30 +4,67 @@ import (
"fmt" "fmt"
"github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/golibs/params"
) )
func (settings *Provider) readIvpn(r reader) (err error) { func (settings *Provider) readIvpn(r reader) (err error) {
settings.Name = constants.Ivpn settings.Name = constants.Ivpn
servers := r.servers.GetIvpn()
settings.ServerSelection.TargetIP, err = readTargetIP(r.env) settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil { if err != nil {
return err return err
} }
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.IvpnCountryChoices()) settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.IvpnCountryChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err) return fmt.Errorf("environment variable COUNTRY: %w", err)
} }
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.IvpnCityChoices()) settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.IvpnCityChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable CITY: %w", err) return fmt.Errorf("environment variable CITY: %w", err)
} }
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.IvpnHostnameChoices()) settings.ServerSelection.ISPs, err = r.env.CSVInside("ISP", constants.IvpnISPChoices(servers))
if err != nil {
return fmt.Errorf("environment variable ISP: %w", err)
}
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.IvpnHostnameChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err) return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
} }
return settings.ServerSelection.OpenVPN.readProtocolOnly(r.env) err = settings.ServerSelection.OpenVPN.readIVPN(r.env)
if err != nil {
return err
}
return settings.ServerSelection.Wireguard.readIVPN(r.env)
}
func (settings *OpenVPNSelection) readIVPN(env params.Interface) (err error) {
settings.TCP, err = readProtocol(env)
if err != nil {
return err
}
settings.CustomPort, err = readOpenVPNCustomPort(env, settings.TCP,
[]uint16{80, 443, 1443}, []uint16{53, 1194, 2049, 2050})
if err != nil {
return err
}
return nil
}
func (settings *WireguardSelection) readIVPN(env params.Interface) (err error) {
settings.CustomPort, err = readWireguardCustomPort(env,
[]uint16{2049, 2050, 53, 30587, 41893, 48574, 58237})
if err != nil {
return err
}
return nil
} }

View File

@@ -7,12 +7,13 @@ import (
"github.com/golang/mock/gomock" "github.com/golang/mock/gomock"
"github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/golibs/params/mock_params" "github.com/qdm12/golibs/params/mock_params"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func Test_Provider_readIvpn(t *testing.T) { func Test_Provider_readIvpn(t *testing.T) { //nolint:gocognit
t.Parallel() t.Parallel()
var errDummy = errors.New("dummy test error") var errDummy = errors.New("dummy test error")
@@ -23,6 +24,15 @@ func Test_Provider_readIvpn(t *testing.T) {
err error err error
} }
type portCall struct {
getCall bool
getValue string // "" or "0"
getErr error
portCall bool
portValue uint16
portErr error
}
type sliceStringCall struct { type sliceStringCall struct {
call bool call bool
values []string values []string
@@ -30,11 +40,14 @@ func Test_Provider_readIvpn(t *testing.T) {
} }
testCases := map[string]struct { testCases := map[string]struct {
protocol singleStringCall
targetIP singleStringCall targetIP singleStringCall
countries sliceStringCall countries sliceStringCall
cities sliceStringCall cities sliceStringCall
isps sliceStringCall
hostnames sliceStringCall hostnames sliceStringCall
protocol singleStringCall
ovpnPort portCall
wgPort portCall
settings Provider settings Provider
err error err error
}{ }{
@@ -62,20 +75,32 @@ func Test_Provider_readIvpn(t *testing.T) {
}, },
err: errors.New("environment variable CITY: dummy test error"), err: errors.New("environment variable CITY: dummy test error"),
}, },
"isps error": {
targetIP: singleStringCall{call: true},
countries: sliceStringCall{call: true},
cities: sliceStringCall{call: true},
isps: sliceStringCall{call: true, err: errDummy},
settings: Provider{
Name: constants.Ivpn,
},
err: errors.New("environment variable ISP: dummy test error"),
},
"hostnames error": { "hostnames error": {
targetIP: singleStringCall{call: true}, targetIP: singleStringCall{call: true},
countries: sliceStringCall{call: true}, countries: sliceStringCall{call: true},
cities: sliceStringCall{call: true}, cities: sliceStringCall{call: true},
isps: sliceStringCall{call: true},
hostnames: sliceStringCall{call: true, err: errDummy}, hostnames: sliceStringCall{call: true, err: errDummy},
settings: Provider{ settings: Provider{
Name: constants.Ivpn, Name: constants.Ivpn,
}, },
err: errors.New("environment variable SERVER_HOSTNAME: dummy test error"), err: errors.New("environment variable SERVER_HOSTNAME: dummy test error"),
}, },
"protocol error": { "openvpn protocol error": {
targetIP: singleStringCall{call: true}, targetIP: singleStringCall{call: true},
countries: sliceStringCall{call: true}, countries: sliceStringCall{call: true},
cities: sliceStringCall{call: true}, cities: sliceStringCall{call: true},
isps: sliceStringCall{call: true},
hostnames: sliceStringCall{call: true}, hostnames: sliceStringCall{call: true},
protocol: singleStringCall{call: true, err: errDummy}, protocol: singleStringCall{call: true, err: errDummy},
settings: Provider{ settings: Provider{
@@ -83,12 +108,42 @@ func Test_Provider_readIvpn(t *testing.T) {
}, },
err: errors.New("environment variable PROTOCOL: dummy test error"), err: errors.New("environment variable PROTOCOL: dummy test error"),
}, },
"openvpn custom port error": {
targetIP: singleStringCall{call: true},
countries: sliceStringCall{call: true},
cities: sliceStringCall{call: true},
isps: sliceStringCall{call: true},
hostnames: sliceStringCall{call: true},
protocol: singleStringCall{call: true},
ovpnPort: portCall{getCall: true, getErr: errDummy},
settings: Provider{
Name: constants.Ivpn,
},
err: errors.New("environment variable PORT: dummy test error"),
},
"wireguard custom port error": {
targetIP: singleStringCall{call: true},
countries: sliceStringCall{call: true},
cities: sliceStringCall{call: true},
isps: sliceStringCall{call: true},
hostnames: sliceStringCall{call: true},
protocol: singleStringCall{call: true},
ovpnPort: portCall{getCall: true, getValue: "0"},
wgPort: portCall{getCall: true, getErr: errDummy},
settings: Provider{
Name: constants.Ivpn,
},
err: errors.New("environment variable WIREGUARD_PORT: dummy test error"),
},
"default settings": { "default settings": {
targetIP: singleStringCall{call: true}, targetIP: singleStringCall{call: true},
countries: sliceStringCall{call: true}, countries: sliceStringCall{call: true},
cities: sliceStringCall{call: true}, cities: sliceStringCall{call: true},
isps: sliceStringCall{call: true},
hostnames: sliceStringCall{call: true}, hostnames: sliceStringCall{call: true},
protocol: singleStringCall{call: true}, protocol: singleStringCall{call: true},
ovpnPort: portCall{getCall: true, getValue: "0"},
wgPort: portCall{getCall: true, getValue: "0"},
settings: Provider{ settings: Provider{
Name: constants.Ivpn, Name: constants.Ivpn,
}, },
@@ -97,17 +152,25 @@ func Test_Provider_readIvpn(t *testing.T) {
targetIP: singleStringCall{call: true, value: "1.2.3.4"}, targetIP: singleStringCall{call: true, value: "1.2.3.4"},
countries: sliceStringCall{call: true, values: []string{"A", "B"}}, countries: sliceStringCall{call: true, values: []string{"A", "B"}},
cities: sliceStringCall{call: true, values: []string{"C", "D"}}, cities: sliceStringCall{call: true, values: []string{"C", "D"}},
isps: sliceStringCall{call: true, values: []string{"ISP 1"}},
hostnames: sliceStringCall{call: true, values: []string{"E", "F"}}, hostnames: sliceStringCall{call: true, values: []string{"E", "F"}},
protocol: singleStringCall{call: true, value: constants.TCP}, protocol: singleStringCall{call: true, value: constants.TCP},
ovpnPort: portCall{getCall: true, portCall: true, portValue: 443},
wgPort: portCall{getCall: true, portCall: true, portValue: 2049},
settings: Provider{ settings: Provider{
Name: constants.Ivpn, Name: constants.Ivpn,
ServerSelection: ServerSelection{ ServerSelection: ServerSelection{
OpenVPN: OpenVPNSelection{ OpenVPN: OpenVPNSelection{
TCP: true, TCP: true,
CustomPort: 443,
},
Wireguard: WireguardSelection{
CustomPort: 2049,
}, },
TargetIP: net.IPv4(1, 2, 3, 4), TargetIP: net.IPv4(1, 2, 3, 4),
Countries: []string{"A", "B"}, Countries: []string{"A", "B"},
Cities: []string{"C", "D"}, Cities: []string{"C", "D"},
ISPs: []string{"ISP 1"},
Hostnames: []string{"E", "F"}, Hostnames: []string{"E", "F"},
}, },
}, },
@@ -120,28 +183,59 @@ func Test_Provider_readIvpn(t *testing.T) {
ctrl := gomock.NewController(t) ctrl := gomock.NewController(t)
env := mock_params.NewMockInterface(ctrl) env := mock_params.NewMockInterface(ctrl)
if testCase.protocol.call {
env.EXPECT().Inside("PROTOCOL", []string{constants.TCP, constants.UDP}, gomock.Any()). servers := []models.IvpnServer{{Hostname: "a"}}
Return(testCase.protocol.value, testCase.protocol.err) allServers := models.AllServers{
Ivpn: models.IvpnServers{
Servers: servers,
},
} }
if testCase.targetIP.call { if testCase.targetIP.call {
env.EXPECT().Get("OPENVPN_TARGET_IP"). env.EXPECT().Get("OPENVPN_TARGET_IP").
Return(testCase.targetIP.value, testCase.targetIP.err) Return(testCase.targetIP.value, testCase.targetIP.err)
} }
if testCase.countries.call { if testCase.countries.call {
env.EXPECT().CSVInside("COUNTRY", constants.IvpnCountryChoices()). env.EXPECT().CSVInside("COUNTRY", constants.IvpnCountryChoices(servers)).
Return(testCase.countries.values, testCase.countries.err) Return(testCase.countries.values, testCase.countries.err)
} }
if testCase.cities.call { if testCase.cities.call {
env.EXPECT().CSVInside("CITY", constants.IvpnCityChoices()). env.EXPECT().CSVInside("CITY", constants.IvpnCityChoices(servers)).
Return(testCase.cities.values, testCase.cities.err) Return(testCase.cities.values, testCase.cities.err)
} }
if testCase.isps.call {
env.EXPECT().CSVInside("ISP", constants.IvpnISPChoices(servers)).
Return(testCase.isps.values, testCase.isps.err)
}
if testCase.hostnames.call { if testCase.hostnames.call {
env.EXPECT().CSVInside("SERVER_HOSTNAME", constants.IvpnHostnameChoices()). env.EXPECT().CSVInside("SERVER_HOSTNAME", constants.IvpnHostnameChoices(servers)).
Return(testCase.hostnames.values, testCase.hostnames.err) Return(testCase.hostnames.values, testCase.hostnames.err)
} }
if testCase.protocol.call {
env.EXPECT().Inside("PROTOCOL", []string{constants.TCP, constants.UDP}, gomock.Any()).
Return(testCase.protocol.value, testCase.protocol.err)
}
if testCase.ovpnPort.getCall {
env.EXPECT().Get("PORT", gomock.Any()).
Return(testCase.ovpnPort.getValue, testCase.ovpnPort.getErr)
}
if testCase.ovpnPort.portCall {
env.EXPECT().Port("PORT").
Return(testCase.ovpnPort.portValue, testCase.ovpnPort.portErr)
}
if testCase.wgPort.getCall {
env.EXPECT().Get("WIREGUARD_PORT", gomock.Any()).
Return(testCase.wgPort.getValue, testCase.wgPort.getErr)
}
if testCase.wgPort.portCall {
env.EXPECT().Port("WIREGUARD_PORT").
Return(testCase.wgPort.portValue, testCase.wgPort.portErr)
}
r := reader{env: env} r := reader{
servers: allServers,
env: env,
}
var settings Provider var settings Provider
err := settings.readIvpn(r) err := settings.readIvpn(r)

View File

@@ -0,0 +1,30 @@
package configuration
import (
"fmt"
"github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/params"
)
type Log struct {
Level logging.Level `json:"level"`
}
func (settings *Log) lines() (lines []string) {
lines = append(lines, lastIndent+"Log:")
lines = append(lines, indent+lastIndent+"Level: "+settings.Level.String())
return lines
}
func (settings *Log) read(env params.Interface) (err error) {
defaultLevel := logging.LevelInfo.String()
settings.Level, err = env.LogLevel("LOG_LEVEL", params.Default(defaultLevel))
if err != nil {
return fmt.Errorf("environment variable LOG_LEVEL: %w", err)
}
return nil
}

View File

@@ -9,28 +9,29 @@ import (
func (settings *Provider) readMullvad(r reader) (err error) { func (settings *Provider) readMullvad(r reader) (err error) {
settings.Name = constants.Mullvad settings.Name = constants.Mullvad
servers := r.servers.GetMullvad()
settings.ServerSelection.TargetIP, err = readTargetIP(r.env) settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil { if err != nil {
return err return err
} }
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.MullvadCountryChoices()) settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.MullvadCountryChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err) return fmt.Errorf("environment variable COUNTRY: %w", err)
} }
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.MullvadCityChoices()) settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.MullvadCityChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable CITY: %w", err) return fmt.Errorf("environment variable CITY: %w", err)
} }
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.MullvadHostnameChoices()) settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.MullvadHostnameChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err) return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
} }
settings.ServerSelection.ISPs, err = r.env.CSVInside("ISP", constants.MullvadISPChoices()) settings.ServerSelection.ISPs, err = r.env.CSVInside("ISP", constants.MullvadISPChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable ISP: %w", err) return fmt.Errorf("environment variable ISP: %w", err)
} }
@@ -40,7 +41,12 @@ func (settings *Provider) readMullvad(r reader) (err error) {
return fmt.Errorf("environment variable OWNED: %w", err) return fmt.Errorf("environment variable OWNED: %w", err)
} }
return settings.ServerSelection.OpenVPN.readMullvad(r.env) err = settings.ServerSelection.OpenVPN.readMullvad(r.env)
if err != nil {
return err
}
return settings.ServerSelection.Wireguard.readMullvad(r.env)
} }
func (settings *OpenVPNSelection) readMullvad(env params.Interface) (err error) { func (settings *OpenVPNSelection) readMullvad(env params.Interface) (err error) {
@@ -57,3 +63,12 @@ func (settings *OpenVPNSelection) readMullvad(env params.Interface) (err error)
return nil return nil
} }
func (settings *WireguardSelection) readMullvad(env params.Interface) (err error) {
settings.CustomPort, err = readWireguardCustomPort(env, nil)
if err != nil {
return err
}
return nil
}

View File

@@ -10,23 +10,24 @@ import (
func (settings *Provider) readNordvpn(r reader) (err error) { func (settings *Provider) readNordvpn(r reader) (err error) {
settings.Name = constants.Nordvpn settings.Name = constants.Nordvpn
servers := r.servers.GetNordvpn()
settings.ServerSelection.TargetIP, err = readTargetIP(r.env) settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil { if err != nil {
return err return err
} }
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.NordvpnRegionChoices()) settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.NordvpnRegionChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable REGION: %w", err) return fmt.Errorf("environment variable REGION: %w", err)
} }
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.NordvpnHostnameChoices()) settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.NordvpnHostnameChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err) return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
} }
settings.ServerSelection.Names, err = r.env.CSVInside("SERVER_NAME", constants.NordvpnHostnameChoices()) settings.ServerSelection.Names, err = r.env.CSVInside("SERVER_NAME", constants.NordvpnHostnameChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_NAME: %w", err) return fmt.Errorf("environment variable SERVER_NAME: %w", err)
} }

View File

@@ -8,28 +8,29 @@ import (
func (settings *Provider) readPrivado(r reader) (err error) { func (settings *Provider) readPrivado(r reader) (err error) {
settings.Name = constants.Privado settings.Name = constants.Privado
servers := r.servers.GetPrivado()
settings.ServerSelection.TargetIP, err = readTargetIP(r.env) settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil { if err != nil {
return err return err
} }
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.PrivadoCountryChoices()) settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.PrivadoCountryChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err) return fmt.Errorf("environment variable COUNTRY: %w", err)
} }
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.PrivadoRegionChoices()) settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.PrivadoRegionChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable REGION: %w", err) return fmt.Errorf("environment variable REGION: %w", err)
} }
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.PrivadoCityChoices()) settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.PrivadoCityChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable CITY: %w", err) return fmt.Errorf("environment variable CITY: %w", err)
} }
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.PrivadoHostnameChoices()) settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.PrivadoHostnameChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err) return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
} }

View File

@@ -9,23 +9,24 @@ import (
func (settings *Provider) readPrivateInternetAccess(r reader) (err error) { func (settings *Provider) readPrivateInternetAccess(r reader) (err error) {
settings.Name = constants.PrivateInternetAccess settings.Name = constants.PrivateInternetAccess
servers := r.servers.GetPia()
settings.ServerSelection.TargetIP, err = readTargetIP(r.env) settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil { if err != nil {
return err return err
} }
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.PIAGeoChoices()) settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.PIAGeoChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable REGION: %w", err) return fmt.Errorf("environment variable REGION: %w", err)
} }
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.PIAHostnameChoices()) settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.PIAHostnameChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err) return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
} }
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_NAME", constants.PIANameChoices()) settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_NAME", constants.PIANameChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_NAME: %w", err) return fmt.Errorf("environment variable SERVER_NAME: %w", err)
} }

View File

@@ -8,23 +8,25 @@ import (
func (settings *Provider) readPrivatevpn(r reader) (err error) { func (settings *Provider) readPrivatevpn(r reader) (err error) {
settings.Name = constants.Privatevpn settings.Name = constants.Privatevpn
servers := r.servers.GetPrivatevpn()
settings.ServerSelection.TargetIP, err = readTargetIP(r.env) settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil { if err != nil {
return err return err
} }
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.PrivatevpnCountryChoices()) settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.PrivatevpnCountryChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err) return fmt.Errorf("environment variable COUNTRY: %w", err)
} }
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.PrivatevpnCityChoices()) settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.PrivatevpnCityChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable CITY: %w", err) return fmt.Errorf("environment variable CITY: %w", err)
} }
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.PrivatevpnHostnameChoices()) settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME",
constants.PrivatevpnHostnameChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err) return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
} }

View File

@@ -9,33 +9,35 @@ import (
func (settings *Provider) readProtonvpn(r reader) (err error) { func (settings *Provider) readProtonvpn(r reader) (err error) {
settings.Name = constants.Protonvpn settings.Name = constants.Protonvpn
servers := r.servers.GetProtonvpn()
settings.ServerSelection.TargetIP, err = readTargetIP(r.env) settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil { if err != nil {
return err return err
} }
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.ProtonvpnCountryChoices()) settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.ProtonvpnCountryChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err) return fmt.Errorf("environment variable COUNTRY: %w", err)
} }
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.ProtonvpnRegionChoices()) settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.ProtonvpnRegionChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable REGION: %w", err) return fmt.Errorf("environment variable REGION: %w", err)
} }
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.ProtonvpnCityChoices()) settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.ProtonvpnCityChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable CITY: %w", err) return fmt.Errorf("environment variable CITY: %w", err)
} }
settings.ServerSelection.Names, err = r.env.CSVInside("SERVER_NAME", constants.ProtonvpnNameChoices()) settings.ServerSelection.Names, err = r.env.CSVInside("SERVER_NAME", constants.ProtonvpnNameChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_NAME: %w", err) return fmt.Errorf("environment variable SERVER_NAME: %w", err)
} }
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.ProtonvpnHostnameChoices()) settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME",
constants.ProtonvpnHostnameChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err) return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
} }

View File

@@ -103,7 +103,8 @@ func (settings *Provider) readVPNServiceProvider(r reader, vpnType string) (err
"privado", "pia", "private internet access", "privatevpn", "protonvpn", "privado", "pia", "private internet access", "privatevpn", "protonvpn",
"purevpn", "surfshark", "torguard", constants.VPNUnlimited, "vyprvpn", "windscribe"} "purevpn", "surfshark", "torguard", constants.VPNUnlimited, "vyprvpn", "windscribe"}
case constants.Wireguard: case constants.Wireguard:
allowedVPNServiceProviders = []string{constants.Mullvad, constants.Windscribe} allowedVPNServiceProviders = []string{constants.Mullvad, constants.Windscribe,
constants.Ivpn}
} }
vpnsp, err := r.env.Inside("VPNSP", allowedVPNServiceProviders, vpnsp, err := r.env.Inside("VPNSP", allowedVPNServiceProviders,
@@ -167,6 +168,7 @@ func readOpenVPNCustomPort(env params.Interface, tcp bool,
ErrInvalidPort, port, portsToString(allowedUDP)) ErrInvalidPort, port, portsToString(allowedUDP))
} }
// note: set allowed to an empty slice to allow all valid ports
func readWireguardCustomPort(env params.Interface, allowed []uint16) (port uint16, err error) { func readWireguardCustomPort(env params.Interface, allowed []uint16) (port uint16, err error) {
port, err = readPortOrZero(env, "WIREGUARD_PORT") port, err = readPortOrZero(env, "WIREGUARD_PORT")
if err != nil { if err != nil {
@@ -175,11 +177,16 @@ func readWireguardCustomPort(env params.Interface, allowed []uint16) (port uint1
return 0, nil return 0, nil
} }
if len(allowed) == 0 {
return port, nil
}
for i := range allowed { for i := range allowed {
if allowed[i] == port { if allowed[i] == port {
return port, nil return port, nil
} }
} }
return 0, fmt.Errorf( return 0, fmt.Errorf(
"environment variable WIREGUARD_PORT: %w: port %d, can only be one of %s", "environment variable WIREGUARD_PORT: %w: port %d, can only be one of %s",
ErrInvalidPort, port, portsToString(allowed)) ErrInvalidPort, port, portsToString(allowed))

View File

@@ -8,28 +8,29 @@ import (
func (settings *Provider) readPurevpn(r reader) (err error) { func (settings *Provider) readPurevpn(r reader) (err error) {
settings.Name = constants.Purevpn settings.Name = constants.Purevpn
servers := r.servers.GetPurevpn()
settings.ServerSelection.TargetIP, err = readTargetIP(r.env) settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil { if err != nil {
return err return err
} }
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.PurevpnRegionChoices()) settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.PurevpnRegionChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable REGION: %w", err) return fmt.Errorf("environment variable REGION: %w", err)
} }
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.PurevpnCountryChoices()) settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.PurevpnCountryChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err) return fmt.Errorf("environment variable COUNTRY: %w", err)
} }
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.PurevpnCityChoices()) settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.PurevpnCityChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable CITY: %w", err) return fmt.Errorf("environment variable CITY: %w", err)
} }
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.PurevpnHostnameChoices()) settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.PurevpnHostnameChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err) return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
} }

View File

@@ -7,19 +7,23 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/golibs/logging" "github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/params" "github.com/qdm12/golibs/params"
"github.com/qdm12/golibs/verification" "github.com/qdm12/golibs/verification"
) )
type reader struct { type reader struct {
servers models.AllServers
env params.Interface env params.Interface
logger logging.Logger logger logging.Logger
regex verification.Regex regex verification.Regex
} }
func newReader(env params.Interface, logger logging.Logger) reader { func newReader(env params.Interface,
servers models.AllServers, logger logging.Logger) reader {
return reader{ return reader{
servers: servers,
env: env, env: env,
logger: logger, logger: logger,
regex: verification.NewRegex(), regex: verification.NewRegex(),

View File

@@ -40,6 +40,9 @@ type ServerSelection struct { //nolint:maligned
// VPNUnlimited // VPNUnlimited
StreamOnly bool `json:"stream_only"` StreamOnly bool `json:"stream_only"`
// Surfshark
MultiHopOnly bool `json:"multihop_only"`
OpenVPN OpenVPNSelection `json:"openvpn"` OpenVPN OpenVPNSelection `json:"openvpn"`
Wireguard WireguardSelection `json:"wireguard"` Wireguard WireguardSelection `json:"wireguard"`
} }

View File

@@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/golibs/logging" "github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/params" "github.com/qdm12/golibs/params"
) )
@@ -22,6 +23,7 @@ type Settings struct {
VersionInformation bool VersionInformation bool
ControlServer ControlServer ControlServer ControlServer
Health Health Health Health
Log Log
} }
func (settings *Settings) String() string { func (settings *Settings) String() string {
@@ -33,6 +35,7 @@ func (settings *Settings) lines() (lines []string) {
lines = append(lines, settings.VPN.lines()...) lines = append(lines, settings.VPN.lines()...)
lines = append(lines, settings.DNS.lines()...) lines = append(lines, settings.DNS.lines()...)
lines = append(lines, settings.Firewall.lines()...) lines = append(lines, settings.Firewall.lines()...)
lines = append(lines, settings.Log.lines()...)
lines = append(lines, settings.System.lines()...) lines = append(lines, settings.System.lines()...)
lines = append(lines, settings.HTTPProxy.lines()...) lines = append(lines, settings.HTTPProxy.lines()...)
lines = append(lines, settings.ShadowSocks.lines()...) lines = append(lines, settings.ShadowSocks.lines()...)
@@ -57,12 +60,14 @@ var (
ErrUpdater = errors.New("cannot read Updater settings") ErrUpdater = errors.New("cannot read Updater settings")
ErrPublicIP = errors.New("cannot read Public IP getter settings") ErrPublicIP = errors.New("cannot read Public IP getter settings")
ErrHealth = errors.New("cannot read health settings") ErrHealth = errors.New("cannot read health settings")
ErrLog = errors.New("cannot read log settings")
) )
// Read obtains all configuration options for the program and returns an error as soon // Read obtains all configuration options for the program and returns an error as soon
// as an error is encountered reading them. // as an error is encountered reading them.
func (settings *Settings) Read(env params.Interface, logger logging.Logger) (err error) { func (settings *Settings) Read(env params.Interface, servers models.AllServers,
r := newReader(env, logger) logger logging.Logger) (err error) {
r := newReader(env, servers, logger)
settings.VersionInformation, err = r.env.OnOff("VERSION_INFORMATION", params.Default("on")) settings.VersionInformation, err = r.env.OnOff("VERSION_INFORMATION", params.Default("on"))
if err != nil { if err != nil {
@@ -113,5 +118,9 @@ func (settings *Settings) Read(env params.Interface, logger logging.Logger) (err
return fmt.Errorf("%w: %s", ErrHealth, err) return fmt.Errorf("%w: %s", ErrHealth, err)
} }
if err := settings.Log.read(r.env); err != nil {
return fmt.Errorf("%w: %s", ErrLog, err)
}
return nil return nil
} }

View File

@@ -43,6 +43,8 @@ func Test_Settings_lines(t *testing.T) {
" |--Protocol: udp", " |--Protocol: udp",
"|--DNS:", "|--DNS:",
"|--Firewall: disabled ⚠️", "|--Firewall: disabled ⚠️",
"|--Log:",
" |--Level: DEBUG",
"|--System:", "|--System:",
" |--Process user ID: 0", " |--Process user ID: 0",
" |--Process group ID: 0", " |--Process group ID: 0",

View File

@@ -2,27 +2,101 @@ package configuration
import ( import (
"fmt" "fmt"
"strings"
"github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/golibs/params"
) )
func (settings *Provider) readSurfshark(r reader) (err error) { func (settings *Provider) readSurfshark(r reader) (err error) {
settings.Name = constants.Surfshark settings.Name = constants.Surfshark
servers := r.servers.GetSurfshark()
settings.ServerSelection.TargetIP, err = readTargetIP(r.env) settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil { if err != nil {
return err return err
} }
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.SurfsharkRegionChoices()) settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.SurfsharkCountryChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable REGION: %w", err) return fmt.Errorf("environment variable COUNTRY: %w", err)
} }
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.SurfsharkHostnameChoices()) settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.SurfsharkCityChoices(servers))
if err != nil {
return fmt.Errorf("environment variable CITY: %w", err)
}
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME",
constants.SurfsharkHostnameChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err) return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
} }
regionChoices := constants.SurfsharkRegionChoices(servers)
regionChoices = append(regionChoices, constants.SurfsharkRetroLocChoices(servers)...)
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", regionChoices)
if err != nil {
return fmt.Errorf("environment variable REGION: %w", err)
}
// Retro compatibility
// TODO remove in v4
settings.ServerSelection = surfsharkRetroRegion(settings.ServerSelection)
settings.ServerSelection.MultiHopOnly, err = r.env.YesNo("MULTIHOP_ONLY", params.Default("no"))
if err != nil {
return fmt.Errorf("environment variable MULTIHOP_ONLY: %w", err)
}
return settings.ServerSelection.OpenVPN.readProtocolOnly(r.env) return settings.ServerSelection.OpenVPN.readProtocolOnly(r.env)
} }
func surfsharkRetroRegion(selection ServerSelection) (
updatedSelection ServerSelection) {
locationData := constants.SurfsharkLocationData()
retroToLocation := make(map[string]models.SurfsharkLocationData, len(locationData))
for _, data := range locationData {
if data.RetroLoc == "" {
continue
}
retroToLocation[strings.ToLower(data.RetroLoc)] = data
}
for i, region := range selection.Regions {
location, ok := retroToLocation[region]
if !ok {
continue
}
selection.Regions[i] = strings.ToLower(location.Region)
selection.Countries = append(selection.Countries, strings.ToLower(location.Country))
selection.Cities = append(selection.Cities, strings.ToLower(location.City)) // even empty string
selection.Hostnames = append(selection.Hostnames, location.Hostname)
}
selection.Regions = dedupSlice(selection.Regions)
selection.Countries = dedupSlice(selection.Countries)
selection.Cities = dedupSlice(selection.Cities)
selection.Hostnames = dedupSlice(selection.Hostnames)
return selection
}
func dedupSlice(slice []string) (deduped []string) {
if slice == nil {
return nil
}
deduped = make([]string, 0, len(slice))
seen := make(map[string]struct{}, len(slice))
for _, s := range slice {
if _, ok := seen[s]; !ok {
seen[s] = struct{}{}
deduped = append(deduped, s)
}
}
return deduped
}

View File

@@ -0,0 +1,305 @@
package configuration
import (
"errors"
"net"
"testing"
"github.com/golang/mock/gomock"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/golibs/params/mock_params"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_Provider_readSurfshark(t *testing.T) {
t.Parallel()
var errDummy = errors.New("dummy test error")
type stringCall struct {
call bool
value string
err error
}
type boolCall struct {
call bool
value bool
err error
}
type sliceStringCall struct {
call bool
values []string
err error
}
testCases := map[string]struct {
targetIP stringCall
countries sliceStringCall
cities sliceStringCall
hostnames sliceStringCall
regions sliceStringCall
multiHop boolCall
protocol stringCall
settings Provider
err error
}{
"target IP error": {
targetIP: stringCall{call: true, value: "something", err: errDummy},
settings: Provider{
Name: constants.Surfshark,
},
err: errors.New("environment variable OPENVPN_TARGET_IP: dummy test error"),
},
"countries error": {
targetIP: stringCall{call: true},
countries: sliceStringCall{call: true, err: errDummy},
settings: Provider{
Name: constants.Surfshark,
},
err: errors.New("environment variable COUNTRY: dummy test error"),
},
"cities error": {
targetIP: stringCall{call: true},
countries: sliceStringCall{call: true},
cities: sliceStringCall{call: true, err: errDummy},
settings: Provider{
Name: constants.Surfshark,
},
err: errors.New("environment variable CITY: dummy test error"),
},
"hostnames error": {
targetIP: stringCall{call: true},
countries: sliceStringCall{call: true},
cities: sliceStringCall{call: true},
hostnames: sliceStringCall{call: true, err: errDummy},
settings: Provider{
Name: constants.Surfshark,
},
err: errors.New("environment variable SERVER_HOSTNAME: dummy test error"),
},
"regions error": {
targetIP: stringCall{call: true},
countries: sliceStringCall{call: true},
cities: sliceStringCall{call: true},
hostnames: sliceStringCall{call: true},
regions: sliceStringCall{call: true, err: errDummy},
settings: Provider{
Name: constants.Surfshark,
},
err: errors.New("environment variable REGION: dummy test error"),
},
"multi hop error": {
targetIP: stringCall{call: true},
countries: sliceStringCall{call: true},
cities: sliceStringCall{call: true},
hostnames: sliceStringCall{call: true},
regions: sliceStringCall{call: true},
multiHop: boolCall{call: true, err: errDummy},
settings: Provider{
Name: constants.Surfshark,
},
err: errors.New("environment variable MULTIHOP_ONLY: dummy test error"),
},
"openvpn protocol error": {
targetIP: stringCall{call: true},
countries: sliceStringCall{call: true},
cities: sliceStringCall{call: true},
hostnames: sliceStringCall{call: true},
regions: sliceStringCall{call: true},
multiHop: boolCall{call: true},
protocol: stringCall{call: true, err: errDummy},
settings: Provider{
Name: constants.Surfshark,
},
err: errors.New("environment variable PROTOCOL: dummy test error"),
},
"default settings": {
targetIP: stringCall{call: true},
countries: sliceStringCall{call: true},
cities: sliceStringCall{call: true},
hostnames: sliceStringCall{call: true},
regions: sliceStringCall{call: true},
multiHop: boolCall{call: true},
protocol: stringCall{call: true},
settings: Provider{
Name: constants.Surfshark,
},
},
"set settings": {
targetIP: stringCall{call: true, value: "1.2.3.4"},
countries: sliceStringCall{call: true, values: []string{"A", "B"}},
cities: sliceStringCall{call: true, values: []string{"C", "D"}},
regions: sliceStringCall{call: true, values: []string{
"E", "F", "netherlands amsterdam",
}}, // Netherlands Amsterdam is for retro compatibility test
multiHop: boolCall{call: true},
hostnames: sliceStringCall{call: true, values: []string{"E", "F"}},
protocol: stringCall{call: true, value: constants.TCP},
settings: Provider{
Name: constants.Surfshark,
ServerSelection: ServerSelection{
OpenVPN: OpenVPNSelection{
TCP: true,
},
TargetIP: net.IPv4(1, 2, 3, 4),
Regions: []string{"E", "F", "europe"},
Countries: []string{"A", "B", "netherlands"},
Cities: []string{"C", "D", "amsterdam"},
Hostnames: []string{"E", "F", "nl-ams.prod.surfshark.com"},
},
},
},
"Netherlands Amsterdam": {
targetIP: stringCall{call: true},
countries: sliceStringCall{call: true},
cities: sliceStringCall{call: true},
regions: sliceStringCall{call: true, values: []string{"netherlands amsterdam"}},
multiHop: boolCall{call: true},
hostnames: sliceStringCall{call: true},
protocol: stringCall{call: true},
settings: Provider{
Name: constants.Surfshark,
ServerSelection: ServerSelection{
Regions: []string{"europe"},
Countries: []string{"netherlands"},
Cities: []string{"amsterdam"},
Hostnames: []string{"nl-ams.prod.surfshark.com"},
},
},
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
ctrl := gomock.NewController(t)
env := mock_params.NewMockInterface(ctrl)
servers := []models.SurfsharkServer{{Hostname: "a"}}
allServers := models.AllServers{
Surfshark: models.SurfsharkServers{
Servers: servers,
},
}
if testCase.targetIP.call {
env.EXPECT().Get("OPENVPN_TARGET_IP").
Return(testCase.targetIP.value, testCase.targetIP.err)
}
if testCase.countries.call {
env.EXPECT().CSVInside("COUNTRY", constants.SurfsharkCountryChoices(servers)).
Return(testCase.countries.values, testCase.countries.err)
}
if testCase.cities.call {
env.EXPECT().CSVInside("CITY", constants.SurfsharkCityChoices(servers)).
Return(testCase.cities.values, testCase.cities.err)
}
if testCase.hostnames.call {
env.EXPECT().CSVInside("SERVER_HOSTNAME", constants.SurfsharkHostnameChoices(servers)).
Return(testCase.hostnames.values, testCase.hostnames.err)
}
if testCase.regions.call {
regionChoices := constants.SurfsharkRegionChoices(servers)
regionChoices = append(regionChoices, constants.SurfsharkRetroLocChoices(servers)...)
env.EXPECT().CSVInside("REGION", regionChoices).
Return(testCase.regions.values, testCase.regions.err)
}
if testCase.multiHop.call {
env.EXPECT().YesNo("MULTIHOP_ONLY", gomock.Any()).
Return(testCase.multiHop.value, testCase.multiHop.err)
}
if testCase.protocol.call {
env.EXPECT().Inside("PROTOCOL", []string{constants.TCP, constants.UDP}, gomock.Any()).
Return(testCase.protocol.value, testCase.protocol.err)
}
r := reader{
servers: allServers,
env: env,
}
var settings Provider
err := settings.readSurfshark(r)
if testCase.err != nil {
require.Error(t, err)
assert.Equal(t, testCase.err.Error(), err.Error())
} else {
assert.NoError(t, err)
}
assert.Equal(t, testCase.settings, settings)
})
}
}
func Test_surfsharkRetroRegion(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
original ServerSelection
modified ServerSelection
}{
"empty": {},
"1 retro region": {
original: ServerSelection{
Regions: []string{"australia adelaide"},
},
modified: ServerSelection{
Regions: []string{"asia pacific"},
Countries: []string{"australia"},
Cities: []string{"adelaide"},
Hostnames: []string{"au-adl.prod.surfshark.com"},
},
},
"2 overlapping retro regions": {
original: ServerSelection{
Regions: []string{"australia adelaide", "australia melbourne"},
},
modified: ServerSelection{
Regions: []string{"asia pacific"},
Countries: []string{"australia"},
Cities: []string{"adelaide", "melbourne"},
Hostnames: []string{"au-adl.prod.surfshark.com", "au-mel.prod.surfshark.com"},
},
},
"2 distinct retro regions": {
original: ServerSelection{
Regions: []string{"australia adelaide", "netherlands amsterdam"},
},
modified: ServerSelection{
Regions: []string{"asia pacific", "europe"},
Countries: []string{"australia", "netherlands"},
Cities: []string{"adelaide", "amsterdam"},
Hostnames: []string{"au-adl.prod.surfshark.com", "nl-ams.prod.surfshark.com"},
},
},
"retro region with existing region": {
// note TestRegion will be ignored in the filters downstream
original: ServerSelection{
Regions: []string{"TestRegion", "australia adelaide"},
},
modified: ServerSelection{
Regions: []string{"TestRegion", "asia pacific"},
Countries: []string{"australia"},
Cities: []string{"adelaide"},
Hostnames: []string{"au-adl.prod.surfshark.com"},
},
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
selection := surfsharkRetroRegion(testCase.original)
assert.Equal(t, testCase.modified, selection)
})
}
}

View File

@@ -8,23 +8,25 @@ import (
func (settings *Provider) readTorguard(r reader) (err error) { func (settings *Provider) readTorguard(r reader) (err error) {
settings.Name = constants.Torguard settings.Name = constants.Torguard
servers := r.servers.GetTorguard()
settings.ServerSelection.TargetIP, err = readTargetIP(r.env) settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil { if err != nil {
return err return err
} }
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.TorguardCountryChoices()) settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.TorguardCountryChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err) return fmt.Errorf("environment variable COUNTRY: %w", err)
} }
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.TorguardCityChoices()) settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.TorguardCityChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable CITY: %w", err) return fmt.Errorf("environment variable CITY: %w", err)
} }
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.TorguardHostnameChoices()) settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME",
constants.TorguardHostnameChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err) return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
} }

View File

@@ -87,3 +87,11 @@ func (settings VPN) isOpenVPNCustomConfig(env params.Interface) (ok bool) {
s, err := env.Get("OPENVPN_CUSTOM_CONFIG") s, err := env.Get("OPENVPN_CUSTOM_CONFIG")
return err == nil && s != "" return err == nil && s != ""
} }
func (settings VPN) VPNInterface() (intf string) {
if settings.Type == constants.Wireguard {
return settings.Wireguard.Interface
}
// OpenVPN
return settings.OpenVPN.Interface
}

View File

@@ -9,23 +9,25 @@ import (
func (settings *Provider) readVPNUnlimited(r reader) (err error) { func (settings *Provider) readVPNUnlimited(r reader) (err error) {
settings.Name = constants.VPNUnlimited settings.Name = constants.VPNUnlimited
servers := r.servers.GetVPNUnlimited()
settings.ServerSelection.TargetIP, err = readTargetIP(r.env) settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil { if err != nil {
return err return err
} }
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.VPNUnlimitedCountryChoices()) settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.VPNUnlimitedCountryChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err) return fmt.Errorf("environment variable COUNTRY: %w", err)
} }
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.VPNUnlimitedCityChoices()) settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.VPNUnlimitedCityChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable CITY: %w", err) return fmt.Errorf("environment variable CITY: %w", err)
} }
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.VPNUnlimitedHostnameChoices()) settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME",
constants.VPNUnlimitedHostnameChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err) return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
} }

View File

@@ -8,13 +8,14 @@ import (
func (settings *Provider) readVyprvpn(r reader) (err error) { func (settings *Provider) readVyprvpn(r reader) (err error) {
settings.Name = constants.Vyprvpn settings.Name = constants.Vyprvpn
servers := r.servers.GetVyprvpn()
settings.ServerSelection.TargetIP, err = readTargetIP(r.env) settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil { if err != nil {
return err return err
} }
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.VyprvpnRegionChoices()) settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.VyprvpnRegionChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable REGION: %w", err) return fmt.Errorf("environment variable REGION: %w", err)
} }

View File

@@ -9,23 +9,25 @@ import (
func (settings *Provider) readWindscribe(r reader) (err error) { func (settings *Provider) readWindscribe(r reader) (err error) {
settings.Name = constants.Windscribe settings.Name = constants.Windscribe
servers := r.servers.GetWindscribe()
settings.ServerSelection.TargetIP, err = readTargetIP(r.env) settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil { if err != nil {
return err return err
} }
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.WindscribeRegionChoices()) settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.WindscribeRegionChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable REGION: %w", err) return fmt.Errorf("environment variable REGION: %w", err)
} }
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.WindscribeCityChoices()) settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.WindscribeCityChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable CITY: %w", err) return fmt.Errorf("environment variable CITY: %w", err)
} }
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.WindscribeHostnameChoices()) settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME",
constants.WindscribeHostnameChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err) return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
} }

View File

@@ -12,7 +12,7 @@ import (
type Wireguard struct { type Wireguard struct {
PrivateKey string `json:"privatekey"` PrivateKey string `json:"privatekey"`
PreSharedKey string `json:"presharedkey"` PreSharedKey string `json:"presharedkey"`
Address *net.IPNet `json:"address"` Addresses []*net.IPNet `json:"addresses"`
Interface string `json:"interface"` Interface string `json:"interface"`
} }
@@ -33,8 +33,11 @@ func (settings *Wireguard) lines() (lines []string) {
lines = append(lines, indent+lastIndent+"Pre-shared key is set") lines = append(lines, indent+lastIndent+"Pre-shared key is set")
} }
if settings.Address != nil { if len(settings.Addresses) > 0 {
lines = append(lines, indent+lastIndent+"Address: "+settings.Address.String()) lines = append(lines, indent+lastIndent+"Addresses: ")
for _, address := range settings.Addresses {
lines = append(lines, indent+indent+lastIndent+address.String())
}
} }
return lines return lines
@@ -53,16 +56,10 @@ func (settings *Wireguard) read(r reader) (err error) {
return fmt.Errorf("environment variable WIREGUARD_PRESHARED_KEY: %w", err) return fmt.Errorf("environment variable WIREGUARD_PRESHARED_KEY: %w", err)
} }
addressString, err := r.env.Get("WIREGUARD_ADDRESS", params.Compulsory()) err = settings.readAddresses(r.env)
if err != nil { if err != nil {
return fmt.Errorf("environment variable WIREGUARD_ADDRESS: %w", err) return err
} }
ip, ipNet, err := net.ParseCIDR(addressString)
if err != nil {
return fmt.Errorf("environment variable WIREGUARD_ADDRESS: %w", err)
}
ipNet.IP = ip
settings.Address = ipNet
settings.Interface, err = r.env.Get("WIREGUARD_INTERFACE", params.Default("wg0")) settings.Interface, err = r.env.Get("WIREGUARD_INTERFACE", params.Default("wg0"))
if err != nil { if err != nil {
@@ -71,3 +68,21 @@ func (settings *Wireguard) read(r reader) (err error) {
return nil return nil
} }
func (settings *Wireguard) readAddresses(env params.Interface) (err error) {
addressStrings, err := env.CSV("WIREGUARD_ADDRESS", params.Compulsory())
if err != nil {
return fmt.Errorf("environment variable WIREGUARD_ADDRESS: %w", err)
}
for _, addressString := range addressStrings {
ip, ipNet, err := net.ParseCIDR(addressString)
if err != nil {
return fmt.Errorf("environment variable WIREGUARD_ADDRESS: address %s: %w", addressString, err)
}
ipNet.IP = ip
settings.Addresses = append(settings.Addresses, ipNet)
}
return nil
}

View File

@@ -11,8 +11,7 @@ const (
CyberghostCertificate = "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" CyberghostCertificate = "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"
) )
func CyberghostRegionChoices() (choices []string) { func CyberghostRegionChoices(servers []models.CyberghostServer) (choices []string) {
servers := CyberghostServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].Region choices[i] = servers[i].Region
@@ -20,8 +19,7 @@ func CyberghostRegionChoices() (choices []string) {
return makeUnique(choices) return makeUnique(choices)
} }
func CyberghostGroupChoices() (choices []string) { func CyberghostGroupChoices(servers []models.CyberghostServer) (choices []string) {
servers := CyberghostServers()
uniqueChoices := map[string]struct{}{} uniqueChoices := map[string]struct{}{}
for _, server := range servers { for _, server := range servers {
uniqueChoices[server.Group] = struct{}{} uniqueChoices[server.Group] = struct{}{}
@@ -38,19 +36,10 @@ func CyberghostGroupChoices() (choices []string) {
return sortable return sortable
} }
func CyberghostHostnameChoices() (choices []string) { func CyberghostHostnameChoices(servers []models.CyberghostServer) (choices []string) {
servers := CyberghostServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].Hostname choices[i] = servers[i].Hostname
} }
return makeUnique(choices) return makeUnique(choices)
} }
// CyberghostServers returns a slice with the server information for each
// of the Cyberghost server.
func CyberghostServers() (servers []models.CyberghostServer) {
servers = make([]models.CyberghostServer, len(allServers.Cyberghost.Servers))
copy(servers, allServers.Cyberghost.Servers)
return servers
}

View File

@@ -3,16 +3,29 @@ package constants
import ( import (
"testing" "testing"
"github.com/golang/mock/gomock"
"github.com/qdm12/gluetun/internal/storage"
"github.com/qdm12/golibs/logging/mock_logging"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
func Test_CyberghostGroupChoices(t *testing.T) { func Test_CyberghostGroupChoices(t *testing.T) {
t.Parallel() t.Parallel()
ctrl := gomock.NewController(t)
logger := mock_logging.NewMockLogger(ctrl)
logger.EXPECT().Info(gomock.Any())
storage, err := storage.New(logger, "")
require.NoError(t, err)
servers := storage.GetServers()
expected := []string{"Premium TCP Asia", "Premium TCP Europe", expected := []string{"Premium TCP Asia", "Premium TCP Europe",
"Premium TCP USA", "Premium UDP Asia", "Premium UDP Europe", "Premium TCP USA", "Premium UDP Asia", "Premium UDP Europe",
"Premium UDP USA"} "Premium UDP USA"}
choices := CyberghostGroupChoices() choices := CyberghostGroupChoices(servers.GetCyberghost())
assert.Equal(t, expected, choices) assert.Equal(t, expected, choices)
} }

View File

@@ -10,8 +10,7 @@ const (
FastestvpnOpenvpnStaticKeyV1 = "697fe793b32cb5091d30f2326d5d124a9412e93d0a44ef7361395d76528fcbfc82c3859dccea70a93cfa8fae409709bff75f844cf5ff0c237f426d0c20969233db0e706edb6bdf195ec3dc11b3f76bc807a77e74662d9a800c8cd1144ebb67b7f0d3f1281d1baf522bfe03b7c3f963b1364fc0769400e413b61ca7b43ab19fac9e0f77e41efd4bda7fd77b1de2d7d7855cbbe3e620cecceac72c21a825b243e651f44d90e290e09c3ad650de8fca99c858bc7caad584bc69b11e5c9fd9381c69c505ec487a65912c672d83ed0113b5a74ddfbd3ab33b3683cec593557520a72c4d6cce46111f56f3396cc3ce7183edce553c68ea0796cf6c4375fad00aaa2a42" FastestvpnOpenvpnStaticKeyV1 = "697fe793b32cb5091d30f2326d5d124a9412e93d0a44ef7361395d76528fcbfc82c3859dccea70a93cfa8fae409709bff75f844cf5ff0c237f426d0c20969233db0e706edb6bdf195ec3dc11b3f76bc807a77e74662d9a800c8cd1144ebb67b7f0d3f1281d1baf522bfe03b7c3f963b1364fc0769400e413b61ca7b43ab19fac9e0f77e41efd4bda7fd77b1de2d7d7855cbbe3e620cecceac72c21a825b243e651f44d90e290e09c3ad650de8fca99c858bc7caad584bc69b11e5c9fd9381c69c505ec487a65912c672d83ed0113b5a74ddfbd3ab33b3683cec593557520a72c4d6cce46111f56f3396cc3ce7183edce553c68ea0796cf6c4375fad00aaa2a42"
) )
func FastestvpnCountriesChoices() (choices []string) { func FastestvpnCountriesChoices(servers []models.FastestvpnServer) (choices []string) {
servers := FastestvpnServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].Country choices[i] = servers[i].Country
@@ -19,18 +18,10 @@ func FastestvpnCountriesChoices() (choices []string) {
return choices return choices
} }
func FastestvpnHostnameChoices() (choices []string) { func FastestvpnHostnameChoices(servers []models.FastestvpnServer) (choices []string) {
servers := FastestvpnServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].Hostname choices[i] = servers[i].Hostname
} }
return choices return choices
} }
// FastestvpnServers returns the list of all VPN servers for FastestVPN.
func FastestvpnServers() (servers []models.FastestvpnServer) {
servers = make([]models.FastestvpnServer, len(allServers.Fastestvpn.Servers))
copy(servers, allServers.Fastestvpn.Servers)
return servers
}

View File

@@ -11,8 +11,7 @@ const (
HideMyAssRSAPrivateKey = "MIIEpAIBAAKCAQEA5XY3ERJYWs/YIeBoybivNlu+M32rJs+CAZsh7BnnetTxytI4ngsMRoqXETuis8udp2hsqEHsglLR9tlk9C8yCuKhxbkpdrXFWdISmUq5sa7/wqg/zJF1AZm5Jy0oHNyTHfG6XW61I/h9IN5dmcR9YLir8DVDBNllbtt0z+DnvOhYJOqC30ENahWkTmNKl1cT7EBrR5slddiBJleAb08z77pwsD310e6jWTBySsBcPy+xu/Jj2QgVil/3mstZZDI+noFzs3SkTFBkha/lNTP7NODBQ6m39iaJxz6ZR1xE3v7XU0H5WnpZIcQ2+kmu5Krk2y1GYMKL+9oaotXFPz9v+QIDAQABAoIBAQCcMcssOMOiFWc3MC3EWo4SP4MKQ9n0Uj5Z34LI151FdJyehlj54+VYQ1Cv71tCbjED2sZUBoP69mtsT/EzcsjqtfiOwgrifrs2+BOm+0HKHKiGlcbP9peiHkT10PxEITWXpYtJvGlbcfOjIxqt6B28cBjCK09ShrVQL9ylAKBearRRUacszppntMNTMtN/uG48ZR9Wm+xAczImdG6CrG5sLI/++JwM5PDChLvn5JgMGyOfQZdjNe1oSOVLmqFeG5uu/FS4oMon9+HtfjHJr4ZgA1yQ2wQh3GvEjlP8zwHxEpRJYbxpj6ZbjHZJ2HLX/Gcd9/cXiN8+fQ2zPIYQyG9dAoGBAPUUmt2nJNvl7gj0GbZZ3XR9o+hvj7bJ74W2NhMrw6kjrrzHTAUQd1sBQS8szAQCLqf2ou1aw9AMMBdsLAHydXxvbH7IBAla7rKr23iethtSfjhTNSgQLJHVZlNHfp3hzNtCQZ7j0qVjrteNotrdVF7kKPHDXAK00ICy6SPNjvrXAoGBAO+vdnO15jLeZbbi3lQNS4r8oCadyqyX7ouKE6MtKNhiPsNPGqHKiGcKs/+QylVgYvSmm7TgpsCAiEYeLSPT+Yq3y7HtwVpULlpfAhEJXmvn/6hGpOizx1WNGWhw7nHPWPDzf+jqCGzHdhK0aEZR3MZZQ+U+uKfGiJ8vrvgB7eGvAoGAWxxp5nU48rcsIw/8bxpBhgkfYk33M5EnBqKSv9XJS5wEXhIJZOiWNrLktNEGl4boKXE7aNoRacreJhcE1UR6AOS7hPZ+6atwiePyF4mJUeb9HZtxa493wk9/Vv6BR9il++1Jz/QKX4oLef8hyBP4Rb60qgxirG7kBLR+j9zfhskCgYEAzA5y5xIeuIIU0H4XUDG9dcebxSSjbwsuYIgeLdb9pjMGQhsvjjyyoh8/nT20tLkJpkXN3FFCRjNnUWLRhWYrVkkh1wqWiYOPrwqh5MU4KN/sDWSPcznTY+drkTpMFoKzsvdrl2zf3VR3FneXKv742bkXj601Ykko+XWMHcLutisCgYBSq8IrsjzfaTQiTGI9a7WWsvzK92bq7Abnfq7swAXWcJd/bnjTQKLrrvt2bmwNvlWKAb3c69BFMn0X4t4PuN0iJQ39D6aQAEaM7HwWAmjf5TbodbmgbGxdsUB4xcCIQQ1mvTkigXWrCg0YAD2GZSoaslXAAVv6nR5qWEIa0Hx9GA==" HideMyAssRSAPrivateKey = "MIIEpAIBAAKCAQEA5XY3ERJYWs/YIeBoybivNlu+M32rJs+CAZsh7BnnetTxytI4ngsMRoqXETuis8udp2hsqEHsglLR9tlk9C8yCuKhxbkpdrXFWdISmUq5sa7/wqg/zJF1AZm5Jy0oHNyTHfG6XW61I/h9IN5dmcR9YLir8DVDBNllbtt0z+DnvOhYJOqC30ENahWkTmNKl1cT7EBrR5slddiBJleAb08z77pwsD310e6jWTBySsBcPy+xu/Jj2QgVil/3mstZZDI+noFzs3SkTFBkha/lNTP7NODBQ6m39iaJxz6ZR1xE3v7XU0H5WnpZIcQ2+kmu5Krk2y1GYMKL+9oaotXFPz9v+QIDAQABAoIBAQCcMcssOMOiFWc3MC3EWo4SP4MKQ9n0Uj5Z34LI151FdJyehlj54+VYQ1Cv71tCbjED2sZUBoP69mtsT/EzcsjqtfiOwgrifrs2+BOm+0HKHKiGlcbP9peiHkT10PxEITWXpYtJvGlbcfOjIxqt6B28cBjCK09ShrVQL9ylAKBearRRUacszppntMNTMtN/uG48ZR9Wm+xAczImdG6CrG5sLI/++JwM5PDChLvn5JgMGyOfQZdjNe1oSOVLmqFeG5uu/FS4oMon9+HtfjHJr4ZgA1yQ2wQh3GvEjlP8zwHxEpRJYbxpj6ZbjHZJ2HLX/Gcd9/cXiN8+fQ2zPIYQyG9dAoGBAPUUmt2nJNvl7gj0GbZZ3XR9o+hvj7bJ74W2NhMrw6kjrrzHTAUQd1sBQS8szAQCLqf2ou1aw9AMMBdsLAHydXxvbH7IBAla7rKr23iethtSfjhTNSgQLJHVZlNHfp3hzNtCQZ7j0qVjrteNotrdVF7kKPHDXAK00ICy6SPNjvrXAoGBAO+vdnO15jLeZbbi3lQNS4r8oCadyqyX7ouKE6MtKNhiPsNPGqHKiGcKs/+QylVgYvSmm7TgpsCAiEYeLSPT+Yq3y7HtwVpULlpfAhEJXmvn/6hGpOizx1WNGWhw7nHPWPDzf+jqCGzHdhK0aEZR3MZZQ+U+uKfGiJ8vrvgB7eGvAoGAWxxp5nU48rcsIw/8bxpBhgkfYk33M5EnBqKSv9XJS5wEXhIJZOiWNrLktNEGl4boKXE7aNoRacreJhcE1UR6AOS7hPZ+6atwiePyF4mJUeb9HZtxa493wk9/Vv6BR9il++1Jz/QKX4oLef8hyBP4Rb60qgxirG7kBLR+j9zfhskCgYEAzA5y5xIeuIIU0H4XUDG9dcebxSSjbwsuYIgeLdb9pjMGQhsvjjyyoh8/nT20tLkJpkXN3FFCRjNnUWLRhWYrVkkh1wqWiYOPrwqh5MU4KN/sDWSPcznTY+drkTpMFoKzsvdrl2zf3VR3FneXKv742bkXj601Ykko+XWMHcLutisCgYBSq8IrsjzfaTQiTGI9a7WWsvzK92bq7Abnfq7swAXWcJd/bnjTQKLrrvt2bmwNvlWKAb3c69BFMn0X4t4PuN0iJQ39D6aQAEaM7HwWAmjf5TbodbmgbGxdsUB4xcCIQQ1mvTkigXWrCg0YAD2GZSoaslXAAVv6nR5qWEIa0Hx9GA=="
) )
func HideMyAssCountryChoices() (choices []string) { func HideMyAssCountryChoices(servers []models.HideMyAssServer) (choices []string) {
servers := HideMyAssServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].Country choices[i] = servers[i].Country
@@ -20,8 +19,7 @@ func HideMyAssCountryChoices() (choices []string) {
return makeUnique(choices) return makeUnique(choices)
} }
func HideMyAssCityChoices() (choices []string) { func HideMyAssCityChoices(servers []models.HideMyAssServer) (choices []string) {
servers := HideMyAssServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].City choices[i] = servers[i].City
@@ -29,18 +27,10 @@ func HideMyAssCityChoices() (choices []string) {
return makeUnique(choices) return makeUnique(choices)
} }
func HideMyAssHostnameChoices() (choices []string) { func HideMyAssHostnameChoices(servers []models.HideMyAssServer) (choices []string) {
servers := HideMyAssServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].Hostname choices[i] = servers[i].Hostname
} }
return makeUnique(choices) return makeUnique(choices)
} }
// HideMyAssServers returns a slice of all the server information for HideMyAss.
func HideMyAssServers() (servers []models.HideMyAssServer) {
servers = make([]models.HideMyAssServer, len(allServers.HideMyAss.Servers))
copy(servers, allServers.HideMyAss.Servers)
return servers
}

View File

@@ -9,8 +9,7 @@ const (
IpvanishCA = "MIIErTCCA5WgAwIBAgIJAMYKzSS8uPKDMA0GCSqGSIb3DQEBDQUAMIGVMQswCQYDVQQGEwJVUzELMAkGA1UECBMCRkwxFDASBgNVBAcTC1dpbnRlciBQYXJrMREwDwYDVQQKEwhJUFZhbmlzaDEVMBMGA1UECxMMSVBWYW5pc2ggVlBOMRQwEgYDVQQDEwtJUFZhbmlzaCBDQTEjMCEGCSqGSIb3DQEJARYUc3VwcG9ydEBpcHZhbmlzaC5jb20wHhcNMTIwMTExMTkzMjIwWhcNMjgxMTAyMTkzMjIwWjCBlTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkZMMRQwEgYDVQQHEwtXaW50ZXIgUGFyazERMA8GA1UEChMISVBWYW5pc2gxFTATBgNVBAsTDElQVmFuaXNoIFZQTjEUMBIGA1UEAxMLSVBWYW5pc2ggQ0ExIzAhBgkqhkiG9w0BCQEWFHN1cHBvcnRAaXB2YW5pc2guY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt9DBWNr/IKOuY3TmDP5x7vYZR0DGxLbXU8TyAzBbjUtFFMbhxlHiXVQrZHmgzih94x7BgXM7tWpmMKYVb+gNaqMdWE680Qm3nOwmhy/dulXDkEHAwD05i/iTx4ZaUdtV2vsKBxRg1vdC4AEiwD7bqV4HOi13xcG971aQ55Mj1KeCdA0aNvpat1LWx2jjWxsfI8s2Lv5Fkoi1HO1+vTnnaEsJZrBgAkLXpItqP29Lik3/OBIvkBIxlKrhiVPixE5qNiD+eSPirsmROvsyIonoJtuY4Dw5K6pcNlKyYiwo1IOFYU3YxffwFJk+bSW4WVBhsdf5dGxq/uOHmuz5gdwxCwIDAQABo4H9MIH6MAwGA1UdEwQFMAMBAf8wHQYDVR0OBBYEFEv9FCWJHefBcIPX9p8RHCVOGe6uMIHKBgNVHSMEgcIwgb+AFEv9FCWJHefBcIPX9p8RHCVOGe6uoYGbpIGYMIGVMQswCQYDVQQGEwJVUzELMAkGA1UECBMCRkwxFDASBgNVBAcTC1dpbnRlciBQYXJrMREwDwYDVQQKEwhJUFZhbmlzaDEVMBMGA1UECxMMSVBWYW5pc2ggVlBOMRQwEgYDVQQDEwtJUFZhbmlzaCBDQTEjMCEGCSqGSIb3DQEJARYUc3VwcG9ydEBpcHZhbmlzaC5jb22CCQDGCs0kvLjygzANBgkqhkiG9w0BAQ0FAAOCAQEAI2dkh/43ksV2fdYpVGhYaFZPVqCJoToCez0IvOmLeLGzow+EOSrY508oyjYeNP4VJEjApqo0NrMbKl8g/8bpLBcotOCF1c1HZ+y9v7648uumh01SMjsbBeHOuQcLb+7gX6c0pEmxWv8qj5JiW3/1L1bktnjW5Yp5oFkFSMXjOnIoYKHyKLjN2jtwH6XowUNYpg4qVtKU0CXPdOznWcd9/zSfa393HwJPeeVLbKYaFMC4IEbIUmKYtWyoJ9pJ58smU3pWsHZUg9Zc0LZZNjkNlBdQSLmUHAJ33Bd7pJS0JQeiWviC+4UTmzEWRKa7pDGnYRYNu2cUo0/voStphv8EVA==" IpvanishCA = "MIIErTCCA5WgAwIBAgIJAMYKzSS8uPKDMA0GCSqGSIb3DQEBDQUAMIGVMQswCQYDVQQGEwJVUzELMAkGA1UECBMCRkwxFDASBgNVBAcTC1dpbnRlciBQYXJrMREwDwYDVQQKEwhJUFZhbmlzaDEVMBMGA1UECxMMSVBWYW5pc2ggVlBOMRQwEgYDVQQDEwtJUFZhbmlzaCBDQTEjMCEGCSqGSIb3DQEJARYUc3VwcG9ydEBpcHZhbmlzaC5jb20wHhcNMTIwMTExMTkzMjIwWhcNMjgxMTAyMTkzMjIwWjCBlTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkZMMRQwEgYDVQQHEwtXaW50ZXIgUGFyazERMA8GA1UEChMISVBWYW5pc2gxFTATBgNVBAsTDElQVmFuaXNoIFZQTjEUMBIGA1UEAxMLSVBWYW5pc2ggQ0ExIzAhBgkqhkiG9w0BCQEWFHN1cHBvcnRAaXB2YW5pc2guY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt9DBWNr/IKOuY3TmDP5x7vYZR0DGxLbXU8TyAzBbjUtFFMbhxlHiXVQrZHmgzih94x7BgXM7tWpmMKYVb+gNaqMdWE680Qm3nOwmhy/dulXDkEHAwD05i/iTx4ZaUdtV2vsKBxRg1vdC4AEiwD7bqV4HOi13xcG971aQ55Mj1KeCdA0aNvpat1LWx2jjWxsfI8s2Lv5Fkoi1HO1+vTnnaEsJZrBgAkLXpItqP29Lik3/OBIvkBIxlKrhiVPixE5qNiD+eSPirsmROvsyIonoJtuY4Dw5K6pcNlKyYiwo1IOFYU3YxffwFJk+bSW4WVBhsdf5dGxq/uOHmuz5gdwxCwIDAQABo4H9MIH6MAwGA1UdEwQFMAMBAf8wHQYDVR0OBBYEFEv9FCWJHefBcIPX9p8RHCVOGe6uMIHKBgNVHSMEgcIwgb+AFEv9FCWJHefBcIPX9p8RHCVOGe6uoYGbpIGYMIGVMQswCQYDVQQGEwJVUzELMAkGA1UECBMCRkwxFDASBgNVBAcTC1dpbnRlciBQYXJrMREwDwYDVQQKEwhJUFZhbmlzaDEVMBMGA1UECxMMSVBWYW5pc2ggVlBOMRQwEgYDVQQDEwtJUFZhbmlzaCBDQTEjMCEGCSqGSIb3DQEJARYUc3VwcG9ydEBpcHZhbmlzaC5jb22CCQDGCs0kvLjygzANBgkqhkiG9w0BAQ0FAAOCAQEAI2dkh/43ksV2fdYpVGhYaFZPVqCJoToCez0IvOmLeLGzow+EOSrY508oyjYeNP4VJEjApqo0NrMbKl8g/8bpLBcotOCF1c1HZ+y9v7648uumh01SMjsbBeHOuQcLb+7gX6c0pEmxWv8qj5JiW3/1L1bktnjW5Yp5oFkFSMXjOnIoYKHyKLjN2jtwH6XowUNYpg4qVtKU0CXPdOznWcd9/zSfa393HwJPeeVLbKYaFMC4IEbIUmKYtWyoJ9pJ58smU3pWsHZUg9Zc0LZZNjkNlBdQSLmUHAJ33Bd7pJS0JQeiWviC+4UTmzEWRKa7pDGnYRYNu2cUo0/voStphv8EVA=="
) )
func IpvanishCountryChoices() (choices []string) { func IpvanishCountryChoices(servers []models.IpvanishServer) (choices []string) {
servers := IpvanishServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].Country choices[i] = servers[i].Country
@@ -18,8 +17,7 @@ func IpvanishCountryChoices() (choices []string) {
return makeUnique(choices) return makeUnique(choices)
} }
func IpvanishCityChoices() (choices []string) { func IpvanishCityChoices(servers []models.IpvanishServer) (choices []string) {
servers := IpvanishServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].City choices[i] = servers[i].City
@@ -27,18 +25,10 @@ func IpvanishCityChoices() (choices []string) {
return makeUnique(choices) return makeUnique(choices)
} }
func IpvanishHostnameChoices() (choices []string) { func IpvanishHostnameChoices(servers []models.IpvanishServer) (choices []string) {
servers := IpvanishServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].Hostname choices[i] = servers[i].Hostname
} }
return makeUnique(choices) return makeUnique(choices)
} }
// IpvanishServers returns a slice of all the server information for Ipvanish.
func IpvanishServers() (servers []models.IpvanishServer) {
servers = make([]models.IpvanishServer, len(allServers.Ipvanish.Servers))
copy(servers, allServers.Ipvanish.Servers)
return servers
}

View File

@@ -10,8 +10,7 @@ const (
IvpnOpenvpnStaticKeyV1 = "ac470c93ff9f5602a8aab37dee84a52814d10f20490ad23c47d5d82120c1bf859e93d0696b455d4a1b8d55d40c2685c41ca1d0aef29a3efd27274c4ef09020a3978fe45784b335da6df2d12db97bbb838416515f2a96f04715fd28949c6fe296a925cfada3f8b8928ed7fc963c1563272f5cf46e5e1d9c845d7703ca881497b7e6564a9d1dea9358adffd435295479f47d5298fabf5359613ff5992cb57ff081a04dfb81a26513a6b44a9b5490ad265f8a02384832a59cc3e075ad545461060b7bcab49bac815163cb80983dd51d5b1fd76170ffd904d8291071e96efc3fb777856c717b148d08a510f5687b8a8285dcffe737b98916dd15ef6235dee4266d3b" IvpnOpenvpnStaticKeyV1 = "ac470c93ff9f5602a8aab37dee84a52814d10f20490ad23c47d5d82120c1bf859e93d0696b455d4a1b8d55d40c2685c41ca1d0aef29a3efd27274c4ef09020a3978fe45784b335da6df2d12db97bbb838416515f2a96f04715fd28949c6fe296a925cfada3f8b8928ed7fc963c1563272f5cf46e5e1d9c845d7703ca881497b7e6564a9d1dea9358adffd435295479f47d5298fabf5359613ff5992cb57ff081a04dfb81a26513a6b44a9b5490ad265f8a02384832a59cc3e075ad545461060b7bcab49bac815163cb80983dd51d5b1fd76170ffd904d8291071e96efc3fb777856c717b148d08a510f5687b8a8285dcffe737b98916dd15ef6235dee4266d3b"
) )
func IvpnCountryChoices() (choices []string) { func IvpnCountryChoices(servers []models.IvpnServer) (choices []string) {
servers := IvpnServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].Country choices[i] = servers[i].Country
@@ -19,8 +18,7 @@ func IvpnCountryChoices() (choices []string) {
return makeUnique(choices) return makeUnique(choices)
} }
func IvpnCityChoices() (choices []string) { func IvpnCityChoices(servers []models.IvpnServer) (choices []string) {
servers := IvpnServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].City choices[i] = servers[i].City
@@ -28,18 +26,18 @@ func IvpnCityChoices() (choices []string) {
return makeUnique(choices) return makeUnique(choices)
} }
func IvpnHostnameChoices() (choices []string) { func IvpnISPChoices(servers []models.IvpnServer) (choices []string) {
servers := IvpnServers() choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].ISP
}
return makeUnique(choices)
}
func IvpnHostnameChoices(servers []models.IvpnServer) (choices []string) {
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].Hostname choices[i] = servers[i].Hostname
} }
return makeUnique(choices) return makeUnique(choices)
} }
// IvpnServers returns a slice of all the server information for Ivpn.
func IvpnServers() (servers []models.IvpnServer) {
servers = make([]models.IvpnServer, len(allServers.Ivpn.Servers))
copy(servers, allServers.Ivpn.Servers)
return servers
}

View File

@@ -9,8 +9,7 @@ const (
MullvadCertificate = "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" MullvadCertificate = "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"
) )
func MullvadCountryChoices() (choices []string) { func MullvadCountryChoices(servers []models.MullvadServer) (choices []string) {
servers := MullvadServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].Country choices[i] = servers[i].Country
@@ -18,8 +17,7 @@ func MullvadCountryChoices() (choices []string) {
return makeUnique(choices) return makeUnique(choices)
} }
func MullvadCityChoices() (choices []string) { func MullvadCityChoices(servers []models.MullvadServer) (choices []string) {
servers := MullvadServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].City choices[i] = servers[i].City
@@ -27,8 +25,7 @@ func MullvadCityChoices() (choices []string) {
return makeUnique(choices) return makeUnique(choices)
} }
func MullvadHostnameChoices() (choices []string) { func MullvadHostnameChoices(servers []models.MullvadServer) (choices []string) {
servers := MullvadServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].Hostname choices[i] = servers[i].Hostname
@@ -36,18 +33,10 @@ func MullvadHostnameChoices() (choices []string) {
return makeUnique(choices) return makeUnique(choices)
} }
func MullvadISPChoices() (choices []string) { func MullvadISPChoices(servers []models.MullvadServer) (choices []string) {
servers := MullvadServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].ISP choices[i] = servers[i].ISP
} }
return makeUnique(choices) return makeUnique(choices)
} }
// MullvadServers returns a slice of all the server information for Mullvad.
func MullvadServers() (servers []models.MullvadServer) {
servers = make([]models.MullvadServer, len(allServers.Mullvad.Servers))
copy(servers, allServers.Mullvad.Servers)
return servers
}

View File

@@ -10,8 +10,7 @@ const (
NordvpnOpenvpnStaticKeyV1 = "e685bdaf659a25a200e2b9e39e51ff030fc72cf1ce07232bd8b2be5e6c670143f51e937e670eee09d4f2ea5a6e4e69965db852c275351b86fc4ca892d78ae002d6f70d029bd79c4d1c26cf14e9588033cf639f8a74809f29f72b9d58f9b8f5fefc7938eade40e9fed6cb92184abb2cc10eb1a296df243b251df0643d53724cdb5a92a1d6cb817804c4a9319b57d53be580815bcfcb2df55018cc83fc43bc7ff82d51f9b88364776ee9d12fc85cc7ea5b9741c4f598c485316db066d52db4540e212e1518a9bd4828219e24b20d88f598a196c9de96012090e333519ae18d35099427e7b372d348d352dc4c85e18cd4b93f8a56ddb2e64eb67adfc9b337157ff4" NordvpnOpenvpnStaticKeyV1 = "e685bdaf659a25a200e2b9e39e51ff030fc72cf1ce07232bd8b2be5e6c670143f51e937e670eee09d4f2ea5a6e4e69965db852c275351b86fc4ca892d78ae002d6f70d029bd79c4d1c26cf14e9588033cf639f8a74809f29f72b9d58f9b8f5fefc7938eade40e9fed6cb92184abb2cc10eb1a296df243b251df0643d53724cdb5a92a1d6cb817804c4a9319b57d53be580815bcfcb2df55018cc83fc43bc7ff82d51f9b88364776ee9d12fc85cc7ea5b9741c4f598c485316db066d52db4540e212e1518a9bd4828219e24b20d88f598a196c9de96012090e333519ae18d35099427e7b372d348d352dc4c85e18cd4b93f8a56ddb2e64eb67adfc9b337157ff4"
) )
func NordvpnRegionChoices() (choices []string) { func NordvpnRegionChoices(servers []models.NordvpnServer) (choices []string) {
servers := NordvpnServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].Region choices[i] = servers[i].Region
@@ -19,8 +18,7 @@ func NordvpnRegionChoices() (choices []string) {
return makeUnique(choices) return makeUnique(choices)
} }
func NordvpnHostnameChoices() (choices []string) { func NordvpnHostnameChoices(servers []models.NordvpnServer) (choices []string) {
servers := NordvpnServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].Hostname choices[i] = servers[i].Hostname
@@ -28,18 +26,10 @@ func NordvpnHostnameChoices() (choices []string) {
return makeUnique(choices) return makeUnique(choices)
} }
func NordvpnNameChoices() (choices []string) { func NordvpnNameChoices(servers []models.NordvpnServer) (choices []string) {
servers := NordvpnServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].Name choices[i] = servers[i].Name
} }
return makeUnique(choices) return makeUnique(choices)
} }
// NordvpnServers returns a slice of all the server information for Nordvpn.
func NordvpnServers() (servers []models.NordvpnServer) {
servers = make([]models.NordvpnServer, len(allServers.Nordvpn.Servers))
copy(servers, allServers.Nordvpn.Servers)
return servers
}

View File

@@ -15,8 +15,7 @@ const (
PIACertificateStrong = "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" PIACertificateStrong = "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"
) )
func PIAGeoChoices() (choices []string) { func PIAGeoChoices(servers []models.PIAServer) (choices []string) {
servers := PIAServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].Region choices[i] = servers[i].Region
@@ -24,8 +23,7 @@ func PIAGeoChoices() (choices []string) {
return makeUnique(choices) return makeUnique(choices)
} }
func PIAHostnameChoices() (choices []string) { func PIAHostnameChoices(servers []models.PIAServer) (choices []string) {
servers := PIAServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].Hostname choices[i] = servers[i].Hostname
@@ -33,8 +31,7 @@ func PIAHostnameChoices() (choices []string) {
return makeUnique(choices) return makeUnique(choices)
} }
func PIANameChoices() (choices []string) { func PIANameChoices(servers []models.PIAServer) (choices []string) {
servers := PIAServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].ServerName choices[i] = servers[i].ServerName
@@ -42,15 +39,8 @@ func PIANameChoices() (choices []string) {
return makeUnique(choices) return makeUnique(choices)
} }
// PIAServers returns a slice of all the server information for PIA. func PIAServerWhereName(servers []models.PIAServer, serverName string) (server models.PIAServer) {
func PIAServers() (servers []models.PIAServer) { for _, server := range servers {
servers = make([]models.PIAServer, len(allServers.Pia.Servers))
copy(servers, allServers.Pia.Servers)
return servers
}
func PIAServerWhereName(serverName string) (server models.PIAServer) {
for _, server := range PIAServers() {
if server.ServerName == serverName { if server.ServerName == serverName {
return server return server
} }

View File

@@ -1,16 +1,13 @@
package constants package constants
import ( import "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/models"
)
//nolint:lll //nolint:lll
const ( const (
PrivadoCertificate = "MIIFKDCCAxCgAwIBAgIJAMtrmqZxIV/OMA0GCSqGSIb3DQEBDQUAMBIxEDAOBgNVBAMMB1ByaXZhZG8wHhcNMjAwMTA4MjEyODQ1WhcNMzUwMTA5MjEyODQ1WjASMRAwDgYDVQQDDAdQcml2YWRvMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxPwOgiwNJzZTnKIXwAB0TSu/Lu2qt2U2I8obtQjwhi/7OrfmbmYykSdro70al2XPhnwAGGdCxW6LDnp0UN/IOhD11mgBPo14f5CLkBQjSJ6VN5miPbvK746LsNZl9H8rQGvDuPo4CG9BfPZMiDRGlsMxij/jztzgT1gmuxQ7WHfFRcNzBas1dHa9hV/d3TU6/t47x4SE/ljdcCtJiu7Zn6ODKQoys3mB7Luz2ngqUJWvkqsg+E4+3eJ0M8Hlbn5TPaRJBID7DAdYo6Vs6xGCYr981ThFcmoIQ10js10yANrrfGAzd03b3TnLAgko0uQMHjliMZL6L8sWOPHxyxJI0us88SFh4UgcFyRHKHPKux7w24SxAlZUYoUcTHp9VjG5XvDKYxzgV2RdM4ulBGbQRQ3y3/CyddsyQYMvA55Ets0LfPaBvDIcct70iXijGsdvlX1du3ArGpG7Vaje/RU4nbbGT6HYRdt5YyZfof288ukMOSj20nVcmS+c/4tqsxSerRb1aq5LOi1IemSkTMeC5gCbexk+L1vl7NT/58sxjGmu5bXwnvev/lIItfi2AlITrfUSEv19iDMKkeshwn/+sFJBMWYyluP+yJ56yR+MWoXvLlSWphLDTqq19yx3BZn0P1tgbXoR0g8PTdJFcz8z3RIb7myVLYulV1oGG/3rka0CAwEAAaOBgDB+MB0GA1UdDgQWBBTFtJkZCVDuDAD6k5bJzefjJdO3DTBCBgNVHSMEOzA5gBTFtJkZCVDuDAD6k5bJzefjJdO3DaEWpBQwEjEQMA4GA1UEAwwHUHJpdmFkb4IJAMtrmqZxIV/OMAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBDQUAA4ICAQB7MUSXMeBb9wlSv4sUaT1JHEwE26nlBw+TKmezfuPU5pBlY0LYr6qQZY95DHqsRJ7ByUzGUrGo17dNGXlcuNc6TAaQQEDRPo6y+LVh2TWMk15TUMI+MkqryJtCret7xGvDigKYMJgBy58HN3RAVr1B7cL9youwzLgc2Y/NcFKvnQJKeiIYAJ7g0CcnJiQvgZTS7xdwkEBXfsngmUCIG320DLPEL+Ze0HiUrxwWljMRya6i40AeH3Zu2i532xX1wV5+cjA4RJWIKg6ri/Q54iFGtZrA9/nc6y9uoQHkmz8cGyVUmJxFzMrrIICVqUtVRxLhkTMe4UzwRWTBeGgtW4tS0yq1QonAKfOyjgRw/CeY55D2UGvnAFZdTadtYXS4Alu2P9zdwoEk3fzHiVmDjqfJVr5wz9383aABUFrPI3nz6ed/Z6LZflKh1k+DUDEp8NxU4klUULWsSOKoa5zGX51G8cdHxwQLImXvtGuN5eSR8jCTgxFZhdps/xes4KkyfIz9FMYG748M+uOTgKITf4zdJ9BAyiQaOufVQZ8WjhWzWk9YHec9VqPkzpWNGkVjiRI5ewuXwZzZ164tMv2hikBXSuUCnFz37/ZNwGlDi0oBdDszCk2GxccdFHHaCSmpjU5MrdJ+5IhtTKGeTx+US2hTIVHQFIO99DmacxSYvLNcSQ==" PrivadoCertificate = "MIIFKDCCAxCgAwIBAgIJAMtrmqZxIV/OMA0GCSqGSIb3DQEBDQUAMBIxEDAOBgNVBAMMB1ByaXZhZG8wHhcNMjAwMTA4MjEyODQ1WhcNMzUwMTA5MjEyODQ1WjASMRAwDgYDVQQDDAdQcml2YWRvMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxPwOgiwNJzZTnKIXwAB0TSu/Lu2qt2U2I8obtQjwhi/7OrfmbmYykSdro70al2XPhnwAGGdCxW6LDnp0UN/IOhD11mgBPo14f5CLkBQjSJ6VN5miPbvK746LsNZl9H8rQGvDuPo4CG9BfPZMiDRGlsMxij/jztzgT1gmuxQ7WHfFRcNzBas1dHa9hV/d3TU6/t47x4SE/ljdcCtJiu7Zn6ODKQoys3mB7Luz2ngqUJWvkqsg+E4+3eJ0M8Hlbn5TPaRJBID7DAdYo6Vs6xGCYr981ThFcmoIQ10js10yANrrfGAzd03b3TnLAgko0uQMHjliMZL6L8sWOPHxyxJI0us88SFh4UgcFyRHKHPKux7w24SxAlZUYoUcTHp9VjG5XvDKYxzgV2RdM4ulBGbQRQ3y3/CyddsyQYMvA55Ets0LfPaBvDIcct70iXijGsdvlX1du3ArGpG7Vaje/RU4nbbGT6HYRdt5YyZfof288ukMOSj20nVcmS+c/4tqsxSerRb1aq5LOi1IemSkTMeC5gCbexk+L1vl7NT/58sxjGmu5bXwnvev/lIItfi2AlITrfUSEv19iDMKkeshwn/+sFJBMWYyluP+yJ56yR+MWoXvLlSWphLDTqq19yx3BZn0P1tgbXoR0g8PTdJFcz8z3RIb7myVLYulV1oGG/3rka0CAwEAAaOBgDB+MB0GA1UdDgQWBBTFtJkZCVDuDAD6k5bJzefjJdO3DTBCBgNVHSMEOzA5gBTFtJkZCVDuDAD6k5bJzefjJdO3DaEWpBQwEjEQMA4GA1UEAwwHUHJpdmFkb4IJAMtrmqZxIV/OMAwGA1UdEwQFMAMBAf8wCwYDVR0PBAQDAgEGMA0GCSqGSIb3DQEBDQUAA4ICAQB7MUSXMeBb9wlSv4sUaT1JHEwE26nlBw+TKmezfuPU5pBlY0LYr6qQZY95DHqsRJ7ByUzGUrGo17dNGXlcuNc6TAaQQEDRPo6y+LVh2TWMk15TUMI+MkqryJtCret7xGvDigKYMJgBy58HN3RAVr1B7cL9youwzLgc2Y/NcFKvnQJKeiIYAJ7g0CcnJiQvgZTS7xdwkEBXfsngmUCIG320DLPEL+Ze0HiUrxwWljMRya6i40AeH3Zu2i532xX1wV5+cjA4RJWIKg6ri/Q54iFGtZrA9/nc6y9uoQHkmz8cGyVUmJxFzMrrIICVqUtVRxLhkTMe4UzwRWTBeGgtW4tS0yq1QonAKfOyjgRw/CeY55D2UGvnAFZdTadtYXS4Alu2P9zdwoEk3fzHiVmDjqfJVr5wz9383aABUFrPI3nz6ed/Z6LZflKh1k+DUDEp8NxU4klUULWsSOKoa5zGX51G8cdHxwQLImXvtGuN5eSR8jCTgxFZhdps/xes4KkyfIz9FMYG748M+uOTgKITf4zdJ9BAyiQaOufVQZ8WjhWzWk9YHec9VqPkzpWNGkVjiRI5ewuXwZzZ164tMv2hikBXSuUCnFz37/ZNwGlDi0oBdDszCk2GxccdFHHaCSmpjU5MrdJ+5IhtTKGeTx+US2hTIVHQFIO99DmacxSYvLNcSQ=="
) )
func PrivadoCountryChoices() (choices []string) { func PrivadoCountryChoices(servers []models.PrivadoServer) (choices []string) {
servers := PrivadoServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].Country choices[i] = servers[i].Country
@@ -18,8 +15,7 @@ func PrivadoCountryChoices() (choices []string) {
return makeUnique(choices) return makeUnique(choices)
} }
func PrivadoRegionChoices() (choices []string) { func PrivadoRegionChoices(servers []models.PrivadoServer) (choices []string) {
servers := PrivadoServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].Region choices[i] = servers[i].Region
@@ -27,8 +23,7 @@ func PrivadoRegionChoices() (choices []string) {
return makeUnique(choices) return makeUnique(choices)
} }
func PrivadoCityChoices() (choices []string) { func PrivadoCityChoices(servers []models.PrivadoServer) (choices []string) {
servers := PrivadoServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].City choices[i] = servers[i].City
@@ -36,18 +31,10 @@ func PrivadoCityChoices() (choices []string) {
return makeUnique(choices) return makeUnique(choices)
} }
func PrivadoHostnameChoices() (choices []string) { func PrivadoHostnameChoices(servers []models.PrivadoServer) (choices []string) {
servers := PrivadoServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].Hostname choices[i] = servers[i].Hostname
} }
return makeUnique(choices) return makeUnique(choices)
} }
// PrivadoServers returns a slice of all the Privado servers.
func PrivadoServers() (servers []models.PrivadoServer) {
servers = make([]models.PrivadoServer, len(allServers.Privado.Servers))
copy(servers, allServers.Privado.Servers)
return servers
}

View File

@@ -1,8 +1,6 @@
package constants package constants
import ( import "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/models"
)
//nolint:lll //nolint:lll
const ( const (
@@ -10,8 +8,7 @@ const (
PrivatevpnOpenvpnStaticKeyV1 = "a49082f082ca89d6a6bb4ecc7c047c6d428a1d3c8254a95206d38a61d7fbe65984214cd7d56eacc5a60803bffd677fa7294d4bfe555036339312de2dfb1335bd9d5fd94b04bba3a15fc5192aeb02fb6d8dd2ca831fad7509be5eefa8d1eaa689dc586c831a23b589c512662652ecf1bb3a4a673816aba434a04f6857b8c2f8bb265bfe48a7b8112539729d2f7d9734a720e1035188118c73fef1824d0237d5579ca382d703b4bb252acaedc753b12199f00154d3769efbcf85ef5ad6ee755cbeaa944cb98e7654286df54c793a8443f5363078e3da548ba0beed079df633283cefb256f6a4bcfc4ab2c4affc24955c1864d5458e84a7c210d0d186269e55dcf6" PrivatevpnOpenvpnStaticKeyV1 = "a49082f082ca89d6a6bb4ecc7c047c6d428a1d3c8254a95206d38a61d7fbe65984214cd7d56eacc5a60803bffd677fa7294d4bfe555036339312de2dfb1335bd9d5fd94b04bba3a15fc5192aeb02fb6d8dd2ca831fad7509be5eefa8d1eaa689dc586c831a23b589c512662652ecf1bb3a4a673816aba434a04f6857b8c2f8bb265bfe48a7b8112539729d2f7d9734a720e1035188118c73fef1824d0237d5579ca382d703b4bb252acaedc753b12199f00154d3769efbcf85ef5ad6ee755cbeaa944cb98e7654286df54c793a8443f5363078e3da548ba0beed079df633283cefb256f6a4bcfc4ab2c4affc24955c1864d5458e84a7c210d0d186269e55dcf6"
) )
func PrivatevpnCountryChoices() (choices []string) { func PrivatevpnCountryChoices(servers []models.PrivatevpnServer) (choices []string) {
servers := PrivatevpnServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].Country choices[i] = servers[i].Country
@@ -19,8 +16,7 @@ func PrivatevpnCountryChoices() (choices []string) {
return makeChoicesUnique(choices) return makeChoicesUnique(choices)
} }
func PrivatevpnCityChoices() (choices []string) { func PrivatevpnCityChoices(servers []models.PrivatevpnServer) (choices []string) {
servers := PrivatevpnServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].City choices[i] = servers[i].City
@@ -28,17 +24,10 @@ func PrivatevpnCityChoices() (choices []string) {
return makeChoicesUnique(choices) return makeChoicesUnique(choices)
} }
func PrivatevpnHostnameChoices() (choices []string) { func PrivatevpnHostnameChoices(servers []models.PrivatevpnServer) (choices []string) {
servers := PrivatevpnServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].Hostname choices[i] = servers[i].Hostname
} }
return makeChoicesUnique(choices) return makeChoicesUnique(choices)
} }
func PrivatevpnServers() (servers []models.PrivatevpnServer) {
servers = make([]models.PrivatevpnServer, len(allServers.Privatevpn.Servers))
copy(servers, allServers.Privatevpn.Servers)
return servers
}

View File

@@ -1,8 +1,6 @@
package constants package constants
import ( import "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/models"
)
//nolint:lll //nolint:lll
const ( const (
@@ -10,8 +8,7 @@ const (
ProtonvpnOpenvpnStaticKeyV1 = "6acef03f62675b4b1bbd03e53b187727423cea742242106cb2916a8a4c8297563d22c7e5cef430b1103c6f66eb1fc5b375a672f158e2e2e936c3faa48b035a6de17beaac23b5f03b10b868d53d03521d8ba115059da777a60cbfd7b2c9c5747278a15b8f6e68a3ef7fd583ec9f398c8bd4735dab40cbd1e3c62a822e97489186c30a0b48c7c38ea32ceb056d3fa5a710e10ccc7a0ddb363b08c3d2777a3395e10c0b6080f56309192ab5aacd4b45f55da61fc77af39bd81a19218a79762c33862df55785075f37d8c71dc8a42097ee43344739a0dd48d03025b0450cf1fb5e8caeb893d9a96d1f15519bb3c4dcb40ee316672ea16c012664f8a9f11255518deb" ProtonvpnOpenvpnStaticKeyV1 = "6acef03f62675b4b1bbd03e53b187727423cea742242106cb2916a8a4c8297563d22c7e5cef430b1103c6f66eb1fc5b375a672f158e2e2e936c3faa48b035a6de17beaac23b5f03b10b868d53d03521d8ba115059da777a60cbfd7b2c9c5747278a15b8f6e68a3ef7fd583ec9f398c8bd4735dab40cbd1e3c62a822e97489186c30a0b48c7c38ea32ceb056d3fa5a710e10ccc7a0ddb363b08c3d2777a3395e10c0b6080f56309192ab5aacd4b45f55da61fc77af39bd81a19218a79762c33862df55785075f37d8c71dc8a42097ee43344739a0dd48d03025b0450cf1fb5e8caeb893d9a96d1f15519bb3c4dcb40ee316672ea16c012664f8a9f11255518deb"
) )
func ProtonvpnCountryChoices() (choices []string) { func ProtonvpnCountryChoices(servers []models.ProtonvpnServer) (choices []string) {
servers := ProtonvpnServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].Country choices[i] = servers[i].Country
@@ -19,8 +16,7 @@ func ProtonvpnCountryChoices() (choices []string) {
return makeChoicesUnique(choices) return makeChoicesUnique(choices)
} }
func ProtonvpnRegionChoices() (choices []string) { func ProtonvpnRegionChoices(servers []models.ProtonvpnServer) (choices []string) {
servers := ProtonvpnServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].Region choices[i] = servers[i].Region
@@ -28,8 +24,7 @@ func ProtonvpnRegionChoices() (choices []string) {
return makeChoicesUnique(choices) return makeChoicesUnique(choices)
} }
func ProtonvpnCityChoices() (choices []string) { func ProtonvpnCityChoices(servers []models.ProtonvpnServer) (choices []string) {
servers := ProtonvpnServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].City choices[i] = servers[i].City
@@ -37,8 +32,7 @@ func ProtonvpnCityChoices() (choices []string) {
return makeChoicesUnique(choices) return makeChoicesUnique(choices)
} }
func ProtonvpnNameChoices() (choices []string) { func ProtonvpnNameChoices(servers []models.ProtonvpnServer) (choices []string) {
servers := ProtonvpnServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].Name choices[i] = servers[i].Name
@@ -46,18 +40,10 @@ func ProtonvpnNameChoices() (choices []string) {
return makeChoicesUnique(choices) return makeChoicesUnique(choices)
} }
func ProtonvpnHostnameChoices() (choices []string) { func ProtonvpnHostnameChoices(servers []models.ProtonvpnServer) (choices []string) {
servers := ProtonvpnServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].Hostname choices[i] = servers[i].Hostname
} }
return makeChoicesUnique(choices) return makeChoicesUnique(choices)
} }
// ProtonvpnServers returns a slice of all the server information for Protonvpn.
func ProtonvpnServers() (servers []models.ProtonvpnServer) {
servers = make([]models.ProtonvpnServer, len(allServers.Protonvpn.Servers))
copy(servers, allServers.Protonvpn.Servers)
return servers
}

View File

@@ -1,8 +1,6 @@
package constants package constants
import ( import "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/models"
)
//nolint:lll //nolint:lll
const ( const (
@@ -12,8 +10,7 @@ const (
PurevpnOpenvpnStaticKeyV1 = "e30af995f56d07426d9ba1f824730521d4283db4b4d0cdda9c6e8759a3799dcb7939b6a5989160c9660de0f6125cbb1f585b41c074b2fe88ecfcf17eab9a33be1352379cdf74952b588fb161a93e13df9135b2b29038231e02d657a6225705e6868ccb0c384ed11614690a1894bfbeb274cebf1fe9c2329bdd5c8a40fe8820624d2ea7540cd79ab76892db51fc371a3ac5fc9573afecb3fffe3281e61d72e91579d9b03d8cbf7909b3aebf4d90850321ee6b7d0a7846d15c27d8290e031e951e19438a4654663cad975e138f5bc5af89c737ad822f27e19057731f41e1e254cc9c95b7175c622422cde9f1f2cfd3510add94498b4d7133d3729dd214a16b27fb" PurevpnOpenvpnStaticKeyV1 = "e30af995f56d07426d9ba1f824730521d4283db4b4d0cdda9c6e8759a3799dcb7939b6a5989160c9660de0f6125cbb1f585b41c074b2fe88ecfcf17eab9a33be1352379cdf74952b588fb161a93e13df9135b2b29038231e02d657a6225705e6868ccb0c384ed11614690a1894bfbeb274cebf1fe9c2329bdd5c8a40fe8820624d2ea7540cd79ab76892db51fc371a3ac5fc9573afecb3fffe3281e61d72e91579d9b03d8cbf7909b3aebf4d90850321ee6b7d0a7846d15c27d8290e031e951e19438a4654663cad975e138f5bc5af89c737ad822f27e19057731f41e1e254cc9c95b7175c622422cde9f1f2cfd3510add94498b4d7133d3729dd214a16b27fb"
) )
func PurevpnRegionChoices() (choices []string) { func PurevpnRegionChoices(servers []models.PurevpnServer) (choices []string) {
servers := PurevpnServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].Region choices[i] = servers[i].Region
@@ -21,8 +18,7 @@ func PurevpnRegionChoices() (choices []string) {
return makeUnique(choices) return makeUnique(choices)
} }
func PurevpnCountryChoices() (choices []string) { func PurevpnCountryChoices(servers []models.PurevpnServer) (choices []string) {
servers := PurevpnServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].Country choices[i] = servers[i].Country
@@ -30,8 +26,7 @@ func PurevpnCountryChoices() (choices []string) {
return makeUnique(choices) return makeUnique(choices)
} }
func PurevpnCityChoices() (choices []string) { func PurevpnCityChoices(servers []models.PurevpnServer) (choices []string) {
servers := PurevpnServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].City choices[i] = servers[i].City
@@ -39,18 +34,10 @@ func PurevpnCityChoices() (choices []string) {
return makeUnique(choices) return makeUnique(choices)
} }
func PurevpnHostnameChoices() (choices []string) { func PurevpnHostnameChoices(servers []models.PurevpnServer) (choices []string) {
servers := PurevpnServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].Hostname choices[i] = servers[i].Hostname
} }
return makeUnique(choices) return makeUnique(choices)
} }
// PurevpnServers returns a slice of all the server information for Purevpn.
func PurevpnServers() (servers []models.PurevpnServer) {
servers = make([]models.PurevpnServer, len(allServers.Purevpn.Servers))
copy(servers, allServers.Purevpn.Servers)
return servers
}

View File

@@ -1,34 +0,0 @@
package constants
import (
"embed"
"encoding/json"
"sync"
"github.com/qdm12/gluetun/internal/models"
)
//go:embed servers.json
var allServersEmbedFS embed.FS //nolint:gochecknoglobals
var allServers models.AllServers //nolint:gochecknoglobals
var parseOnce sync.Once //nolint:gochecknoglobals
func init() { //nolint:gochecknoinits
// error returned covered by unit test
parseOnce.Do(func() { allServers, _ = parseAllServers() })
}
func parseAllServers() (allServers models.AllServers, err error) {
f, err := allServersEmbedFS.Open("servers.json")
if err != nil {
return allServers, err
}
decoder := json.NewDecoder(f)
err = decoder.Decode(&allServers)
return allServers, err
}
func GetAllServers() models.AllServers {
parseOnce.Do(func() { allServers, _ = parseAllServers() }) // init did not execute, used in tests
return allServers
}

View File

@@ -10,8 +10,7 @@ const (
SurfsharkOpenvpnStaticKeyV1 = "b02cb1d7c6fee5d4f89b8de72b51a8d0c7b282631d6fc19be1df6ebae9e2779e6d9f097058a31c97f57f0c35526a44ae09a01d1284b50b954d9246725a1ead1ff224a102ed9ab3da0152a15525643b2eee226c37041dc55539d475183b889a10e18bb94f079a4a49888da566b99783460ece01daaf93548beea6c827d9674897e7279ff1a19cb092659e8c1860fbad0db4ad0ad5732f1af4655dbd66214e552f04ed8fd0104e1d4bf99c249ac229ce169d9ba22068c6c0ab742424760911d4636aafb4b85f0c952a9ce4275bc821391aa65fcd0d2394f006e3fba0fd34c4bc4ab260f4b45dec3285875589c97d3087c9134d3a3aa2f904512e85aa2dc2202498" SurfsharkOpenvpnStaticKeyV1 = "b02cb1d7c6fee5d4f89b8de72b51a8d0c7b282631d6fc19be1df6ebae9e2779e6d9f097058a31c97f57f0c35526a44ae09a01d1284b50b954d9246725a1ead1ff224a102ed9ab3da0152a15525643b2eee226c37041dc55539d475183b889a10e18bb94f079a4a49888da566b99783460ece01daaf93548beea6c827d9674897e7279ff1a19cb092659e8c1860fbad0db4ad0ad5732f1af4655dbd66214e552f04ed8fd0104e1d4bf99c249ac229ce169d9ba22068c6c0ab742424760911d4636aafb4b85f0c952a9ce4275bc821391aa65fcd0d2394f006e3fba0fd34c4bc4ab260f4b45dec3285875589c97d3087c9134d3a3aa2f904512e85aa2dc2202498"
) )
func SurfsharkRegionChoices() (choices []string) { func SurfsharkRegionChoices(servers []models.SurfsharkServer) (choices []string) {
servers := SurfsharkServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].Region choices[i] = servers[i].Region
@@ -19,18 +18,211 @@ func SurfsharkRegionChoices() (choices []string) {
return makeUnique(choices) return makeUnique(choices)
} }
func SurfsharkHostnameChoices() (choices []string) { func SurfsharkCountryChoices(servers []models.SurfsharkServer) (choices []string) {
servers := SurfsharkServers() choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Country
}
return makeUnique(choices)
}
func SurfsharkCityChoices(servers []models.SurfsharkServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].City
}
return makeUnique(choices)
}
// TODO remove in v4.
func SurfsharkRetroLocChoices(servers []models.SurfsharkServer) (choices []string) {
locationData := SurfsharkLocationData()
choices = make([]string, len(locationData))
for i := range locationData {
choices[i] = locationData[i].RetroLoc
}
return makeUnique(choices)
}
func SurfsharkHostToLocation() (hostToLocation map[string]models.SurfsharkLocationData) {
locationData := SurfsharkLocationData()
hostToLocation = make(map[string]models.SurfsharkLocationData, len(locationData))
for _, data := range locationData {
hostToLocation[data.Hostname] = data
}
return hostToLocation
}
// TODO remove retroRegion and servers from API in v4.
func SurfsharkLocationData() (data []models.SurfsharkLocationData) {
//nolint:lll
return []models.SurfsharkLocationData{
{Region: "Asia Pacific", Country: "Australia", City: "Adelaide", RetroLoc: "Australia Adelaide", Hostname: "au-adl.prod.surfshark.com", MultiHop: false},
{Region: "Asia Pacific", Country: "Australia", City: "Brisbane", RetroLoc: "Australia Brisbane", Hostname: "au-bne.prod.surfshark.com", MultiHop: false},
{Region: "Asia Pacific", Country: "Australia", City: "Melbourne", RetroLoc: "Australia Melbourne", Hostname: "au-mel.prod.surfshark.com", MultiHop: false},
{Region: "Asia Pacific", Country: "Australia", City: "Perth", RetroLoc: "Australia Perth", Hostname: "au-per.prod.surfshark.com", MultiHop: false},
{Region: "Asia Pacific", Country: "Australia", City: "Sydney", RetroLoc: "Australia Sydney", Hostname: "au-syd.prod.surfshark.com", MultiHop: false},
{Region: "Asia Pacific", Country: "Azerbaijan", City: "Baku", RetroLoc: "Azerbaijan", Hostname: "az-bak.prod.surfshark.com", MultiHop: false},
{Region: "Asia Pacific", Country: "Hong Kong", City: "Hong Kong", RetroLoc: "Hong Kong", Hostname: "hk-hkg.prod.surfshark.com", MultiHop: false},
{Region: "Asia Pacific", Country: "India", City: "Chennai", RetroLoc: "India Chennai", Hostname: "in-chn.prod.surfshark.com", MultiHop: false},
{Region: "Asia Pacific", Country: "India", City: "Indore", RetroLoc: "India Indore", Hostname: "in-idr.prod.surfshark.com", MultiHop: false},
{Region: "Asia Pacific", Country: "India", City: "Mumbai", RetroLoc: "India Mumbai", Hostname: "in-mum.prod.surfshark.com", MultiHop: false},
{Region: "Asia Pacific", Country: "Indonesia", City: "Jakarta", RetroLoc: "Indonesia", Hostname: "id-jak.prod.surfshark.com", MultiHop: false},
{Region: "Asia Pacific", Country: "Japan", City: "Tokyo", RetroLoc: "Japan Tokyo st001", Hostname: "jp-tok-st001.prod.surfshark.com", MultiHop: false},
{Region: "Asia Pacific", Country: "Japan", City: "Tokyo", RetroLoc: "Japan Tokyo st002", Hostname: "jp-tok-st002.prod.surfshark.com", MultiHop: false},
{Region: "Asia Pacific", Country: "Japan", City: "Tokyo", RetroLoc: "Japan Tokyo st003", Hostname: "jp-tok-st003.prod.surfshark.com", MultiHop: false},
{Region: "Asia Pacific", Country: "Japan", City: "Tokyo", RetroLoc: "Japan Tokyo st004", Hostname: "jp-tok-st004.prod.surfshark.com", MultiHop: false},
{Region: "Asia Pacific", Country: "Japan", City: "Tokyo", RetroLoc: "Japan Tokyo st005", Hostname: "jp-tok-st005.prod.surfshark.com", MultiHop: false},
{Region: "Asia Pacific", Country: "Japan", City: "Tokyo", RetroLoc: "Japan Tokyo st006", Hostname: "jp-tok-st006.prod.surfshark.com", MultiHop: false},
{Region: "Asia Pacific", Country: "Japan", City: "Tokyo", RetroLoc: "Japan Tokyo st007", Hostname: "jp-tok-st007.prod.surfshark.com", MultiHop: false},
{Region: "Asia Pacific", Country: "Japan", City: "Tokyo", RetroLoc: "Japan Tokyo st008", Hostname: "jp-tok-st008.prod.surfshark.com", MultiHop: false},
{Region: "Asia Pacific", Country: "Japan", City: "Tokyo", RetroLoc: "Japan Tokyo st009", Hostname: "jp-tok-st009.prod.surfshark.com", MultiHop: false},
{Region: "Asia Pacific", Country: "Japan", City: "Tokyo", RetroLoc: "Japan Tokyo st010", Hostname: "jp-tok-st010.prod.surfshark.com", MultiHop: false},
{Region: "Asia Pacific", Country: "Japan", City: "Tokyo", RetroLoc: "Japan Tokyo st011", Hostname: "jp-tok-st011.prod.surfshark.com", MultiHop: false},
{Region: "Asia Pacific", Country: "Japan", City: "Tokyo", RetroLoc: "Japan Tokyo st012", Hostname: "jp-tok-st012.prod.surfshark.com", MultiHop: false},
{Region: "Asia Pacific", Country: "Japan", City: "Tokyo", RetroLoc: "Japan Tokyo st013", Hostname: "jp-tok-st013.prod.surfshark.com", MultiHop: false},
{Region: "Asia Pacific", Country: "Japan", City: "Tokyo", RetroLoc: "Japan Tokyo", Hostname: "jp-tok.prod.surfshark.com", MultiHop: false},
{Region: "Asia Pacific", Country: "Kazakhstan", City: "Oral", RetroLoc: "Kazakhstan", Hostname: "kz-ura.prod.surfshark.com", MultiHop: false},
{Region: "Asia Pacific", Country: "Malaysia", City: "Kuala Lumpur", RetroLoc: "Malaysia", Hostname: "my-kul.prod.surfshark.com", MultiHop: false},
{Region: "Asia Pacific", Country: "New Zealand", City: "Auckland", RetroLoc: "New Zealand", Hostname: "nz-akl.prod.surfshark.com", MultiHop: false},
{Region: "Asia Pacific", Country: "Philippines", City: "Manila", RetroLoc: "Philippines", Hostname: "ph-mnl.prod.surfshark.com", MultiHop: false},
{Region: "Asia Pacific", Country: "Singapore Hong Kong", City: "Hong Kong", RetroLoc: "Singapore Hong Kong", Hostname: "sg-hk.prod.surfshark.com", MultiHop: true},
{Region: "Asia Pacific", Country: "Singapore in", City: "", RetroLoc: "Singapore in", Hostname: "sg-in.prod.surfshark.com", MultiHop: true},
{Region: "Asia Pacific", Country: "Singapore", City: "Singapore", RetroLoc: "Singapore mp001", Hostname: "sg-sng-mp001.prod.surfshark.com", MultiHop: false},
{Region: "Asia Pacific", Country: "Singapore", City: "Singapore", RetroLoc: "Singapore st001", Hostname: "sg-sng-st001.prod.surfshark.com", MultiHop: false},
{Region: "Asia Pacific", Country: "Singapore", City: "Singapore", RetroLoc: "Singapore st002", Hostname: "sg-sng-st002.prod.surfshark.com", MultiHop: false},
{Region: "Asia Pacific", Country: "Singapore", City: "Singapore", RetroLoc: "Singapore st003", Hostname: "sg-sng-st003.prod.surfshark.com", MultiHop: false},
{Region: "Asia Pacific", Country: "Singapore", City: "Singapore", RetroLoc: "Singapore st004", Hostname: "sg-sng-st004.prod.surfshark.com", MultiHop: false},
{Region: "Asia Pacific", Country: "Singapore", City: "Singapore", RetroLoc: "Singapore", Hostname: "sg-sng.prod.surfshark.com", MultiHop: false},
{Region: "Asia Pacific", Country: "South Korea", City: "Seoul", RetroLoc: "Korea", Hostname: "kr-seo.prod.surfshark.com", MultiHop: false},
{Region: "Asia Pacific", Country: "Taiwan", City: "Taichung City", RetroLoc: "Taiwan", Hostname: "tw-tai.prod.surfshark.com", MultiHop: false},
{Region: "Asia Pacific", Country: "Thailand", City: "Bangkok", RetroLoc: "Thailand", Hostname: "th-bkk.prod.surfshark.com", MultiHop: false},
{Region: "Asia Pacific", Country: "Vietnam", City: "Ho Chi Minh City", RetroLoc: "Vietnam", Hostname: "vn-hcm.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "Albania", City: "Tirana", RetroLoc: "Albania", Hostname: "al-tia.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "Austria", City: "Vienna", RetroLoc: "Austria", Hostname: "at-vie.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "Belgium", City: "Brussels", RetroLoc: "Belgium", Hostname: "be-bru.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "Bosnia and Herzegovina", City: "Sarajevo", RetroLoc: "Bosnia and Herzegovina", Hostname: "ba-sjj.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "Bulgaria", City: "Sofia", RetroLoc: "Bulgaria", Hostname: "bg-sof.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "Croatia", City: "Zagreb", RetroLoc: "Croatia", Hostname: "hr-zag.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "Cyprus", City: "Nicosia", RetroLoc: "Cyprus", Hostname: "cy-nic.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "Czech Republic", City: "Prague", RetroLoc: "Czech Republic", Hostname: "cz-prg.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "Denmark", City: "Copenhagen", RetroLoc: "Denmark", Hostname: "dk-cph.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "Estonia", City: "Tallinn", RetroLoc: "Estonia", Hostname: "ee-tll.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "Finland", City: "Helsinki", RetroLoc: "Finland", Hostname: "fi-hel.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "France Sweden", City: "", RetroLoc: "France Sweden", Hostname: "fr-se.prod.surfshark.com", MultiHop: true},
{Region: "Europe", Country: "France", City: "Bordeaux", RetroLoc: "France Bordeaux", Hostname: "fr-bod.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "France", City: "Marseille", RetroLoc: "France Marseilles", Hostname: "fr-mrs.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "France", City: "Paris", RetroLoc: "France Paris", Hostname: "fr-par.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "Germany Singapour", City: "", RetroLoc: "Germany Singapour", Hostname: "de-sg.prod.surfshark.com", MultiHop: true},
{Region: "Europe", Country: "Germany UK", City: "", RetroLoc: "Germany UK", Hostname: "de-uk.prod.surfshark.com", MultiHop: true},
{Region: "Europe", Country: "Germany", City: "Berlin", RetroLoc: "Germany Berlin", Hostname: "de-ber.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "Germany", City: "Frankfurt am Main", RetroLoc: "Germany Frankfurt am Main st001", Hostname: "de-fra-st001.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "Germany", City: "Frankfurt am Main", RetroLoc: "Germany Frankfurt am Main st002", Hostname: "de-fra-st002.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "Germany", City: "Frankfurt am Main", RetroLoc: "Germany Frankfurt am Main st003", Hostname: "de-fra-st003.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "Germany", City: "Frankfurt am Main", RetroLoc: "Germany Frankfurt am Main st004", Hostname: "de-fra-st004.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "Germany", City: "Frankfurt am Main", RetroLoc: "Germany Frankfurt am Main st005", Hostname: "de-fra-st005.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "Germany", City: "Frankfurt am Main", RetroLoc: "Germany Frankfurt am Main", Hostname: "de-fra.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "Germany", City: "Frankfurt am Main", RetroLoc: "Germany Frankfurt mp001", Hostname: "de-fra-mp001.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "Greece", City: "Athens", RetroLoc: "Greece", Hostname: "gr-ath.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "Hungary", City: "Budapest", RetroLoc: "Hungary", Hostname: "hu-bud.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "Iceland", City: "Reykjavik", RetroLoc: "Iceland", Hostname: "is-rkv.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "India UK", City: "", RetroLoc: "India UK", Hostname: "in-uk.prod.surfshark.com", MultiHop: true},
{Region: "Europe", Country: "Ireland", City: "Dublin", RetroLoc: "Ireland", Hostname: "ie-dub.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "Italy", City: "Milan", RetroLoc: "Italy Milan", Hostname: "it-mil.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "Italy", City: "Rome", RetroLoc: "Italy Rome", Hostname: "it-rom.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "Latvia", City: "Riga", RetroLoc: "Latvia", Hostname: "lv-rig.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "Luxembourg", City: "Luxembourg", RetroLoc: "Luxembourg", Hostname: "lu-ste.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "Moldova", City: "Chisinau", RetroLoc: "Moldova", Hostname: "md-chi.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "Netherlands", City: "Amsterdam", RetroLoc: "Netherlands Amsterdam mp001", Hostname: "nl-ams-mp001.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "Netherlands", City: "Amsterdam", RetroLoc: "Netherlands Amsterdam st001", Hostname: "nl-ams-st001.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "Netherlands", City: "Amsterdam", RetroLoc: "Netherlands Amsterdam", Hostname: "nl-ams.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "North Macedonia", City: "Skopje", RetroLoc: "North Macedonia", Hostname: "mk-skp.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "Norway", City: "Oslo", RetroLoc: "Norway", Hostname: "no-osl.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "Poland", City: "Gdansk", RetroLoc: "Poland Gdansk", Hostname: "pl-gdn.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "Poland", City: "Warsaw", RetroLoc: "Poland Warsaw", Hostname: "pl-waw.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "Portugal", City: "Lisbon", RetroLoc: "Portugal Lisbon", Hostname: "pt-lis.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "Portugal", City: "Porto", RetroLoc: "Portugal Porto", Hostname: "pt-opo.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "Romania", City: "Bucharest", RetroLoc: "Romania", Hostname: "ro-buc.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "Russia", City: "Moscow", RetroLoc: "Russia Moscow", Hostname: "ru-mos.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "Serbia", City: "Belgrade", RetroLoc: "Serbia", Hostname: "rs-beg.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "Singapore Netherlands", City: "", RetroLoc: "Singapore Netherlands", Hostname: "sg-nl.prod.surfshark.com", MultiHop: true},
{Region: "Europe", Country: "Slovakia", City: "Bratislava", RetroLoc: "Slovekia", Hostname: "sk-bts.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "Slovenia", City: "Ljubljana", RetroLoc: "Slovenia", Hostname: "si-lju.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "Spain", City: "Barcelona", RetroLoc: "Spain Barcelona", Hostname: "es-bcn.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "Spain", City: "Madrid", RetroLoc: "Spain Madrid", Hostname: "es-mad.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "Spain", City: "Valencia", RetroLoc: "Spain Valencia", Hostname: "es-vlc.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "Sweden", City: "Stockholm", RetroLoc: "Sweden", Hostname: "se-sto.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "Switzerland", City: "Zurich", RetroLoc: "Switzerland", Hostname: "ch-zur.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "Turkey", City: "Istanbul", RetroLoc: "Turkey Istanbul", Hostname: "tr-ist.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "UK France", City: "", RetroLoc: "UK France", Hostname: "uk-fr.prod.surfshark.com", MultiHop: true},
{Region: "Europe", Country: "UK Germany", City: "", RetroLoc: "UK Germany", Hostname: "uk-de.prod.surfshark.com", MultiHop: true},
{Region: "Europe", Country: "Ukraine", City: "Kyiv", RetroLoc: "Ukraine", Hostname: "ua-iev.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "United Kingdom", City: "Glasgow", RetroLoc: "UK Glasgow", Hostname: "uk-gla.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "United Kingdom", City: "London", RetroLoc: "UK London mp001", Hostname: "uk-lon-mp001.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "United Kingdom", City: "London", RetroLoc: "UK London st001", Hostname: "uk-lon-st001.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "United Kingdom", City: "London", RetroLoc: "UK London st002", Hostname: "uk-lon-st002.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "United Kingdom", City: "London", RetroLoc: "UK London st003", Hostname: "uk-lon-st003.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "United Kingdom", City: "London", RetroLoc: "UK London st004", Hostname: "uk-lon-st004.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "United Kingdom", City: "London", RetroLoc: "UK London st005", Hostname: "uk-lon-st005.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "United Kingdom", City: "London", RetroLoc: "UK London", Hostname: "uk-lon.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "United Kingdom", City: "Manchester", RetroLoc: "UK Manchester", Hostname: "uk-man.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "US Netherlands", City: "", RetroLoc: "US Netherlands", Hostname: "us-nl.prod.surfshark.com", MultiHop: false},
{Region: "Europe", Country: "US Portugal", City: "", RetroLoc: "US Portugal", Hostname: "us-pt.prod.surfshark.com", MultiHop: true},
{Region: "Middle East and Africa", Country: "Israel", City: "Tel Aviv", RetroLoc: "Israel", Hostname: "il-tlv.prod.surfshark.com", MultiHop: false},
{Region: "Middle East and Africa", Country: "Nigeria", City: "Lagos", RetroLoc: "Nigeria", Hostname: "ng-lag.prod.surfshark.com", MultiHop: false},
{Region: "Middle East and Africa", Country: "South Africa", City: "Johannesburg", RetroLoc: "South Africa", Hostname: "za-jnb.prod.surfshark.com", MultiHop: false},
{Region: "Middle East and Africa", Country: "United Arab Emirates", City: "Dubai", RetroLoc: "United Arab Emirates", Hostname: "ae-dub.prod.surfshark.com", MultiHop: false},
{Region: "The Americas", Country: "Argentina", City: "Buenos Aires", RetroLoc: "Argentina Buenos Aires", Hostname: "ar-bua.prod.surfshark.com", MultiHop: false},
{Region: "The Americas", Country: "Australia US", City: "", RetroLoc: "Australia US", Hostname: "au-us.prod.surfshark.com", MultiHop: true},
{Region: "The Americas", Country: "Brazil", City: "Sao Paulo", RetroLoc: "Brazil", Hostname: "br-sao.prod.surfshark.com", MultiHop: false},
{Region: "The Americas", Country: "Canada US", City: "", RetroLoc: "Canada US", Hostname: "ca-us.prod.surfshark.com", MultiHop: true},
{Region: "The Americas", Country: "Canada", City: "Montreal", RetroLoc: "Canada Montreal", Hostname: "ca-mon.prod.surfshark.com", MultiHop: false},
{Region: "The Americas", Country: "Canada", City: "Toronto", RetroLoc: "Canada Toronto mp001", Hostname: "ca-tor-mp001.prod.surfshark.com", MultiHop: false},
{Region: "The Americas", Country: "Canada", City: "Toronto", RetroLoc: "Canada Toronto", Hostname: "ca-tor.prod.surfshark.com", MultiHop: false},
{Region: "The Americas", Country: "Canada", City: "Vancouver", RetroLoc: "Canada Vancouver", Hostname: "ca-van.prod.surfshark.com", MultiHop: false},
{Region: "The Americas", Country: "Chile", City: "Santiago", RetroLoc: "Chile", Hostname: "cl-san.prod.surfshark.com", MultiHop: false},
{Region: "The Americas", Country: "Colombia", City: "Bogota", RetroLoc: "Colombia", Hostname: "co-bog.prod.surfshark.com", MultiHop: false},
{Region: "The Americas", Country: "Costa Rica", City: "San Jose", RetroLoc: "Costa Rica", Hostname: "cr-sjn.prod.surfshark.com", MultiHop: false},
{Region: "The Americas", Country: "Mexico", City: "Mexico City", RetroLoc: "Mexico City Mexico", Hostname: "mx-mex.prod.surfshark.com", MultiHop: false},
{Region: "The Americas", Country: "Netherlands US", City: "", RetroLoc: "Netherlands US", Hostname: "nl-us.prod.surfshark.com", MultiHop: true},
{Region: "The Americas", Country: "United States", City: "Atlanta", RetroLoc: "US Atlanta", Hostname: "us-atl.prod.surfshark.com", MultiHop: false},
{Region: "The Americas", Country: "United States", City: "Bend", RetroLoc: "US Bend", Hostname: "us-bdn.prod.surfshark.com", MultiHop: false},
{Region: "The Americas", Country: "United States", City: "Boston", RetroLoc: "US Boston", Hostname: "us-bos.prod.surfshark.com", MultiHop: false},
{Region: "The Americas", Country: "United States", City: "Buffalo", RetroLoc: "US Buffalo", Hostname: "us-buf.prod.surfshark.com", MultiHop: false},
{Region: "The Americas", Country: "United States", City: "Charlotte", RetroLoc: "US Charlotte", Hostname: "us-clt.prod.surfshark.com", MultiHop: false},
{Region: "The Americas", Country: "United States", City: "Chicago", RetroLoc: "US Chicago", Hostname: "us-chi.prod.surfshark.com", MultiHop: false},
{Region: "The Americas", Country: "United States", City: "Dallas", RetroLoc: "US Dallas", Hostname: "us-dal.prod.surfshark.com", MultiHop: false},
{Region: "The Americas", Country: "United States", City: "Denver", RetroLoc: "US Denver", Hostname: "us-den.prod.surfshark.com", MultiHop: false},
{Region: "The Americas", Country: "United States", City: "Detroit", RetroLoc: "US Gahanna", Hostname: "us-dtw.prod.surfshark.com", MultiHop: false},
{Region: "The Americas", Country: "United States", City: "Houston", RetroLoc: "US Houston", Hostname: "us-hou.prod.surfshark.com", MultiHop: false},
{Region: "The Americas", Country: "United States", City: "Kansas City", RetroLoc: "US Kansas City", Hostname: "us-kan.prod.surfshark.com", MultiHop: false},
{Region: "The Americas", Country: "United States", City: "Las Vegas", RetroLoc: "US Las Vegas", Hostname: "us-las.prod.surfshark.com", MultiHop: false},
{Region: "The Americas", Country: "United States", City: "Latham", RetroLoc: "US Latham", Hostname: "us-ltm.prod.surfshark.com", MultiHop: false},
{Region: "The Americas", Country: "United States", City: "Los Angeles", RetroLoc: "US Los Angeles", Hostname: "us-lax.prod.surfshark.com", MultiHop: false},
{Region: "The Americas", Country: "United States", City: "Manassas", RetroLoc: "US Maryland", Hostname: "us-mnz.prod.surfshark.com", MultiHop: false},
{Region: "The Americas", Country: "United States", City: "Miami", RetroLoc: "US Miami", Hostname: "us-mia.prod.surfshark.com", MultiHop: false},
{Region: "The Americas", Country: "United States", City: "New York", RetroLoc: "US New York City mp001", Hostname: "us-nyc-mp001.prod.surfshark.com", MultiHop: false},
{Region: "The Americas", Country: "United States", City: "New York", RetroLoc: "US New York City st001", Hostname: "us-nyc-st001.prod.surfshark.com", MultiHop: false},
{Region: "The Americas", Country: "United States", City: "New York", RetroLoc: "US New York City st002", Hostname: "us-nyc-st002.prod.surfshark.com", MultiHop: false},
{Region: "The Americas", Country: "United States", City: "New York", RetroLoc: "US New York City st003", Hostname: "us-nyc-st003.prod.surfshark.com", MultiHop: false},
{Region: "The Americas", Country: "United States", City: "New York", RetroLoc: "US New York City st004", Hostname: "us-nyc-st004.prod.surfshark.com", MultiHop: false},
{Region: "The Americas", Country: "United States", City: "New York", RetroLoc: "US New York City st005", Hostname: "us-nyc-st005.prod.surfshark.com", MultiHop: false},
{Region: "The Americas", Country: "United States", City: "New York", RetroLoc: "US New York City", Hostname: "us-nyc.prod.surfshark.com", MultiHop: false},
{Region: "The Americas", Country: "United States", City: "Orlando", RetroLoc: "US Orlando", Hostname: "us-orl.prod.surfshark.com", MultiHop: false},
{Region: "The Americas", Country: "United States", City: "Phoenix", RetroLoc: "US Phoenix", Hostname: "us-phx.prod.surfshark.com", MultiHop: false},
{Region: "The Americas", Country: "United States", City: "Salt Lake City", RetroLoc: "US Salt Lake City", Hostname: "us-slc.prod.surfshark.com", MultiHop: false},
{Region: "The Americas", Country: "United States", City: "San Francisco", RetroLoc: "US San Francisco mp001", Hostname: "us-sfo-mp001.prod.surfshark.com", MultiHop: false},
{Region: "The Americas", Country: "United States", City: "San Francisco", RetroLoc: "US San Francisco", Hostname: "us-sfo.prod.surfshark.com", MultiHop: false},
{Region: "The Americas", Country: "United States", City: "Seattle", RetroLoc: "US Seatle", Hostname: "us-sea.prod.surfshark.com", MultiHop: false},
{Region: "The Americas", Country: "United States", City: "St. Louis", RetroLoc: "US Saint Louis", Hostname: "us-stl.prod.surfshark.com", MultiHop: false},
{Region: "The Americas", Country: "United States", City: "Tampa", RetroLoc: "US Tampa", Hostname: "us-tpa.prod.surfshark.com", MultiHop: false},
}
}
func SurfsharkHostnameChoices(servers []models.SurfsharkServer) (choices []string) {
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].Hostname choices[i] = servers[i].Hostname
} }
return makeUnique(choices) return makeUnique(choices)
} }
// SurfsharkServers returns a slice of all the server information for Surfshark.
func SurfsharkServers() (servers []models.SurfsharkServer) {
servers = make([]models.SurfsharkServer, len(allServers.Surfshark.Servers))
copy(servers, allServers.Surfshark.Servers)
return servers
}

View File

@@ -10,8 +10,7 @@ const (
TorguardOpenvpnStaticKeyV1 = "770e8de5fc56e0248cc7b5aab56be80d0e19cbf003c1b3ed68efbaf08613c3a1a019dac6a4b84f13a6198f73229ffc21fa512394e288f82aa2cf0180f01fb3eb1a71e00a077a20f6d7a83633f5b4f47f27e30617eaf8485dd8c722a8606d56b3c183f65da5d3c9001a8cbdb96c793d936251098b24fe52a6dd2472e98cfccbc466e63520d63ade7a0eacc36208c3142a1068236a52142fbb7b3ed83d785e12a28261bccfb3bcb62a8d2f6d18f5df5f3652e59c5627d8d9c8f7877c4d7b08e19a5c363556ba68d392be78b75152dd55ba0f74d45089e84f77f4492d886524ea6c82b9f4dd83d46528d4f5c3b51cfeaf2838d938bd0597c426b0e440434f2c451f" TorguardOpenvpnStaticKeyV1 = "770e8de5fc56e0248cc7b5aab56be80d0e19cbf003c1b3ed68efbaf08613c3a1a019dac6a4b84f13a6198f73229ffc21fa512394e288f82aa2cf0180f01fb3eb1a71e00a077a20f6d7a83633f5b4f47f27e30617eaf8485dd8c722a8606d56b3c183f65da5d3c9001a8cbdb96c793d936251098b24fe52a6dd2472e98cfccbc466e63520d63ade7a0eacc36208c3142a1068236a52142fbb7b3ed83d785e12a28261bccfb3bcb62a8d2f6d18f5df5f3652e59c5627d8d9c8f7877c4d7b08e19a5c363556ba68d392be78b75152dd55ba0f74d45089e84f77f4492d886524ea6c82b9f4dd83d46528d4f5c3b51cfeaf2838d938bd0597c426b0e440434f2c451f"
) )
func TorguardCountryChoices() (choices []string) { func TorguardCountryChoices(servers []models.TorguardServer) (choices []string) {
servers := TorguardServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].Country choices[i] = servers[i].Country
@@ -19,8 +18,7 @@ func TorguardCountryChoices() (choices []string) {
return makeUnique(choices) return makeUnique(choices)
} }
func TorguardCityChoices() (choices []string) { func TorguardCityChoices(servers []models.TorguardServer) (choices []string) {
servers := TorguardServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].City choices[i] = servers[i].City
@@ -28,18 +26,10 @@ func TorguardCityChoices() (choices []string) {
return makeUnique(choices) return makeUnique(choices)
} }
func TorguardHostnameChoices() (choices []string) { func TorguardHostnameChoices(servers []models.TorguardServer) (choices []string) {
servers := TorguardServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].Hostname choices[i] = servers[i].Hostname
} }
return makeUnique(choices) return makeUnique(choices)
} }
// TorguardServers returns a slice of all the server information for Torguard.
func TorguardServers() (servers []models.TorguardServer) {
servers = make([]models.TorguardServer, len(allServers.Torguard.Servers))
copy(servers, allServers.Torguard.Servers)
return servers
}

View File

@@ -9,8 +9,7 @@ const (
VPNUnlimitedCertificateAuthority = "MIIEjjCCA/egAwIBAgIJAKsVbHBdakXuMA0GCSqGSIb3DQEBBQUAMIHgMQswCQYDVQQGEwJVUzELMAkGA1UECBMCTlkxETAPBgNVBAcTCE5ldyBZb3JrMR8wHQYDVQQKExZTaW1wbGV4IFNvbHV0aW9ucyBJbmMuMRYwFAYDVQQLEw1WcG4gVW5saW1pdGVkMSMwIQYDVQQDExpzZXJ2ZXIudnBudW5saW1pdGVkYXBwLmNvbTEjMCEGA1UEKRMac2VydmVyLnZwbnVubGltaXRlZGFwcC5jb20xLjAsBgkqhkiG9w0BCQEWH3N1cHBvcnRAc2ltcGxleHNvbHV0aW9uc2luYy5jb20wHhcNMTMxMjE2MTM1OTQ0WhcNMjMxMjE0MTM1OTQ0WjCB4DELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAk5ZMREwDwYDVQQHEwhOZXcgWW9yazEfMB0GA1UEChMWU2ltcGxleCBTb2x1dGlvbnMgSW5jLjEWMBQGA1UECxMNVnBuIFVubGltaXRlZDEjMCEGA1UEAxMac2VydmVyLnZwbnVubGltaXRlZGFwcC5jb20xIzAhBgNVBCkTGnNlcnZlci52cG51bmxpbWl0ZWRhcHAuY29tMS4wLAYJKoZIhvcNAQkBFh9zdXBwb3J0QHNpbXBsZXhzb2x1dGlvbnNpbmMuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDADUzS8QWGvdhLFKsEzAiq5+b0ukKjBza0k6/dmCeYTvCVqGKg/h1IAtQdVVLAkmEp0zvGH7PuOhXm7zZrCouBr/RiG4tEcoRHwc6AJmowkYERlY7+xGx3OuNrD00QceNTsan0bn78jwt0zhFNmHdoTtFjgK3oqmQYSAtbEVWYgwIDAQABo4IBTDCCAUgwHQYDVR0OBBYEFKClmYP+tMNgWagUJCCHjtaui2k+MIIBFwYDVR0jBIIBDjCCAQqAFKClmYP+tMNgWagUJCCHjtaui2k+oYHmpIHjMIHgMQswCQYDVQQGEwJVUzELMAkGA1UECBMCTlkxETAPBgNVBAcTCE5ldyBZb3JrMR8wHQYDVQQKExZTaW1wbGV4IFNvbHV0aW9ucyBJbmMuMRYwFAYDVQQLEw1WcG4gVW5saW1pdGVkMSMwIQYDVQQDExpzZXJ2ZXIudnBudW5saW1pdGVkYXBwLmNvbTEjMCEGA1UEKRMac2VydmVyLnZwbnVubGltaXRlZGFwcC5jb20xLjAsBgkqhkiG9w0BCQEWH3N1cHBvcnRAc2ltcGxleHNvbHV0aW9uc2luYy5jb22CCQCrFWxwXWpF7jAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBALkWhfw7SSV7it+ZYZmT+cQbExjlYgQ40zae2J2CqIYACRcfsDHvh7Q+fiwSocevv2NE0dWi6WB2H6SiudYjvDvubAX/QbzfBxtbxCCoAIlfPCm8xOnWFN7TUJAzWwHJkKgEnu29GZHu2x8J+7VeDbKH5RTMHHe8FkSxh6Zz/BMN" VPNUnlimitedCertificateAuthority = "MIIEjjCCA/egAwIBAgIJAKsVbHBdakXuMA0GCSqGSIb3DQEBBQUAMIHgMQswCQYDVQQGEwJVUzELMAkGA1UECBMCTlkxETAPBgNVBAcTCE5ldyBZb3JrMR8wHQYDVQQKExZTaW1wbGV4IFNvbHV0aW9ucyBJbmMuMRYwFAYDVQQLEw1WcG4gVW5saW1pdGVkMSMwIQYDVQQDExpzZXJ2ZXIudnBudW5saW1pdGVkYXBwLmNvbTEjMCEGA1UEKRMac2VydmVyLnZwbnVubGltaXRlZGFwcC5jb20xLjAsBgkqhkiG9w0BCQEWH3N1cHBvcnRAc2ltcGxleHNvbHV0aW9uc2luYy5jb20wHhcNMTMxMjE2MTM1OTQ0WhcNMjMxMjE0MTM1OTQ0WjCB4DELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAk5ZMREwDwYDVQQHEwhOZXcgWW9yazEfMB0GA1UEChMWU2ltcGxleCBTb2x1dGlvbnMgSW5jLjEWMBQGA1UECxMNVnBuIFVubGltaXRlZDEjMCEGA1UEAxMac2VydmVyLnZwbnVubGltaXRlZGFwcC5jb20xIzAhBgNVBCkTGnNlcnZlci52cG51bmxpbWl0ZWRhcHAuY29tMS4wLAYJKoZIhvcNAQkBFh9zdXBwb3J0QHNpbXBsZXhzb2x1dGlvbnNpbmMuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDADUzS8QWGvdhLFKsEzAiq5+b0ukKjBza0k6/dmCeYTvCVqGKg/h1IAtQdVVLAkmEp0zvGH7PuOhXm7zZrCouBr/RiG4tEcoRHwc6AJmowkYERlY7+xGx3OuNrD00QceNTsan0bn78jwt0zhFNmHdoTtFjgK3oqmQYSAtbEVWYgwIDAQABo4IBTDCCAUgwHQYDVR0OBBYEFKClmYP+tMNgWagUJCCHjtaui2k+MIIBFwYDVR0jBIIBDjCCAQqAFKClmYP+tMNgWagUJCCHjtaui2k+oYHmpIHjMIHgMQswCQYDVQQGEwJVUzELMAkGA1UECBMCTlkxETAPBgNVBAcTCE5ldyBZb3JrMR8wHQYDVQQKExZTaW1wbGV4IFNvbHV0aW9ucyBJbmMuMRYwFAYDVQQLEw1WcG4gVW5saW1pdGVkMSMwIQYDVQQDExpzZXJ2ZXIudnBudW5saW1pdGVkYXBwLmNvbTEjMCEGA1UEKRMac2VydmVyLnZwbnVubGltaXRlZGFwcC5jb20xLjAsBgkqhkiG9w0BCQEWH3N1cHBvcnRAc2ltcGxleHNvbHV0aW9uc2luYy5jb22CCQCrFWxwXWpF7jAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBALkWhfw7SSV7it+ZYZmT+cQbExjlYgQ40zae2J2CqIYACRcfsDHvh7Q+fiwSocevv2NE0dWi6WB2H6SiudYjvDvubAX/QbzfBxtbxCCoAIlfPCm8xOnWFN7TUJAzWwHJkKgEnu29GZHu2x8J+7VeDbKH5RTMHHe8FkSxh6Zz/BMN"
) )
func VPNUnlimitedCountryChoices() (choices []string) { func VPNUnlimitedCountryChoices(servers []models.VPNUnlimitedServer) (choices []string) {
servers := VPNUnlimitedServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].Country choices[i] = servers[i].Country
@@ -18,8 +17,7 @@ func VPNUnlimitedCountryChoices() (choices []string) {
return makeUnique(choices) return makeUnique(choices)
} }
func VPNUnlimitedCityChoices() (choices []string) { func VPNUnlimitedCityChoices(servers []models.VPNUnlimitedServer) (choices []string) {
servers := VPNUnlimitedServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].City choices[i] = servers[i].City
@@ -27,18 +25,10 @@ func VPNUnlimitedCityChoices() (choices []string) {
return makeUnique(choices) return makeUnique(choices)
} }
func VPNUnlimitedHostnameChoices() (choices []string) { func VPNUnlimitedHostnameChoices(servers []models.VPNUnlimitedServer) (choices []string) {
servers := VPNUnlimitedServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].Hostname choices[i] = servers[i].Hostname
} }
return makeUnique(choices) return makeUnique(choices)
} }
// VPNUnlimitedServers returns a slice of all the server information for VPNUnlimited.
func VPNUnlimitedServers() (servers []models.VPNUnlimitedServer) {
servers = make([]models.VPNUnlimitedServer, len(allServers.VPNUnlimited.Servers))
copy(servers, allServers.VPNUnlimited.Servers)
return servers
}

View File

@@ -9,18 +9,10 @@ const (
VyprvpnCertificate = "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" VyprvpnCertificate = "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"
) )
func VyprvpnRegionChoices() (choices []string) { func VyprvpnRegionChoices(servers []models.VyprvpnServer) (choices []string) {
servers := VyprvpnServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].Region choices[i] = servers[i].Region
} }
return makeUnique(choices) return makeUnique(choices)
} }
// VyprvpnServers returns a slice of all the VyprVPN servers.
func VyprvpnServers() (servers []models.VyprvpnServer) {
servers = make([]models.VyprvpnServer, len(allServers.Vyprvpn.Servers))
copy(servers, allServers.Vyprvpn.Servers)
return servers
}

View File

@@ -1,8 +1,6 @@
package constants package constants
import ( import "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/models"
)
//nolint:lll //nolint:lll
const ( const (
@@ -10,8 +8,7 @@ const (
WindscribeOpenvpnStaticKeyV1 = "5801926a57ac2ce27e3dfd1dd6ef82042d82bd4f3f0021296f57734f6f1ea714a6623845541c4b0c3dea0a050fe6746cb66dfab14cda27e5ae09d7c155aa554f399fa4a863f0e8c1af787e5c602a801d3a2ec41e395a978d56729457fe6102d7d9e9119aa83643210b33c678f9d4109e3154ac9c759e490cb309b319cf708cae83ddadc3060a7a26564d1a24411cd552fe6620ea16b755697a4fc5e6e9d0cfc0c5c4a1874685429046a424c026db672e4c2c492898052ba59128d46200b40f880027a8b6610a4d559bdc9346d33a0a6b08e75c7fd43192b162bfd0aef0c716b31584827693f676f9a5047123466f0654eade34972586b31c6ce7e395f4b478cb" WindscribeOpenvpnStaticKeyV1 = "5801926a57ac2ce27e3dfd1dd6ef82042d82bd4f3f0021296f57734f6f1ea714a6623845541c4b0c3dea0a050fe6746cb66dfab14cda27e5ae09d7c155aa554f399fa4a863f0e8c1af787e5c602a801d3a2ec41e395a978d56729457fe6102d7d9e9119aa83643210b33c678f9d4109e3154ac9c759e490cb309b319cf708cae83ddadc3060a7a26564d1a24411cd552fe6620ea16b755697a4fc5e6e9d0cfc0c5c4a1874685429046a424c026db672e4c2c492898052ba59128d46200b40f880027a8b6610a4d559bdc9346d33a0a6b08e75c7fd43192b162bfd0aef0c716b31584827693f676f9a5047123466f0654eade34972586b31c6ce7e395f4b478cb"
) )
func WindscribeRegionChoices() (choices []string) { func WindscribeRegionChoices(servers []models.WindscribeServer) (choices []string) {
servers := WindscribeServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].Region choices[i] = servers[i].Region
@@ -19,8 +16,7 @@ func WindscribeRegionChoices() (choices []string) {
return makeUnique(choices) return makeUnique(choices)
} }
func WindscribeCityChoices() (choices []string) { func WindscribeCityChoices(servers []models.WindscribeServer) (choices []string) {
servers := WindscribeServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].City choices[i] = servers[i].City
@@ -28,18 +24,10 @@ func WindscribeCityChoices() (choices []string) {
return makeUnique(choices) return makeUnique(choices)
} }
func WindscribeHostnameChoices() (choices []string) { func WindscribeHostnameChoices(servers []models.WindscribeServer) (choices []string) {
servers := WindscribeServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].Hostname choices[i] = servers[i].Hostname
} }
return makeUnique(choices) return makeUnique(choices)
} }
// WindscribeServers returns a slice of all the Windscribe servers.
func WindscribeServers() (servers []models.WindscribeServer) {
servers = make([]models.WindscribeServer, len(allServers.Windscribe.Servers))
copy(servers, allServers.Windscribe.Servers)
return servers
}

View File

@@ -4,6 +4,8 @@ import (
"context" "context"
"fmt" "fmt"
"net" "net"
"github.com/qdm12/gluetun/internal/subnet"
) )
type OutboundSubnetsSetter interface { type OutboundSubnetsSetter interface {
@@ -23,8 +25,7 @@ func (c *Config) SetOutboundSubnets(ctx context.Context, subnets []net.IPNet) (e
c.logger.Info("setting allowed subnets through firewall...") c.logger.Info("setting allowed subnets through firewall...")
subnetsToAdd := findSubnetsToAdd(c.outboundSubnets, subnets) subnetsToAdd, subnetsToRemove := subnet.FindSubnetsToChange(c.outboundSubnets, subnets)
subnetsToRemove := findSubnetsToRemove(c.outboundSubnets, subnets)
if len(subnetsToAdd) == 0 && len(subnetsToRemove) == 0 { if len(subnetsToAdd) == 0 && len(subnetsToRemove) == 0 {
return nil return nil
} }
@@ -39,12 +40,12 @@ func (c *Config) SetOutboundSubnets(ctx context.Context, subnets []net.IPNet) (e
func (c *Config) removeOutboundSubnets(ctx context.Context, subnets []net.IPNet) { func (c *Config) removeOutboundSubnets(ctx context.Context, subnets []net.IPNet) {
const remove = true const remove = true
for _, subnet := range subnets { for _, subNet := range subnets {
if err := c.acceptOutputFromIPToSubnet(ctx, c.defaultInterface, c.localIP, subnet, remove); err != nil { if err := c.acceptOutputFromIPToSubnet(ctx, c.defaultInterface, c.localIP, subNet, remove); err != nil {
c.logger.Error("cannot remove outdated outbound subnet through firewall: " + err.Error()) c.logger.Error("cannot remove outdated outbound subnet through firewall: " + err.Error())
continue continue
} }
c.outboundSubnets = removeSubnetFromSubnets(c.outboundSubnets, subnet) c.outboundSubnets = subnet.RemoveSubnetFromSubnets(c.outboundSubnets, subNet)
} }
} }

View File

@@ -1,11 +1,9 @@
package httpproxy package httpproxy
import ( import (
"context"
"io" "io"
"net" "net"
"net/http" "net/http"
"sync"
) )
func (h *handler) handleHTTPS(responseWriter http.ResponseWriter, request *http.Request) { func (h *handler) handleHTTPS(responseWriter http.ResponseWriter, request *http.Request) {
@@ -38,27 +36,30 @@ func (h *handler) handleHTTPS(responseWriter http.ResponseWriter, request *http.
} }
h.wg.Add(1) h.wg.Add(1)
ctx, cancel := context.WithCancel(h.ctx)
const transferGoroutines = 2 serverToClientDone := make(chan struct{})
wg := &sync.WaitGroup{} clientToServerClientDone := make(chan struct{})
wg.Add(transferGoroutines) go transfer(destinationConn, clientConnection, clientToServerClientDone)
go func() { // trigger cleanup when done go transfer(clientConnection, destinationConn, serverToClientDone)
wg.Wait()
cancel() select {
}() case <-h.ctx.Done():
go func() { // cleanup
<-ctx.Done()
destinationConn.Close() destinationConn.Close()
clientConnection.Close() clientConnection.Close()
<-serverToClientDone
<-clientToServerClientDone
case <-serverToClientDone:
<-clientToServerClientDone
case <-clientToServerClientDone: // happens more rarely, when a connection is closed on the client side
<-serverToClientDone
}
h.wg.Done() h.wg.Done()
}()
go transfer(destinationConn, clientConnection, wg)
go transfer(clientConnection, destinationConn, wg)
} }
func transfer(destination io.WriteCloser, source io.ReadCloser, wg *sync.WaitGroup) { func transfer(destination io.WriteCloser, source io.ReadCloser, done chan<- struct{}) {
_, _ = io.Copy(destination, source) _, _ = io.Copy(destination, source)
_ = source.Close() _ = source.Close()
_ = destination.Close() _ = destination.Close()
wg.Done() close(done)
} }

View File

@@ -9,11 +9,7 @@ import (
"github.com/qdm12/golibs/logging" "github.com/qdm12/golibs/logging"
) )
type Server interface { type Server struct {
Run(ctx context.Context, errorCh chan<- error)
}
type server struct {
address string address string
handler http.Handler handler http.Handler
logger logging.Logger logger logging.Logger
@@ -21,9 +17,9 @@ type server struct {
} }
func New(ctx context.Context, address string, logger logging.Logger, func New(ctx context.Context, address string, logger logging.Logger,
stealth, verbose bool, username, password string) Server { stealth, verbose bool, username, password string) *Server {
wg := &sync.WaitGroup{} wg := &sync.WaitGroup{}
return &server{ return &Server{
address: address, address: address,
handler: newHandler(ctx, wg, logger, stealth, verbose, username, password), handler: newHandler(ctx, wg, logger, stealth, verbose, username, password),
logger: logger, logger: logger,
@@ -31,7 +27,7 @@ func New(ctx context.Context, address string, logger logging.Logger,
} }
} }
func (s *server) Run(ctx context.Context, errorCh chan<- error) { func (s *Server) Run(ctx context.Context, errorCh chan<- error) {
server := http.Server{Addr: s.address, Handler: s.handler} server := http.Server{Addr: s.address, Handler: s.handler}
go func() { go func() {
<-ctx.Done() <-ctx.Done()

View File

@@ -0,0 +1,252 @@
package models
import (
"net"
)
func (a AllServers) GetCopy() (servers AllServers) {
servers = a // copy versions and timestamps
servers.Cyberghost.Servers = a.GetCyberghost()
servers.Fastestvpn.Servers = a.GetFastestvpn()
servers.HideMyAss.Servers = a.GetHideMyAss()
servers.Ipvanish.Servers = a.GetIpvanish()
servers.Ivpn.Servers = a.GetIvpn()
servers.Mullvad.Servers = a.GetMullvad()
servers.Nordvpn.Servers = a.GetNordvpn()
servers.Privado.Servers = a.GetPrivado()
servers.Pia.Servers = a.GetPia()
servers.Privatevpn.Servers = a.GetPrivatevpn()
servers.Protonvpn.Servers = a.GetProtonvpn()
servers.Purevpn.Servers = a.GetPurevpn()
servers.Surfshark.Servers = a.GetSurfshark()
servers.Torguard.Servers = a.GetTorguard()
servers.VPNUnlimited.Servers = a.GetVPNUnlimited()
servers.Vyprvpn.Servers = a.GetVyprvpn()
servers.Windscribe.Servers = a.GetWindscribe()
return servers
}
func (a *AllServers) GetCyberghost() (servers []CyberghostServer) {
if a.Cyberghost.Servers == nil {
return nil
}
servers = make([]CyberghostServer, len(a.Cyberghost.Servers))
for i, serverToCopy := range a.Cyberghost.Servers {
servers[i] = serverToCopy
servers[i].IPs = copyIPs(serverToCopy.IPs)
}
return servers
}
func (a *AllServers) GetFastestvpn() (servers []FastestvpnServer) {
if a.Fastestvpn.Servers == nil {
return nil
}
servers = make([]FastestvpnServer, len(a.Fastestvpn.Servers))
for i, serverToCopy := range a.Fastestvpn.Servers {
servers[i] = serverToCopy
servers[i].IPs = copyIPs(serverToCopy.IPs)
}
return servers
}
func (a *AllServers) GetHideMyAss() (servers []HideMyAssServer) {
if a.HideMyAss.Servers == nil {
return nil
}
servers = make([]HideMyAssServer, len(a.HideMyAss.Servers))
for i, serverToCopy := range a.HideMyAss.Servers {
servers[i] = serverToCopy
servers[i].IPs = copyIPs(serverToCopy.IPs)
}
return servers
}
func (a *AllServers) GetIpvanish() (servers []IpvanishServer) {
if a.Ipvanish.Servers == nil {
return nil
}
servers = make([]IpvanishServer, len(a.Ipvanish.Servers))
for i, serverToCopy := range a.Ipvanish.Servers {
servers[i] = serverToCopy
servers[i].IPs = copyIPs(serverToCopy.IPs)
}
return servers
}
func (a *AllServers) GetIvpn() (servers []IvpnServer) {
if a.Ivpn.Servers == nil {
return nil
}
servers = make([]IvpnServer, len(a.Ivpn.Servers))
for i, serverToCopy := range a.Ivpn.Servers {
servers[i] = serverToCopy
servers[i].IPs = copyIPs(serverToCopy.IPs)
}
return servers
}
func (a *AllServers) GetMullvad() (servers []MullvadServer) {
if a.Mullvad.Servers == nil {
return nil
}
servers = make([]MullvadServer, len(a.Mullvad.Servers))
for i, serverToCopy := range a.Mullvad.Servers {
servers[i] = serverToCopy
servers[i].IPs = copyIPs(serverToCopy.IPs)
servers[i].IPsV6 = copyIPs(serverToCopy.IPsV6)
}
return servers
}
func (a *AllServers) GetNordvpn() (servers []NordvpnServer) {
if a.Nordvpn.Servers == nil {
return nil
}
servers = make([]NordvpnServer, len(a.Nordvpn.Servers))
for i, serverToCopy := range a.Nordvpn.Servers {
servers[i] = serverToCopy
servers[i].IP = copyIP(serverToCopy.IP)
}
return servers
}
func (a *AllServers) GetPia() (servers []PIAServer) {
if a.Pia.Servers == nil {
return nil
}
servers = make([]PIAServer, len(a.Pia.Servers))
for i, serverToCopy := range a.Pia.Servers {
servers[i] = serverToCopy
servers[i].IPs = copyIPs(serverToCopy.IPs)
}
return servers
}
func (a *AllServers) GetPrivado() (servers []PrivadoServer) {
if a.Privado.Servers == nil {
return nil
}
servers = make([]PrivadoServer, len(a.Privado.Servers))
for i, serverToCopy := range a.Privado.Servers {
servers[i] = serverToCopy
servers[i].IP = copyIP(serverToCopy.IP)
}
return servers
}
func (a *AllServers) GetPrivatevpn() (servers []PrivatevpnServer) {
if a.Privatevpn.Servers == nil {
return nil
}
servers = make([]PrivatevpnServer, len(a.Privatevpn.Servers))
for i, serverToCopy := range a.Privatevpn.Servers {
servers[i] = serverToCopy
servers[i].IPs = copyIPs(serverToCopy.IPs)
}
return servers
}
func (a *AllServers) GetProtonvpn() (servers []ProtonvpnServer) {
if a.Protonvpn.Servers == nil {
return nil
}
servers = make([]ProtonvpnServer, len(a.Protonvpn.Servers))
for i, serverToCopy := range a.Protonvpn.Servers {
servers[i] = serverToCopy
servers[i].EntryIP = copyIP(serverToCopy.EntryIP)
servers[i].ExitIP = copyIP(serverToCopy.ExitIP)
}
return servers
}
func (a *AllServers) GetPurevpn() (servers []PurevpnServer) {
if a.Purevpn.Servers == nil {
return nil
}
servers = make([]PurevpnServer, len(a.Purevpn.Servers))
for i, serverToCopy := range a.Purevpn.Servers {
servers[i] = serverToCopy
servers[i].IPs = copyIPs(serverToCopy.IPs)
}
return servers
}
func (a *AllServers) GetSurfshark() (servers []SurfsharkServer) {
if a.Surfshark.Servers == nil {
return nil
}
servers = make([]SurfsharkServer, len(a.Surfshark.Servers))
for i, serverToCopy := range a.Surfshark.Servers {
servers[i] = serverToCopy
servers[i].IPs = copyIPs(serverToCopy.IPs)
}
return servers
}
func (a *AllServers) GetTorguard() (servers []TorguardServer) {
if a.Torguard.Servers == nil {
return nil
}
servers = make([]TorguardServer, len(a.Torguard.Servers))
for i, serverToCopy := range a.Torguard.Servers {
servers[i] = serverToCopy
servers[i].IPs = copyIPs(serverToCopy.IPs)
}
return servers
}
func (a *AllServers) GetVPNUnlimited() (servers []VPNUnlimitedServer) {
if a.VPNUnlimited.Servers == nil {
return nil
}
servers = make([]VPNUnlimitedServer, len(a.VPNUnlimited.Servers))
for i, serverToCopy := range a.VPNUnlimited.Servers {
servers[i] = serverToCopy
servers[i].IPs = copyIPs(serverToCopy.IPs)
}
return servers
}
func (a *AllServers) GetVyprvpn() (servers []VyprvpnServer) {
if a.Vyprvpn.Servers == nil {
return nil
}
servers = make([]VyprvpnServer, len(a.Vyprvpn.Servers))
for i, serverToCopy := range a.Vyprvpn.Servers {
servers[i] = serverToCopy
servers[i].IPs = copyIPs(serverToCopy.IPs)
}
return servers
}
func (a *AllServers) GetWindscribe() (servers []WindscribeServer) {
if a.Windscribe.Servers == nil {
return nil
}
servers = make([]WindscribeServer, len(a.Windscribe.Servers))
for i, serverToCopy := range a.Windscribe.Servers {
servers[i] = serverToCopy
servers[i].IPs = copyIPs(serverToCopy.IPs)
}
return servers
}
func copyIPs(toCopy []net.IP) (copied []net.IP) {
if toCopy == nil {
return nil
}
copied = make([]net.IP, len(toCopy))
for i := range toCopy {
copied[i] = copyIP(toCopy[i])
}
return copied
}
func copyIP(toCopy net.IP) (copied net.IP) {
copied = make(net.IP, len(toCopy))
copy(copied, toCopy)
return copied
}

View File

@@ -0,0 +1,181 @@
package models
import (
"net"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_AllServers_GetCopy(t *testing.T) {
allServers := AllServers{
Cyberghost: CyberghostServers{
Version: 2,
Servers: []CyberghostServer{{
IPs: []net.IP{{1, 2, 3, 4}},
}},
},
Fastestvpn: FastestvpnServers{
Servers: []FastestvpnServer{{
IPs: []net.IP{{1, 2, 3, 4}},
}},
},
HideMyAss: HideMyAssServers{
Servers: []HideMyAssServer{{
IPs: []net.IP{{1, 2, 3, 4}},
}},
},
Ipvanish: IpvanishServers{
Servers: []IpvanishServer{{
IPs: []net.IP{{1, 2, 3, 4}},
}},
},
Ivpn: IvpnServers{
Servers: []IvpnServer{{
IPs: []net.IP{{1, 2, 3, 4}},
}},
},
Mullvad: MullvadServers{
Servers: []MullvadServer{{
IPs: []net.IP{{1, 2, 3, 4}},
}},
},
Nordvpn: NordvpnServers{
Servers: []NordvpnServer{{
IP: net.IP{1, 2, 3, 4},
}},
},
Privado: PrivadoServers{
Servers: []PrivadoServer{{
IP: net.IP{1, 2, 3, 4},
}},
},
Pia: PiaServers{
Servers: []PIAServer{{
IPs: []net.IP{{1, 2, 3, 4}},
}},
},
Privatevpn: PrivatevpnServers{
Servers: []PrivatevpnServer{{
IPs: []net.IP{{1, 2, 3, 4}},
}},
},
Protonvpn: ProtonvpnServers{
Servers: []ProtonvpnServer{{
EntryIP: net.IP{1, 2, 3, 4},
ExitIP: net.IP{1, 2, 3, 4},
}},
},
Purevpn: PurevpnServers{
Version: 1,
Servers: []PurevpnServer{{
IPs: []net.IP{{1, 2, 3, 4}},
}},
},
Surfshark: SurfsharkServers{
Servers: []SurfsharkServer{{
IPs: []net.IP{{1, 2, 3, 4}},
}},
},
Torguard: TorguardServers{
Servers: []TorguardServer{{
IPs: []net.IP{{1, 2, 3, 4}},
}},
},
VPNUnlimited: VPNUnlimitedServers{
Servers: []VPNUnlimitedServer{{
IPs: []net.IP{{1, 2, 3, 4}},
}},
},
Vyprvpn: VyprvpnServers{
Servers: []VyprvpnServer{{
IPs: []net.IP{{1, 2, 3, 4}},
}},
},
Windscribe: WindscribeServers{
Servers: []WindscribeServer{{
IPs: []net.IP{{1, 2, 3, 4}},
}},
},
}
servers := allServers.GetCopy()
assert.Equal(t, allServers, servers)
}
func Test_AllServers_GetVyprvpn(t *testing.T) {
allServers := AllServers{
Vyprvpn: VyprvpnServers{
Servers: []VyprvpnServer{
{Hostname: "a", IPs: []net.IP{{1, 1, 1, 1}}},
{Hostname: "b", IPs: []net.IP{{2, 2, 2, 2}}},
},
},
}
servers := allServers.GetVyprvpn()
expectedServers := []VyprvpnServer{
{Hostname: "a", IPs: []net.IP{{1, 1, 1, 1}}},
{Hostname: "b", IPs: []net.IP{{2, 2, 2, 2}}},
}
assert.Equal(t, expectedServers, servers)
allServers.Vyprvpn.Servers[0].IPs[0][0] = 9
assert.NotEqual(t, 9, servers[0].IPs[0][0])
allServers.Vyprvpn.Servers[0].IPs[0][0] = 1
servers[0].IPs[0][0] = 9
assert.NotEqual(t, 9, allServers.Vyprvpn.Servers[0].IPs[0][0])
}
func Test_copyIPs(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
toCopy []net.IP
copied []net.IP
}{
"nil": {},
"empty": {
toCopy: []net.IP{},
copied: []net.IP{},
},
"single IP": {
toCopy: []net.IP{{1, 1, 1, 1}},
copied: []net.IP{{1, 1, 1, 1}},
},
"two IPs": {
toCopy: []net.IP{{1, 1, 1, 1}, {2, 2, 2, 2}},
copied: []net.IP{{1, 1, 1, 1}, {2, 2, 2, 2}},
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
// Reserver leading 9 for copy modifications below
for _, ipToCopy := range testCase.toCopy {
require.NotEqual(t, 9, ipToCopy[0])
}
copied := copyIPs(testCase.toCopy)
assert.Equal(t, testCase.copied, copied)
if len(copied) > 0 {
original := testCase.toCopy[0][0]
testCase.toCopy[0][0] = 9
assert.NotEqual(t, 9, copied[0][0])
testCase.toCopy[0][0] = original
copied[0][0] = 9
assert.NotEqual(t, 9, testCase.toCopy[0][0])
}
})
}
}

View File

@@ -0,0 +1,12 @@
package models
// SurfsharkLocationData is required to keep location data on Surfshark
// servers that are not obtained through their API.
type SurfsharkLocationData struct {
Region string
Country string
City string
RetroLoc string // TODO remove in v4
Hostname string
MultiHop bool
}

View File

@@ -39,9 +39,12 @@ type IpvanishServer struct {
} }
type IvpnServer struct { type IvpnServer struct {
VPN string `json:"vpn"`
Country string `json:"country"` Country string `json:"country"`
City string `json:"city"` City string `json:"city"`
ISP string `json:"isp"`
Hostname string `json:"hostname"` Hostname string `json:"hostname"`
WgPubKey string `json:"wgpubkey,omitempty"`
TCP bool `json:"tcp"` TCP bool `json:"tcp"`
UDP bool `json:"udp"` UDP bool `json:"udp"`
IPs []net.IP `json:"ips"` IPs []net.IP `json:"ips"`
@@ -116,7 +119,11 @@ type PurevpnServer struct {
type SurfsharkServer struct { type SurfsharkServer struct {
Region string `json:"region"` Region string `json:"region"`
Country string `json:"country"` // Country is also used for multi-hop
City string `json:"city"`
RetroLoc string `json:"retroloc"` // TODO remove in v4
Hostname string `json:"hostname"` Hostname string `json:"hostname"`
MultiHop bool `json:"multihop"`
TCP bool `json:"tcp"` TCP bool `json:"tcp"`
UDP bool `json:"udp"` UDP bool `json:"udp"`
IPs []net.IP `json:"ips"` IPs []net.IP `json:"ips"`

View File

@@ -2,6 +2,21 @@ package netlink
import "github.com/vishvananda/netlink" import "github.com/vishvananda/netlink"
type Addr = netlink.Addr
var _ Addresser = (*NetLink)(nil)
type Addresser interface {
AddrList(link netlink.Link, family int) (
addresses []netlink.Addr, err error)
AddrAdd(link netlink.Link, addr *netlink.Addr) error
}
func (n *NetLink) AddrList(link netlink.Link, family int) (
addresses []netlink.Addr, err error) {
return netlink.AddrList(link, family)
}
func (n *NetLink) AddrAdd(link netlink.Link, addr *netlink.Addr) error { func (n *NetLink) AddrAdd(link netlink.Link, addr *netlink.Addr) error {
return netlink.AddrAdd(link, addr) return netlink.AddrAdd(link, addr)
} }

View File

@@ -0,0 +1,9 @@
package netlink
import "github.com/vishvananda/netlink"
//nolint:revive
const (
FAMILY_ALL = netlink.FAMILY_ALL
FAMILY_V4 = netlink.FAMILY_V4
)

View File

@@ -1,14 +1,12 @@
package netlink package netlink
import "github.com/vishvananda/netlink"
//go:generate mockgen -destination=mock_$GOPACKAGE/$GOFILE . NetLinker //go:generate mockgen -destination=mock_$GOPACKAGE/$GOFILE . NetLinker
var _ NetLinker = (*NetLink)(nil) var _ NetLinker = (*NetLink)(nil)
type NetLinker interface { type NetLinker interface {
AddrAdd(link netlink.Link, addr *netlink.Addr) error Addresser
RouteAdd(route *netlink.Route) error Linker
RuleAdd(rule *netlink.Rule) error Router
RuleDel(rule *netlink.Rule) error Ruler
} }

11
internal/netlink/ipnet.go Normal file
View File

@@ -0,0 +1,11 @@
package netlink
import (
"net"
"github.com/vishvananda/netlink"
)
func NewIPNet(ip net.IP) *net.IPNet {
return netlink.NewIPNet(ip)
}

48
internal/netlink/link.go Normal file
View File

@@ -0,0 +1,48 @@
package netlink
import "github.com/vishvananda/netlink"
type (
Link = netlink.Link
Bridge = netlink.Bridge
)
var _ Linker = (*NetLink)(nil)
type Linker interface {
LinkList() (links []netlink.Link, err error)
LinkByName(name string) (link netlink.Link, err error)
LinkByIndex(index int) (link netlink.Link, err error)
LinkAdd(link netlink.Link) (err error)
LinkDel(link netlink.Link) (err error)
LinkSetUp(link netlink.Link) (err error)
LinkSetDown(link netlink.Link) (err error)
}
func (n *NetLink) LinkList() (links []netlink.Link, err error) {
return netlink.LinkList()
}
func (n *NetLink) LinkByName(name string) (link netlink.Link, err error) {
return netlink.LinkByName(name)
}
func (n *NetLink) LinkByIndex(index int) (link netlink.Link, err error) {
return netlink.LinkByIndex(index)
}
func (n *NetLink) LinkAdd(link netlink.Link) (err error) {
return netlink.LinkAdd(link)
}
func (n *NetLink) LinkDel(link netlink.Link) (err error) {
return netlink.LinkDel(link)
}
func (n *NetLink) LinkSetUp(link netlink.Link) (err error) {
return netlink.LinkSetUp(link)
}
func (n *NetLink) LinkSetDown(link netlink.Link) (err error) {
return netlink.LinkSetDown(link)
}

View File

@@ -0,0 +1,9 @@
package netlink
import "github.com/vishvananda/netlink"
type LinkAttrs = netlink.LinkAttrs
func NewLinkAttrs() LinkAttrs {
return netlink.NewLinkAttrs()
}

View File

@@ -0,0 +1,265 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/qdm12/gluetun/internal/netlink (interfaces: NetLinker)
// Package mock_netlink is a generated GoMock package.
package mock_netlink
import (
reflect "reflect"
gomock "github.com/golang/mock/gomock"
netlink "github.com/vishvananda/netlink"
)
// MockNetLinker is a mock of NetLinker interface.
type MockNetLinker struct {
ctrl *gomock.Controller
recorder *MockNetLinkerMockRecorder
}
// MockNetLinkerMockRecorder is the mock recorder for MockNetLinker.
type MockNetLinkerMockRecorder struct {
mock *MockNetLinker
}
// NewMockNetLinker creates a new mock instance.
func NewMockNetLinker(ctrl *gomock.Controller) *MockNetLinker {
mock := &MockNetLinker{ctrl: ctrl}
mock.recorder = &MockNetLinkerMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockNetLinker) EXPECT() *MockNetLinkerMockRecorder {
return m.recorder
}
// AddrAdd mocks base method.
func (m *MockNetLinker) AddrAdd(arg0 netlink.Link, arg1 *netlink.Addr) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "AddrAdd", arg0, arg1)
ret0, _ := ret[0].(error)
return ret0
}
// AddrAdd indicates an expected call of AddrAdd.
func (mr *MockNetLinkerMockRecorder) AddrAdd(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddrAdd", reflect.TypeOf((*MockNetLinker)(nil).AddrAdd), arg0, arg1)
}
// AddrList mocks base method.
func (m *MockNetLinker) AddrList(arg0 netlink.Link, arg1 int) ([]netlink.Addr, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "AddrList", arg0, arg1)
ret0, _ := ret[0].([]netlink.Addr)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// AddrList indicates an expected call of AddrList.
func (mr *MockNetLinkerMockRecorder) AddrList(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddrList", reflect.TypeOf((*MockNetLinker)(nil).AddrList), arg0, arg1)
}
// LinkAdd mocks base method.
func (m *MockNetLinker) LinkAdd(arg0 netlink.Link) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "LinkAdd", arg0)
ret0, _ := ret[0].(error)
return ret0
}
// LinkAdd indicates an expected call of LinkAdd.
func (mr *MockNetLinkerMockRecorder) LinkAdd(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkAdd", reflect.TypeOf((*MockNetLinker)(nil).LinkAdd), arg0)
}
// LinkByIndex mocks base method.
func (m *MockNetLinker) LinkByIndex(arg0 int) (netlink.Link, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "LinkByIndex", arg0)
ret0, _ := ret[0].(netlink.Link)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// LinkByIndex indicates an expected call of LinkByIndex.
func (mr *MockNetLinkerMockRecorder) LinkByIndex(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkByIndex", reflect.TypeOf((*MockNetLinker)(nil).LinkByIndex), arg0)
}
// LinkByName mocks base method.
func (m *MockNetLinker) LinkByName(arg0 string) (netlink.Link, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "LinkByName", arg0)
ret0, _ := ret[0].(netlink.Link)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// LinkByName indicates an expected call of LinkByName.
func (mr *MockNetLinkerMockRecorder) LinkByName(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkByName", reflect.TypeOf((*MockNetLinker)(nil).LinkByName), arg0)
}
// LinkDel mocks base method.
func (m *MockNetLinker) LinkDel(arg0 netlink.Link) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "LinkDel", arg0)
ret0, _ := ret[0].(error)
return ret0
}
// LinkDel indicates an expected call of LinkDel.
func (mr *MockNetLinkerMockRecorder) LinkDel(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkDel", reflect.TypeOf((*MockNetLinker)(nil).LinkDel), arg0)
}
// LinkList mocks base method.
func (m *MockNetLinker) LinkList() ([]netlink.Link, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "LinkList")
ret0, _ := ret[0].([]netlink.Link)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// LinkList indicates an expected call of LinkList.
func (mr *MockNetLinkerMockRecorder) LinkList() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkList", reflect.TypeOf((*MockNetLinker)(nil).LinkList))
}
// LinkSetDown mocks base method.
func (m *MockNetLinker) LinkSetDown(arg0 netlink.Link) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "LinkSetDown", arg0)
ret0, _ := ret[0].(error)
return ret0
}
// LinkSetDown indicates an expected call of LinkSetDown.
func (mr *MockNetLinkerMockRecorder) LinkSetDown(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkSetDown", reflect.TypeOf((*MockNetLinker)(nil).LinkSetDown), arg0)
}
// LinkSetUp mocks base method.
func (m *MockNetLinker) LinkSetUp(arg0 netlink.Link) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "LinkSetUp", arg0)
ret0, _ := ret[0].(error)
return ret0
}
// LinkSetUp indicates an expected call of LinkSetUp.
func (mr *MockNetLinkerMockRecorder) LinkSetUp(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LinkSetUp", reflect.TypeOf((*MockNetLinker)(nil).LinkSetUp), arg0)
}
// RouteAdd mocks base method.
func (m *MockNetLinker) RouteAdd(arg0 *netlink.Route) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RouteAdd", arg0)
ret0, _ := ret[0].(error)
return ret0
}
// RouteAdd indicates an expected call of RouteAdd.
func (mr *MockNetLinkerMockRecorder) RouteAdd(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RouteAdd", reflect.TypeOf((*MockNetLinker)(nil).RouteAdd), arg0)
}
// RouteDel mocks base method.
func (m *MockNetLinker) RouteDel(arg0 *netlink.Route) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RouteDel", arg0)
ret0, _ := ret[0].(error)
return ret0
}
// RouteDel indicates an expected call of RouteDel.
func (mr *MockNetLinkerMockRecorder) RouteDel(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RouteDel", reflect.TypeOf((*MockNetLinker)(nil).RouteDel), arg0)
}
// RouteList mocks base method.
func (m *MockNetLinker) RouteList(arg0 netlink.Link, arg1 int) ([]netlink.Route, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RouteList", arg0, arg1)
ret0, _ := ret[0].([]netlink.Route)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// RouteList indicates an expected call of RouteList.
func (mr *MockNetLinkerMockRecorder) RouteList(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RouteList", reflect.TypeOf((*MockNetLinker)(nil).RouteList), arg0, arg1)
}
// RouteReplace mocks base method.
func (m *MockNetLinker) RouteReplace(arg0 *netlink.Route) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RouteReplace", arg0)
ret0, _ := ret[0].(error)
return ret0
}
// RouteReplace indicates an expected call of RouteReplace.
func (mr *MockNetLinkerMockRecorder) RouteReplace(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RouteReplace", reflect.TypeOf((*MockNetLinker)(nil).RouteReplace), arg0)
}
// RuleAdd mocks base method.
func (m *MockNetLinker) RuleAdd(arg0 *netlink.Rule) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RuleAdd", arg0)
ret0, _ := ret[0].(error)
return ret0
}
// RuleAdd indicates an expected call of RuleAdd.
func (mr *MockNetLinkerMockRecorder) RuleAdd(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RuleAdd", reflect.TypeOf((*MockNetLinker)(nil).RuleAdd), arg0)
}
// RuleDel mocks base method.
func (m *MockNetLinker) RuleDel(arg0 *netlink.Rule) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RuleDel", arg0)
ret0, _ := ret[0].(error)
return ret0
}
// RuleDel indicates an expected call of RuleDel.
func (mr *MockNetLinkerMockRecorder) RuleDel(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RuleDel", reflect.TypeOf((*MockNetLinker)(nil).RuleDel), arg0)
}
// RuleList mocks base method.
func (m *MockNetLinker) RuleList(arg0 int) ([]netlink.Rule, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RuleList", arg0)
ret0, _ := ret[0].([]netlink.Rule)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// RuleList indicates an expected call of RuleList.
func (mr *MockNetLinkerMockRecorder) RuleList(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RuleList", reflect.TypeOf((*MockNetLinker)(nil).RuleList), arg0)
}

View File

@@ -2,6 +2,31 @@ package netlink
import "github.com/vishvananda/netlink" import "github.com/vishvananda/netlink"
type Route = netlink.Route
var _ Router = (*NetLink)(nil)
type Router interface {
RouteList(link netlink.Link, family int) (
routes []netlink.Route, err error)
RouteAdd(route *netlink.Route) error
RouteDel(route *netlink.Route) error
RouteReplace(route *netlink.Route) error
}
func (n *NetLink) RouteList(link netlink.Link, family int) (
routes []netlink.Route, err error) {
return netlink.RouteList(link, family)
}
func (n *NetLink) RouteAdd(route *netlink.Route) error { func (n *NetLink) RouteAdd(route *netlink.Route) error {
return netlink.RouteAdd(route) return netlink.RouteAdd(route)
} }
func (n *NetLink) RouteDel(route *netlink.Route) error {
return netlink.RouteDel(route)
}
func (n *NetLink) RouteReplace(route *netlink.Route) error {
return netlink.RouteReplace(route)
}

View File

@@ -2,6 +2,24 @@ package netlink
import "github.com/vishvananda/netlink" import "github.com/vishvananda/netlink"
type Rule = netlink.Rule
func NewRule() *Rule {
return netlink.NewRule()
}
var _ Ruler = (*NetLink)(nil)
type Ruler interface {
RuleList(family int) (rules []netlink.Rule, err error)
RuleAdd(rule *netlink.Rule) error
RuleDel(rule *netlink.Rule) error
}
func (n *NetLink) RuleList(family int) (rules []netlink.Rule, err error) {
return netlink.RuleList(family)
}
func (n *NetLink) RuleAdd(rule *netlink.Rule) error { func (n *NetLink) RuleAdd(rule *netlink.Rule) error {
return netlink.RuleAdd(rule) return netlink.RuleAdd(rule)
} }

View File

@@ -33,9 +33,5 @@ func (c *Cyberghost) GetConnection(selection configuration.ServerSelection) (
} }
} }
if selection.TargetIP != nil { return utils.PickConnection(connections, selection, c.randSource)
return utils.GetTargetIPConnection(connections, selection.TargetIP)
}
return utils.PickRandomConnection(connections, c.randSource), nil
} }

View File

@@ -17,9 +17,9 @@ func (c *Cyberghost) filterServers(selection configuration.ServerSelection) (
servers []models.CyberghostServer, err error) { servers []models.CyberghostServer, err error) {
if len(selection.Groups) == 0 { if len(selection.Groups) == 0 {
if selection.OpenVPN.TCP { if selection.OpenVPN.TCP {
selection.Groups = tcpGroupChoices() selection.Groups = tcpGroupChoices(c.servers)
} else { } else {
selection.Groups = udpGroupChoices() selection.Groups = udpGroupChoices(c.servers)
} }
} }
@@ -50,18 +50,18 @@ func (c *Cyberghost) filterServers(selection configuration.ServerSelection) (
return servers, nil return servers, nil
} }
func tcpGroupChoices() (choices []string) { func tcpGroupChoices(servers []models.CyberghostServer) (choices []string) {
const tcp = true const tcp = true
return groupsForTCP(tcp) return groupsForTCP(servers, tcp)
} }
func udpGroupChoices() (choices []string) { func udpGroupChoices(servers []models.CyberghostServer) (choices []string) {
const tcp = false const tcp = false
return groupsForTCP(tcp) return groupsForTCP(servers, tcp)
} }
func groupsForTCP(tcp bool) (choices []string) { func groupsForTCP(servers []models.CyberghostServer, tcp bool) (choices []string) {
allGroups := constants.CyberghostGroupChoices() allGroups := constants.CyberghostGroupChoices(servers)
choices = make([]string, 0, len(allGroups)) choices = make([]string, 0, len(allGroups))
for _, group := range allGroups { for _, group := range allGroups {
switch { switch {

View File

@@ -19,7 +19,7 @@ func Test_Cyberghost_filterServers(t *testing.T) {
filteredServers []models.CyberghostServer filteredServers []models.CyberghostServer
err error err error
}{ }{
"no servers": { "no server": {
selection: configuration.ServerSelection{VPN: constants.OpenVPN}, selection: configuration.ServerSelection{VPN: constants.OpenVPN},
err: errors.New("no server found: for VPN openvpn; protocol udp"), err: errors.New("no server found: for VPN openvpn; protocol udp"),
}, },
@@ -146,10 +146,18 @@ func Test_Cyberghost_filterServers(t *testing.T) {
func Test_tcpGroupChoices(t *testing.T) { func Test_tcpGroupChoices(t *testing.T) {
t.Parallel() t.Parallel()
servers := []models.CyberghostServer{
{Group: "Premium TCP Asia"},
{Group: "Premium TCP Europe"},
{Group: "Premium TCP USA"},
{Group: "Premium UDP Asia"},
{Group: "Premium UDP Europe"},
{Group: "Premium UDP USA"},
}
expected := []string{ expected := []string{
"Premium TCP Asia", "Premium TCP Europe", "Premium TCP USA", "Premium TCP Asia", "Premium TCP Europe", "Premium TCP USA",
} }
choices := tcpGroupChoices() choices := tcpGroupChoices(servers)
assert.Equal(t, expected, choices) assert.Equal(t, expected, choices)
} }
@@ -157,10 +165,18 @@ func Test_tcpGroupChoices(t *testing.T) {
func Test_udpGroupChoices(t *testing.T) { func Test_udpGroupChoices(t *testing.T) {
t.Parallel() t.Parallel()
servers := []models.CyberghostServer{
{Group: "Premium TCP Asia"},
{Group: "Premium TCP Europe"},
{Group: "Premium TCP USA"},
{Group: "Premium UDP Asia"},
{Group: "Premium UDP Europe"},
{Group: "Premium UDP USA"},
}
expected := []string{ expected := []string{
"Premium UDP Asia", "Premium UDP Europe", "Premium UDP USA", "Premium UDP Asia", "Premium UDP Europe", "Premium UDP USA",
} }
choices := udpGroupChoices() choices := udpGroupChoices(servers)
assert.Equal(t, expected, choices) assert.Equal(t, expected, choices)
} }

View File

@@ -33,9 +33,5 @@ func (f *Fastestvpn) GetConnection(selection configuration.ServerSelection) (
} }
} }
if selection.TargetIP != nil { return utils.PickConnection(connections, selection, f.randSource)
return utils.GetTargetIPConnection(connections, selection.TargetIP)
}
return utils.PickRandomConnection(connections, f.randSource), nil
} }

View File

@@ -13,8 +13,7 @@ func (f *Fastestvpn) filterServers(selection configuration.ServerSelection) (
case case
utils.FilterByPossibilities(server.Country, selection.Countries), utils.FilterByPossibilities(server.Country, selection.Countries),
utils.FilterByPossibilities(server.Hostname, selection.Hostnames), utils.FilterByPossibilities(server.Hostname, selection.Hostnames),
selection.OpenVPN.TCP && !server.TCP, utils.FilterByProtocol(selection, server.TCP, server.UDP):
!selection.OpenVPN.TCP && !server.UDP:
default: default:
servers = append(servers, server) servers = append(servers, server)
} }

View File

@@ -38,9 +38,5 @@ func (h *HideMyAss) GetConnection(selection configuration.ServerSelection) (
} }
} }
if selection.TargetIP != nil { return utils.PickConnection(connections, selection, h.randSource)
return utils.GetTargetIPConnection(connections, selection.TargetIP)
}
return utils.PickRandomConnection(connections, h.randSource), nil
} }

View File

@@ -14,8 +14,7 @@ func (h *HideMyAss) filterServers(selection configuration.ServerSelection) (
utils.FilterByPossibilities(server.Country, selection.Countries), utils.FilterByPossibilities(server.Country, selection.Countries),
utils.FilterByPossibilities(server.City, selection.Cities), utils.FilterByPossibilities(server.City, selection.Cities),
utils.FilterByPossibilities(server.Hostname, selection.Hostnames), utils.FilterByPossibilities(server.Hostname, selection.Hostnames),
selection.OpenVPN.TCP && !server.TCP, utils.FilterByProtocol(selection, server.TCP, server.UDP):
!selection.OpenVPN.TCP && !server.UDP:
default: default:
servers = append(servers, server) servers = append(servers, server)
} }

View File

@@ -38,9 +38,5 @@ func (i *Ipvanish) GetConnection(selection configuration.ServerSelection) (
} }
} }
if selection.TargetIP != nil { return utils.PickConnection(connections, selection, i.randSource)
return utils.GetTargetIPConnection(connections, selection.TargetIP)
}
return utils.PickRandomConnection(connections, i.randSource), nil
} }

View File

@@ -14,8 +14,7 @@ func (i *Ipvanish) filterServers(selection configuration.ServerSelection) (
utils.FilterByPossibilities(server.Country, selection.Countries), utils.FilterByPossibilities(server.Country, selection.Countries),
utils.FilterByPossibilities(server.City, selection.Cities), utils.FilterByPossibilities(server.City, selection.Cities),
utils.FilterByPossibilities(server.Hostname, selection.Hostnames), utils.FilterByPossibilities(server.Hostname, selection.Hostnames),
selection.OpenVPN.TCP && !server.TCP, utils.FilterByProtocol(selection, server.TCP, server.UDP):
!selection.OpenVPN.TCP && !server.UDP:
default: default:
servers = append(servers, server) servers = append(servers, server)
} }

View File

@@ -1,23 +1,15 @@
package ivpn package ivpn
import ( import (
"errors"
"github.com/qdm12/gluetun/internal/configuration" "github.com/qdm12/gluetun/internal/configuration"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/provider/utils" "github.com/qdm12/gluetun/internal/provider/utils"
) )
var ErrProtocolUnsupported = errors.New("network protocol is not supported")
func (i *Ivpn) GetConnection(selection configuration.ServerSelection) ( func (i *Ivpn) GetConnection(selection configuration.ServerSelection) (
connection models.Connection, err error) { connection models.Connection, err error) {
const port = 2049 port := getPort(selection)
const protocol = constants.UDP protocol := utils.GetProtocol(selection)
if selection.OpenVPN.TCP {
return connection, ErrProtocolUnsupported
}
servers, err := i.filterServers(selection) servers, err := i.filterServers(selection)
if err != nil { if err != nil {
@@ -33,14 +25,21 @@ func (i *Ivpn) GetConnection(selection configuration.ServerSelection) (
Port: port, Port: port,
Protocol: protocol, Protocol: protocol,
Hostname: server.Hostname, Hostname: server.Hostname,
PubKey: server.WgPubKey, // Wireguard only
} }
connections = append(connections, connection) connections = append(connections, connection)
} }
} }
if selection.TargetIP != nil { return utils.PickConnection(connections, selection, i.randSource)
return utils.GetTargetIPConnection(connections, selection.TargetIP) }
}
func getPort(selection configuration.ServerSelection) (port uint16) {
return utils.PickRandomConnection(connections, i.randSource), nil const (
defaultOpenVPNTCP = 443
defaultOpenVPNUDP = 1194
defaultWireguard = 58237
)
return utils.GetPort(selection, defaultOpenVPNTCP,
defaultOpenVPNUDP, defaultWireguard)
} }

View File

@@ -0,0 +1,97 @@
package ivpn
import (
"errors"
"math/rand"
"net"
"testing"
"github.com/qdm12/gluetun/internal/configuration"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/models"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_Ivpn_GetConnection(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
servers []models.IvpnServer
selection configuration.ServerSelection
connection models.Connection
err error
}{
"no server available": {
selection: configuration.ServerSelection{
VPN: constants.OpenVPN,
},
err: errors.New("no server found: for VPN openvpn; protocol udp"),
},
"no filter": {
servers: []models.IvpnServer{
{IPs: []net.IP{net.IPv4(1, 1, 1, 1)}, UDP: true},
{IPs: []net.IP{net.IPv4(2, 2, 2, 2)}, UDP: true},
{IPs: []net.IP{net.IPv4(3, 3, 3, 3)}, UDP: true},
},
connection: models.Connection{
IP: net.IPv4(1, 1, 1, 1),
Port: 1194,
Protocol: constants.UDP,
},
},
"target IP": {
selection: configuration.ServerSelection{
TargetIP: net.IPv4(2, 2, 2, 2),
},
servers: []models.IvpnServer{
{IPs: []net.IP{net.IPv4(1, 1, 1, 1)}, UDP: true},
{IPs: []net.IP{net.IPv4(2, 2, 2, 2)}, UDP: true},
{IPs: []net.IP{net.IPv4(3, 3, 3, 3)}, UDP: true},
},
connection: models.Connection{
IP: net.IPv4(2, 2, 2, 2),
Port: 1194,
Protocol: constants.UDP,
},
},
"with filter": {
selection: configuration.ServerSelection{
Hostnames: []string{"b"},
},
servers: []models.IvpnServer{
{Hostname: "a", IPs: []net.IP{net.IPv4(1, 1, 1, 1)}, UDP: true},
{Hostname: "b", IPs: []net.IP{net.IPv4(2, 2, 2, 2)}, UDP: true},
{Hostname: "a", IPs: []net.IP{net.IPv4(3, 3, 3, 3)}, UDP: true},
},
connection: models.Connection{
IP: net.IPv4(2, 2, 2, 2),
Port: 1194,
Protocol: constants.UDP,
Hostname: "b",
},
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
randSource := rand.NewSource(0)
m := New(testCase.servers, randSource)
connection, err := m.GetConnection(testCase.selection)
if testCase.err != nil {
require.Error(t, err)
assert.Equal(t, testCase.err.Error(), err.Error())
} else {
assert.NoError(t, err)
}
assert.Equal(t, testCase.connection, connection)
})
}
}

View File

@@ -11,11 +11,12 @@ func (i *Ivpn) filterServers(selection configuration.ServerSelection) (
for _, server := range i.servers { for _, server := range i.servers {
switch { switch {
case case
server.VPN != selection.VPN,
utils.FilterByPossibilities(server.ISP, selection.ISPs),
utils.FilterByPossibilities(server.Country, selection.Countries), utils.FilterByPossibilities(server.Country, selection.Countries),
utils.FilterByPossibilities(server.City, selection.Cities), utils.FilterByPossibilities(server.City, selection.Cities),
utils.FilterByPossibilities(server.Hostname, selection.Hostnames), utils.FilterByPossibilities(server.Hostname, selection.Hostnames),
selection.OpenVPN.TCP && !server.TCP, utils.FilterByProtocol(selection, server.TCP, server.UDP):
!selection.OpenVPN.TCP && !server.UDP:
default: default:
servers = append(servers, server) servers = append(servers, server)
} }

View File

@@ -0,0 +1,132 @@
package ivpn
import (
"errors"
"math/rand"
"testing"
"github.com/qdm12/gluetun/internal/configuration"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/models"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_Ivpn_filterServers(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
servers []models.IvpnServer
selection configuration.ServerSelection
filtered []models.IvpnServer
err error
}{
"no server available": {
selection: configuration.ServerSelection{
VPN: constants.OpenVPN,
},
err: errors.New("no server found: for VPN openvpn; protocol udp"),
},
"no filter": {
servers: []models.IvpnServer{
{Hostname: "a", UDP: true},
{Hostname: "b", UDP: true},
{Hostname: "c", UDP: true},
},
filtered: []models.IvpnServer{
{Hostname: "a", UDP: true},
{Hostname: "b", UDP: true},
{Hostname: "c", UDP: true},
},
},
"filter by country": {
selection: configuration.ServerSelection{
Countries: []string{"b"},
},
servers: []models.IvpnServer{
{Country: "a", UDP: true},
{Country: "b", UDP: true},
{Country: "c", UDP: true},
},
filtered: []models.IvpnServer{
{Country: "b", UDP: true},
},
},
"filter by city": {
selection: configuration.ServerSelection{
Cities: []string{"b"},
},
servers: []models.IvpnServer{
{City: "a", UDP: true},
{City: "b", UDP: true},
{City: "c", UDP: true},
},
filtered: []models.IvpnServer{
{City: "b", UDP: true},
},
},
"filter by ISP": {
selection: configuration.ServerSelection{
ISPs: []string{"b"},
},
servers: []models.IvpnServer{
{ISP: "a", UDP: true},
{ISP: "b", UDP: true},
{ISP: "c", UDP: true},
},
filtered: []models.IvpnServer{
{ISP: "b", UDP: true},
},
},
"filter by hostname": {
selection: configuration.ServerSelection{
Hostnames: []string{"b"},
},
servers: []models.IvpnServer{
{Hostname: "a", UDP: true},
{Hostname: "b", UDP: true},
{Hostname: "c", UDP: true},
},
filtered: []models.IvpnServer{
{Hostname: "b", UDP: true},
},
},
"filter by protocol": {
selection: configuration.ServerSelection{
OpenVPN: configuration.OpenVPNSelection{
TCP: true,
},
},
servers: []models.IvpnServer{
{Hostname: "a", UDP: true},
{Hostname: "b", UDP: true, TCP: true},
{Hostname: "c", UDP: true},
},
filtered: []models.IvpnServer{
{Hostname: "b", UDP: true, TCP: true},
},
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
randSource := rand.NewSource(0)
m := New(testCase.servers, randSource)
servers, err := m.filterServers(testCase.selection)
if testCase.err != nil {
require.Error(t, err)
assert.Equal(t, testCase.err.Error(), err.Error())
} else {
assert.NoError(t, err)
}
assert.Equal(t, testCase.filtered, servers)
})
}
}

View File

@@ -2,7 +2,6 @@ package mullvad
import ( import (
"github.com/qdm12/gluetun/internal/configuration" "github.com/qdm12/gluetun/internal/configuration"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/provider/utils" "github.com/qdm12/gluetun/internal/provider/utils"
) )
@@ -10,7 +9,7 @@ import (
func (m *Mullvad) GetConnection(selection configuration.ServerSelection) ( func (m *Mullvad) GetConnection(selection configuration.ServerSelection) (
connection models.Connection, err error) { connection models.Connection, err error) {
port := getPort(selection) port := getPort(selection)
protocol := getProtocol(selection) protocol := utils.GetProtocol(selection)
servers, err := m.filterServers(selection) servers, err := m.filterServers(selection)
if err != nil { if err != nil {
@@ -31,39 +30,15 @@ func (m *Mullvad) GetConnection(selection configuration.ServerSelection) (
} }
} }
if selection.TargetIP != nil { return utils.PickConnection(connections, selection, m.randSource)
return utils.GetTargetIPConnection(connections, selection.TargetIP)
}
return utils.PickRandomConnection(connections, m.randSource), nil
} }
func getPort(selection configuration.ServerSelection) (port uint16) { func getPort(selection configuration.ServerSelection) (port uint16) {
switch selection.VPN { const (
case constants.Wireguard: defaultOpenVPNTCP = 443
customPort := selection.Wireguard.CustomPort defaultOpenVPNUDP = 1194
if customPort > 0 { defaultWireguard = 51820
return customPort )
} return utils.GetPort(selection, defaultOpenVPNTCP,
const defaultPort = 51820 defaultOpenVPNUDP, defaultWireguard)
return defaultPort
default: // OpenVPN
customPort := selection.OpenVPN.CustomPort
if customPort > 0 {
return customPort
}
port = 1194
if selection.OpenVPN.TCP {
port = 443
}
return port
}
}
func getProtocol(selection configuration.ServerSelection) (protocol string) {
protocol = constants.UDP
if selection.VPN == constants.OpenVPN && selection.OpenVPN.TCP {
protocol = constants.TCP
}
return protocol
} }

View File

@@ -94,111 +94,3 @@ func Test_Mullvad_GetConnection(t *testing.T) {
}) })
} }
} }
func Test_getPort(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
selection configuration.ServerSelection
port uint16
}{
"default": {
port: 1194,
},
"OpenVPN UDP": {
selection: configuration.ServerSelection{
VPN: constants.OpenVPN,
},
port: 1194,
},
"OpenVPN TCP": {
selection: configuration.ServerSelection{
VPN: constants.OpenVPN,
OpenVPN: configuration.OpenVPNSelection{
TCP: true,
},
},
port: 443,
},
"OpenVPN custom port": {
selection: configuration.ServerSelection{
VPN: constants.OpenVPN,
OpenVPN: configuration.OpenVPNSelection{
CustomPort: 1234,
},
},
port: 1234,
},
"Wireguard": {
selection: configuration.ServerSelection{
VPN: constants.Wireguard,
},
port: 51820,
},
"Wireguard custom port": {
selection: configuration.ServerSelection{
VPN: constants.Wireguard,
Wireguard: configuration.WireguardSelection{
CustomPort: 1234,
},
},
port: 1234,
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
port := getPort(testCase.selection)
assert.Equal(t, testCase.port, port)
})
}
}
func Test_getProtocol(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
selection configuration.ServerSelection
protocol string
}{
"default": {
protocol: constants.UDP,
},
"OpenVPN UDP": {
selection: configuration.ServerSelection{
VPN: constants.OpenVPN,
},
protocol: constants.UDP,
},
"OpenVPN TCP": {
selection: configuration.ServerSelection{
VPN: constants.OpenVPN,
OpenVPN: configuration.OpenVPNSelection{
TCP: true,
},
},
protocol: constants.TCP,
},
"Wireguard": {
selection: configuration.ServerSelection{
VPN: constants.Wireguard,
},
protocol: constants.UDP,
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
protocol := getProtocol(testCase.selection)
assert.Equal(t, testCase.protocol, protocol)
})
}
}

View File

@@ -32,9 +32,5 @@ func (n *Nordvpn) GetConnection(selection configuration.ServerSelection) (
connections[i] = connection connections[i] = connection
} }
if selection.TargetIP != nil { return utils.PickConnection(connections, selection, n.randSource)
return utils.GetTargetIPConnection(connections, selection.TargetIP)
}
return utils.PickRandomConnection(connections, n.randSource), nil
} }

View File

@@ -23,8 +23,7 @@ func (n *Nordvpn) filterServers(selection configuration.ServerSelection) (
utils.FilterByPossibilities(server.Hostname, selection.Hostnames), utils.FilterByPossibilities(server.Hostname, selection.Hostnames),
utils.FilterByPossibilities(server.Name, selection.Names), utils.FilterByPossibilities(server.Name, selection.Names),
utils.FilterByPossibilities(serverNumber, selectedNumbers), utils.FilterByPossibilities(serverNumber, selectedNumbers),
selection.OpenVPN.TCP && !server.TCP, utils.FilterByProtocol(selection, server.TCP, server.UDP):
!selection.OpenVPN.TCP && !server.UDP:
default: default:
servers = append(servers, server) servers = append(servers, server)
} }

View File

@@ -37,9 +37,5 @@ func (p *Privado) GetConnection(selection configuration.ServerSelection) (
connections[i] = connection connections[i] = connection
} }
if selection.TargetIP != nil { return utils.PickConnection(connections, selection, p.randSource)
return utils.GetTargetIPConnection(connections, selection.TargetIP)
}
return utils.PickRandomConnection(connections, p.randSource), nil
} }

View File

@@ -38,15 +38,5 @@ func (p *PIA) GetConnection(selection configuration.ServerSelection) (
} }
} }
if selection.TargetIP != nil { return utils.PickConnection(connections, selection, p.randSource)
connection, err = utils.GetTargetIPConnection(connections, selection.TargetIP)
} else {
connection, err = utils.PickRandomConnection(connections, p.randSource), nil
}
if err != nil {
return connection, err
}
return connection, nil
} }

View File

@@ -14,8 +14,7 @@ func (p *PIA) filterServers(selection configuration.ServerSelection) (
utils.FilterByPossibilities(server.Region, selection.Regions), utils.FilterByPossibilities(server.Region, selection.Regions),
utils.FilterByPossibilities(server.Hostname, selection.Hostnames), utils.FilterByPossibilities(server.Hostname, selection.Hostnames),
utils.FilterByPossibilities(server.ServerName, selection.Names), utils.FilterByPossibilities(server.ServerName, selection.Names),
selection.OpenVPN.TCP && !server.TCP, utils.FilterByProtocol(selection, server.TCP, server.UDP):
!selection.OpenVPN.TCP && !server.UDP:
default: default:
servers = append(servers, server) servers = append(servers, server)
} }

View File

@@ -33,7 +33,7 @@ var (
func (p *PIA) PortForward(ctx context.Context, client *http.Client, func (p *PIA) PortForward(ctx context.Context, client *http.Client,
logger logging.Logger, gateway net.IP, serverName string) ( logger logging.Logger, gateway net.IP, serverName string) (
port uint16, err error) { port uint16, err error) {
server := constants.PIAServerWhereName(serverName) server := constants.PIAServerWhereName(p.servers, serverName)
if !server.PortForward { if !server.PortForward {
logger.Error("The server " + serverName + logger.Error("The server " + serverName +
" (region " + server.Region + ") does not support port forwarding") " (region " + server.Region + ") does not support port forwarding")

View File

@@ -34,9 +34,5 @@ func (p *Privatevpn) GetConnection(selection configuration.ServerSelection) (
} }
} }
if selection.TargetIP != nil { return utils.PickConnection(connections, selection, p.randSource)
return utils.GetTargetIPConnection(connections, selection.TargetIP)
}
return utils.PickRandomConnection(connections, p.randSource), nil
} }

View File

@@ -34,9 +34,5 @@ func (p *Protonvpn) GetConnection(selection configuration.ServerSelection) (
} }
} }
if selection.TargetIP != nil { return utils.PickConnection(connections, selection, p.randSource)
return utils.GetTargetIPConnection(connections, selection.TargetIP)
}
return utils.PickRandomConnection(connections, p.randSource), nil
} }

View File

@@ -34,9 +34,5 @@ func (p *Purevpn) GetConnection(selection configuration.ServerSelection) (
} }
} }
if selection.TargetIP != nil { return utils.PickConnection(connections, selection, p.randSource)
return utils.GetTargetIPConnection(connections, selection.TargetIP)
}
return utils.PickRandomConnection(connections, p.randSource), nil
} }

View File

@@ -15,8 +15,7 @@ func (p *Purevpn) filterServers(selection configuration.ServerSelection) (
utils.FilterByPossibilities(server.Country, selection.Countries), utils.FilterByPossibilities(server.Country, selection.Countries),
utils.FilterByPossibilities(server.City, selection.Cities), utils.FilterByPossibilities(server.City, selection.Cities),
utils.FilterByPossibilities(server.Hostname, selection.Hostnames), utils.FilterByPossibilities(server.Hostname, selection.Hostnames),
selection.OpenVPN.TCP && !server.TCP, utils.FilterByProtocol(selection, server.TCP, server.UDP):
!selection.OpenVPN.TCP && !server.UDP:
default: default:
servers = append(servers, server) servers = append(servers, server)
} }

View File

@@ -34,9 +34,5 @@ func (s *Surfshark) GetConnection(selection configuration.ServerSelection) (
} }
} }
if selection.TargetIP != nil { return utils.PickConnection(connections, selection, s.randSource)
return utils.GetTargetIPConnection(connections, selection.TargetIP)
}
return utils.PickRandomConnection(connections, s.randSource), nil
} }

View File

@@ -12,9 +12,11 @@ func (s *Surfshark) filterServers(selection configuration.ServerSelection) (
switch { switch {
case case
utils.FilterByPossibilities(server.Region, selection.Regions), utils.FilterByPossibilities(server.Region, selection.Regions),
utils.FilterByPossibilities(server.Country, selection.Countries),
utils.FilterByPossibilities(server.City, selection.Cities),
utils.FilterByPossibilities(server.Hostname, selection.Hostnames), utils.FilterByPossibilities(server.Hostname, selection.Hostnames),
selection.OpenVPN.TCP && !server.TCP, utils.FilterByProtocol(selection, server.TCP, server.UDP),
!selection.OpenVPN.TCP && !server.UDP: selection.MultiHopOnly && !server.MultiHop:
default: default:
servers = append(servers, server) servers = append(servers, server)
} }

View File

@@ -0,0 +1,145 @@
package surfshark
import (
"errors"
"math/rand"
"testing"
"github.com/qdm12/gluetun/internal/configuration"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/models"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_Surfshark_filterServers(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
servers []models.SurfsharkServer
selection configuration.ServerSelection
filtered []models.SurfsharkServer
err error
}{
"no server available": {
selection: configuration.ServerSelection{
VPN: constants.OpenVPN,
},
err: errors.New("no server found: for VPN openvpn; protocol udp"),
},
"no filter": {
servers: []models.SurfsharkServer{
{Hostname: "a", UDP: true},
{Hostname: "b", UDP: true},
{Hostname: "c", UDP: true},
},
filtered: []models.SurfsharkServer{
{Hostname: "a", UDP: true},
{Hostname: "b", UDP: true},
{Hostname: "c", UDP: true},
},
},
"filter by region": {
selection: configuration.ServerSelection{
Regions: []string{"b"},
},
servers: []models.SurfsharkServer{
{Region: "a", UDP: true},
{Region: "b", UDP: true},
{Region: "c", UDP: true},
},
filtered: []models.SurfsharkServer{
{Region: "b", UDP: true},
},
},
"filter by country": {
selection: configuration.ServerSelection{
Countries: []string{"b"},
},
servers: []models.SurfsharkServer{
{Country: "a", UDP: true},
{Country: "b", UDP: true},
{Country: "c", UDP: true},
},
filtered: []models.SurfsharkServer{
{Country: "b", UDP: true},
},
},
"filter by city": {
selection: configuration.ServerSelection{
Cities: []string{"b"},
},
servers: []models.SurfsharkServer{
{City: "a", UDP: true},
{City: "b", UDP: true},
{City: "c", UDP: true},
},
filtered: []models.SurfsharkServer{
{City: "b", UDP: true},
},
},
"filter by hostname": {
selection: configuration.ServerSelection{
Hostnames: []string{"b"},
},
servers: []models.SurfsharkServer{
{Hostname: "a", UDP: true},
{Hostname: "b", UDP: true},
{Hostname: "c", UDP: true},
},
filtered: []models.SurfsharkServer{
{Hostname: "b", UDP: true},
},
},
"filter by protocol": {
selection: configuration.ServerSelection{
OpenVPN: configuration.OpenVPNSelection{
TCP: true,
},
},
servers: []models.SurfsharkServer{
{Hostname: "a", UDP: true},
{Hostname: "b", UDP: true, TCP: true},
{Hostname: "c", UDP: true},
},
filtered: []models.SurfsharkServer{
{Hostname: "b", UDP: true, TCP: true},
},
},
"filter by multihop only": {
selection: configuration.ServerSelection{
MultiHopOnly: true,
},
servers: []models.SurfsharkServer{
{Hostname: "a", UDP: true},
{Hostname: "b", MultiHop: true, UDP: true},
{Hostname: "c", UDP: true},
},
filtered: []models.SurfsharkServer{
{Hostname: "b", MultiHop: true, UDP: true},
},
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
randSource := rand.NewSource(0)
s := New(testCase.servers, randSource)
servers, err := s.filterServers(testCase.selection)
if testCase.err != nil {
require.Error(t, err)
assert.Equal(t, testCase.err.Error(), err.Error())
} else {
assert.NoError(t, err)
}
assert.Equal(t, testCase.filtered, servers)
})
}
}

View File

@@ -37,9 +37,5 @@ func (t *Torguard) GetConnection(selection configuration.ServerSelection) (
} }
} }
if selection.TargetIP != nil { return utils.PickConnection(connections, selection, t.randSource)
return utils.GetTargetIPConnection(connections, selection.TargetIP)
}
return utils.PickRandomConnection(connections, t.randSource), nil
} }

View File

@@ -14,8 +14,7 @@ func (t *Torguard) filterServers(selection configuration.ServerSelection) (
utils.FilterByPossibilities(server.Country, selection.Countries), utils.FilterByPossibilities(server.Country, selection.Countries),
utils.FilterByPossibilities(server.City, selection.Cities), utils.FilterByPossibilities(server.City, selection.Cities),
utils.FilterByPossibilities(server.Hostname, selection.Hostnames), utils.FilterByPossibilities(server.Hostname, selection.Hostnames),
selection.OpenVPN.TCP && !server.TCP, utils.FilterByProtocol(selection, server.TCP, server.UDP):
!selection.OpenVPN.TCP && !server.UDP:
default: default:
servers = append(servers, server) servers = append(servers, server)
} }

View File

@@ -27,7 +27,7 @@ func NoServerFoundError(selection configuration.ServerSelection) (err error) {
} }
messageParts = append(messageParts, "protocol "+protocol) messageParts = append(messageParts, "protocol "+protocol)
switch len(selection.Countries) { switch len(selection.Groups) {
case 0: case 0:
case 1: case 1:
part := "group " + selection.Groups[0] part := "group " + selection.Groups[0]

Some files were not shown because too many files have changed in this diff Show More