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"
ENV VPNSP=pia \
VERSION_INFORMATION=on \
LOG_LEVEL=info \
VPN_TYPE=openvpn \
PROTOCOL=udp \
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
- Supports: **Cyberghost**, **FastestVPN**, **HideMyAss**, **IPVanish**, **IVPN**, **Mullvad**, **NordVPN**, **Privado**, **Private Internet Access**, **PrivateVPN**, **ProtonVPN**, **PureVPN**, **Surfshark**, **TorGuard**, **VPNUnlimited**, **Vyprvpn**, **Windscribe** servers
- 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 fine blocking of malicious/ads/surveillance hostnames and IP addresses, with live update every 24 hours
- 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
err := allSettings.Read(env,
err = allSettings.Read(env, allServers,
logger.NewChild(logging.Settings{Prefix: "configuration: "}))
if err != nil {
return err
}
logger.PatchLevel(allSettings.Log.Level)
puid, pgid := allSettings.System.PUID, allSettings.System.PGID
@@ -167,7 +176,7 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
Version: buildInfo.Version,
Commit: buildInfo.Commit,
BuildDate: buildInfo.Created,
Announcement: "Wireguard is now supported!",
Announcement: "Wireguard is now supported for Mullvad, IVPN and Windscribe!",
AnnounceExp: announcementExp,
// Sponsor information
PaypalUser: "qmcgaw",
@@ -199,15 +208,6 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
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"
nonRootUsername, err := alpineConf.CreateUser(defaultUsername, puid)
if err != nil {
@@ -225,7 +225,7 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
return err
}
firewallLogLevel := logging.LevelInfo
firewallLogLevel := allSettings.Log.Level
if allSettings.Firewall.Debug {
firewallLogLevel = logging.LevelDebug
}
@@ -233,7 +233,7 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
Prefix: "routing: ",
Level: firewallLogLevel,
})
routingConf := routing.NewRouting(routingLogger)
routingConf := routing.New(netLinker, routingLogger)
defaultInterface, defaultGateway, err := routingConf.DefaultRoute()
if err != nil {
@@ -293,7 +293,8 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
}
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 {
return err
}

View File

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

View File

@@ -20,7 +20,7 @@ import (
var (
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")
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
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 {
return fmt.Errorf("%w: %s", ErrSyncServers, err)
return fmt.Errorf("%w: %s", ErrNewStorage, err)
}
currentServers := storage.GetServers()
updater := updater.New(options, httpClient, currentServers, logger)
allServers, err := updater.UpdateServers(ctx)
if err != nil {

View File

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

View File

@@ -8,18 +8,20 @@ import (
func (settings *Provider) readFastestvpn(r reader) (err error) {
settings.Name = constants.Fastestvpn
servers := r.servers.GetFastestvpn()
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
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 {
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 {
return fmt.Errorf("environment variable COUNTRY: %w", err)
}

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"strings"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/golibs/logging"
"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.
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)
}

View File

@@ -8,28 +8,30 @@ import (
func (settings *Provider) readHideMyAss(r reader) (err error) {
settings.Name = constants.HideMyAss
servers := r.servers.GetHideMyAss()
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
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 {
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 {
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 {
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 {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
}

View File

@@ -8,23 +8,25 @@ import (
func (settings *Provider) readIpvanish(r reader) (err error) {
settings.Name = constants.Ipvanish
servers := r.servers.GetIpvanish()
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
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 {
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 {
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 {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
}

View File

@@ -7,6 +7,7 @@ import (
"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"
@@ -119,21 +120,28 @@ func Test_Provider_readIpvanish(t *testing.T) {
t.Parallel()
ctrl := gomock.NewController(t)
servers := []models.IpvanishServer{{Hostname: "a"}}
allServers := models.AllServers{
Ipvanish: models.IpvanishServers{
Servers: servers,
},
}
env := mock_params.NewMockInterface(ctrl)
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.IpvanishCountryChoices()).
env.EXPECT().CSVInside("COUNTRY", constants.IpvanishCountryChoices(servers)).
Return(testCase.countries.values, testCase.countries.err)
}
if testCase.cities.call {
env.EXPECT().CSVInside("CITY", constants.IpvanishCityChoices()).
env.EXPECT().CSVInside("CITY", constants.IpvanishCityChoices(servers)).
Return(testCase.cities.values, testCase.cities.err)
}
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)
}
if testCase.protocol.call {
@@ -141,7 +149,10 @@ func Test_Provider_readIpvanish(t *testing.T) {
Return(testCase.protocol.value, testCase.protocol.err)
}
r := reader{env: env}
r := reader{
servers: allServers,
env: env,
}
var settings Provider
err := settings.readIpvanish(r)

View File

@@ -4,30 +4,67 @@ import (
"fmt"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/golibs/params"
)
func (settings *Provider) readIvpn(r reader) (err error) {
settings.Name = constants.Ivpn
servers := r.servers.GetIvpn()
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
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 {
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 {
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 {
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/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_readIvpn(t *testing.T) {
func Test_Provider_readIvpn(t *testing.T) { //nolint:gocognit
t.Parallel()
var errDummy = errors.New("dummy test error")
@@ -23,6 +24,15 @@ func Test_Provider_readIvpn(t *testing.T) {
err error
}
type portCall struct {
getCall bool
getValue string // "" or "0"
getErr error
portCall bool
portValue uint16
portErr error
}
type sliceStringCall struct {
call bool
values []string
@@ -30,11 +40,14 @@ func Test_Provider_readIvpn(t *testing.T) {
}
testCases := map[string]struct {
protocol singleStringCall
targetIP singleStringCall
countries sliceStringCall
cities sliceStringCall
isps sliceStringCall
hostnames sliceStringCall
protocol singleStringCall
ovpnPort portCall
wgPort portCall
settings Provider
err error
}{
@@ -62,20 +75,32 @@ func Test_Provider_readIvpn(t *testing.T) {
},
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": {
targetIP: singleStringCall{call: true},
countries: sliceStringCall{call: true},
cities: sliceStringCall{call: true},
isps: sliceStringCall{call: true},
hostnames: sliceStringCall{call: true, err: errDummy},
settings: Provider{
Name: constants.Ivpn,
},
err: errors.New("environment variable SERVER_HOSTNAME: dummy test error"),
},
"protocol error": {
"openvpn protocol 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, err: errDummy},
settings: Provider{
@@ -83,12 +108,42 @@ func Test_Provider_readIvpn(t *testing.T) {
},
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": {
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, getValue: "0"},
settings: Provider{
Name: constants.Ivpn,
},
@@ -97,17 +152,25 @@ func Test_Provider_readIvpn(t *testing.T) {
targetIP: singleStringCall{call: true, value: "1.2.3.4"},
countries: sliceStringCall{call: true, values: []string{"A", "B"}},
cities: sliceStringCall{call: true, values: []string{"C", "D"}},
isps: sliceStringCall{call: true, values: []string{"ISP 1"}},
hostnames: sliceStringCall{call: true, values: []string{"E", "F"}},
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{
Name: constants.Ivpn,
ServerSelection: ServerSelection{
OpenVPN: OpenVPNSelection{
TCP: true,
TCP: true,
CustomPort: 443,
},
Wireguard: WireguardSelection{
CustomPort: 2049,
},
TargetIP: net.IPv4(1, 2, 3, 4),
Countries: []string{"A", "B"},
Cities: []string{"C", "D"},
ISPs: []string{"ISP 1"},
Hostnames: []string{"E", "F"},
},
},
@@ -120,28 +183,59 @@ func Test_Provider_readIvpn(t *testing.T) {
ctrl := gomock.NewController(t)
env := mock_params.NewMockInterface(ctrl)
if testCase.protocol.call {
env.EXPECT().Inside("PROTOCOL", []string{constants.TCP, constants.UDP}, gomock.Any()).
Return(testCase.protocol.value, testCase.protocol.err)
servers := []models.IvpnServer{{Hostname: "a"}}
allServers := models.AllServers{
Ivpn: models.IvpnServers{
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.IvpnCountryChoices()).
env.EXPECT().CSVInside("COUNTRY", constants.IvpnCountryChoices(servers)).
Return(testCase.countries.values, testCase.countries.err)
}
if testCase.cities.call {
env.EXPECT().CSVInside("CITY", constants.IvpnCityChoices()).
env.EXPECT().CSVInside("CITY", constants.IvpnCityChoices(servers)).
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 {
env.EXPECT().CSVInside("SERVER_HOSTNAME", constants.IvpnHostnameChoices()).
env.EXPECT().CSVInside("SERVER_HOSTNAME", constants.IvpnHostnameChoices(servers)).
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
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) {
settings.Name = constants.Mullvad
servers := r.servers.GetMullvad()
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
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 {
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 {
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 {
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 {
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 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) {
@@ -57,3 +63,12 @@ func (settings *OpenVPNSelection) readMullvad(env params.Interface) (err error)
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) {
settings.Name = constants.Nordvpn
servers := r.servers.GetNordvpn()
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
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 {
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 {
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 {
return fmt.Errorf("environment variable SERVER_NAME: %w", err)
}

View File

@@ -8,28 +8,29 @@ import (
func (settings *Provider) readPrivado(r reader) (err error) {
settings.Name = constants.Privado
servers := r.servers.GetPrivado()
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
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 {
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 {
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 {
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 {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
}

View File

@@ -9,23 +9,24 @@ import (
func (settings *Provider) readPrivateInternetAccess(r reader) (err error) {
settings.Name = constants.PrivateInternetAccess
servers := r.servers.GetPia()
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
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 {
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 {
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 {
return fmt.Errorf("environment variable SERVER_NAME: %w", err)
}

View File

@@ -8,23 +8,25 @@ import (
func (settings *Provider) readPrivatevpn(r reader) (err error) {
settings.Name = constants.Privatevpn
servers := r.servers.GetPrivatevpn()
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
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 {
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 {
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 {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
}

View File

@@ -9,33 +9,35 @@ import (
func (settings *Provider) readProtonvpn(r reader) (err error) {
settings.Name = constants.Protonvpn
servers := r.servers.GetProtonvpn()
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
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 {
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 {
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 {
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 {
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 {
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",
"purevpn", "surfshark", "torguard", constants.VPNUnlimited, "vyprvpn", "windscribe"}
case constants.Wireguard:
allowedVPNServiceProviders = []string{constants.Mullvad, constants.Windscribe}
allowedVPNServiceProviders = []string{constants.Mullvad, constants.Windscribe,
constants.Ivpn}
}
vpnsp, err := r.env.Inside("VPNSP", allowedVPNServiceProviders,
@@ -167,6 +168,7 @@ func readOpenVPNCustomPort(env params.Interface, tcp bool,
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) {
port, err = readPortOrZero(env, "WIREGUARD_PORT")
if err != nil {
@@ -175,11 +177,16 @@ func readWireguardCustomPort(env params.Interface, allowed []uint16) (port uint1
return 0, nil
}
if len(allowed) == 0 {
return port, nil
}
for i := range allowed {
if allowed[i] == port {
return port, nil
}
}
return 0, fmt.Errorf(
"environment variable WIREGUARD_PORT: %w: port %d, can only be one of %s",
ErrInvalidPort, port, portsToString(allowed))

View File

@@ -8,28 +8,29 @@ import (
func (settings *Provider) readPurevpn(r reader) (err error) {
settings.Name = constants.Purevpn
servers := r.servers.GetPurevpn()
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
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 {
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 {
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 {
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 {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
}

View File

@@ -7,22 +7,26 @@ import (
"strconv"
"strings"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/params"
"github.com/qdm12/golibs/verification"
)
type reader struct {
env params.Interface
logger logging.Logger
regex verification.Regex
servers models.AllServers
env params.Interface
logger logging.Logger
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{
env: env,
logger: logger,
regex: verification.NewRegex(),
servers: servers,
env: env,
logger: logger,
regex: verification.NewRegex(),
}
}

View File

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

View File

@@ -5,6 +5,7 @@ import (
"fmt"
"strings"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/params"
)
@@ -22,6 +23,7 @@ type Settings struct {
VersionInformation bool
ControlServer ControlServer
Health Health
Log Log
}
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.DNS.lines()...)
lines = append(lines, settings.Firewall.lines()...)
lines = append(lines, settings.Log.lines()...)
lines = append(lines, settings.System.lines()...)
lines = append(lines, settings.HTTPProxy.lines()...)
lines = append(lines, settings.ShadowSocks.lines()...)
@@ -57,12 +60,14 @@ var (
ErrUpdater = errors.New("cannot read Updater settings")
ErrPublicIP = errors.New("cannot read Public IP getter 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
// as an error is encountered reading them.
func (settings *Settings) Read(env params.Interface, logger logging.Logger) (err error) {
r := newReader(env, logger)
func (settings *Settings) Read(env params.Interface, servers models.AllServers,
logger logging.Logger) (err error) {
r := newReader(env, servers, logger)
settings.VersionInformation, err = r.env.OnOff("VERSION_INFORMATION", params.Default("on"))
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)
}
if err := settings.Log.read(r.env); err != nil {
return fmt.Errorf("%w: %s", ErrLog, err)
}
return nil
}

View File

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

View File

@@ -2,27 +2,101 @@ package configuration
import (
"fmt"
"strings"
"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) {
settings.Name = constants.Surfshark
servers := r.servers.GetSurfshark()
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
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 {
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 {
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)
}
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) {
settings.Name = constants.Torguard
servers := r.servers.GetTorguard()
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
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 {
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 {
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 {
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")
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) {
settings.Name = constants.VPNUnlimited
servers := r.servers.GetVPNUnlimited()
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
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 {
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 {
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 {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
}

View File

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

View File

@@ -9,23 +9,25 @@ import (
func (settings *Provider) readWindscribe(r reader) (err error) {
settings.Name = constants.Windscribe
servers := r.servers.GetWindscribe()
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil {
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 {
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 {
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 {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
}

View File

@@ -10,10 +10,10 @@ import (
// Wireguard contains settings to configure the Wireguard client.
type Wireguard struct {
PrivateKey string `json:"privatekey"`
PreSharedKey string `json:"presharedkey"`
Address *net.IPNet `json:"address"`
Interface string `json:"interface"`
PrivateKey string `json:"privatekey"`
PreSharedKey string `json:"presharedkey"`
Addresses []*net.IPNet `json:"addresses"`
Interface string `json:"interface"`
}
func (settings *Wireguard) String() string {
@@ -33,8 +33,11 @@ func (settings *Wireguard) lines() (lines []string) {
lines = append(lines, indent+lastIndent+"Pre-shared key is set")
}
if settings.Address != nil {
lines = append(lines, indent+lastIndent+"Address: "+settings.Address.String())
if len(settings.Addresses) > 0 {
lines = append(lines, indent+lastIndent+"Addresses: ")
for _, address := range settings.Addresses {
lines = append(lines, indent+indent+lastIndent+address.String())
}
}
return lines
@@ -53,16 +56,10 @@ func (settings *Wireguard) read(r reader) (err error) {
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 {
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"))
if err != nil {
@@ -71,3 +68,21 @@ func (settings *Wireguard) read(r reader) (err error) {
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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"
)
func CyberghostRegionChoices() (choices []string) {
servers := CyberghostServers()
func CyberghostRegionChoices(servers []models.CyberghostServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Region
@@ -20,8 +19,7 @@ func CyberghostRegionChoices() (choices []string) {
return makeUnique(choices)
}
func CyberghostGroupChoices() (choices []string) {
servers := CyberghostServers()
func CyberghostGroupChoices(servers []models.CyberghostServer) (choices []string) {
uniqueChoices := map[string]struct{}{}
for _, server := range servers {
uniqueChoices[server.Group] = struct{}{}
@@ -38,19 +36,10 @@ func CyberghostGroupChoices() (choices []string) {
return sortable
}
func CyberghostHostnameChoices() (choices []string) {
servers := CyberghostServers()
func CyberghostHostnameChoices(servers []models.CyberghostServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Hostname
}
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 (
"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/require"
)
func Test_CyberghostGroupChoices(t *testing.T) {
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",
"Premium TCP USA", "Premium UDP Asia", "Premium UDP Europe",
"Premium UDP USA"}
choices := CyberghostGroupChoices()
choices := CyberghostGroupChoices(servers.GetCyberghost())
assert.Equal(t, expected, choices)
}

View File

@@ -10,8 +10,7 @@ const (
FastestvpnOpenvpnStaticKeyV1 = "697fe793b32cb5091d30f2326d5d124a9412e93d0a44ef7361395d76528fcbfc82c3859dccea70a93cfa8fae409709bff75f844cf5ff0c237f426d0c20969233db0e706edb6bdf195ec3dc11b3f76bc807a77e74662d9a800c8cd1144ebb67b7f0d3f1281d1baf522bfe03b7c3f963b1364fc0769400e413b61ca7b43ab19fac9e0f77e41efd4bda7fd77b1de2d7d7855cbbe3e620cecceac72c21a825b243e651f44d90e290e09c3ad650de8fca99c858bc7caad584bc69b11e5c9fd9381c69c505ec487a65912c672d83ed0113b5a74ddfbd3ab33b3683cec593557520a72c4d6cce46111f56f3396cc3ce7183edce553c68ea0796cf6c4375fad00aaa2a42"
)
func FastestvpnCountriesChoices() (choices []string) {
servers := FastestvpnServers()
func FastestvpnCountriesChoices(servers []models.FastestvpnServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Country
@@ -19,18 +18,10 @@ func FastestvpnCountriesChoices() (choices []string) {
return choices
}
func FastestvpnHostnameChoices() (choices []string) {
servers := FastestvpnServers()
func FastestvpnHostnameChoices(servers []models.FastestvpnServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Hostname
}
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=="
)
func HideMyAssCountryChoices() (choices []string) {
servers := HideMyAssServers()
func HideMyAssCountryChoices(servers []models.HideMyAssServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Country
@@ -20,8 +19,7 @@ func HideMyAssCountryChoices() (choices []string) {
return makeUnique(choices)
}
func HideMyAssCityChoices() (choices []string) {
servers := HideMyAssServers()
func HideMyAssCityChoices(servers []models.HideMyAssServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].City
@@ -29,18 +27,10 @@ func HideMyAssCityChoices() (choices []string) {
return makeUnique(choices)
}
func HideMyAssHostnameChoices() (choices []string) {
servers := HideMyAssServers()
func HideMyAssHostnameChoices(servers []models.HideMyAssServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Hostname
}
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=="
)
func IpvanishCountryChoices() (choices []string) {
servers := IpvanishServers()
func IpvanishCountryChoices(servers []models.IpvanishServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Country
@@ -18,8 +17,7 @@ func IpvanishCountryChoices() (choices []string) {
return makeUnique(choices)
}
func IpvanishCityChoices() (choices []string) {
servers := IpvanishServers()
func IpvanishCityChoices(servers []models.IpvanishServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].City
@@ -27,18 +25,10 @@ func IpvanishCityChoices() (choices []string) {
return makeUnique(choices)
}
func IpvanishHostnameChoices() (choices []string) {
servers := IpvanishServers()
func IpvanishHostnameChoices(servers []models.IpvanishServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Hostname
}
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"
)
func IvpnCountryChoices() (choices []string) {
servers := IvpnServers()
func IvpnCountryChoices(servers []models.IvpnServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Country
@@ -19,8 +18,7 @@ func IvpnCountryChoices() (choices []string) {
return makeUnique(choices)
}
func IvpnCityChoices() (choices []string) {
servers := IvpnServers()
func IvpnCityChoices(servers []models.IvpnServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].City
@@ -28,18 +26,18 @@ func IvpnCityChoices() (choices []string) {
return makeUnique(choices)
}
func IvpnHostnameChoices() (choices []string) {
servers := IvpnServers()
func IvpnISPChoices(servers []models.IvpnServer) (choices []string) {
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))
for i := range servers {
choices[i] = servers[i].Hostname
}
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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"
)
func MullvadCountryChoices() (choices []string) {
servers := MullvadServers()
func MullvadCountryChoices(servers []models.MullvadServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Country
@@ -18,8 +17,7 @@ func MullvadCountryChoices() (choices []string) {
return makeUnique(choices)
}
func MullvadCityChoices() (choices []string) {
servers := MullvadServers()
func MullvadCityChoices(servers []models.MullvadServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].City
@@ -27,8 +25,7 @@ func MullvadCityChoices() (choices []string) {
return makeUnique(choices)
}
func MullvadHostnameChoices() (choices []string) {
servers := MullvadServers()
func MullvadHostnameChoices(servers []models.MullvadServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Hostname
@@ -36,18 +33,10 @@ func MullvadHostnameChoices() (choices []string) {
return makeUnique(choices)
}
func MullvadISPChoices() (choices []string) {
servers := MullvadServers()
func MullvadISPChoices(servers []models.MullvadServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].ISP
}
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"
)
func NordvpnRegionChoices() (choices []string) {
servers := NordvpnServers()
func NordvpnRegionChoices(servers []models.NordvpnServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Region
@@ -19,8 +18,7 @@ func NordvpnRegionChoices() (choices []string) {
return makeUnique(choices)
}
func NordvpnHostnameChoices() (choices []string) {
servers := NordvpnServers()
func NordvpnHostnameChoices(servers []models.NordvpnServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Hostname
@@ -28,18 +26,10 @@ func NordvpnHostnameChoices() (choices []string) {
return makeUnique(choices)
}
func NordvpnNameChoices() (choices []string) {
servers := NordvpnServers()
func NordvpnNameChoices(servers []models.NordvpnServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Name
}
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 = "MIIHqzCCBZOgAwIBAgIJAJ0u+vODZJntMA0GCSqGSIb3DQEBDQUAMIHoMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEzARBgNVBAcTCkxvc0FuZ2VsZXMxIDAeBgNVBAoTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMSAwHgYDVQQLExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4GA1UEAxMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxIDAeBgNVBCkTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMS8wLQYJKoZIhvcNAQkBFiBzZWN1cmVAcHJpdmF0ZWludGVybmV0YWNjZXNzLmNvbTAeFw0xNDA0MTcxNzQwMzNaFw0zNDA0MTIxNzQwMzNaMIHoMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEzARBgNVBAcTCkxvc0FuZ2VsZXMxIDAeBgNVBAoTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMSAwHgYDVQQLExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4GA1UEAxMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxIDAeBgNVBCkTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMS8wLQYJKoZIhvcNAQkBFiBzZWN1cmVAcHJpdmF0ZWludGVybmV0YWNjZXNzLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALVkhjumaqBbL8aSgj6xbX1QPTfTd1qHsAZd2B97m8Vw31c/2yQgZNf5qZY0+jOIHULNDe4R9TIvyBEbvnAg/OkPw8n/+ScgYOeH876VUXzjLDBnDb8DLr/+w9oVsuDeFJ9KV2UFM1OYX0SnkHnrYAN2QLF98ESK4NCSU01h5zkcgmQ+qKSfA9Ny0/UpsKPBFqsQ25NvjDWFhCpeqCHKUJ4Be27CDbSl7lAkBuHMPHJs8f8xPgAbHRXZOxVCpayZ2SNDfCwsnGWpWFoMGvdMbygngCn6jA/W1VSFOlRlfLuuGe7QFfDwA0jaLCxuWt/BgZylp7tAzYKR8lnWmtUCPm4+BtjyVDYtDCiGBD9Z4P13RFWvJHw5aapx/5W/CuvVyI7pKwvc2IT+KPxCUhH1XI8ca5RN3C9NoPJJf6qpg4g0rJH3aaWkoMRrYvQ+5PXXYUzjtRHImghRGd/ydERYoAZXuGSbPkm9Y/p2X8unLcW+F0xpJD98+ZI+tzSsI99Zs5wijSUGYr9/j18KHFTMQ8n+1jauc5bCCegN27dPeKXNSZ5riXFL2XX6BkY68y58UaNzmeGMiUL9BOV1iV+PMb7B7PYs7oFLjAhh0EdyvfHkrh/ZV9BEhtFa7yXp8XR0J6vz1YV9R6DYJmLjOEbhU8N0gc3tZm4Qz39lIIG6w3FDAgMBAAGjggFUMIIBUDAdBgNVHQ4EFgQUrsRtyWJftjpdRM0+925Y6Cl08SUwggEfBgNVHSMEggEWMIIBEoAUrsRtyWJftjpdRM0+925Y6Cl08SWhge6kgeswgegxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTETMBEGA1UEBxMKTG9zQW5nZWxlczEgMB4GA1UEChMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxIDAeBgNVBAsTF1ByaXZhdGUgSW50ZXJuZXQgQWNjZXNzMSAwHgYDVQQDExdQcml2YXRlIEludGVybmV0IEFjY2VzczEgMB4GA1UEKRMXUHJpdmF0ZSBJbnRlcm5ldCBBY2Nlc3MxLzAtBgkqhkiG9w0BCQEWIHNlY3VyZUBwcml2YXRlaW50ZXJuZXRhY2Nlc3MuY29tggkAnS7684Nkme0wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQ0FAAOCAgEAJsfhsPk3r8kLXLxY+v+vHzbr4ufNtqnL9/1Uuf8NrsCtpXAoyZ0YqfbkWx3NHTZ7OE9ZRhdMP/RqHQE1p4N4Sa1nZKhTKasV6KhHDqSCt/dvEm89xWm2MVA7nyzQxVlHa9AkcBaemcXEiyT19XdpiXOP4Vhs+J1R5m8zQOxZlV1GtF9vsXmJqWZpOVPmZ8f35BCsYPvv4yMewnrtAC8PFEK/bOPeYcKN50bol22QYaZuLfpkHfNiFTnfMh8sl/ablPyNY7DUNiP5DRcMdIwmfGQxR5WEQoHL3yPJ42LkB5zs6jIm26DGNXfwura/mi105+ENH1CaROtRYwkiHb08U6qLXXJz80mWJkT90nr8Asj35xN2cUppg74nG3YVav/38P48T56hG1NHbYF5uOCske19F6wi9maUoto/3vEr0rnXJUp2KODmKdvBI7co245lHBABWikk8VfejQSlCtDBXn644ZMtAdoxKNfR2WTFVEwJiyd1Fzx0yujuiXDROLhISLQDRjVVAvawrAtLZWYK31bY7KlezPlQnl/D9Asxe85l8jO5+0LdJ6VyOs/Hd4w52alDW/MFySDZSfQHMTIc30hLBJ8OnCEIvluVQQ2UQvoW+no177N9L2Y+M9TcTA62ZyMXShHQGeh20rb4kK8f+iFX8NxtdHVSkxMEFSfDDyQ="
)
func PIAGeoChoices() (choices []string) {
servers := PIAServers()
func PIAGeoChoices(servers []models.PIAServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Region
@@ -24,8 +23,7 @@ func PIAGeoChoices() (choices []string) {
return makeUnique(choices)
}
func PIAHostnameChoices() (choices []string) {
servers := PIAServers()
func PIAHostnameChoices(servers []models.PIAServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Hostname
@@ -33,8 +31,7 @@ func PIAHostnameChoices() (choices []string) {
return makeUnique(choices)
}
func PIANameChoices() (choices []string) {
servers := PIAServers()
func PIANameChoices(servers []models.PIAServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].ServerName
@@ -42,15 +39,8 @@ func PIANameChoices() (choices []string) {
return makeUnique(choices)
}
// PIAServers returns a slice of all the server information for PIA.
func PIAServers() (servers []models.PIAServer) {
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() {
func PIAServerWhereName(servers []models.PIAServer, serverName string) (server models.PIAServer) {
for _, server := range servers {
if server.ServerName == serverName {
return server
}

View File

@@ -1,16 +1,13 @@
package constants
import (
"github.com/qdm12/gluetun/internal/models"
)
import "github.com/qdm12/gluetun/internal/models"
//nolint:lll
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=="
)
func PrivadoCountryChoices() (choices []string) {
servers := PrivadoServers()
func PrivadoCountryChoices(servers []models.PrivadoServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Country
@@ -18,8 +15,7 @@ func PrivadoCountryChoices() (choices []string) {
return makeUnique(choices)
}
func PrivadoRegionChoices() (choices []string) {
servers := PrivadoServers()
func PrivadoRegionChoices(servers []models.PrivadoServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Region
@@ -27,8 +23,7 @@ func PrivadoRegionChoices() (choices []string) {
return makeUnique(choices)
}
func PrivadoCityChoices() (choices []string) {
servers := PrivadoServers()
func PrivadoCityChoices(servers []models.PrivadoServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].City
@@ -36,18 +31,10 @@ func PrivadoCityChoices() (choices []string) {
return makeUnique(choices)
}
func PrivadoHostnameChoices() (choices []string) {
servers := PrivadoServers()
func PrivadoHostnameChoices(servers []models.PrivadoServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Hostname
}
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
import (
"github.com/qdm12/gluetun/internal/models"
)
import "github.com/qdm12/gluetun/internal/models"
//nolint:lll
const (
@@ -10,8 +8,7 @@ const (
PrivatevpnOpenvpnStaticKeyV1 = "a49082f082ca89d6a6bb4ecc7c047c6d428a1d3c8254a95206d38a61d7fbe65984214cd7d56eacc5a60803bffd677fa7294d4bfe555036339312de2dfb1335bd9d5fd94b04bba3a15fc5192aeb02fb6d8dd2ca831fad7509be5eefa8d1eaa689dc586c831a23b589c512662652ecf1bb3a4a673816aba434a04f6857b8c2f8bb265bfe48a7b8112539729d2f7d9734a720e1035188118c73fef1824d0237d5579ca382d703b4bb252acaedc753b12199f00154d3769efbcf85ef5ad6ee755cbeaa944cb98e7654286df54c793a8443f5363078e3da548ba0beed079df633283cefb256f6a4bcfc4ab2c4affc24955c1864d5458e84a7c210d0d186269e55dcf6"
)
func PrivatevpnCountryChoices() (choices []string) {
servers := PrivatevpnServers()
func PrivatevpnCountryChoices(servers []models.PrivatevpnServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Country
@@ -19,8 +16,7 @@ func PrivatevpnCountryChoices() (choices []string) {
return makeChoicesUnique(choices)
}
func PrivatevpnCityChoices() (choices []string) {
servers := PrivatevpnServers()
func PrivatevpnCityChoices(servers []models.PrivatevpnServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].City
@@ -28,17 +24,10 @@ func PrivatevpnCityChoices() (choices []string) {
return makeChoicesUnique(choices)
}
func PrivatevpnHostnameChoices() (choices []string) {
servers := PrivatevpnServers()
func PrivatevpnHostnameChoices(servers []models.PrivatevpnServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Hostname
}
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
import (
"github.com/qdm12/gluetun/internal/models"
)
import "github.com/qdm12/gluetun/internal/models"
//nolint:lll
const (
@@ -10,8 +8,7 @@ const (
ProtonvpnOpenvpnStaticKeyV1 = "6acef03f62675b4b1bbd03e53b187727423cea742242106cb2916a8a4c8297563d22c7e5cef430b1103c6f66eb1fc5b375a672f158e2e2e936c3faa48b035a6de17beaac23b5f03b10b868d53d03521d8ba115059da777a60cbfd7b2c9c5747278a15b8f6e68a3ef7fd583ec9f398c8bd4735dab40cbd1e3c62a822e97489186c30a0b48c7c38ea32ceb056d3fa5a710e10ccc7a0ddb363b08c3d2777a3395e10c0b6080f56309192ab5aacd4b45f55da61fc77af39bd81a19218a79762c33862df55785075f37d8c71dc8a42097ee43344739a0dd48d03025b0450cf1fb5e8caeb893d9a96d1f15519bb3c4dcb40ee316672ea16c012664f8a9f11255518deb"
)
func ProtonvpnCountryChoices() (choices []string) {
servers := ProtonvpnServers()
func ProtonvpnCountryChoices(servers []models.ProtonvpnServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Country
@@ -19,8 +16,7 @@ func ProtonvpnCountryChoices() (choices []string) {
return makeChoicesUnique(choices)
}
func ProtonvpnRegionChoices() (choices []string) {
servers := ProtonvpnServers()
func ProtonvpnRegionChoices(servers []models.ProtonvpnServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Region
@@ -28,8 +24,7 @@ func ProtonvpnRegionChoices() (choices []string) {
return makeChoicesUnique(choices)
}
func ProtonvpnCityChoices() (choices []string) {
servers := ProtonvpnServers()
func ProtonvpnCityChoices(servers []models.ProtonvpnServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].City
@@ -37,8 +32,7 @@ func ProtonvpnCityChoices() (choices []string) {
return makeChoicesUnique(choices)
}
func ProtonvpnNameChoices() (choices []string) {
servers := ProtonvpnServers()
func ProtonvpnNameChoices(servers []models.ProtonvpnServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Name
@@ -46,18 +40,10 @@ func ProtonvpnNameChoices() (choices []string) {
return makeChoicesUnique(choices)
}
func ProtonvpnHostnameChoices() (choices []string) {
servers := ProtonvpnServers()
func ProtonvpnHostnameChoices(servers []models.ProtonvpnServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Hostname
}
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
import (
"github.com/qdm12/gluetun/internal/models"
)
import "github.com/qdm12/gluetun/internal/models"
//nolint:lll
const (
@@ -12,8 +10,7 @@ const (
PurevpnOpenvpnStaticKeyV1 = "e30af995f56d07426d9ba1f824730521d4283db4b4d0cdda9c6e8759a3799dcb7939b6a5989160c9660de0f6125cbb1f585b41c074b2fe88ecfcf17eab9a33be1352379cdf74952b588fb161a93e13df9135b2b29038231e02d657a6225705e6868ccb0c384ed11614690a1894bfbeb274cebf1fe9c2329bdd5c8a40fe8820624d2ea7540cd79ab76892db51fc371a3ac5fc9573afecb3fffe3281e61d72e91579d9b03d8cbf7909b3aebf4d90850321ee6b7d0a7846d15c27d8290e031e951e19438a4654663cad975e138f5bc5af89c737ad822f27e19057731f41e1e254cc9c95b7175c622422cde9f1f2cfd3510add94498b4d7133d3729dd214a16b27fb"
)
func PurevpnRegionChoices() (choices []string) {
servers := PurevpnServers()
func PurevpnRegionChoices(servers []models.PurevpnServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Region
@@ -21,8 +18,7 @@ func PurevpnRegionChoices() (choices []string) {
return makeUnique(choices)
}
func PurevpnCountryChoices() (choices []string) {
servers := PurevpnServers()
func PurevpnCountryChoices(servers []models.PurevpnServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Country
@@ -30,8 +26,7 @@ func PurevpnCountryChoices() (choices []string) {
return makeUnique(choices)
}
func PurevpnCityChoices() (choices []string) {
servers := PurevpnServers()
func PurevpnCityChoices(servers []models.PurevpnServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].City
@@ -39,18 +34,10 @@ func PurevpnCityChoices() (choices []string) {
return makeUnique(choices)
}
func PurevpnHostnameChoices() (choices []string) {
servers := PurevpnServers()
func PurevpnHostnameChoices(servers []models.PurevpnServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Hostname
}
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"
)
func SurfsharkRegionChoices() (choices []string) {
servers := SurfsharkServers()
func SurfsharkRegionChoices(servers []models.SurfsharkServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Region
@@ -19,18 +18,211 @@ func SurfsharkRegionChoices() (choices []string) {
return makeUnique(choices)
}
func SurfsharkHostnameChoices() (choices []string) {
servers := SurfsharkServers()
func SurfsharkCountryChoices(servers []models.SurfsharkServer) (choices []string) {
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))
for i := range servers {
choices[i] = servers[i].Hostname
}
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"
)
func TorguardCountryChoices() (choices []string) {
servers := TorguardServers()
func TorguardCountryChoices(servers []models.TorguardServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Country
@@ -19,8 +18,7 @@ func TorguardCountryChoices() (choices []string) {
return makeUnique(choices)
}
func TorguardCityChoices() (choices []string) {
servers := TorguardServers()
func TorguardCityChoices(servers []models.TorguardServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].City
@@ -28,18 +26,10 @@ func TorguardCityChoices() (choices []string) {
return makeUnique(choices)
}
func TorguardHostnameChoices() (choices []string) {
servers := TorguardServers()
func TorguardHostnameChoices(servers []models.TorguardServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Hostname
}
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"
)
func VPNUnlimitedCountryChoices() (choices []string) {
servers := VPNUnlimitedServers()
func VPNUnlimitedCountryChoices(servers []models.VPNUnlimitedServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Country
@@ -18,8 +17,7 @@ func VPNUnlimitedCountryChoices() (choices []string) {
return makeUnique(choices)
}
func VPNUnlimitedCityChoices() (choices []string) {
servers := VPNUnlimitedServers()
func VPNUnlimitedCityChoices(servers []models.VPNUnlimitedServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].City
@@ -27,18 +25,10 @@ func VPNUnlimitedCityChoices() (choices []string) {
return makeUnique(choices)
}
func VPNUnlimitedHostnameChoices() (choices []string) {
servers := VPNUnlimitedServers()
func VPNUnlimitedHostnameChoices(servers []models.VPNUnlimitedServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Hostname
}
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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"
)
func VyprvpnRegionChoices() (choices []string) {
servers := VyprvpnServers()
func VyprvpnRegionChoices(servers []models.VyprvpnServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Region
}
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
import (
"github.com/qdm12/gluetun/internal/models"
)
import "github.com/qdm12/gluetun/internal/models"
//nolint:lll
const (
@@ -10,8 +8,7 @@ const (
WindscribeOpenvpnStaticKeyV1 = "5801926a57ac2ce27e3dfd1dd6ef82042d82bd4f3f0021296f57734f6f1ea714a6623845541c4b0c3dea0a050fe6746cb66dfab14cda27e5ae09d7c155aa554f399fa4a863f0e8c1af787e5c602a801d3a2ec41e395a978d56729457fe6102d7d9e9119aa83643210b33c678f9d4109e3154ac9c759e490cb309b319cf708cae83ddadc3060a7a26564d1a24411cd552fe6620ea16b755697a4fc5e6e9d0cfc0c5c4a1874685429046a424c026db672e4c2c492898052ba59128d46200b40f880027a8b6610a4d559bdc9346d33a0a6b08e75c7fd43192b162bfd0aef0c716b31584827693f676f9a5047123466f0654eade34972586b31c6ce7e395f4b478cb"
)
func WindscribeRegionChoices() (choices []string) {
servers := WindscribeServers()
func WindscribeRegionChoices(servers []models.WindscribeServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Region
@@ -19,8 +16,7 @@ func WindscribeRegionChoices() (choices []string) {
return makeUnique(choices)
}
func WindscribeCityChoices() (choices []string) {
servers := WindscribeServers()
func WindscribeCityChoices(servers []models.WindscribeServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].City
@@ -28,18 +24,10 @@ func WindscribeCityChoices() (choices []string) {
return makeUnique(choices)
}
func WindscribeHostnameChoices() (choices []string) {
servers := WindscribeServers()
func WindscribeHostnameChoices(servers []models.WindscribeServer) (choices []string) {
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].Hostname
}
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"
"fmt"
"net"
"github.com/qdm12/gluetun/internal/subnet"
)
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...")
subnetsToAdd := findSubnetsToAdd(c.outboundSubnets, subnets)
subnetsToRemove := findSubnetsToRemove(c.outboundSubnets, subnets)
subnetsToAdd, subnetsToRemove := subnet.FindSubnetsToChange(c.outboundSubnets, subnets)
if len(subnetsToAdd) == 0 && len(subnetsToRemove) == 0 {
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) {
const remove = true
for _, subnet := range subnets {
if err := c.acceptOutputFromIPToSubnet(ctx, c.defaultInterface, c.localIP, subnet, remove); err != nil {
for _, subNet := range subnets {
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())
continue
}
c.outboundSubnets = removeSubnetFromSubnets(c.outboundSubnets, subnet)
c.outboundSubnets = subnet.RemoveSubnetFromSubnets(c.outboundSubnets, subNet)
}
}

View File

@@ -1,11 +1,9 @@
package httpproxy
import (
"context"
"io"
"net"
"net/http"
"sync"
)
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)
ctx, cancel := context.WithCancel(h.ctx)
const transferGoroutines = 2
wg := &sync.WaitGroup{}
wg.Add(transferGoroutines)
go func() { // trigger cleanup when done
wg.Wait()
cancel()
}()
go func() { // cleanup
<-ctx.Done()
serverToClientDone := make(chan struct{})
clientToServerClientDone := make(chan struct{})
go transfer(destinationConn, clientConnection, clientToServerClientDone)
go transfer(clientConnection, destinationConn, serverToClientDone)
select {
case <-h.ctx.Done():
destinationConn.Close()
clientConnection.Close()
h.wg.Done()
}()
go transfer(destinationConn, clientConnection, wg)
go transfer(clientConnection, destinationConn, wg)
<-serverToClientDone
<-clientToServerClientDone
case <-serverToClientDone:
<-clientToServerClientDone
case <-clientToServerClientDone: // happens more rarely, when a connection is closed on the client side
<-serverToClientDone
}
h.wg.Done()
}
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)
_ = source.Close()
_ = destination.Close()
wg.Done()
close(done)
}

View File

@@ -9,11 +9,7 @@ import (
"github.com/qdm12/golibs/logging"
)
type Server interface {
Run(ctx context.Context, errorCh chan<- error)
}
type server struct {
type Server struct {
address string
handler http.Handler
logger logging.Logger
@@ -21,9 +17,9 @@ type server struct {
}
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{}
return &server{
return &Server{
address: address,
handler: newHandler(ctx, wg, logger, stealth, verbose, username, password),
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}
go func() {
<-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 {
VPN string `json:"vpn"`
Country string `json:"country"`
City string `json:"city"`
ISP string `json:"isp"`
Hostname string `json:"hostname"`
WgPubKey string `json:"wgpubkey,omitempty"`
TCP bool `json:"tcp"`
UDP bool `json:"udp"`
IPs []net.IP `json:"ips"`
@@ -116,7 +119,11 @@ type PurevpnServer struct {
type SurfsharkServer struct {
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"`
MultiHop bool `json:"multihop"`
TCP bool `json:"tcp"`
UDP bool `json:"udp"`
IPs []net.IP `json:"ips"`

View File

@@ -2,6 +2,21 @@ package 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 {
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
import "github.com/vishvananda/netlink"
//go:generate mockgen -destination=mock_$GOPACKAGE/$GOFILE . NetLinker
var _ NetLinker = (*NetLink)(nil)
type NetLinker interface {
AddrAdd(link netlink.Link, addr *netlink.Addr) error
RouteAdd(route *netlink.Route) error
RuleAdd(rule *netlink.Rule) error
RuleDel(rule *netlink.Rule) error
Addresser
Linker
Router
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"
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 {
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"
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 {
return netlink.RuleAdd(rule)
}

View File

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

View File

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

View File

@@ -19,7 +19,7 @@ func Test_Cyberghost_filterServers(t *testing.T) {
filteredServers []models.CyberghostServer
err error
}{
"no servers": {
"no server": {
selection: configuration.ServerSelection{VPN: constants.OpenVPN},
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) {
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{
"Premium TCP Asia", "Premium TCP Europe", "Premium TCP USA",
}
choices := tcpGroupChoices()
choices := tcpGroupChoices(servers)
assert.Equal(t, expected, choices)
}
@@ -157,10 +165,18 @@ func Test_tcpGroupChoices(t *testing.T) {
func Test_udpGroupChoices(t *testing.T) {
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{
"Premium UDP Asia", "Premium UDP Europe", "Premium UDP USA",
}
choices := udpGroupChoices()
choices := udpGroupChoices(servers)
assert.Equal(t, expected, choices)
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,23 +1,15 @@
package ivpn
import (
"errors"
"github.com/qdm12/gluetun/internal/configuration"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/provider/utils"
)
var ErrProtocolUnsupported = errors.New("network protocol is not supported")
func (i *Ivpn) GetConnection(selection configuration.ServerSelection) (
connection models.Connection, err error) {
const port = 2049
const protocol = constants.UDP
if selection.OpenVPN.TCP {
return connection, ErrProtocolUnsupported
}
port := getPort(selection)
protocol := utils.GetProtocol(selection)
servers, err := i.filterServers(selection)
if err != nil {
@@ -33,14 +25,21 @@ func (i *Ivpn) GetConnection(selection configuration.ServerSelection) (
Port: port,
Protocol: protocol,
Hostname: server.Hostname,
PubKey: server.WgPubKey, // Wireguard only
}
connections = append(connections, connection)
}
}
if selection.TargetIP != nil {
return utils.GetTargetIPConnection(connections, selection.TargetIP)
}
return utils.PickRandomConnection(connections, i.randSource), nil
return utils.PickConnection(connections, selection, i.randSource)
}
func getPort(selection configuration.ServerSelection) (port uint16) {
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 {
switch {
case
server.VPN != selection.VPN,
utils.FilterByPossibilities(server.ISP, selection.ISPs),
utils.FilterByPossibilities(server.Country, selection.Countries),
utils.FilterByPossibilities(server.City, selection.Cities),
utils.FilterByPossibilities(server.Hostname, selection.Hostnames),
selection.OpenVPN.TCP && !server.TCP,
!selection.OpenVPN.TCP && !server.UDP:
utils.FilterByProtocol(selection, server.TCP, server.UDP):
default:
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 (
"github.com/qdm12/gluetun/internal/configuration"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/provider/utils"
)
@@ -10,7 +9,7 @@ import (
func (m *Mullvad) GetConnection(selection configuration.ServerSelection) (
connection models.Connection, err error) {
port := getPort(selection)
protocol := getProtocol(selection)
protocol := utils.GetProtocol(selection)
servers, err := m.filterServers(selection)
if err != nil {
@@ -31,39 +30,15 @@ func (m *Mullvad) GetConnection(selection configuration.ServerSelection) (
}
}
if selection.TargetIP != nil {
return utils.GetTargetIPConnection(connections, selection.TargetIP)
}
return utils.PickRandomConnection(connections, m.randSource), nil
return utils.PickConnection(connections, selection, m.randSource)
}
func getPort(selection configuration.ServerSelection) (port uint16) {
switch selection.VPN {
case constants.Wireguard:
customPort := selection.Wireguard.CustomPort
if customPort > 0 {
return customPort
}
const defaultPort = 51820
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
const (
defaultOpenVPNTCP = 443
defaultOpenVPNUDP = 1194
defaultWireguard = 51820
)
return utils.GetPort(selection, defaultOpenVPNTCP,
defaultOpenVPNUDP, defaultWireguard)
}

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
}
if selection.TargetIP != nil {
return utils.GetTargetIPConnection(connections, selection.TargetIP)
}
return utils.PickRandomConnection(connections, n.randSource), nil
return utils.PickConnection(connections, selection, n.randSource)
}

View File

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

View File

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

View File

@@ -38,15 +38,5 @@ func (p *PIA) GetConnection(selection configuration.ServerSelection) (
}
}
if selection.TargetIP != nil {
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
return utils.PickConnection(connections, selection, p.randSource)
}

View File

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

View File

@@ -33,7 +33,7 @@ var (
func (p *PIA) PortForward(ctx context.Context, client *http.Client,
logger logging.Logger, gateway net.IP, serverName string) (
port uint16, err error) {
server := constants.PIAServerWhereName(serverName)
server := constants.PIAServerWhereName(p.servers, serverName)
if !server.PortForward {
logger.Error("The server " + serverName +
" (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.GetTargetIPConnection(connections, selection.TargetIP)
}
return utils.PickRandomConnection(connections, p.randSource), nil
return utils.PickConnection(connections, selection, p.randSource)
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -12,9 +12,11 @@ func (s *Surfshark) filterServers(selection configuration.ServerSelection) (
switch {
case
utils.FilterByPossibilities(server.Region, selection.Regions),
utils.FilterByPossibilities(server.Country, selection.Countries),
utils.FilterByPossibilities(server.City, selection.Cities),
utils.FilterByPossibilities(server.Hostname, selection.Hostnames),
selection.OpenVPN.TCP && !server.TCP,
!selection.OpenVPN.TCP && !server.UDP:
utils.FilterByProtocol(selection, server.TCP, server.UDP),
selection.MultiHopOnly && !server.MultiHop:
default:
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.GetTargetIPConnection(connections, selection.TargetIP)
}
return utils.PickRandomConnection(connections, t.randSource), nil
return utils.PickConnection(connections, selection, t.randSource)
}

View File

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

View File

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

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