Compare commits

..

3 Commits

Author SHA1 Message Date
Quentin McGaw (desktop)
f569998c93 Fix: install latest apk-tools before using apk 2021-08-09 14:44:06 +00:00
Quentin McGaw (desktop)
9877366c51 Fix: install latest apk-tools by default 2021-08-09 14:43:46 +00:00
Quentin McGaw (desktop)
61066e3896 Fix restart mutex unlocking for loops 2021-08-09 14:38:15 +00:00
331 changed files with 8600 additions and 26851 deletions

View File

@@ -1,2 +1 @@
FROM qmcgaw/godevcontainer FROM qmcgaw/godevcontainer
RUN apk add wireguard-tools

View File

@@ -4,8 +4,6 @@ services:
vscode: vscode:
build: . build: .
image: godevcontainer image: godevcontainer
devices:
- /dev/net/tun:/dev/net/tun
volumes: volumes:
- ../:/workspace - ../:/workspace
# Docker socket to access Docker server # Docker socket to access Docker server
@@ -25,8 +23,7 @@ services:
- TZ= - TZ=
cap_add: cap_add:
# For debugging with dlv # For debugging with dlv
# - SYS_PTRACE - SYS_PTRACE
- NET_ADMIN
security_opt: security_opt:
# For debugging with dlv # For debugging with dlv
- seccomp:unconfined - seccomp:unconfined

View File

@@ -15,10 +15,6 @@ assignees: qdm12
**Host OS** (approximate answer is fine too): Ubuntu 18 **Host OS** (approximate answer is fine too): Ubuntu 18
<!---
🚧 If this is about the Unraid template see https://github.com/qdm12/gluetun/discussions/550
-->
**CPU arch** or **device name**: amd64 **CPU arch** or **device name**: amd64
**What VPN provider are you using**: **What VPN provider are you using**:

3
.github/labels.yml vendored
View File

@@ -70,9 +70,6 @@
- name: "Openvpn" - name: "Openvpn"
color: "ffc7ea" color: "ffc7ea"
description: "" description: ""
- name: "Wireguard"
color: "ffc7ea"
description: ""
- name: "Unbound (DNS over TLS)" - name: "Unbound (DNS over TLS)"
color: "ffc7ea" color: "ffc7ea"
description: "" description: ""

View File

@@ -1,6 +1,6 @@
ARG ALPINE_VERSION=3.14 ARG ALPINE_VERSION=3.14
ARG GO_ALPINE_VERSION=3.14 ARG GO_ALPINE_VERSION=3.13
ARG GO_VERSION=1.17 ARG GO_VERSION=1.16
ARG XCPUTRANSLATE_VERSION=v0.6.0 ARG XCPUTRANSLATE_VERSION=v0.6.0
ARG GOLANGCI_LINT_VERSION=v1.41.1 ARG GOLANGCI_LINT_VERSION=v1.41.1
ARG BUILDPLATFORM=linux/amd64 ARG BUILDPLATFORM=linux/amd64
@@ -68,8 +68,6 @@ LABEL \
org.opencontainers.image.description="VPN swiss-knife like client to tunnel to multiple VPN servers using OpenVPN, IPtables, DNS over TLS, Shadowsocks, an HTTP proxy and Alpine Linux" org.opencontainers.image.description="VPN swiss-knife like client to tunnel to multiple VPN servers using OpenVPN, IPtables, DNS over TLS, Shadowsocks, an HTTP proxy and Alpine Linux"
ENV VPNSP=pia \ ENV VPNSP=pia \
VERSION_INFORMATION=on \ VERSION_INFORMATION=on \
LOG_LEVEL=info \
VPN_TYPE=openvpn \
PROTOCOL=udp \ PROTOCOL=udp \
OPENVPN_VERSION=2.5 \ OPENVPN_VERSION=2.5 \
OPENVPN_VERBOSITY=1 \ OPENVPN_VERBOSITY=1 \
@@ -78,12 +76,6 @@ ENV VPNSP=pia \
OPENVPN_TARGET_IP= \ OPENVPN_TARGET_IP= \
OPENVPN_IPV6=off \ OPENVPN_IPV6=off \
OPENVPN_CUSTOM_CONFIG= \ OPENVPN_CUSTOM_CONFIG= \
OPENVPN_INTERFACE=tun0 \
WIREGUARD_PRIVATE_KEY= \
WIREGUARD_PRESHARED_KEY= \
WIREGUARD_ADDRESS= \
WIREGUARD_PORT= \
WIREGUARD_INTERFACE=wg0 \
TZ= \ TZ= \
PUID= \ PUID= \
PGID= \ PGID= \
@@ -155,10 +147,10 @@ ENV VPNSP=pia \
# Shadowsocks # Shadowsocks
SHADOWSOCKS=off \ SHADOWSOCKS=off \
SHADOWSOCKS_LOG=off \ SHADOWSOCKS_LOG=off \
SHADOWSOCKS_ADDRESS=":8388" \ SHADOWSOCKS_PORT=8388 \
SHADOWSOCKS_PASSWORD= \ SHADOWSOCKS_PASSWORD= \
SHADOWSOCKS_PASSWORD_SECRETFILE=/run/secrets/shadowsocks_password \ SHADOWSOCKS_PASSWORD_SECRETFILE=/run/secrets/shadowsocks_password \
SHADOWSOCKS_CIPHER=chacha20-ietf-poly1305 \ SHADOWSOCKS_METHOD=chacha20-ietf-poly1305 \
UPDATER_PERIOD=0 UPDATER_PERIOD=0
ENTRYPOINT ["/entrypoint"] ENTRYPOINT ["/entrypoint"]
EXPOSE 8000/tcp 8888/tcp 8388/tcp 8388/udp EXPOSE 8000/tcp 8888/tcp 8388/tcp 8388/udp

View File

@@ -5,7 +5,7 @@ HideMyAss, IPVanish, IVPN, Mullvad, NordVPN, Privado, Private Internet Access, P
ProtonVPN, PureVPN, Surfshark, TorGuard, VPNUnlimited, VyprVPN and Windscribe VPN servers ProtonVPN, PureVPN, Surfshark, TorGuard, VPNUnlimited, VyprVPN and Windscribe VPN servers
using Go, OpenVPN, iptables, DNS over TLS, ShadowSocks and an HTTP proxy* using Go, OpenVPN, iptables, DNS over TLS, ShadowSocks and an HTTP proxy*
**ANNOUNCEMENT**: Wireguard is now supported for all providers supporting it! **ANNOUNCEMENT**:
![Title image](https://raw.githubusercontent.com/qdm12/gluetun/master/title.svg) ![Title image](https://raw.githubusercontent.com/qdm12/gluetun/master/title.svg)
@@ -55,10 +55,9 @@ using Go, OpenVPN, iptables, DNS over TLS, ShadowSocks and an HTTP proxy*
## Features ## Features
- Based on Alpine 3.14 for a small Docker image of 31MB - Based on Alpine 3.13 for a small Docker image of 54MB
- Supports: **Cyberghost**, **FastestVPN**, **HideMyAss**, **IPVanish**, **IVPN**, **Mullvad**, **NordVPN**, **Privado**, **Private Internet Access**, **PrivateVPN**, **ProtonVPN**, **PureVPN**, **Surfshark**, **TorGuard**, **VPNUnlimited**, **Vyprvpn**, **Windscribe** servers - Supports: **Cyberghost**, **FastestVPN**, **HideMyAss**, **IPVanish**, **IVPN**, **Mullvad**, **NordVPN**, **Privado**, **Private Internet Access**, **PrivateVPN**, **ProtonVPN**, **PureVPN**, **Surfshark**, **TorGuard**, **VPNUnlimited**, **Vyprvpn**, **Windscribe** servers
- Supports OpenVPN - Supports Openvpn only for now
- Supports Wireguard for **Mullvad**, **Ivpn** and **Windscribe** (more in progress, see #134)
- DNS over TLS baked in with service provider(s) of your choice - DNS over TLS baked in with service provider(s) of your choice
- DNS fine blocking of malicious/ads/surveillance hostnames and IP addresses, with live update every 24 hours - DNS fine blocking of malicious/ads/surveillance hostnames and IP addresses, with live update every 24 hours
- Choose the vpn network protocol, `udp` or `tcp` - Choose the vpn network protocol, `udp` or `tcp`
@@ -111,7 +110,6 @@ The following points are all optional but should give you insights on all the po
- [Test your setup](https://github.com/qdm12/gluetun/wiki/Test-your-setup) - [Test your setup](https://github.com/qdm12/gluetun/wiki/Test-your-setup)
- [How to connect other containers and devices to Gluetun](https://github.com/qdm12/gluetun/wiki/Connect-to-gluetun) - [How to connect other containers and devices to Gluetun](https://github.com/qdm12/gluetun/wiki/Connect-to-gluetun)
- [How to use Wireguard](https://github.com/qdm12/gluetun/wiki/Wireguard)
- [VPN server side port forwarding](https://github.com/qdm12/gluetun/wiki/Port-forwarding) - [VPN server side port forwarding](https://github.com/qdm12/gluetun/wiki/Port-forwarding)
- [HTTP control server](https://github.com/qdm12/gluetun/wiki/HTTP-Control-server) to automate things, restart Openvpn etc. - [HTTP control server](https://github.com/qdm12/gluetun/wiki/HTTP-Control-server) to automate things, restart Openvpn etc.
- Update the image with `docker pull qmcgaw/gluetun:latest`. See this [Wiki document](https://github.com/qdm12/gluetun/wiki/Docker-image-tags) for Docker tags available. - Update the image with `docker pull qmcgaw/gluetun:latest`. See this [Wiki document](https://github.com/qdm12/gluetun/wiki/Docker-image-tags) for Docker tags available.

View File

@@ -4,6 +4,7 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"net"
"net/http" "net/http"
"os" "os"
"os/signal" "os/signal"
@@ -22,17 +23,15 @@ import (
"github.com/qdm12/gluetun/internal/healthcheck" "github.com/qdm12/gluetun/internal/healthcheck"
"github.com/qdm12/gluetun/internal/httpproxy" "github.com/qdm12/gluetun/internal/httpproxy"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/netlink"
"github.com/qdm12/gluetun/internal/openvpn" "github.com/qdm12/gluetun/internal/openvpn"
"github.com/qdm12/gluetun/internal/portforward"
"github.com/qdm12/gluetun/internal/publicip" "github.com/qdm12/gluetun/internal/publicip"
"github.com/qdm12/gluetun/internal/routing" "github.com/qdm12/gluetun/internal/routing"
"github.com/qdm12/gluetun/internal/server" "github.com/qdm12/gluetun/internal/server"
"github.com/qdm12/gluetun/internal/shadowsocks" "github.com/qdm12/gluetun/internal/shadowsocks"
"github.com/qdm12/gluetun/internal/storage" "github.com/qdm12/gluetun/internal/storage"
"github.com/qdm12/gluetun/internal/tun" "github.com/qdm12/gluetun/internal/unix"
"github.com/qdm12/gluetun/internal/updater" "github.com/qdm12/gluetun/internal/updater"
"github.com/qdm12/gluetun/internal/vpn" versionpkg "github.com/qdm12/gluetun/internal/version"
"github.com/qdm12/golibs/command" "github.com/qdm12/golibs/command"
"github.com/qdm12/golibs/logging" "github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/params" "github.com/qdm12/golibs/params"
@@ -60,32 +59,29 @@ func main() {
Created: created, Created: created,
} }
background := context.Background() ctx := context.Background()
signalCtx, stop := signal.NotifyContext(background, syscall.SIGINT, syscall.SIGTERM, os.Interrupt) ctx, stop := signal.NotifyContext(ctx, syscall.SIGINT, syscall.SIGTERM, os.Interrupt)
ctx, cancel := context.WithCancel(background) ctx, cancel := context.WithCancel(ctx)
logger := logging.New(logging.Settings{ logger := logging.NewParent(logging.Settings{
Level: logging.LevelInfo, Level: logging.LevelInfo,
}) })
args := os.Args args := os.Args
tun := tun.New() unix := unix.New()
netLinker := netlink.New()
cli := cli.New() cli := cli.New()
env := params.New() env := params.NewEnv()
cmder := command.NewCmder() cmder := command.NewCmder()
errorCh := make(chan error) errorCh := make(chan error)
go func() { go func() {
errorCh <- _main(ctx, buildInfo, args, logger, env, tun, netLinker, cmder, cli) errorCh <- _main(ctx, buildInfo, args, logger, env, unix, cmder, cli)
}() }()
select { select {
case <-signalCtx.Done(): case <-ctx.Done():
stop() stop()
fmt.Println("")
logger.Warn("Caught OS signal, shutting down") logger.Warn("Caught OS signal, shutting down")
cancel()
case err := <-errorCh: case err := <-errorCh:
stop() stop()
close(errorCh) close(errorCh)
@@ -117,9 +113,8 @@ var (
//nolint:gocognit,gocyclo //nolint:gocognit,gocyclo
func _main(ctx context.Context, buildInfo models.BuildInformation, func _main(ctx context.Context, buildInfo models.BuildInformation,
args []string, logger logging.ParentLogger, env params.Interface, args []string, logger logging.ParentLogger, env params.Env,
tun tun.Interface, netLinker netlink.NetLinker, cmder command.RunStarter, unix unix.Unix, cmder command.RunStarter, cli cli.CLIer) error {
cli cli.CLIer) error {
if len(args) > 1 { // cli operation if len(args) > 1 { // cli operation
switch args[1] { switch args[1] {
case "healthcheck": case "healthcheck":
@@ -127,7 +122,7 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
case "clientkey": case "clientkey":
return cli.ClientKey(args[2:]) return cli.ClientKey(args[2:])
case "openvpnconfig": case "openvpnconfig":
return cli.OpenvpnConfig(logger, env) return cli.OpenvpnConfig(logger)
case "update": case "update":
return cli.Update(ctx, args[2:], logger) return cli.Update(ctx, args[2:], logger)
default: default:
@@ -135,37 +130,19 @@ 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, 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
const clientTimeout = 15 * time.Second const clientTimeout = 15 * time.Second
httpClient := &http.Client{Timeout: clientTimeout} httpClient := &http.Client{Timeout: clientTimeout}
// Create configurators // Create configurators
alpineConf := alpine.New() alpineConf := alpine.New()
ovpnConf := openvpn.New( ovpnConf := openvpn.NewConfigurator(
logger.NewChild(logging.Settings{Prefix: "openvpn configurator: "}), logger.NewChild(logging.Settings{Prefix: "openvpn configurator: "}),
cmder, puid, pgid) unix, cmder)
dnsCrypto := dnscrypto.New(httpClient, "", "") dnsCrypto := dnscrypto.New(httpClient, "", "")
const cacertsPath = "/etc/ssl/certs/ca-certificates.crt" const cacertsPath = "/etc/ssl/certs/ca-certificates.crt"
dnsConf := unbound.NewConfigurator(nil, cmder, dnsCrypto, dnsConf := unbound.NewConfigurator(nil, cmder, dnsCrypto,
"/etc/unbound", "/usr/sbin/unbound", cacertsPath) "/etc/unbound", "/usr/sbin/unbound", cacertsPath)
announcementExp, err := time.Parse(time.RFC3339, "2021-10-02T00:00:00Z") announcementExp, err := time.Parse(time.RFC3339, "2021-07-22T00:00:00Z")
if err != nil { if err != nil {
return err return err
} }
@@ -176,7 +153,7 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
Version: buildInfo.Version, Version: buildInfo.Version,
Commit: buildInfo.Commit, Commit: buildInfo.Commit,
BuildDate: buildInfo.Created, BuildDate: buildInfo.Created,
Announcement: "Wireguard is now supported for Mullvad, IVPN and Windscribe!", Announcement: "",
AnnounceExp: announcementExp, AnnounceExp: announcementExp,
// Sponsor information // Sponsor information
PaypalUser: "qmcgaw", PaypalUser: "qmcgaw",
@@ -199,6 +176,12 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
return err return err
} }
var allSettings configuration.Settings
err = allSettings.Read(env,
logger.NewChild(logging.Settings{Prefix: "configuration: "}))
if err != nil {
return err
}
logger.Info(allSettings.String()) logger.Info(allSettings.String())
if err := os.MkdirAll("/tmp/gluetun", 0644); err != nil { if err := os.MkdirAll("/tmp/gluetun", 0644); err != nil {
@@ -208,6 +191,18 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
return err return err
} }
// TODO run this in a loop or in openvpn to reload from file without restarting
storage := storage.New(
logger.NewChild(logging.Settings{Prefix: "storage: "}),
constants.ServersData)
allServers, err := storage.SyncServers(constants.GetAllServers())
if err != nil {
return err
}
// Should never change
puid, pgid := allSettings.System.PUID, allSettings.System.PGID
const defaultUsername = "nonrootuser" const defaultUsername = "nonrootuser"
nonRootUsername, err := alpineConf.CreateUser(defaultUsername, puid) nonRootUsername, err := alpineConf.CreateUser(defaultUsername, puid)
if err != nil { if err != nil {
@@ -219,13 +214,12 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
// set it for Unbound // set it for Unbound
// TODO remove this when migrating to qdm12/dns v2 // TODO remove this when migrating to qdm12/dns v2
allSettings.DNS.Unbound.Username = nonRootUsername allSettings.DNS.Unbound.Username = nonRootUsername
allSettings.VPN.OpenVPN.ProcUser = nonRootUsername
if err := os.Chown("/etc/unbound", puid, pgid); err != nil { if err := os.Chown("/etc/unbound", puid, pgid); err != nil {
return err return err
} }
firewallLogLevel := allSettings.Log.Level firewallLogLevel := logging.LevelInfo
if allSettings.Firewall.Debug { if allSettings.Firewall.Debug {
firewallLogLevel = logging.LevelDebug firewallLogLevel = logging.LevelDebug
} }
@@ -233,7 +227,7 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
Prefix: "routing: ", Prefix: "routing: ",
Level: firewallLogLevel, Level: firewallLogLevel,
}) })
routingConf := routing.New(netLinker, routingLogger) routingConf := routing.NewRouting(routingLogger)
defaultInterface, defaultGateway, err := routingConf.DefaultRoute() defaultInterface, defaultGateway, err := routingConf.DefaultRoute()
if err != nil { if err != nil {
@@ -277,14 +271,17 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
return err return err
} }
if err := tun.Check(constants.TunnelDevice); err != nil { if err := ovpnConf.CheckTUN(); err != nil {
logger.Info(err.Error() + "; creating it...") logger.Warn(err.Error())
err = tun.Create(constants.TunnelDevice) err = ovpnConf.CreateTUN()
if err != nil { if err != nil {
return err return err
} }
} }
tunnelReadyCh := make(chan struct{})
defer close(tunnelReadyCh)
if allSettings.Firewall.Enabled { if allSettings.Firewall.Enabled {
err := firewallConf.SetEnabled(ctx, true) // disabled by default err := firewallConf.SetEnabled(ctx, true) // disabled by default
if err != nil { if err != nil {
@@ -293,8 +290,7 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
} }
for _, vpnPort := range allSettings.Firewall.VPNInputPorts { for _, vpnPort := range allSettings.Firewall.VPNInputPorts {
vpnIntf := allSettings.VPN.VPNInterface() err = firewallConf.SetAllowedPort(ctx, vpnPort, string(constants.TUN))
err = firewallConf.SetAllowedPort(ctx, vpnPort, vpnIntf)
if err != nil { if err != nil {
return err return err
} }
@@ -325,12 +321,21 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
tickersGroupHandler := goshutdown.NewGroupHandler("tickers", defaultGroupSettings) tickersGroupHandler := goshutdown.NewGroupHandler("tickers", defaultGroupSettings)
otherGroupHandler := goshutdown.NewGroupHandler("other", defaultGroupSettings) otherGroupHandler := goshutdown.NewGroupHandler("other", defaultGroupSettings)
portForwardLogger := logger.NewChild(logging.Settings{Prefix: "port forwarding: "}) openvpnLooper := openvpn.NewLoop(allSettings.OpenVPN, nonRootUsername, puid, pgid, allServers,
portForwardLooper := portforward.NewLoop(allSettings.VPN.Provider.PortForwarding, ovpnConf, firewallConf, logger, httpClient, tunnelReadyCh)
httpClient, firewallConf, portForwardLogger) openvpnHandler, openvpnCtx, openvpnDone := goshutdown.NewGoRoutineHandler(
portForwardHandler, portForwardCtx, portForwardDone := goshutdown.NewGoRoutineHandler( "openvpn", goshutdown.GoRoutineSettings{Timeout: time.Second})
"port forwarding", goshutdown.GoRoutineSettings{Timeout: time.Second}) // wait for restartOpenvpn
go portForwardLooper.Run(portForwardCtx, portForwardDone) go openvpnLooper.Run(openvpnCtx, openvpnDone)
updaterLooper := updater.NewLooper(allSettings.Updater,
allServers, storage, openvpnLooper.SetServers, httpClient,
logger.NewChild(logging.Settings{Prefix: "updater: "}))
updaterHandler, updaterCtx, updaterDone := goshutdown.NewGoRoutineHandler(
"updater", defaultGoRoutineSettings)
// wait for updaterLooper.Restart() or its ticket launched with RunRestartTicker
go updaterLooper.Run(updaterCtx, updaterDone)
tickersGroupHandler.Add(updaterHandler)
unboundLogger := logger.NewChild(logging.Settings{Prefix: "dns over tls: "}) unboundLogger := logger.NewChild(logging.Settings{Prefix: "dns over tls: "})
unboundLooper := dns.NewLoop(dnsConf, allSettings.DNS, httpClient, unboundLooper := dns.NewLoop(dnsConf, allSettings.DNS, httpClient,
@@ -341,11 +346,6 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
go unboundLooper.Run(dnsCtx, dnsDone) go unboundLooper.Run(dnsCtx, dnsDone)
otherGroupHandler.Add(dnsHandler) otherGroupHandler.Add(dnsHandler)
dnsTickerHandler, dnsTickerCtx, dnsTickerDone := goshutdown.NewGoRoutineHandler(
"dns ticker", defaultGoRoutineSettings)
go unboundLooper.RunRestartTicker(dnsTickerCtx, dnsTickerDone)
controlGroupHandler.Add(dnsTickerHandler)
publicIPLooper := publicip.NewLoop(httpClient, publicIPLooper := publicip.NewLoop(httpClient,
logger.NewChild(logging.Settings{Prefix: "ip getter: "}), logger.NewChild(logging.Settings{Prefix: "ip getter: "}),
allSettings.PublicIP, puid, pgid) allSettings.PublicIP, puid, pgid)
@@ -359,29 +359,6 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
go publicIPLooper.RunRestartTicker(pubIPTickerCtx, pubIPTickerDone) go publicIPLooper.RunRestartTicker(pubIPTickerCtx, pubIPTickerDone)
tickersGroupHandler.Add(pubIPTickerHandler) tickersGroupHandler.Add(pubIPTickerHandler)
vpnLogger := logger.NewChild(logging.Settings{Prefix: "vpn: "})
vpnLooper := vpn.NewLoop(allSettings.VPN,
allServers, ovpnConf, netLinker, firewallConf, routingConf, portForwardLooper,
cmder, publicIPLooper, unboundLooper, vpnLogger, httpClient,
buildInfo, allSettings.VersionInformation)
vpnHandler, vpnCtx, vpnDone := goshutdown.NewGoRoutineHandler(
"vpn", goshutdown.GoRoutineSettings{Timeout: time.Second})
go vpnLooper.Run(vpnCtx, vpnDone)
updaterLooper := updater.NewLooper(allSettings.Updater,
allServers, storage, vpnLooper.SetServers, httpClient,
logger.NewChild(logging.Settings{Prefix: "updater: "}))
updaterHandler, updaterCtx, updaterDone := goshutdown.NewGoRoutineHandler(
"updater", defaultGoRoutineSettings)
// wait for updaterLooper.Restart() or its ticket launched with RunRestartTicker
go updaterLooper.Run(updaterCtx, updaterDone)
tickersGroupHandler.Add(updaterHandler)
updaterTickerHandler, updaterTickerCtx, updaterTickerDone := goshutdown.NewGoRoutineHandler(
"updater ticker", defaultGoRoutineSettings)
go updaterLooper.RunRestartTicker(updaterTickerCtx, updaterTickerDone)
controlGroupHandler.Add(updaterTickerHandler)
httpProxyLooper := httpproxy.NewLoop( httpProxyLooper := httpproxy.NewLoop(
logger.NewChild(logging.Settings{Prefix: "http proxy: "}), logger.NewChild(logging.Settings{Prefix: "http proxy: "}),
allSettings.HTTPProxy) allSettings.HTTPProxy)
@@ -397,18 +374,26 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
go shadowsocksLooper.Run(shadowsocksCtx, shadowsocksDone) go shadowsocksLooper.Run(shadowsocksCtx, shadowsocksDone)
otherGroupHandler.Add(shadowsocksHandler) otherGroupHandler.Add(shadowsocksHandler)
eventsRoutingHandler, eventsRoutingCtx, eventsRoutingDone := goshutdown.NewGoRoutineHandler(
"events routing", defaultGoRoutineSettings)
go routeReadyEvents(eventsRoutingCtx, eventsRoutingDone, buildInfo, tunnelReadyCh,
unboundLooper, updaterLooper, publicIPLooper, routingConf, logger, httpClient,
allSettings.VersionInformation, allSettings.OpenVPN.Provider.PortForwarding.Enabled, openvpnLooper.PortForward,
)
controlGroupHandler.Add(eventsRoutingHandler)
controlServerAddress := ":" + strconv.Itoa(int(allSettings.ControlServer.Port)) controlServerAddress := ":" + strconv.Itoa(int(allSettings.ControlServer.Port))
controlServerLogging := allSettings.ControlServer.Log controlServerLogging := allSettings.ControlServer.Log
httpServerHandler, httpServerCtx, httpServerDone := goshutdown.NewGoRoutineHandler( httpServerHandler, httpServerCtx, httpServerDone := goshutdown.NewGoRoutineHandler(
"http server", defaultGoRoutineSettings) "http server", defaultGoRoutineSettings)
httpServer := server.New(httpServerCtx, controlServerAddress, controlServerLogging, httpServer := server.New(httpServerCtx, controlServerAddress, controlServerLogging,
logger.NewChild(logging.Settings{Prefix: "http server: "}), logger.NewChild(logging.Settings{Prefix: "http server: "}),
buildInfo, vpnLooper, portForwardLooper, unboundLooper, updaterLooper, publicIPLooper) buildInfo, openvpnLooper, unboundLooper, updaterLooper, publicIPLooper)
go httpServer.Run(httpServerCtx, httpServerDone) go httpServer.Run(httpServerCtx, httpServerDone)
controlGroupHandler.Add(httpServerHandler) controlGroupHandler.Add(httpServerHandler)
healthLogger := logger.NewChild(logging.Settings{Prefix: "healthcheck: "}) healthLogger := logger.NewChild(logging.Settings{Prefix: "healthcheck: "})
healthcheckServer := healthcheck.NewServer(allSettings.Health, healthLogger, vpnLooper) healthcheckServer := healthcheck.NewServer(allSettings.Health, healthLogger, openvpnLooper)
healthServerHandler, healthServerCtx, healthServerDone := goshutdown.NewGoRoutineHandler( healthServerHandler, healthServerCtx, healthServerDone := goshutdown.NewGoRoutineHandler(
"HTTP health server", defaultGoRoutineSettings) "HTTP health server", defaultGoRoutineSettings)
go healthcheckServer.Run(healthServerCtx, healthServerDone) go healthcheckServer.Run(healthServerCtx, healthServerDone)
@@ -421,14 +406,21 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
} }
orderHandler := goshutdown.NewOrder("gluetun", orderSettings) orderHandler := goshutdown.NewOrder("gluetun", orderSettings)
orderHandler.Append(controlGroupHandler, tickersGroupHandler, healthServerHandler, orderHandler.Append(controlGroupHandler, tickersGroupHandler, healthServerHandler,
vpnHandler, portForwardHandler, otherGroupHandler) openvpnHandler, otherGroupHandler)
// Start VPN for the first time in a blocking call // Start openvpn for the first time in a blocking call
// until the VPN is launched // until openvpn is launched
_, _ = vpnLooper.ApplyStatus(ctx, constants.Running) // TODO option to disable with variable _, _ = openvpnLooper.ApplyStatus(ctx, constants.Running) // TODO option to disable with variable
<-ctx.Done() <-ctx.Done()
if allSettings.OpenVPN.Provider.PortForwarding.Enabled {
logger.Info("Clearing forwarded port status file " + allSettings.OpenVPN.Provider.PortForwarding.Filepath)
if err := os.Remove(allSettings.OpenVPN.Provider.PortForwarding.Filepath); err != nil {
logger.Error(err.Error())
}
}
return orderHandler.Shutdown(context.Background()) return orderHandler.Shutdown(context.Background())
} }
@@ -453,3 +445,73 @@ func printVersions(ctx context.Context, logger logging.Logger,
return nil return nil
} }
func routeReadyEvents(ctx context.Context, done chan<- struct{}, buildInfo models.BuildInformation,
tunnelReadyCh <-chan struct{},
unboundLooper dns.Looper, updaterLooper updater.Looper, publicIPLooper publicip.Looper,
routing routing.VPNGetter, logger logging.Logger, httpClient *http.Client,
versionInformation, portForwardingEnabled bool, startPortForward func(vpnGateway net.IP)) {
defer close(done)
// for linters only
var restartTickerContext context.Context
var restartTickerCancel context.CancelFunc = func() {}
unboundTickerDone := make(chan struct{})
close(unboundTickerDone)
updaterTickerDone := make(chan struct{})
close(updaterTickerDone)
first := true
for {
select {
case <-ctx.Done():
restartTickerCancel() // for linters only
<-unboundTickerDone
<-updaterTickerDone
return
case <-tunnelReadyCh: // blocks until openvpn is connected
vpnDestination, err := routing.VPNDestinationIP()
if err != nil {
logger.Warn(err.Error())
} else {
logger.Info("VPN routing IP address: " + vpnDestination.String())
}
if unboundLooper.GetSettings().Enabled {
_, _ = unboundLooper.ApplyStatus(ctx, constants.Running)
}
restartTickerCancel() // stop previous restart tickers
<-unboundTickerDone
<-updaterTickerDone
restartTickerContext, restartTickerCancel = context.WithCancel(ctx)
// Runs the Public IP getter job once
_, _ = publicIPLooper.ApplyStatus(ctx, constants.Running)
if versionInformation && first {
first = false
message, err := versionpkg.GetMessage(ctx, buildInfo, httpClient)
if err != nil {
logger.Error("cannot get version information: " + err.Error())
} else {
logger.Info(message)
}
}
unboundTickerDone = make(chan struct{})
updaterTickerDone = make(chan struct{})
go unboundLooper.RunRestartTicker(restartTickerContext, unboundTickerDone)
go updaterLooper.RunRestartTicker(restartTickerContext, updaterTickerDone)
if portForwardingEnabled {
// vpnGateway required only for PIA
vpnGateway, err := routing.VPNLocalGatewayIP()
if err != nil {
logger.Error("cannot get VPN local gateway IP: " + err.Error())
}
logger.Info("VPN gateway IP address: " + vpnGateway.String())
startPortForward(vpnGateway)
}
}
}
}

View File

@@ -15,11 +15,10 @@ services:
volumes: volumes:
- /yourpath:/gluetun - /yourpath:/gluetun
environment: environment:
# More variables are available, see the Wiki table # More variables are available, see the readme table
- OPENVPN_USER= - OPENVPN_USER=
- OPENVPN_PASSWORD= - OPENVPN_PASSWORD=
- VPNSP=private internet access - VPNSP=private internet access
- VPN_TYPE=openvpn
# Timezone for accurate logs times # Timezone for accurate logs times
- TZ= - TZ=
restart: always restart: always

28
go.mod
View File

@@ -1,40 +1,18 @@
module github.com/qdm12/gluetun module github.com/qdm12/gluetun
go 1.17 go 1.16
require ( require (
github.com/fatih/color v1.12.0 github.com/fatih/color v1.12.0
github.com/golang/mock v1.6.0 github.com/golang/mock v1.6.0
github.com/qdm12/dns v1.11.0 github.com/qdm12/dns v1.11.0
github.com/qdm12/golibs v0.0.0-20210822203818-5c568b0777b6 github.com/qdm12/golibs v0.0.0-20210723175634-a75ca7fd74c2
github.com/qdm12/goshutdown v0.1.0 github.com/qdm12/goshutdown v0.1.0
github.com/qdm12/gosplash v0.1.0 github.com/qdm12/gosplash v0.1.0
github.com/qdm12/ss-server v0.3.0 github.com/qdm12/ss-server v0.2.0
github.com/qdm12/updated v0.0.0-20210603204757-205acfe6937e github.com/qdm12/updated v0.0.0-20210603204757-205acfe6937e
github.com/stretchr/testify v1.7.0 github.com/stretchr/testify v1.7.0
github.com/vishvananda/netlink v1.1.0 github.com/vishvananda/netlink v1.1.0
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c
golang.zx2c4.com/wireguard v0.0.0-20210805125648-3957e9b9dd19
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20210803171230-4253848d036c
inet.af/netaddr v0.0.0-20210718074554-06ca8145d722 inet.af/netaddr v0.0.0-20210718074554-06ca8145d722
) )
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/google/go-cmp v0.5.5 // indirect
github.com/josharian/native v0.0.0-20200817173448-b6b71def0850 // indirect
github.com/mattn/go-colorable v0.1.8 // indirect
github.com/mattn/go-isatty v0.0.12 // indirect
github.com/mdlayher/genetlink v1.0.0 // indirect
github.com/mdlayher/netlink v1.4.0 // indirect
github.com/miekg/dns v1.1.40 // indirect
github.com/mr-tron/base58 v1.2.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df // indirect
go4.org/intern v0.0.0-20210108033219-3eb7198706b2 // indirect
go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222180813-1025295fd063 // indirect
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 // indirect
golang.org/x/net v0.0.0-20210504132125-bbd867fde50d // indirect
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
)

80
go.sum
View File

@@ -33,28 +33,11 @@ github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3K
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gotify/go-api-client/v2 v2.0.4/go.mod h1:VKiah/UK20bXsr0JObE1eBVLW44zbBouzjuri9iwjFU= github.com/gotify/go-api-client/v2 v2.0.4/go.mod h1:VKiah/UK20bXsr0JObE1eBVLW44zbBouzjuri9iwjFU=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/josharian/native v0.0.0-20200817173448-b6b71def0850 h1:uhL5Gw7BINiiPAo24A2sxkcDI0Jt/sqp1v5xQCniEFA=
github.com/josharian/native v0.0.0-20200817173448-b6b71def0850/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw=
github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ=
github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok=
github.com/jsimonetti/rtnetlink v0.0.0-20201216134343-bde56ed16391/go.mod h1:cR77jAZG3Y3bsb8hF6fHJbFoyFukLFOkQ98S0pQz3xw=
github.com/jsimonetti/rtnetlink v0.0.0-20201220180245-69540ac93943/go.mod h1:z4c53zj6Eex712ROyh8WI0ihysb5j2ROyV42iNogmAs=
github.com/jsimonetti/rtnetlink v0.0.0-20210122163228-8d122574c736/go.mod h1:ZXpIyOK59ZnN7J0BV99cZUPmsqDRZ3eq5X+st7u/oSA=
github.com/jsimonetti/rtnetlink v0.0.0-20210212075122-66c871082f2b h1:c3NTyLNozICy8B4mlMXemD3z/gXgQzVXZS/HqT+i3do=
github.com/jsimonetti/rtnetlink v0.0.0-20210212075122-66c871082f2b/go.mod h1:8w9Rh8m+aHZIG69YPGGem1i5VzoyRC8nw2kA8B+ik5U=
github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
@@ -68,24 +51,8 @@ github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43 h1:WgyLFv10Ov49JAQI/ZLUkCZ7VJS3r74hwFIGXJsgZlY=
github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43/go.mod h1:+t7E0lkKfbBsebllff1xdTmyJt8lH37niI6kwFk9OTo=
github.com/mdlayher/genetlink v1.0.0 h1:OoHN1OdyEIkScEmRgxLEe2M9U8ClMytqA5niynLtfj0=
github.com/mdlayher/genetlink v1.0.0/go.mod h1:0rJ0h4itni50A86M2kHcgS85ttZazNt7a8H2a2cw0Gc=
github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA=
github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M=
github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY=
github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o=
github.com/mdlayher/netlink v1.2.0/go.mod h1:kwVW1io0AZy9A1E2YYgaD4Cj+C+GPkU6klXCMzIJ9p8=
github.com/mdlayher/netlink v1.2.1/go.mod h1:bacnNlfhqHqqLo4WsYeXSqfyXkInQ9JneWI68v1KwSU=
github.com/mdlayher/netlink v1.2.2-0.20210123213345-5cc92139ae3e/go.mod h1:bacnNlfhqHqqLo4WsYeXSqfyXkInQ9JneWI68v1KwSU=
github.com/mdlayher/netlink v1.3.0/go.mod h1:xK/BssKuwcRXHrtN04UBkwQ6dY9VviGGuriDdoPSWys=
github.com/mdlayher/netlink v1.4.0 h1:n3ARR+Fm0dDv37dj5wSWZXDKcy+U0zwcXS3zKMnSiT0=
github.com/mdlayher/netlink v1.4.0/go.mod h1:dRJi5IABcZpBD2A3D0Mv/AiX8I9uDEu5oGkAVrekmf8=
github.com/miekg/dns v1.1.40 h1:pyyPFfGMnciYUk/mXpKkVmeMQjfXqt3FAJ2hy7tPiLA= github.com/miekg/dns v1.1.40 h1:pyyPFfGMnciYUk/mXpKkVmeMQjfXqt3FAJ2hy7tPiLA=
github.com/miekg/dns v1.1.40/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/miekg/dns v1.1.40/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721 h1:RlZweED6sbSArvlE924+mUcZuXKLBHA35U7LN621Bws=
github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721/go.mod h1:Ickgr2WtCLZ2MDGd4Gr0geeCH5HybhRJbonOgQpvSxc=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
@@ -99,15 +66,14 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/qdm12/dns v1.11.0 h1:jpcD5DZXXQSQe5a263PL09ghukiIdptvXFOZvyKEm6Q= github.com/qdm12/dns v1.11.0 h1:jpcD5DZXXQSQe5a263PL09ghukiIdptvXFOZvyKEm6Q=
github.com/qdm12/dns v1.11.0/go.mod h1:FmQsNOUcrrZ4UFzWAiED56AKXeNgaX3ySbmPwEfNjjE= github.com/qdm12/dns v1.11.0/go.mod h1:FmQsNOUcrrZ4UFzWAiED56AKXeNgaX3ySbmPwEfNjjE=
github.com/qdm12/golibs v0.0.0-20210603202746-e5494e9c2ebb/go.mod h1:15RBzkun0i8XB7ADIoLJWp9ITRgsz3LroEI2FiOXLRg= github.com/qdm12/golibs v0.0.0-20210603202746-e5494e9c2ebb/go.mod h1:15RBzkun0i8XB7ADIoLJWp9ITRgsz3LroEI2FiOXLRg=
github.com/qdm12/golibs v0.0.0-20210723175634-a75ca7fd74c2 h1:FMeOhe/bGloI0T5Wb6QB7/rfOqgFeI//UF/N/f7PUCI=
github.com/qdm12/golibs v0.0.0-20210723175634-a75ca7fd74c2/go.mod h1:6aRbg4Z/bTbm9JfxsGXfWKHi7zsOvPfUTK1S5HuAFKg= github.com/qdm12/golibs v0.0.0-20210723175634-a75ca7fd74c2/go.mod h1:6aRbg4Z/bTbm9JfxsGXfWKHi7zsOvPfUTK1S5HuAFKg=
github.com/qdm12/golibs v0.0.0-20210822203818-5c568b0777b6 h1:bge5AL7cjHJMPz+5IOz5yF01q/l8No6+lIEBieA8gMg=
github.com/qdm12/golibs v0.0.0-20210822203818-5c568b0777b6/go.mod h1:6aRbg4Z/bTbm9JfxsGXfWKHi7zsOvPfUTK1S5HuAFKg=
github.com/qdm12/goshutdown v0.1.0 h1:lmwnygdXtnr2pa6VqfR/bm8077/BnBef1+7CP96B7Sw= github.com/qdm12/goshutdown v0.1.0 h1:lmwnygdXtnr2pa6VqfR/bm8077/BnBef1+7CP96B7Sw=
github.com/qdm12/goshutdown v0.1.0/go.mod h1:/LP3MWLqI+wGH/ijfaUG+RHzBbKXIiVKnrg5vXOCf6Q= github.com/qdm12/goshutdown v0.1.0/go.mod h1:/LP3MWLqI+wGH/ijfaUG+RHzBbKXIiVKnrg5vXOCf6Q=
github.com/qdm12/gosplash v0.1.0 h1:Sfl+zIjFZFP7b0iqf2l5UkmEY97XBnaKkH3FNY6Gf7g= github.com/qdm12/gosplash v0.1.0 h1:Sfl+zIjFZFP7b0iqf2l5UkmEY97XBnaKkH3FNY6Gf7g=
github.com/qdm12/gosplash v0.1.0/go.mod h1:+A3fWW4/rUeDXhY3ieBzwghKdnIPFJgD8K3qQkenJlw= github.com/qdm12/gosplash v0.1.0/go.mod h1:+A3fWW4/rUeDXhY3ieBzwghKdnIPFJgD8K3qQkenJlw=
github.com/qdm12/ss-server v0.3.0 h1:BfKv4OU6dYb2KcDMYpTc7LIuO2jB73g3JCzy988GrLI= github.com/qdm12/ss-server v0.2.0 h1:+togLzeeLAJ68MD1JqOWvYi9rl9t/fx1Qh7wKzZhY1g=
github.com/qdm12/ss-server v0.3.0/go.mod h1:ug+nWfuzKw/h5fxL1B6e9/OhkVuWJX4i2V1Pf0pJU1o= github.com/qdm12/ss-server v0.2.0/go.mod h1:+1bWO1EfWNvsGM5Cuep6vneChK2OHniqtAsED9Fh1y0=
github.com/qdm12/updated v0.0.0-20210603204757-205acfe6937e h1:4q+uFLawkaQRq3yARYLsjJPZd2wYwxn4g6G/5v0xW1g= github.com/qdm12/updated v0.0.0-20210603204757-205acfe6937e h1:4q+uFLawkaQRq3yARYLsjJPZd2wYwxn4g6G/5v0xW1g=
github.com/qdm12/updated v0.0.0-20210603204757-205acfe6937e/go.mod h1:UvJRGkZ9XL3/D7e7JiTTVLm1F3Cymd3/gFpD6frEpBo= github.com/qdm12/updated v0.0.0-20210603204757-205acfe6937e/go.mod h1:UvJRGkZ9XL3/D7e7JiTTVLm1F3Cymd3/gFpD6frEpBo=
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg= github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
@@ -139,8 +105,6 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210503195802-e9a32991a82e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 h1:/UOmuWzQfxxo9UtlXMwuQU8CMgg1eZXqTRwkSQJWKOI=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
@@ -148,67 +112,38 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210504132125-bbd867fde50d h1:nTDGCTeAu2LhcsHTRzjyIUbZHCJ4QePArsm27Hka0UM=
golang.org/x/net v0.0.0-20210504132125-bbd867fde50d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201118182958-a01c418693c7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201218084310-7d0127a74742/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210110051926-789bb1bd4061/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210123111255-9b0068b26619/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210216163648-f7da38b97c65/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210309040221-94ec62e08169/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210503173754-0981d6026fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI= golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@@ -217,14 +152,7 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.zx2c4.com/wireguard v0.0.0-20210427022245-097af6e1351b/go.mod h1:a057zjmoc00UN7gVkaJt2sXVK523kMJcogDTEvPIasg=
golang.zx2c4.com/wireguard v0.0.0-20210805125648-3957e9b9dd19 h1:ab2jcw2W91Rz07eHAb8Lic7sFQKO0NhBftjv6m/gL/0=
golang.zx2c4.com/wireguard v0.0.0-20210805125648-3957e9b9dd19/go.mod h1:laHzsbfMhGSobUmruXWAyMKKHSqvIcrqZJMyHD+/3O8=
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20210803171230-4253848d036c h1:ADNrRDI5NR23/TUCnEmlLZLt4u9DnZ2nwRkPrAcFvto=
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20210803171230-4253848d036c/go.mod h1:+1XihzyZUBJcSc5WO9SwNA7v26puQwOEDwanaxfNXPQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@@ -13,10 +13,10 @@ import (
) )
type HealthChecker interface { type HealthChecker interface {
HealthCheck(ctx context.Context, env params.Interface, logger logging.Logger) error HealthCheck(ctx context.Context, env params.Env, logger logging.Logger) error
} }
func (c *CLI) HealthCheck(ctx context.Context, env params.Interface, func (c *CLI) HealthCheck(ctx context.Context, env params.Env,
logger logging.Logger) error { logger logging.Logger) error {
// Extract the health server port from the configuration. // Extract the health server port from the configuration.
config := configuration.Health{} config := configuration.Health{}

View File

@@ -14,27 +14,26 @@ import (
) )
type OpenvpnConfigMaker interface { type OpenvpnConfigMaker interface {
OpenvpnConfig(logger logging.Logger, env params.Interface) error OpenvpnConfig(logger logging.Logger) error
} }
func (c *CLI) OpenvpnConfig(logger logging.Logger, env params.Interface) error { func (c *CLI) OpenvpnConfig(logger logging.Logger) error {
storage, err := storage.New(logger, constants.ServersData)
if err != nil {
return err
}
allServers := storage.GetServers()
var allSettings configuration.Settings var allSettings configuration.Settings
err = allSettings.Read(env, allServers, logger) err := allSettings.Read(params.NewEnv(), logger)
if err != nil { if err != nil {
return err return err
} }
providerConf := provider.New(allSettings.VPN.Provider.Name, allServers, time.Now) allServers, err := storage.New(logger, constants.ServersData).
connection, err := providerConf.GetConnection(allSettings.VPN.Provider.ServerSelection) SyncServers(constants.GetAllServers())
if err != nil { if err != nil {
return err return err
} }
lines := providerConf.BuildConf(connection, allSettings.VPN.OpenVPN) providerConf := provider.New(allSettings.OpenVPN.Provider.Name, allServers, time.Now)
connection, err := providerConf.GetOpenVPNConnection(allSettings.OpenVPN.Provider.ServerSelection)
if err != nil {
return err
}
lines := providerConf.BuildConf(connection, "nonroortuser", allSettings.OpenVPN)
fmt.Println(strings.Join(lines, "\n")) fmt.Println(strings.Join(lines, "\n"))
return nil return nil
} }

View File

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

View File

@@ -4,47 +4,69 @@ import (
"fmt" "fmt"
"github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/golibs/params"
) )
func (settings *Provider) cyberghostLines() (lines []string) {
lines = append(lines, lastIndent+"Server group: "+settings.ServerSelection.Group)
if len(settings.ServerSelection.Regions) > 0 {
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
}
if len(settings.ServerSelection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
if settings.ExtraConfigOptions.ClientKey != "" {
lines = append(lines, lastIndent+"Client key is set")
}
if settings.ExtraConfigOptions.ClientCertificate != "" {
lines = append(lines, lastIndent+"Client certificate is set")
}
return lines
}
func (settings *Provider) readCyberghost(r reader) (err error) { func (settings *Provider) readCyberghost(r reader) (err error) {
settings.Name = constants.Cyberghost settings.Name = constants.Cyberghost
servers := r.servers.GetCyberghost()
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env) settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil { if err != nil {
return err return err
} }
settings.ServerSelection.Groups, err = r.env.CSVInside("CYBERGHOST_GROUP", settings.ExtraConfigOptions.ClientKey, err = readClientKey(r)
constants.CyberghostGroupChoices(servers)) if err != nil {
return err
}
settings.ExtraConfigOptions.ClientCertificate, err = readClientCertificate(r)
if err != nil {
return err
}
settings.ServerSelection.Group, err = r.env.Inside("CYBERGHOST_GROUP",
constants.CyberghostGroupChoices(), params.Default("Premium UDP Europe"))
if err != nil { if err != nil {
return fmt.Errorf("environment variable CYBERGHOST_GROUP: %w", err) return fmt.Errorf("environment variable CYBERGHOST_GROUP: %w", err)
} }
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.CyberghostRegionChoices(servers)) settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.CyberghostRegionChoices())
if err != nil { if err != nil {
return fmt.Errorf("environment variable REGION: %w", err) return fmt.Errorf("environment variable REGION: %w", err)
} }
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.CyberghostHostnameChoices())
constants.CyberghostHostnameChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err) return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
} }
return settings.ServerSelection.OpenVPN.readProtocolAndPort(r.env)
}
func (settings *OpenVPN) readCyberghost(r reader) (err error) {
settings.ClientKey, err = readClientKey(r)
if err != nil {
return err
}
settings.ClientCrt, err = readClientCertificate(r)
if err != nil {
return err
}
return nil return nil
} }

View File

@@ -102,7 +102,7 @@ var (
ErrDNSAddressNotAnIP = errors.New("DNS plaintext address is not an IP address") ErrDNSAddressNotAnIP = errors.New("DNS plaintext address is not an IP address")
) )
func (settings *DNS) readDNSPlaintext(env params.Interface) error { func (settings *DNS) readDNSPlaintext(env params.Env) error {
s, err := env.Get("DNS_PLAINTEXT_ADDRESS", params.Default("1.1.1.1")) s, err := env.Get("DNS_PLAINTEXT_ADDRESS", params.Default("1.1.1.1"))
if err != nil { if err != nil {
return fmt.Errorf("environment variable DNS_PLAINTEXT_ADDRESS: %w", err) return fmt.Errorf("environment variable DNS_PLAINTEXT_ADDRESS: %w", err)

View File

@@ -36,7 +36,7 @@ var (
ErrInvalidPrivateAddress = errors.New("private address is not a valid IP or CIDR range") ErrInvalidPrivateAddress = errors.New("private address is not a valid IP or CIDR range")
) )
func (settings *DNS) readPrivateAddresses(env params.Interface) (err error) { func (settings *DNS) readPrivateAddresses(env params.Env) (err error) {
privateAddresses, err := env.CSV("DOT_PRIVATE_ADDRESS") privateAddresses, err := env.CSV("DOT_PRIVATE_ADDRESS")
if err != nil { if err != nil {
return fmt.Errorf("environment variable DOT_PRIVATE_ADDRESS: %w", err) return fmt.Errorf("environment variable DOT_PRIVATE_ADDRESS: %w", err)

View File

@@ -6,25 +6,40 @@ import (
"github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/constants"
) )
func (settings *Provider) fastestvpnLines() (lines []string) {
if len(settings.ServerSelection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
if len(settings.ServerSelection.Countries) > 0 {
lines = append(lines, lastIndent+"Countries: "+commaJoin(settings.ServerSelection.Countries))
}
return lines
}
func (settings *Provider) readFastestvpn(r reader) (err error) { func (settings *Provider) readFastestvpn(r reader) (err error) {
settings.Name = constants.Fastestvpn settings.Name = constants.Fastestvpn
servers := r.servers.GetFastestvpn()
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env) settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil { if err != nil {
return err return err
} }
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.FastestvpnHostnameChoices())
constants.FastestvpnHostnameChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err) return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
} }
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.FastestvpnCountriesChoices(servers)) settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.FastestvpnCountriesChoices())
if err != nil { if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err) return fmt.Errorf("environment variable COUNTRY: %w", err)
} }
return settings.ServerSelection.OpenVPN.readProtocolOnly(r.env) return nil
} }

View File

@@ -73,7 +73,7 @@ func (settings *Firewall) read(r reader) (err error) {
return settings.readOutboundSubnets(r) return settings.readOutboundSubnets(r)
} }
func (settings *Firewall) readVPNInputPorts(env params.Interface) (err error) { func (settings *Firewall) readVPNInputPorts(env params.Env) (err error) {
settings.VPNInputPorts, err = readCSVPorts(env, "FIREWALL_VPN_INPUT_PORTS") settings.VPNInputPorts, err = readCSVPorts(env, "FIREWALL_VPN_INPUT_PORTS")
if err != nil { if err != nil {
return fmt.Errorf("environment variable FIREWALL_VPN_INPUT_PORTS: %w", err) return fmt.Errorf("environment variable FIREWALL_VPN_INPUT_PORTS: %w", err)
@@ -81,7 +81,7 @@ func (settings *Firewall) readVPNInputPorts(env params.Interface) (err error) {
return nil return nil
} }
func (settings *Firewall) readInputPorts(env params.Interface) (err error) { func (settings *Firewall) readInputPorts(env params.Env) (err error) {
settings.InputPorts, err = readCSVPorts(env, "FIREWALL_INPUT_PORTS") settings.InputPorts, err = readCSVPorts(env, "FIREWALL_INPUT_PORTS")
if err != nil { if err != nil {
return fmt.Errorf("environment variable FIREWALL_INPUT_PORTS: %w", err) return fmt.Errorf("environment variable FIREWALL_INPUT_PORTS: %w", err)

View File

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

View File

@@ -139,7 +139,7 @@ func Test_Health_read(t *testing.T) {
ctrl := gomock.NewController(t) ctrl := gomock.NewController(t)
env := mock_params.NewMockInterface(ctrl) env := mock_params.NewMockEnv(ctrl)
logger := mock_logging.NewMockLogger(ctrl) logger := mock_logging.NewMockLogger(ctrl)
env.EXPECT().ListeningAddress("HEALTH_SERVER_ADDRESS", gomock.Any()). env.EXPECT().ListeningAddress("HEALTH_SERVER_ADDRESS", gomock.Any()).

View File

@@ -6,35 +6,58 @@ import (
"github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/constants"
) )
func (settings *Provider) hideMyAssLines() (lines []string) {
if len(settings.ServerSelection.Countries) > 0 {
lines = append(lines, lastIndent+"Countries: "+commaJoin(settings.ServerSelection.Countries))
}
if len(settings.ServerSelection.Regions) > 0 {
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
}
if len(settings.ServerSelection.Cities) > 0 {
lines = append(lines, lastIndent+"Cities: "+commaJoin(settings.ServerSelection.Cities))
}
if len(settings.ServerSelection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
return lines
}
func (settings *Provider) readHideMyAss(r reader) (err error) { func (settings *Provider) readHideMyAss(r reader) (err error) {
settings.Name = constants.HideMyAss settings.Name = constants.HideMyAss
servers := r.servers.GetHideMyAss()
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env) settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil { if err != nil {
return err return err
} }
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.HideMyAssCountryChoices(servers)) settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.HideMyAssCountryChoices())
if err != nil { if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err) return fmt.Errorf("environment variable COUNTRY: %w", err)
} }
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.HideMyAssCountryChoices(servers)) settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.HideMyAssCountryChoices())
if err != nil { if err != nil {
return fmt.Errorf("environment variable REGION: %w", err) return fmt.Errorf("environment variable REGION: %w", err)
} }
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.HideMyAssCityChoices(servers)) settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.HideMyAssCityChoices())
if err != nil { if err != nil {
return fmt.Errorf("environment variable CITY: %w", err) return fmt.Errorf("environment variable CITY: %w", err)
} }
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.HideMyAssHostnameChoices())
constants.HideMyAssHostnameChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err) return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
} }
return settings.ServerSelection.OpenVPN.readProtocolAndPort(r.env) return nil
} }

View File

@@ -6,30 +6,49 @@ import (
"github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/constants"
) )
func (settings *Provider) ipvanishLines() (lines []string) {
if len(settings.ServerSelection.Countries) > 0 {
lines = append(lines, lastIndent+"Countries: "+commaJoin(settings.ServerSelection.Countries))
}
if len(settings.ServerSelection.Cities) > 0 {
lines = append(lines, lastIndent+"Cities: "+commaJoin(settings.ServerSelection.Cities))
}
if len(settings.ServerSelection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
return lines
}
func (settings *Provider) readIpvanish(r reader) (err error) { func (settings *Provider) readIpvanish(r reader) (err error) {
settings.Name = constants.Ipvanish settings.Name = constants.Ipvanish
servers := r.servers.GetIpvanish()
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env) settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil { if err != nil {
return err return err
} }
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.IpvanishCountryChoices(servers)) settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.IpvanishCountryChoices())
if err != nil { if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err) return fmt.Errorf("environment variable COUNTRY: %w", err)
} }
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.IpvanishCityChoices(servers)) settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.IpvanishCityChoices())
if err != nil { if err != nil {
return fmt.Errorf("environment variable CITY: %w", err) return fmt.Errorf("environment variable CITY: %w", err)
} }
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.IpvanishHostnameChoices())
constants.IpvanishHostnameChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err) return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
} }
return settings.ServerSelection.OpenVPN.readProtocolOnly(r.env) return nil
} }

View File

@@ -7,12 +7,46 @@ import (
"github.com/golang/mock/gomock" "github.com/golang/mock/gomock"
"github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/golibs/params/mock_params" "github.com/qdm12/golibs/params/mock_params"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func Test_Provider_ipvanishLines(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
settings Provider
lines []string
}{
"empty settings": {},
"full settings": {
settings: Provider{
ServerSelection: ServerSelection{
Countries: []string{"A", "B"},
Cities: []string{"C", "D"},
Hostnames: []string{"E", "F"},
},
},
lines: []string{
"|--Countries: A, B",
"|--Cities: C, D",
"|--Hostnames: E, F",
},
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
lines := testCase.settings.ipvanishLines()
assert.Equal(t, testCase.lines, lines)
})
}
}
func Test_Provider_readIpvanish(t *testing.T) { func Test_Provider_readIpvanish(t *testing.T) {
t.Parallel() t.Parallel()
@@ -31,15 +65,23 @@ func Test_Provider_readIpvanish(t *testing.T) {
} }
testCases := map[string]struct { testCases := map[string]struct {
protocol singleStringCall
targetIP singleStringCall targetIP singleStringCall
countries sliceStringCall countries sliceStringCall
cities sliceStringCall cities sliceStringCall
hostnames sliceStringCall hostnames sliceStringCall
protocol singleStringCall
settings Provider settings Provider
err error err error
}{ }{
"protocol error": {
protocol: singleStringCall{call: true, err: errDummy},
settings: Provider{
Name: constants.Ipvanish,
},
err: errors.New("environment variable PROTOCOL: dummy test error"),
},
"target IP error": { "target IP error": {
protocol: singleStringCall{call: true},
targetIP: singleStringCall{call: true, value: "something", err: errDummy}, targetIP: singleStringCall{call: true, value: "something", err: errDummy},
settings: Provider{ settings: Provider{
Name: constants.Ipvanish, Name: constants.Ipvanish,
@@ -47,6 +89,7 @@ func Test_Provider_readIpvanish(t *testing.T) {
err: errors.New("environment variable OPENVPN_TARGET_IP: dummy test error"), err: errors.New("environment variable OPENVPN_TARGET_IP: dummy test error"),
}, },
"countries error": { "countries error": {
protocol: singleStringCall{call: true},
targetIP: singleStringCall{call: true}, targetIP: singleStringCall{call: true},
countries: sliceStringCall{call: true, err: errDummy}, countries: sliceStringCall{call: true, err: errDummy},
settings: Provider{ settings: Provider{
@@ -55,6 +98,7 @@ func Test_Provider_readIpvanish(t *testing.T) {
err: errors.New("environment variable COUNTRY: dummy test error"), err: errors.New("environment variable COUNTRY: dummy test error"),
}, },
"cities error": { "cities error": {
protocol: singleStringCall{call: true},
targetIP: singleStringCall{call: true}, targetIP: singleStringCall{call: true},
countries: sliceStringCall{call: true}, countries: sliceStringCall{call: true},
cities: sliceStringCall{call: true, err: errDummy}, cities: sliceStringCall{call: true, err: errDummy},
@@ -64,6 +108,7 @@ func Test_Provider_readIpvanish(t *testing.T) {
err: errors.New("environment variable CITY: dummy test error"), err: errors.New("environment variable CITY: dummy test error"),
}, },
"hostnames error": { "hostnames error": {
protocol: singleStringCall{call: true},
targetIP: singleStringCall{call: true}, targetIP: singleStringCall{call: true},
countries: sliceStringCall{call: true}, countries: sliceStringCall{call: true},
cities: sliceStringCall{call: true}, cities: sliceStringCall{call: true},
@@ -73,39 +118,26 @@ func Test_Provider_readIpvanish(t *testing.T) {
}, },
err: errors.New("environment variable SERVER_HOSTNAME: dummy test error"), err: errors.New("environment variable SERVER_HOSTNAME: dummy test error"),
}, },
"protocol error": {
targetIP: singleStringCall{call: true},
countries: sliceStringCall{call: true},
cities: sliceStringCall{call: true},
hostnames: sliceStringCall{call: true},
protocol: singleStringCall{call: true, err: errDummy},
settings: Provider{
Name: constants.Ipvanish,
},
err: errors.New("environment variable PROTOCOL: dummy test error"),
},
"default settings": { "default settings": {
protocol: singleStringCall{call: true},
targetIP: singleStringCall{call: true}, targetIP: singleStringCall{call: true},
countries: sliceStringCall{call: true}, countries: sliceStringCall{call: true},
cities: sliceStringCall{call: true}, cities: sliceStringCall{call: true},
hostnames: sliceStringCall{call: true}, hostnames: sliceStringCall{call: true},
protocol: singleStringCall{call: true},
settings: Provider{ settings: Provider{
Name: constants.Ipvanish, Name: constants.Ipvanish,
}, },
}, },
"set settings": { "set settings": {
protocol: singleStringCall{call: true, value: constants.TCP},
targetIP: singleStringCall{call: true, value: "1.2.3.4"}, targetIP: singleStringCall{call: true, value: "1.2.3.4"},
countries: sliceStringCall{call: true, values: []string{"A", "B"}}, countries: sliceStringCall{call: true, values: []string{"A", "B"}},
cities: sliceStringCall{call: true, values: []string{"C", "D"}}, cities: sliceStringCall{call: true, values: []string{"C", "D"}},
hostnames: sliceStringCall{call: true, values: []string{"E", "F"}}, hostnames: sliceStringCall{call: true, values: []string{"E", "F"}},
protocol: singleStringCall{call: true, value: constants.TCP},
settings: Provider{ settings: Provider{
Name: constants.Ipvanish, Name: constants.Ipvanish,
ServerSelection: ServerSelection{ ServerSelection: ServerSelection{
OpenVPN: OpenVPNSelection{ TCP: true,
TCP: true,
},
TargetIP: net.IPv4(1, 2, 3, 4), TargetIP: net.IPv4(1, 2, 3, 4),
Countries: []string{"A", "B"}, Countries: []string{"A", "B"},
Cities: []string{"C", "D"}, Cities: []string{"C", "D"},
@@ -120,39 +152,29 @@ func Test_Provider_readIpvanish(t *testing.T) {
t.Parallel() t.Parallel()
ctrl := gomock.NewController(t) ctrl := gomock.NewController(t)
servers := []models.IpvanishServer{{Hostname: "a"}} env := mock_params.NewMockEnv(ctrl)
allServers := models.AllServers{ if testCase.protocol.call {
Ipvanish: models.IpvanishServers{ env.EXPECT().Inside("PROTOCOL", []string{constants.TCP, constants.UDP}, gomock.Any()).
Servers: servers, Return(testCase.protocol.value, testCase.protocol.err)
},
} }
env := mock_params.NewMockInterface(ctrl)
if testCase.targetIP.call { if testCase.targetIP.call {
env.EXPECT().Get("OPENVPN_TARGET_IP"). env.EXPECT().Get("OPENVPN_TARGET_IP").
Return(testCase.targetIP.value, testCase.targetIP.err) Return(testCase.targetIP.value, testCase.targetIP.err)
} }
if testCase.countries.call { if testCase.countries.call {
env.EXPECT().CSVInside("COUNTRY", constants.IpvanishCountryChoices(servers)). env.EXPECT().CSVInside("COUNTRY", constants.IpvanishCountryChoices()).
Return(testCase.countries.values, testCase.countries.err) Return(testCase.countries.values, testCase.countries.err)
} }
if testCase.cities.call { if testCase.cities.call {
env.EXPECT().CSVInside("CITY", constants.IpvanishCityChoices(servers)). env.EXPECT().CSVInside("CITY", constants.IpvanishCityChoices()).
Return(testCase.cities.values, testCase.cities.err) Return(testCase.cities.values, testCase.cities.err)
} }
if testCase.hostnames.call { if testCase.hostnames.call {
env.EXPECT().CSVInside("SERVER_HOSTNAME", constants.IpvanishHostnameChoices(servers)). env.EXPECT().CSVInside("SERVER_HOSTNAME", constants.IpvanishHostnameChoices()).
Return(testCase.hostnames.values, testCase.hostnames.err) Return(testCase.hostnames.values, testCase.hostnames.err)
} }
if testCase.protocol.call {
env.EXPECT().Inside("PROTOCOL", []string{constants.TCP, constants.UDP}, gomock.Any()).
Return(testCase.protocol.value, testCase.protocol.err)
}
r := reader{ r := reader{env: env}
servers: allServers,
env: env,
}
var settings Provider var settings Provider
err := settings.readIpvanish(r) err := settings.readIpvanish(r)

View File

@@ -4,67 +4,51 @@ import (
"fmt" "fmt"
"github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/golibs/params"
) )
func (settings *Provider) ivpnLines() (lines []string) {
if len(settings.ServerSelection.Countries) > 0 {
lines = append(lines, lastIndent+"Countries: "+commaJoin(settings.ServerSelection.Countries))
}
if len(settings.ServerSelection.Cities) > 0 {
lines = append(lines, lastIndent+"Cities: "+commaJoin(settings.ServerSelection.Cities))
}
if len(settings.ServerSelection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
return lines
}
func (settings *Provider) readIvpn(r reader) (err error) { func (settings *Provider) readIvpn(r reader) (err error) {
settings.Name = constants.Ivpn settings.Name = constants.Ivpn
servers := r.servers.GetIvpn()
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env) settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil { if err != nil {
return err return err
} }
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.IvpnCountryChoices(servers)) settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.IvpnCountryChoices())
if err != nil { if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err) return fmt.Errorf("environment variable COUNTRY: %w", err)
} }
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.IvpnCityChoices(servers)) settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.IvpnCityChoices())
if err != nil { if err != nil {
return fmt.Errorf("environment variable CITY: %w", err) return fmt.Errorf("environment variable CITY: %w", err)
} }
settings.ServerSelection.ISPs, err = r.env.CSVInside("ISP", constants.IvpnISPChoices(servers)) settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.IvpnHostnameChoices())
if err != nil {
return fmt.Errorf("environment variable ISP: %w", err)
}
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.IvpnHostnameChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err) return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
} }
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 return nil
} }

View File

@@ -7,13 +7,47 @@ import (
"github.com/golang/mock/gomock" "github.com/golang/mock/gomock"
"github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/golibs/params/mock_params" "github.com/qdm12/golibs/params/mock_params"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func Test_Provider_readIvpn(t *testing.T) { //nolint:gocognit func Test_Provider_ivpnLines(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
settings Provider
lines []string
}{
"empty settings": {},
"full settings": {
settings: Provider{
ServerSelection: ServerSelection{
Countries: []string{"A", "B"},
Cities: []string{"C", "D"},
Hostnames: []string{"E", "F"},
},
},
lines: []string{
"|--Countries: A, B",
"|--Cities: C, D",
"|--Hostnames: E, F",
},
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
lines := testCase.settings.ivpnLines()
assert.Equal(t, testCase.lines, lines)
})
}
}
func Test_Provider_readIvpn(t *testing.T) {
t.Parallel() t.Parallel()
var errDummy = errors.New("dummy test error") var errDummy = errors.New("dummy test error")
@@ -24,15 +58,6 @@ func Test_Provider_readIvpn(t *testing.T) { //nolint:gocognit
err error err error
} }
type portCall struct {
getCall bool
getValue string // "" or "0"
getErr error
portCall bool
portValue uint16
portErr error
}
type sliceStringCall struct { type sliceStringCall struct {
call bool call bool
values []string values []string
@@ -40,18 +65,23 @@ func Test_Provider_readIvpn(t *testing.T) { //nolint:gocognit
} }
testCases := map[string]struct { testCases := map[string]struct {
protocol singleStringCall
targetIP singleStringCall targetIP singleStringCall
countries sliceStringCall countries sliceStringCall
cities sliceStringCall cities sliceStringCall
isps sliceStringCall
hostnames sliceStringCall hostnames sliceStringCall
protocol singleStringCall
ovpnPort portCall
wgPort portCall
settings Provider settings Provider
err error err error
}{ }{
"protocol error": {
protocol: singleStringCall{call: true, err: errDummy},
settings: Provider{
Name: constants.Ivpn,
},
err: errors.New("environment variable PROTOCOL: dummy test error"),
},
"target IP error": { "target IP error": {
protocol: singleStringCall{call: true},
targetIP: singleStringCall{call: true, value: "something", err: errDummy}, targetIP: singleStringCall{call: true, value: "something", err: errDummy},
settings: Provider{ settings: Provider{
Name: constants.Ivpn, Name: constants.Ivpn,
@@ -59,6 +89,7 @@ func Test_Provider_readIvpn(t *testing.T) { //nolint:gocognit
err: errors.New("environment variable OPENVPN_TARGET_IP: dummy test error"), err: errors.New("environment variable OPENVPN_TARGET_IP: dummy test error"),
}, },
"countries error": { "countries error": {
protocol: singleStringCall{call: true},
targetIP: singleStringCall{call: true}, targetIP: singleStringCall{call: true},
countries: sliceStringCall{call: true, err: errDummy}, countries: sliceStringCall{call: true, err: errDummy},
settings: Provider{ settings: Provider{
@@ -67,6 +98,7 @@ func Test_Provider_readIvpn(t *testing.T) { //nolint:gocognit
err: errors.New("environment variable COUNTRY: dummy test error"), err: errors.New("environment variable COUNTRY: dummy test error"),
}, },
"cities error": { "cities error": {
protocol: singleStringCall{call: true},
targetIP: singleStringCall{call: true}, targetIP: singleStringCall{call: true},
countries: sliceStringCall{call: true}, countries: sliceStringCall{call: true},
cities: sliceStringCall{call: true, err: errDummy}, cities: sliceStringCall{call: true, err: errDummy},
@@ -75,102 +107,40 @@ func Test_Provider_readIvpn(t *testing.T) { //nolint:gocognit
}, },
err: errors.New("environment variable CITY: dummy test error"), err: errors.New("environment variable CITY: dummy test error"),
}, },
"isps error": {
targetIP: singleStringCall{call: true},
countries: sliceStringCall{call: true},
cities: sliceStringCall{call: true},
isps: sliceStringCall{call: true, err: errDummy},
settings: Provider{
Name: constants.Ivpn,
},
err: errors.New("environment variable ISP: dummy test error"),
},
"hostnames error": { "hostnames error": {
protocol: singleStringCall{call: true},
targetIP: singleStringCall{call: true}, targetIP: singleStringCall{call: true},
countries: sliceStringCall{call: true}, countries: sliceStringCall{call: true},
cities: sliceStringCall{call: true}, cities: sliceStringCall{call: true},
isps: sliceStringCall{call: true},
hostnames: sliceStringCall{call: true, err: errDummy}, hostnames: sliceStringCall{call: true, err: errDummy},
settings: Provider{ settings: Provider{
Name: constants.Ivpn, Name: constants.Ivpn,
}, },
err: errors.New("environment variable SERVER_HOSTNAME: dummy test error"), err: errors.New("environment variable SERVER_HOSTNAME: dummy test error"),
}, },
"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{
Name: constants.Ivpn,
},
err: errors.New("environment variable PROTOCOL: dummy test error"),
},
"openvpn custom port error": {
targetIP: singleStringCall{call: true},
countries: sliceStringCall{call: true},
cities: sliceStringCall{call: true},
isps: sliceStringCall{call: true},
hostnames: sliceStringCall{call: true},
protocol: singleStringCall{call: true},
ovpnPort: portCall{getCall: true, getErr: errDummy},
settings: Provider{
Name: constants.Ivpn,
},
err: errors.New("environment variable PORT: dummy test error"),
},
"wireguard custom port error": {
targetIP: singleStringCall{call: true},
countries: sliceStringCall{call: true},
cities: sliceStringCall{call: true},
isps: sliceStringCall{call: true},
hostnames: sliceStringCall{call: true},
protocol: singleStringCall{call: true},
ovpnPort: portCall{getCall: true, getValue: "0"},
wgPort: portCall{getCall: true, getErr: errDummy},
settings: Provider{
Name: constants.Ivpn,
},
err: errors.New("environment variable WIREGUARD_PORT: dummy test error"),
},
"default settings": { "default settings": {
protocol: singleStringCall{call: true},
targetIP: singleStringCall{call: true}, targetIP: singleStringCall{call: true},
countries: sliceStringCall{call: true}, countries: sliceStringCall{call: true},
cities: sliceStringCall{call: true}, cities: sliceStringCall{call: true},
isps: sliceStringCall{call: true},
hostnames: sliceStringCall{call: true}, hostnames: sliceStringCall{call: true},
protocol: singleStringCall{call: true},
ovpnPort: portCall{getCall: true, getValue: "0"},
wgPort: portCall{getCall: true, getValue: "0"},
settings: Provider{ settings: Provider{
Name: constants.Ivpn, Name: constants.Ivpn,
}, },
}, },
"set settings": { "set settings": {
protocol: singleStringCall{call: true, value: constants.TCP},
targetIP: singleStringCall{call: true, value: "1.2.3.4"}, targetIP: singleStringCall{call: true, value: "1.2.3.4"},
countries: sliceStringCall{call: true, values: []string{"A", "B"}}, countries: sliceStringCall{call: true, values: []string{"A", "B"}},
cities: sliceStringCall{call: true, values: []string{"C", "D"}}, cities: sliceStringCall{call: true, values: []string{"C", "D"}},
isps: sliceStringCall{call: true, values: []string{"ISP 1"}},
hostnames: sliceStringCall{call: true, values: []string{"E", "F"}}, hostnames: sliceStringCall{call: true, values: []string{"E", "F"}},
protocol: singleStringCall{call: true, value: constants.TCP},
ovpnPort: portCall{getCall: true, portCall: true, portValue: 443},
wgPort: portCall{getCall: true, portCall: true, portValue: 2049},
settings: Provider{ settings: Provider{
Name: constants.Ivpn, Name: constants.Ivpn,
ServerSelection: ServerSelection{ ServerSelection: ServerSelection{
OpenVPN: OpenVPNSelection{ TCP: true,
TCP: true,
CustomPort: 443,
},
Wireguard: WireguardSelection{
CustomPort: 2049,
},
TargetIP: net.IPv4(1, 2, 3, 4), TargetIP: net.IPv4(1, 2, 3, 4),
Countries: []string{"A", "B"}, Countries: []string{"A", "B"},
Cities: []string{"C", "D"}, Cities: []string{"C", "D"},
ISPs: []string{"ISP 1"},
Hostnames: []string{"E", "F"}, Hostnames: []string{"E", "F"},
}, },
}, },
@@ -182,60 +152,29 @@ func Test_Provider_readIvpn(t *testing.T) { //nolint:gocognit
t.Parallel() t.Parallel()
ctrl := gomock.NewController(t) ctrl := gomock.NewController(t)
env := mock_params.NewMockInterface(ctrl) env := mock_params.NewMockEnv(ctrl)
if testCase.protocol.call {
servers := []models.IvpnServer{{Hostname: "a"}} env.EXPECT().Inside("PROTOCOL", []string{constants.TCP, constants.UDP}, gomock.Any()).
allServers := models.AllServers{ Return(testCase.protocol.value, testCase.protocol.err)
Ivpn: models.IvpnServers{
Servers: servers,
},
} }
if testCase.targetIP.call { if testCase.targetIP.call {
env.EXPECT().Get("OPENVPN_TARGET_IP"). env.EXPECT().Get("OPENVPN_TARGET_IP").
Return(testCase.targetIP.value, testCase.targetIP.err) Return(testCase.targetIP.value, testCase.targetIP.err)
} }
if testCase.countries.call { if testCase.countries.call {
env.EXPECT().CSVInside("COUNTRY", constants.IvpnCountryChoices(servers)). env.EXPECT().CSVInside("COUNTRY", constants.IvpnCountryChoices()).
Return(testCase.countries.values, testCase.countries.err) Return(testCase.countries.values, testCase.countries.err)
} }
if testCase.cities.call { if testCase.cities.call {
env.EXPECT().CSVInside("CITY", constants.IvpnCityChoices(servers)). env.EXPECT().CSVInside("CITY", constants.IvpnCityChoices()).
Return(testCase.cities.values, testCase.cities.err) Return(testCase.cities.values, testCase.cities.err)
} }
if testCase.isps.call {
env.EXPECT().CSVInside("ISP", constants.IvpnISPChoices(servers)).
Return(testCase.isps.values, testCase.isps.err)
}
if testCase.hostnames.call { if testCase.hostnames.call {
env.EXPECT().CSVInside("SERVER_HOSTNAME", constants.IvpnHostnameChoices(servers)). env.EXPECT().CSVInside("SERVER_HOSTNAME", constants.IvpnHostnameChoices()).
Return(testCase.hostnames.values, testCase.hostnames.err) Return(testCase.hostnames.values, testCase.hostnames.err)
} }
if testCase.protocol.call {
env.EXPECT().Inside("PROTOCOL", []string{constants.TCP, constants.UDP}, gomock.Any()).
Return(testCase.protocol.value, testCase.protocol.err)
}
if testCase.ovpnPort.getCall {
env.EXPECT().Get("PORT", gomock.Any()).
Return(testCase.ovpnPort.getValue, testCase.ovpnPort.getErr)
}
if testCase.ovpnPort.portCall {
env.EXPECT().Port("PORT").
Return(testCase.ovpnPort.portValue, testCase.ovpnPort.portErr)
}
if testCase.wgPort.getCall {
env.EXPECT().Get("WIREGUARD_PORT", gomock.Any()).
Return(testCase.wgPort.getValue, testCase.wgPort.getErr)
}
if testCase.wgPort.portCall {
env.EXPECT().Port("WIREGUARD_PORT").
Return(testCase.wgPort.portValue, testCase.wgPort.portErr)
}
r := reader{ r := reader{env: env}
servers: allServers,
env: env,
}
var settings Provider var settings Provider
err := settings.readIvpn(r) err := settings.readIvpn(r)

View File

@@ -1,30 +0,0 @@
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

@@ -2,72 +2,87 @@ package configuration
import ( import (
"fmt" "fmt"
"strconv"
"github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/golibs/params" "github.com/qdm12/golibs/params"
) )
func (settings *Provider) mullvadLines() (lines []string) {
if len(settings.ServerSelection.Countries) > 0 {
lines = append(lines, lastIndent+"Countries: "+commaJoin(settings.ServerSelection.Countries))
}
if len(settings.ServerSelection.Cities) > 0 {
lines = append(lines, lastIndent+"Cities: "+commaJoin(settings.ServerSelection.Cities))
}
if len(settings.ServerSelection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
if len(settings.ServerSelection.ISPs) > 0 {
lines = append(lines, lastIndent+"ISPs: "+commaJoin(settings.ServerSelection.ISPs))
}
if settings.ServerSelection.CustomPort > 0 {
lines = append(lines, lastIndent+"Custom port: "+strconv.Itoa(int(settings.ServerSelection.CustomPort)))
}
if settings.ExtraConfigOptions.OpenVPNIPv6 {
lines = append(lines, lastIndent+"IPv6: enabled")
}
return lines
}
func (settings *Provider) readMullvad(r reader) (err error) { func (settings *Provider) readMullvad(r reader) (err error) {
settings.Name = constants.Mullvad settings.Name = constants.Mullvad
servers := r.servers.GetMullvad()
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env) settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil { if err != nil {
return err return err
} }
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.MullvadCountryChoices(servers)) settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.MullvadCountryChoices())
if err != nil { if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err) return fmt.Errorf("environment variable COUNTRY: %w", err)
} }
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.MullvadCityChoices(servers)) settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.MullvadCityChoices())
if err != nil { if err != nil {
return fmt.Errorf("environment variable CITY: %w", err) return fmt.Errorf("environment variable CITY: %w", err)
} }
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.MullvadHostnameChoices(servers)) settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.MullvadHostnameChoices())
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err) return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
} }
settings.ServerSelection.ISPs, err = r.env.CSVInside("ISP", constants.MullvadISPChoices(servers)) settings.ServerSelection.ISPs, err = r.env.CSVInside("ISP", constants.MullvadISPChoices())
if err != nil { if err != nil {
return fmt.Errorf("environment variable ISP: %w", err) return fmt.Errorf("environment variable ISP: %w", err)
} }
settings.ServerSelection.CustomPort, err = readCustomPort(r.env, settings.ServerSelection.TCP,
[]uint16{80, 443, 1401}, []uint16{53, 1194, 1195, 1196, 1197, 1300, 1301, 1302, 1303, 1400})
if err != nil {
return err
}
settings.ServerSelection.Owned, err = r.env.YesNo("OWNED", params.Default("no")) settings.ServerSelection.Owned, err = r.env.YesNo("OWNED", params.Default("no"))
if err != nil { if err != nil {
return fmt.Errorf("environment variable OWNED: %w", err) return fmt.Errorf("environment variable OWNED: %w", err)
} }
err = settings.ServerSelection.OpenVPN.readMullvad(r.env) settings.ExtraConfigOptions.OpenVPNIPv6, err = r.env.OnOff("OPENVPN_IPV6", params.Default("off"))
if err != nil { if err != nil {
return err return fmt.Errorf("environment variable OPENVPN_IPV6: %w", err)
}
return settings.ServerSelection.Wireguard.readMullvad(r.env)
}
func (settings *OpenVPNSelection) readMullvad(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, 1401}, []uint16{53, 1194, 1195, 1196, 1197, 1300, 1301, 1302, 1303, 1400})
if err != nil {
return err
}
return nil
}
func (settings *WireguardSelection) readMullvad(env params.Interface) (err error) {
settings.CustomPort, err = readWireguardCustomPort(env, nil)
if err != nil {
return err
} }
return nil return nil

View File

@@ -8,26 +8,54 @@ import (
"github.com/qdm12/golibs/params" "github.com/qdm12/golibs/params"
) )
func (settings *Provider) nordvpnLines() (lines []string) {
if len(settings.ServerSelection.Regions) > 0 {
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
}
if len(settings.ServerSelection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
if len(settings.ServerSelection.Names) > 0 {
lines = append(lines, lastIndent+"Names: "+commaJoin(settings.ServerSelection.Hostnames))
}
if numbersUint16 := settings.ServerSelection.Numbers; len(numbersUint16) > 0 {
numbersString := make([]string, len(numbersUint16))
for i, numberUint16 := range numbersUint16 {
numbersString[i] = strconv.Itoa(int(numberUint16))
}
lines = append(lines, lastIndent+"Numbers: "+commaJoin(numbersString))
}
return lines
}
func (settings *Provider) readNordvpn(r reader) (err error) { func (settings *Provider) readNordvpn(r reader) (err error) {
settings.Name = constants.Nordvpn settings.Name = constants.Nordvpn
servers := r.servers.GetNordvpn()
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env) settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil { if err != nil {
return err return err
} }
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.NordvpnRegionChoices(servers)) settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.NordvpnRegionChoices())
if err != nil { if err != nil {
return fmt.Errorf("environment variable REGION: %w", err) return fmt.Errorf("environment variable REGION: %w", err)
} }
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.NordvpnHostnameChoices(servers)) settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.NordvpnHostnameChoices())
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err) return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
} }
settings.ServerSelection.Names, err = r.env.CSVInside("SERVER_NAME", constants.NordvpnHostnameChoices(servers)) settings.ServerSelection.Names, err = r.env.CSVInside("SERVER_NAME", constants.NordvpnHostnameChoices())
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_NAME: %w", err) return fmt.Errorf("environment variable SERVER_NAME: %w", err)
} }
@@ -37,10 +65,10 @@ func (settings *Provider) readNordvpn(r reader) (err error) {
return err return err
} }
return settings.ServerSelection.OpenVPN.readProtocolOnly(r.env) return nil
} }
func readNordVPNServerNumbers(env params.Interface) (numbers []uint16, err error) { func readNordVPNServerNumbers(env params.Env) (numbers []uint16, err error) {
const possiblePortsCount = 65537 const possiblePortsCount = 65537
possibilities := make([]string, possiblePortsCount) possibilities := make([]string, possiblePortsCount)
for i := range possibilities { for i := range possibilities {

View File

@@ -3,7 +3,6 @@ package configuration
import ( import (
"errors" "errors"
"fmt" "fmt"
"regexp"
"strconv" "strconv"
"strings" "strings"
@@ -21,14 +20,9 @@ type OpenVPN struct {
Root bool `json:"run_as_root"` Root bool `json:"run_as_root"`
Cipher string `json:"cipher"` Cipher string `json:"cipher"`
Auth string `json:"auth"` Auth string `json:"auth"`
Provider Provider `json:"provider"`
Config string `json:"custom_config"` Config string `json:"custom_config"`
Version string `json:"version"` Version string `json:"version"`
ClientCrt string `json:"-"` // Cyberghost
ClientKey string `json:"-"` // Cyberghost, VPNUnlimited
EncPreset string `json:"encryption_preset"` // PIA
IPv6 bool `json:"ipv6"` // Mullvad
ProcUser string `json:"procuser"` // Process username
Interface string `json:"interface"`
} }
func (settings *OpenVPN) String() string { func (settings *OpenVPN) String() string {
@@ -42,8 +36,6 @@ func (settings *OpenVPN) lines() (lines []string) {
lines = append(lines, indent+lastIndent+"Verbosity level: "+strconv.Itoa(settings.Verbosity)) lines = append(lines, indent+lastIndent+"Verbosity level: "+strconv.Itoa(settings.Verbosity))
lines = append(lines, indent+lastIndent+"Network interface: "+settings.Interface)
if len(settings.Flags) > 0 { if len(settings.Flags) > 0 {
lines = append(lines, indent+lastIndent+"Flags: "+strings.Join(settings.Flags, " ")) lines = append(lines, indent+lastIndent+"Flags: "+strings.Join(settings.Flags, " "))
} }
@@ -63,32 +55,48 @@ func (settings *OpenVPN) lines() (lines []string) {
lines = append(lines, indent+lastIndent+"Custom configuration: "+settings.Config) lines = append(lines, indent+lastIndent+"Custom configuration: "+settings.Config)
} }
if settings.ClientKey != "" { if settings.Provider.Name == "" {
lines = append(lines, indent+lastIndent+"Client key is set") lines = append(lines, indent+lastIndent+"Provider: custom configuration")
} } else {
lines = append(lines, indent+lastIndent+"Provider:")
if settings.ClientCrt != "" { for _, line := range settings.Provider.lines() {
lines = append(lines, indent+lastIndent+"Client certificate is set") lines = append(lines, indent+indent+line)
} }
if settings.IPv6 {
lines = append(lines, indent+lastIndent+"IPv6: enabled")
}
if settings.EncPreset != "" { // PIA only
lines = append(lines, indent+lastIndent+"Encryption preset: "+settings.EncPreset)
} }
return lines return lines
} }
func (settings *OpenVPN) read(r reader, serviceProvider string) (err error) { var (
ErrInvalidVPNProvider = errors.New("invalid VPN provider")
)
func (settings *OpenVPN) read(r reader) (err error) {
vpnsp, err := r.env.Inside("VPNSP", []string{
"cyberghost", "fastestvpn", "hidemyass", "ipvanish", "ivpn", "mullvad", "nordvpn",
"privado", "pia", "private internet access", "privatevpn", "protonvpn",
"purevpn", "surfshark", "torguard", constants.VPNUnlimited, "vyprvpn", "windscribe"},
params.Default("private internet access"))
if err != nil {
return fmt.Errorf("environment variable VPNSP: %w", err)
}
if vpnsp == "pia" { // retro compatibility
vpnsp = "private internet access"
}
settings.Provider.Name = vpnsp
settings.Config, err = r.env.Get("OPENVPN_CUSTOM_CONFIG", params.CaseSensitiveValue()) settings.Config, err = r.env.Get("OPENVPN_CUSTOM_CONFIG", params.CaseSensitiveValue())
if err != nil { if err != nil {
return fmt.Errorf("environment variable OPENVPN_CUSTOM_CONFIG: %w", err) return fmt.Errorf("environment variable OPENVPN_CUSTOM_CONFIG: %w", err)
} }
customConfig := settings.Config != ""
credentialsRequired := settings.Config == "" && serviceProvider != constants.VPNUnlimited if customConfig {
settings.Provider.Name = ""
}
credentialsRequired := !customConfig && settings.Provider.Name != constants.VPNUnlimited
settings.User, err = r.getFromEnvOrSecretFile("OPENVPN_USER", credentialsRequired, []string{"USER"}) settings.User, err = r.getFromEnvOrSecretFile("OPENVPN_USER", credentialsRequired, []string{"USER"})
if err != nil { if err != nil {
@@ -97,7 +105,7 @@ func (settings *OpenVPN) read(r reader, serviceProvider string) (err error) {
// Remove spaces in user ID to simplify user's life, thanks @JeordyR // Remove spaces in user ID to simplify user's life, thanks @JeordyR
settings.User = strings.ReplaceAll(settings.User, " ", "") settings.User = strings.ReplaceAll(settings.User, " ", "")
if serviceProvider == constants.Mullvad { if settings.Provider.Name == constants.Mullvad {
settings.Password = "m" settings.Password = "m"
} else { } else {
settings.Password, err = r.getFromEnvOrSecretFile("OPENVPN_PASSWORD", credentialsRequired, []string{"PASSWORD"}) settings.Password, err = r.getFromEnvOrSecretFile("OPENVPN_PASSWORD", credentialsRequired, []string{"PASSWORD"})
@@ -147,58 +155,50 @@ func (settings *OpenVPN) read(r reader, serviceProvider string) (err error) {
return fmt.Errorf("environment variable OPENVPN_MSSFIX: %w", err) return fmt.Errorf("environment variable OPENVPN_MSSFIX: %w", err)
} }
settings.MSSFix = uint16(mssFix) settings.MSSFix = uint16(mssFix)
return settings.readProvider(r)
}
settings.IPv6, err = r.env.OnOff("OPENVPN_IPV6", params.Default("off")) func (settings *OpenVPN) readProvider(r reader) error {
if err != nil { var readProvider func(r reader) error
return fmt.Errorf("environment variable OPENVPN_IPV6: %w", err) switch settings.Provider.Name {
} case "": // custom config
readProvider = func(r reader) error { return nil }
settings.Interface, err = readInterface(r.env)
if err != nil {
return err
}
settings.EncPreset, err = getPIAEncryptionPreset(r)
if err != nil {
return err
}
switch serviceProvider {
case constants.Cyberghost: case constants.Cyberghost:
err = settings.readCyberghost(r) readProvider = settings.Provider.readCyberghost
case constants.Fastestvpn:
readProvider = settings.Provider.readFastestvpn
case constants.HideMyAss:
readProvider = settings.Provider.readHideMyAss
case constants.Ipvanish:
readProvider = settings.Provider.readIpvanish
case constants.Ivpn:
readProvider = settings.Provider.readIvpn
case constants.Mullvad:
readProvider = settings.Provider.readMullvad
case constants.Nordvpn:
readProvider = settings.Provider.readNordvpn
case constants.Privado:
readProvider = settings.Provider.readPrivado
case constants.PrivateInternetAccess:
readProvider = settings.Provider.readPrivateInternetAccess
case constants.Privatevpn:
readProvider = settings.Provider.readPrivatevpn
case constants.Protonvpn:
readProvider = settings.Provider.readProtonvpn
case constants.Purevpn:
readProvider = settings.Provider.readPurevpn
case constants.Surfshark:
readProvider = settings.Provider.readSurfshark
case constants.Torguard:
readProvider = settings.Provider.readTorguard
case constants.VPNUnlimited: case constants.VPNUnlimited:
err = settings.readVPNUnlimited(r) readProvider = settings.Provider.readVPNUnlimited
case constants.Vyprvpn:
readProvider = settings.Provider.readVyprvpn
case constants.Windscribe:
readProvider = settings.Provider.readWindscribe
default:
return fmt.Errorf("%w: %s", ErrInvalidVPNProvider, settings.Provider.Name)
} }
if err != nil { return readProvider(r)
return err
}
return nil
}
func readProtocol(env params.Interface) (tcp bool, err error) {
protocol, err := env.Inside("PROTOCOL", []string{constants.TCP, constants.UDP}, params.Default(constants.UDP))
if err != nil {
return false, fmt.Errorf("environment variable PROTOCOL: %w", err)
}
return protocol == constants.TCP, nil
}
const openvpnIntfRegexString = `^.*[0-9]$`
var openvpnIntfRegexp = regexp.MustCompile(openvpnIntfRegexString)
var errInterfaceNameNotValid = errors.New("interface name is not valid")
func readInterface(env params.Interface) (intf string, err error) {
intf, err = env.Get("OPENVPN_INTERFACE", params.Default("tun0"))
if err != nil {
return "", fmt.Errorf("environment variable OPENVPN_INTERFACE: %w", err)
}
if !openvpnIntfRegexp.MatchString(intf) {
return "", fmt.Errorf("%w: does not match regex %s: %s",
errInterfaceNameNotValid, openvpnIntfRegexString, intf)
}
return intf, nil
} }

View File

@@ -13,6 +13,9 @@ func Test_OpenVPN_JSON(t *testing.T) {
in := OpenVPN{ in := OpenVPN{
Root: true, Root: true,
Flags: []string{}, Flags: []string{},
Provider: Provider{
Name: "name",
},
} }
data, err := json.MarshalIndent(in, "", " ") data, err := json.MarshalIndent(in, "", " ")
require.NoError(t, err) require.NoError(t, err)
@@ -25,12 +28,35 @@ func Test_OpenVPN_JSON(t *testing.T) {
"run_as_root": true, "run_as_root": true,
"cipher": "", "cipher": "",
"auth": "", "auth": "",
"provider": {
"name": "name",
"server_selection": {
"tcp": false,
"regions": null,
"group": "",
"countries": null,
"cities": null,
"hostnames": null,
"names": null,
"isps": null,
"owned": false,
"custom_port": 0,
"numbers": null,
"encryption_preset": "",
"free_only": false,
"stream_only": false
},
"extra_config": {
"encryption_preset": "",
"openvpn_ipv6": false
},
"port_forwarding": {
"enabled": false,
"filepath": ""
}
},
"custom_config": "", "custom_config": "",
"version": "", "version": ""
"encryption_preset": "",
"ipv6": false,
"procuser": "",
"interface": ""
}`, string(data)) }`, string(data))
var out OpenVPN var out OpenVPN
err = json.Unmarshal(data, &out) err = json.Unmarshal(data, &out)

View File

@@ -6,31 +6,55 @@ import (
"github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/constants"
) )
func (settings *Provider) privadoLines() (lines []string) {
if len(settings.ServerSelection.Countries) > 0 {
lines = append(lines, lastIndent+"Countries: "+commaJoin(settings.ServerSelection.Countries))
}
if len(settings.ServerSelection.Regions) > 0 {
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
}
if len(settings.ServerSelection.Cities) > 0 {
lines = append(lines, lastIndent+"Cities: "+commaJoin(settings.ServerSelection.Cities))
}
if len(settings.ServerSelection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
return lines
}
func (settings *Provider) readPrivado(r reader) (err error) { func (settings *Provider) readPrivado(r reader) (err error) {
settings.Name = constants.Privado settings.Name = constants.Privado
servers := r.servers.GetPrivado()
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env) settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil { if err != nil {
return err return err
} }
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.PrivadoCountryChoices(servers)) settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.PrivadoCountryChoices())
if err != nil { if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err) return fmt.Errorf("environment variable COUNTRY: %w", err)
} }
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.PrivadoRegionChoices(servers)) settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.PrivadoRegionChoices())
if err != nil { if err != nil {
return fmt.Errorf("environment variable REGION: %w", err) return fmt.Errorf("environment variable REGION: %w", err)
} }
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.PrivadoCityChoices(servers)) settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.PrivadoCityChoices())
if err != nil { if err != nil {
return fmt.Errorf("environment variable CITY: %w", err) return fmt.Errorf("environment variable CITY: %w", err)
} }
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.PrivadoHostnameChoices(servers)) settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.PrivadoHostnameChoices())
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err) return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
} }

View File

@@ -2,35 +2,85 @@ package configuration
import ( import (
"fmt" "fmt"
"strconv"
"github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/golibs/params" "github.com/qdm12/golibs/params"
) )
func (settings *Provider) privateinternetaccessLines() (lines []string) {
if len(settings.ServerSelection.Regions) > 0 {
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
}
if len(settings.ServerSelection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
if len(settings.ServerSelection.Names) > 0 {
lines = append(lines, lastIndent+"Names: "+commaJoin(settings.ServerSelection.Names))
}
lines = append(lines, lastIndent+"Encryption preset: "+settings.ServerSelection.EncryptionPreset)
if settings.ServerSelection.CustomPort > 0 {
lines = append(lines, lastIndent+"Custom port: "+strconv.Itoa(int(settings.ServerSelection.CustomPort)))
}
if settings.PortForwarding.Enabled {
lines = append(lines, lastIndent+"Port forwarding:")
for _, line := range settings.PortForwarding.lines() {
lines = append(lines, indent+line)
}
}
return lines
}
func (settings *Provider) readPrivateInternetAccess(r reader) (err error) { func (settings *Provider) readPrivateInternetAccess(r reader) (err error) {
settings.Name = constants.PrivateInternetAccess settings.Name = constants.PrivateInternetAccess
servers := r.servers.GetPia()
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env) settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil { if err != nil {
return err return err
} }
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.PIAGeoChoices(servers)) encryptionPreset, err := r.env.Inside("PIA_ENCRYPTION",
[]string{constants.PIAEncryptionPresetNone, constants.PIAEncryptionPresetNormal, constants.PIAEncryptionPresetStrong},
params.RetroKeys([]string{"ENCRYPTION"}, r.onRetroActive),
params.Default(constants.PIACertificateStrong),
)
if err != nil {
return fmt.Errorf("environment variable PIA_ENCRYPTION: %w", err)
}
settings.ServerSelection.EncryptionPreset = encryptionPreset
settings.ExtraConfigOptions.EncryptionPreset = encryptionPreset
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.PIAGeoChoices())
if err != nil { if err != nil {
return fmt.Errorf("environment variable REGION: %w", err) return fmt.Errorf("environment variable REGION: %w", err)
} }
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.PIAHostnameChoices(servers)) settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.PIAHostnameChoices())
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err) return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
} }
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_NAME", constants.PIANameChoices(servers)) settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_NAME", constants.PIANameChoices())
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_NAME: %w", err) return fmt.Errorf("environment variable SERVER_NAME: %w", err)
} }
settings.ServerSelection.CustomPort, err = readPortOrZero(r.env, "PORT")
if err != nil {
return fmt.Errorf("environment variable PORT: %w", err)
}
settings.PortForwarding.Enabled, err = r.env.OnOff("PORT_FORWARDING", params.Default("off")) settings.PortForwarding.Enabled, err = r.env.OnOff("PORT_FORWARDING", params.Default("off"))
if err != nil { if err != nil {
return fmt.Errorf("environment variable PORT_FORWARDING: %w", err) return fmt.Errorf("environment variable PORT_FORWARDING: %w", err)
@@ -44,32 +94,5 @@ func (settings *Provider) readPrivateInternetAccess(r reader) (err error) {
} }
} }
return settings.ServerSelection.OpenVPN.readPrivateInternetAccess(r)
}
func (settings *OpenVPNSelection) readPrivateInternetAccess(r reader) (err error) {
settings.EncPreset, err = getPIAEncryptionPreset(r)
if err != nil {
return err
}
settings.CustomPort, err = readPortOrZero(r.env, "PORT")
if err != nil {
return fmt.Errorf("environment variable PORT: %w", err)
}
return nil return nil
} }
func getPIAEncryptionPreset(r reader) (encryptionPreset string, err error) {
encryptionPreset, err = r.env.Inside("PIA_ENCRYPTION",
[]string{constants.PIAEncryptionPresetNone, constants.PIAEncryptionPresetNormal, constants.PIAEncryptionPresetStrong},
params.RetroKeys([]string{"ENCRYPTION"}, r.onRetroActive),
params.Default(constants.PIACertificateStrong),
)
if err != nil {
return "", fmt.Errorf("environment variable PIA_ENCRYPTION: %w", err)
}
return encryptionPreset, nil
}

View File

@@ -6,30 +6,49 @@ import (
"github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/constants"
) )
func (settings *Provider) privatevpnLines() (lines []string) {
if len(settings.ServerSelection.Countries) > 0 {
lines = append(lines, lastIndent+"Countries: "+commaJoin(settings.ServerSelection.Countries))
}
if len(settings.ServerSelection.Cities) > 0 {
lines = append(lines, lastIndent+"Cities: "+commaJoin(settings.ServerSelection.Cities))
}
if len(settings.ServerSelection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
return lines
}
func (settings *Provider) readPrivatevpn(r reader) (err error) { func (settings *Provider) readPrivatevpn(r reader) (err error) {
settings.Name = constants.Privatevpn settings.Name = constants.Privatevpn
servers := r.servers.GetPrivatevpn()
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env) settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil { if err != nil {
return err return err
} }
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.PrivatevpnCountryChoices(servers)) settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.PrivatevpnCountryChoices())
if err != nil { if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err) return fmt.Errorf("environment variable COUNTRY: %w", err)
} }
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.PrivatevpnCityChoices(servers)) settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.PrivatevpnCityChoices())
if err != nil { if err != nil {
return fmt.Errorf("environment variable CITY: %w", err) return fmt.Errorf("environment variable CITY: %w", err)
} }
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.PrivatevpnHostnameChoices())
constants.PrivatevpnHostnameChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err) return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
} }
return settings.ServerSelection.OpenVPN.readProtocolAndPort(r.env) return nil
} }

View File

@@ -7,37 +7,73 @@ import (
"github.com/qdm12/golibs/params" "github.com/qdm12/golibs/params"
) )
func (settings *Provider) protonvpnLines() (lines []string) {
if len(settings.ServerSelection.Countries) > 0 {
lines = append(lines, lastIndent+"Countries: "+commaJoin(settings.ServerSelection.Countries))
}
if len(settings.ServerSelection.Regions) > 0 {
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
}
if len(settings.ServerSelection.Cities) > 0 {
lines = append(lines, lastIndent+"Cities: "+commaJoin(settings.ServerSelection.Cities))
}
if len(settings.ServerSelection.Names) > 0 {
lines = append(lines, lastIndent+"Names: "+commaJoin(settings.ServerSelection.Names))
}
if len(settings.ServerSelection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
if settings.ServerSelection.FreeOnly {
lines = append(lines, lastIndent+"Free only: yes")
}
return lines
}
func (settings *Provider) readProtonvpn(r reader) (err error) { func (settings *Provider) readProtonvpn(r reader) (err error) {
settings.Name = constants.Protonvpn settings.Name = constants.Protonvpn
servers := r.servers.GetProtonvpn()
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env) settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil { if err != nil {
return err return err
} }
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.ProtonvpnCountryChoices(servers)) settings.ServerSelection.CustomPort, err = readPortOrZero(r.env, "PORT")
if err != nil {
return fmt.Errorf("environment variable PORT: %w", err)
}
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.ProtonvpnCountryChoices())
if err != nil { if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err) return fmt.Errorf("environment variable COUNTRY: %w", err)
} }
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.ProtonvpnRegionChoices(servers)) settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.ProtonvpnRegionChoices())
if err != nil { if err != nil {
return fmt.Errorf("environment variable REGION: %w", err) return fmt.Errorf("environment variable REGION: %w", err)
} }
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.ProtonvpnCityChoices(servers)) settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.ProtonvpnCityChoices())
if err != nil { if err != nil {
return fmt.Errorf("environment variable CITY: %w", err) return fmt.Errorf("environment variable CITY: %w", err)
} }
settings.ServerSelection.Names, err = r.env.CSVInside("SERVER_NAME", constants.ProtonvpnNameChoices(servers)) settings.ServerSelection.Names, err = r.env.CSVInside("SERVER_NAME", constants.ProtonvpnNameChoices())
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_NAME: %w", err) return fmt.Errorf("environment variable SERVER_NAME: %w", err)
} }
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.ProtonvpnHostnameChoices())
constants.ProtonvpnHostnameChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err) return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
} }
@@ -47,5 +83,5 @@ func (settings *Provider) readProtonvpn(r reader) (err error) {
return fmt.Errorf("environment variable FREE_ONLY: %w", err) return fmt.Errorf("environment variable FREE_ONLY: %w", err)
} }
return settings.ServerSelection.OpenVPN.readProtocolAndPort(r.env) return nil
} }

View File

@@ -1,7 +1,6 @@
package configuration package configuration
import ( import (
"errors"
"fmt" "fmt"
"net" "net"
"strings" "strings"
@@ -12,118 +11,83 @@ import (
// Provider contains settings specific to a VPN provider. // Provider contains settings specific to a VPN provider.
type Provider struct { type Provider struct {
Name string `json:"name"` Name string `json:"name"`
ServerSelection ServerSelection `json:"server_selection"` ServerSelection ServerSelection `json:"server_selection"`
PortForwarding PortForwarding `json:"port_forwarding"` ExtraConfigOptions ExtraConfigOptions `json:"extra_config"`
PortForwarding PortForwarding `json:"port_forwarding"`
} }
func (settings *Provider) lines() (lines []string) { func (settings *Provider) lines() (lines []string) {
if settings.Name == "" { // custom OpenVPN configuration
return nil
}
lines = append(lines, lastIndent+strings.Title(settings.Name)+" settings:") lines = append(lines, lastIndent+strings.Title(settings.Name)+" settings:")
for _, line := range settings.ServerSelection.toLines() { selection := settings.ServerSelection
lines = append(lines, indent+line)
lines = append(lines, indent+lastIndent+"Network protocol: "+protoToString(selection.TCP))
if selection.TargetIP != nil {
lines = append(lines, indent+lastIndent+"Target IP address: "+selection.TargetIP.String())
} }
if settings.PortForwarding.Enabled { // PIA var providerLines []string
lines = append(lines, indent+lastIndent+"Port forwarding:") switch strings.ToLower(settings.Name) {
for _, line := range settings.PortForwarding.lines() { case "cyberghost":
lines = append(lines, indent+indent+line) providerLines = settings.cyberghostLines()
} case "fastestvpn":
providerLines = settings.fastestvpnLines()
case "hidemyass":
providerLines = settings.hideMyAssLines()
case "ipvanish":
providerLines = settings.ipvanishLines()
case "ivpn":
providerLines = settings.ivpnLines()
case "mullvad":
providerLines = settings.mullvadLines()
case "nordvpn":
providerLines = settings.nordvpnLines()
case "privado":
providerLines = settings.privadoLines()
case "privatevpn":
providerLines = settings.privatevpnLines()
case "private internet access":
providerLines = settings.privateinternetaccessLines()
case "protonvpn":
providerLines = settings.protonvpnLines()
case "purevpn":
providerLines = settings.purevpnLines()
case "surfshark":
providerLines = settings.surfsharkLines()
case "torguard":
providerLines = settings.torguardLines()
case strings.ToLower(constants.VPNUnlimited):
providerLines = settings.vpnUnlimitedLines()
case "vyprvpn":
providerLines = settings.vyprvpnLines()
case "windscribe":
providerLines = settings.windscribeLines()
default:
panic(`Missing lines method for provider "` +
settings.Name + `"! Please create a Github issue.`)
}
for _, line := range providerLines {
lines = append(lines, indent+line)
} }
return lines return lines
} }
var (
ErrInvalidVPNProvider = errors.New("invalid VPN provider")
)
func (settings *Provider) read(r reader, vpnType string) error {
err := settings.readVPNServiceProvider(r, vpnType)
if err != nil {
return err
}
switch settings.Name {
case constants.Cyberghost:
err = settings.readCyberghost(r)
case constants.Fastestvpn:
err = settings.readFastestvpn(r)
case constants.HideMyAss:
err = settings.readHideMyAss(r)
case constants.Ipvanish:
err = settings.readIpvanish(r)
case constants.Ivpn:
err = settings.readIvpn(r)
case constants.Mullvad:
err = settings.readMullvad(r)
case constants.Nordvpn:
err = settings.readNordvpn(r)
case constants.Privado:
err = settings.readPrivado(r)
case constants.PrivateInternetAccess:
err = settings.readPrivateInternetAccess(r)
case constants.Privatevpn:
err = settings.readPrivatevpn(r)
case constants.Protonvpn:
err = settings.readProtonvpn(r)
case constants.Purevpn:
err = settings.readPurevpn(r)
case constants.Surfshark:
err = settings.readSurfshark(r)
case constants.Torguard:
err = settings.readTorguard(r)
case constants.VPNUnlimited:
err = settings.readVPNUnlimited(r)
case constants.Vyprvpn:
err = settings.readVyprvpn(r)
case constants.Windscribe:
err = settings.readWindscribe(r)
default:
return fmt.Errorf("%w: %s", ErrInvalidVPNProvider, settings.Name)
}
if err != nil {
return err
}
settings.ServerSelection.VPN = vpnType
return nil
}
func (settings *Provider) readVPNServiceProvider(r reader, vpnType string) (err error) {
var allowedVPNServiceProviders []string
switch vpnType {
case constants.OpenVPN:
allowedVPNServiceProviders = []string{
"cyberghost", "fastestvpn", "hidemyass", "ipvanish", "ivpn", "mullvad", "nordvpn",
"privado", "pia", "private internet access", "privatevpn", "protonvpn",
"purevpn", "surfshark", "torguard", constants.VPNUnlimited, "vyprvpn", "windscribe"}
case constants.Wireguard:
allowedVPNServiceProviders = []string{constants.Mullvad, constants.Windscribe,
constants.Ivpn}
}
vpnsp, err := r.env.Inside("VPNSP", allowedVPNServiceProviders,
params.Default("private internet access"))
if err != nil {
return fmt.Errorf("environment variable VPNSP: %w", err)
}
if vpnsp == "pia" { // retro compatibility
vpnsp = "private internet access"
}
settings.Name = vpnsp
return nil
}
func commaJoin(slice []string) string { func commaJoin(slice []string) string {
return strings.Join(slice, ", ") return strings.Join(slice, ", ")
} }
func readProtocol(env params.Env) (tcp bool, err error) {
protocol, err := env.Inside("PROTOCOL", []string{constants.TCP, constants.UDP}, params.Default(constants.UDP))
if err != nil {
return false, fmt.Errorf("environment variable PROTOCOL: %w", err)
}
return protocol == constants.TCP, nil
}
func protoToString(tcp bool) string { func protoToString(tcp bool) string {
if tcp { if tcp {
return constants.TCP return constants.TCP
@@ -131,7 +95,7 @@ func protoToString(tcp bool) string {
return constants.UDP return constants.UDP
} }
func readTargetIP(env params.Interface) (targetIP net.IP, err error) { func readTargetIP(env params.Env) (targetIP net.IP, err error) {
targetIP, err = readIP(env, "OPENVPN_TARGET_IP") targetIP, err = readIP(env, "OPENVPN_TARGET_IP")
if err != nil { if err != nil {
return nil, fmt.Errorf("environment variable OPENVPN_TARGET_IP: %w", err) return nil, fmt.Errorf("environment variable OPENVPN_TARGET_IP: %w", err)
@@ -139,7 +103,7 @@ func readTargetIP(env params.Interface) (targetIP net.IP, err error) {
return targetIP, nil return targetIP, nil
} }
func readOpenVPNCustomPort(env params.Interface, tcp bool, func readCustomPort(env params.Env, tcp bool,
allowedTCP, allowedUDP []uint16) (port uint16, err error) { allowedTCP, allowedUDP []uint16) (port uint16, err error) {
port, err = readPortOrZero(env, "PORT") port, err = readPortOrZero(env, "PORT")
if err != nil { if err != nil {
@@ -154,48 +118,12 @@ func readOpenVPNCustomPort(env params.Interface, tcp bool,
return port, nil return port, nil
} }
} }
return 0, fmt.Errorf( return 0, fmt.Errorf("environment variable PORT: %w: port %d for TCP protocol", ErrInvalidPort, port)
"environment variable PORT: %w: port %d for TCP protocol, can only be one of %s",
ErrInvalidPort, port, portsToString(allowedTCP))
} }
for i := range allowedUDP { for i := range allowedUDP {
if allowedUDP[i] == port { if allowedUDP[i] == port {
return port, nil return port, nil
} }
} }
return 0, fmt.Errorf( return 0, fmt.Errorf("environment variable PORT: %w: port %d for UDP protocol", ErrInvalidPort, port)
"environment variable PORT: %w: port %d for UDP protocol, can only be one of %s",
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 {
return 0, fmt.Errorf("environment variable WIREGUARD_PORT: %w", err)
} else if port == 0 {
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))
}
func portsToString(ports []uint16) string {
slice := make([]string, len(ports))
for i := range ports {
slice[i] = fmt.Sprint(ports[i])
}
return strings.Join(slice, ", ")
} }

View File

@@ -24,41 +24,42 @@ func Test_Provider_lines(t *testing.T) {
settings: Provider{ settings: Provider{
Name: constants.Cyberghost, Name: constants.Cyberghost,
ServerSelection: ServerSelection{ ServerSelection: ServerSelection{
VPN: constants.OpenVPN, Group: "group",
Groups: []string{"group"},
Regions: []string{"a", "El country"}, Regions: []string{"a", "El country"},
}, },
ExtraConfigOptions: ExtraConfigOptions{
ClientKey: "a",
ClientCertificate: "a",
},
}, },
lines: []string{ lines: []string{
"|--Cyberghost settings:", "|--Cyberghost settings:",
" |--Server groups: group", " |--Network protocol: udp",
" |--Server group: group",
" |--Regions: a, El country", " |--Regions: a, El country",
" |--OpenVPN selection:", " |--Client key is set",
" |--Protocol: udp", " |--Client certificate is set",
}, },
}, },
"fastestvpn": { "fastestvpn": {
settings: Provider{ settings: Provider{
Name: constants.Fastestvpn, Name: constants.Fastestvpn,
ServerSelection: ServerSelection{ ServerSelection: ServerSelection{
VPN: constants.OpenVPN,
Hostnames: []string{"a", "b"}, Hostnames: []string{"a", "b"},
Countries: []string{"c", "d"}, Countries: []string{"c", "d"},
}, },
}, },
lines: []string{ lines: []string{
"|--Fastestvpn settings:", "|--Fastestvpn settings:",
" |--Countries: c, d", " |--Network protocol: udp",
" |--Hostnames: a, b", " |--Hostnames: a, b",
" |--OpenVPN selection:", " |--Countries: c, d",
" |--Protocol: udp",
}, },
}, },
"hidemyass": { "hidemyass": {
settings: Provider{ settings: Provider{
Name: constants.HideMyAss, Name: constants.HideMyAss,
ServerSelection: ServerSelection{ ServerSelection: ServerSelection{
VPN: constants.OpenVPN,
Countries: []string{"a", "b"}, Countries: []string{"a", "b"},
Cities: []string{"c", "d"}, Cities: []string{"c", "d"},
Hostnames: []string{"e", "f"}, Hostnames: []string{"e", "f"},
@@ -66,18 +67,16 @@ func Test_Provider_lines(t *testing.T) {
}, },
lines: []string{ lines: []string{
"|--Hidemyass settings:", "|--Hidemyass settings:",
" |--Network protocol: udp",
" |--Countries: a, b", " |--Countries: a, b",
" |--Cities: c, d", " |--Cities: c, d",
" |--Hostnames: e, f", " |--Hostnames: e, f",
" |--OpenVPN selection:",
" |--Protocol: udp",
}, },
}, },
"ipvanish": { "ipvanish": {
settings: Provider{ settings: Provider{
Name: constants.Ipvanish, Name: constants.Ipvanish,
ServerSelection: ServerSelection{ ServerSelection: ServerSelection{
VPN: constants.OpenVPN,
Countries: []string{"a", "b"}, Countries: []string{"a", "b"},
Cities: []string{"c", "d"}, Cities: []string{"c", "d"},
Hostnames: []string{"e", "f"}, Hostnames: []string{"e", "f"},
@@ -85,18 +84,16 @@ func Test_Provider_lines(t *testing.T) {
}, },
lines: []string{ lines: []string{
"|--Ipvanish settings:", "|--Ipvanish settings:",
" |--Network protocol: udp",
" |--Countries: a, b", " |--Countries: a, b",
" |--Cities: c, d", " |--Cities: c, d",
" |--Hostnames: e, f", " |--Hostnames: e, f",
" |--OpenVPN selection:",
" |--Protocol: udp",
}, },
}, },
"ivpn": { "ivpn": {
settings: Provider{ settings: Provider{
Name: constants.Ivpn, Name: constants.Ivpn,
ServerSelection: ServerSelection{ ServerSelection: ServerSelection{
VPN: constants.OpenVPN,
Countries: []string{"a", "b"}, Countries: []string{"a", "b"},
Cities: []string{"c", "d"}, Cities: []string{"c", "d"},
Hostnames: []string{"e", "f"}, Hostnames: []string{"e", "f"},
@@ -104,73 +101,67 @@ func Test_Provider_lines(t *testing.T) {
}, },
lines: []string{ lines: []string{
"|--Ivpn settings:", "|--Ivpn settings:",
" |--Network protocol: udp",
" |--Countries: a, b", " |--Countries: a, b",
" |--Cities: c, d", " |--Cities: c, d",
" |--Hostnames: e, f", " |--Hostnames: e, f",
" |--OpenVPN selection:",
" |--Protocol: udp",
}, },
}, },
"mullvad": { "mullvad": {
settings: Provider{ settings: Provider{
Name: constants.Mullvad, Name: constants.Mullvad,
ServerSelection: ServerSelection{ ServerSelection: ServerSelection{
VPN: constants.OpenVPN, Countries: []string{"a", "b"},
Countries: []string{"a", "b"}, Cities: []string{"c", "d"},
Cities: []string{"c", "d"}, ISPs: []string{"e", "f"},
ISPs: []string{"e", "f"}, CustomPort: 1,
OpenVPN: OpenVPNSelection{ },
CustomPort: 1, ExtraConfigOptions: ExtraConfigOptions{
}, OpenVPNIPv6: true,
}, },
}, },
lines: []string{ lines: []string{
"|--Mullvad settings:", "|--Mullvad settings:",
" |--Network protocol: udp",
" |--Countries: a, b", " |--Countries: a, b",
" |--Cities: c, d", " |--Cities: c, d",
" |--ISPs: e, f", " |--ISPs: e, f",
" |--OpenVPN selection:", " |--Custom port: 1",
" |--Protocol: udp", " |--IPv6: enabled",
" |--Custom port: 1",
}, },
}, },
"nordvpn": { "nordvpn": {
settings: Provider{ settings: Provider{
Name: constants.Nordvpn, Name: constants.Nordvpn,
ServerSelection: ServerSelection{ ServerSelection: ServerSelection{
VPN: constants.OpenVPN,
Regions: []string{"a", "b"}, Regions: []string{"a", "b"},
Numbers: []uint16{1, 2}, Numbers: []uint16{1, 2},
}, },
}, },
lines: []string{ lines: []string{
"|--Nordvpn settings:", "|--Nordvpn settings:",
" |--Network protocol: udp",
" |--Regions: a, b", " |--Regions: a, b",
" |--Numbers: 1, 2", " |--Numbers: 1, 2",
" |--OpenVPN selection:",
" |--Protocol: udp",
}, },
}, },
"privado": { "privado": {
settings: Provider{ settings: Provider{
Name: constants.Privado, Name: constants.Privado,
ServerSelection: ServerSelection{ ServerSelection: ServerSelection{
VPN: constants.OpenVPN,
Hostnames: []string{"a", "b"}, Hostnames: []string{"a", "b"},
}, },
}, },
lines: []string{ lines: []string{
"|--Privado settings:", "|--Privado settings:",
" |--Network protocol: udp",
" |--Hostnames: a, b", " |--Hostnames: a, b",
" |--OpenVPN selection:",
" |--Protocol: udp",
}, },
}, },
"privatevpn": { "privatevpn": {
settings: Provider{ settings: Provider{
Name: constants.Privatevpn, Name: constants.Privatevpn,
ServerSelection: ServerSelection{ ServerSelection: ServerSelection{
VPN: constants.OpenVPN,
Hostnames: []string{"a", "b"}, Hostnames: []string{"a", "b"},
Countries: []string{"c", "d"}, Countries: []string{"c", "d"},
Cities: []string{"e", "f"}, Cities: []string{"e", "f"},
@@ -178,18 +169,16 @@ func Test_Provider_lines(t *testing.T) {
}, },
lines: []string{ lines: []string{
"|--Privatevpn settings:", "|--Privatevpn settings:",
" |--Network protocol: udp",
" |--Countries: c, d", " |--Countries: c, d",
" |--Cities: e, f", " |--Cities: e, f",
" |--Hostnames: a, b", " |--Hostnames: a, b",
" |--OpenVPN selection:",
" |--Protocol: udp",
}, },
}, },
"protonvpn": { "protonvpn": {
settings: Provider{ settings: Provider{
Name: constants.Protonvpn, Name: constants.Protonvpn,
ServerSelection: ServerSelection{ ServerSelection: ServerSelection{
VPN: constants.OpenVPN,
Countries: []string{"a", "b"}, Countries: []string{"a", "b"},
Regions: []string{"c", "d"}, Regions: []string{"c", "d"},
Cities: []string{"e", "f"}, Cities: []string{"e", "f"},
@@ -199,24 +188,21 @@ func Test_Provider_lines(t *testing.T) {
}, },
lines: []string{ lines: []string{
"|--Protonvpn settings:", "|--Protonvpn settings:",
" |--Network protocol: udp",
" |--Countries: a, b", " |--Countries: a, b",
" |--Regions: c, d", " |--Regions: c, d",
" |--Cities: e, f", " |--Cities: e, f",
" |--Hostnames: i, j",
" |--Names: g, h", " |--Names: g, h",
" |--OpenVPN selection:", " |--Hostnames: i, j",
" |--Protocol: udp",
}, },
}, },
"private internet access": { "private internet access": {
settings: Provider{ settings: Provider{
Name: constants.PrivateInternetAccess, Name: constants.PrivateInternetAccess,
ServerSelection: ServerSelection{ ServerSelection: ServerSelection{
VPN: constants.OpenVPN, Regions: []string{"a", "b"},
Regions: []string{"a", "b"}, EncryptionPreset: constants.PIAEncryptionPresetStrong,
OpenVPN: OpenVPNSelection{ CustomPort: 1,
CustomPort: 1,
},
}, },
PortForwarding: PortForwarding{ PortForwarding: PortForwarding{
Enabled: true, Enabled: true,
@@ -225,10 +211,10 @@ func Test_Provider_lines(t *testing.T) {
}, },
lines: []string{ lines: []string{
"|--Private Internet Access settings:", "|--Private Internet Access settings:",
" |--Network protocol: udp",
" |--Regions: a, b", " |--Regions: a, b",
" |--OpenVPN selection:", " |--Encryption preset: strong",
" |--Protocol: udp", " |--Custom port: 1",
" |--Custom port: 1",
" |--Port forwarding:", " |--Port forwarding:",
" |--File path: /here", " |--File path: /here",
}, },
@@ -237,7 +223,6 @@ func Test_Provider_lines(t *testing.T) {
settings: Provider{ settings: Provider{
Name: constants.Purevpn, Name: constants.Purevpn,
ServerSelection: ServerSelection{ ServerSelection: ServerSelection{
VPN: constants.OpenVPN,
Regions: []string{"a", "b"}, Regions: []string{"a", "b"},
Countries: []string{"c", "d"}, Countries: []string{"c", "d"},
Cities: []string{"e", "f"}, Cities: []string{"e", "f"},
@@ -245,33 +230,29 @@ func Test_Provider_lines(t *testing.T) {
}, },
lines: []string{ lines: []string{
"|--Purevpn settings:", "|--Purevpn settings:",
" |--Countries: c, d", " |--Network protocol: udp",
" |--Regions: a, b", " |--Regions: a, b",
" |--Countries: c, d",
" |--Cities: e, f", " |--Cities: e, f",
" |--OpenVPN selection:",
" |--Protocol: udp",
}, },
}, },
"surfshark": { "surfshark": {
settings: Provider{ settings: Provider{
Name: constants.Surfshark, Name: constants.Surfshark,
ServerSelection: ServerSelection{ ServerSelection: ServerSelection{
VPN: constants.OpenVPN,
Regions: []string{"a", "b"}, Regions: []string{"a", "b"},
}, },
}, },
lines: []string{ lines: []string{
"|--Surfshark settings:", "|--Surfshark settings:",
" |--Network protocol: udp",
" |--Regions: a, b", " |--Regions: a, b",
" |--OpenVPN selection:",
" |--Protocol: udp",
}, },
}, },
"torguard": { "torguard": {
settings: Provider{ settings: Provider{
Name: constants.Torguard, Name: constants.Torguard,
ServerSelection: ServerSelection{ ServerSelection: ServerSelection{
VPN: constants.OpenVPN,
Countries: []string{"a", "b"}, Countries: []string{"a", "b"},
Cities: []string{"c", "d"}, Cities: []string{"c", "d"},
Hostnames: []string{"e"}, Hostnames: []string{"e"},
@@ -279,72 +260,67 @@ func Test_Provider_lines(t *testing.T) {
}, },
lines: []string{ lines: []string{
"|--Torguard settings:", "|--Torguard settings:",
" |--Network protocol: udp",
" |--Countries: a, b", " |--Countries: a, b",
" |--Cities: c, d", " |--Cities: c, d",
" |--Hostnames: e", " |--Hostnames: e",
" |--OpenVPN selection:",
" |--Protocol: udp",
}, },
}, },
constants.VPNUnlimited: { constants.VPNUnlimited: {
settings: Provider{ settings: Provider{
Name: constants.VPNUnlimited, Name: constants.VPNUnlimited,
ServerSelection: ServerSelection{ ServerSelection: ServerSelection{
VPN: constants.OpenVPN,
Countries: []string{"a", "b"}, Countries: []string{"a", "b"},
Cities: []string{"c", "d"}, Cities: []string{"c", "d"},
Hostnames: []string{"e", "f"}, Hostnames: []string{"e", "f"},
FreeOnly: true, FreeOnly: true,
StreamOnly: true, StreamOnly: true,
}, },
ExtraConfigOptions: ExtraConfigOptions{
ClientKey: "a",
},
}, },
lines: []string{ lines: []string{
"|--Vpn Unlimited settings:", "|--Vpn Unlimited settings:",
" |--Network protocol: udp",
" |--Countries: a, b", " |--Countries: a, b",
" |--Cities: c, d", " |--Cities: c, d",
" |--Hostnames: e, f",
" |--Free servers only", " |--Free servers only",
" |--Stream servers only", " |--Stream servers only",
" |--Hostnames: e, f", " |--Client key is set",
" |--OpenVPN selection:",
" |--Protocol: udp",
}, },
}, },
"vyprvpn": { "vyprvpn": {
settings: Provider{ settings: Provider{
Name: constants.Vyprvpn, Name: constants.Vyprvpn,
ServerSelection: ServerSelection{ ServerSelection: ServerSelection{
VPN: constants.OpenVPN,
Regions: []string{"a", "b"}, Regions: []string{"a", "b"},
}, },
}, },
lines: []string{ lines: []string{
"|--Vyprvpn settings:", "|--Vyprvpn settings:",
" |--Network protocol: udp",
" |--Regions: a, b", " |--Regions: a, b",
" |--OpenVPN selection:",
" |--Protocol: udp",
}, },
}, },
"windscribe": { "windscribe": {
settings: Provider{ settings: Provider{
Name: constants.Windscribe, Name: constants.Windscribe,
ServerSelection: ServerSelection{ ServerSelection: ServerSelection{
VPN: constants.OpenVPN, Regions: []string{"a", "b"},
Regions: []string{"a", "b"}, Cities: []string{"c", "d"},
Cities: []string{"c", "d"}, Hostnames: []string{"e", "f"},
Hostnames: []string{"e", "f"}, CustomPort: 1,
OpenVPN: OpenVPNSelection{
CustomPort: 1,
},
}, },
}, },
lines: []string{ lines: []string{
"|--Windscribe settings:", "|--Windscribe settings:",
" |--Network protocol: udp",
" |--Regions: a, b", " |--Regions: a, b",
" |--Cities: c, d", " |--Cities: c, d",
" |--Hostnames: e, f", " |--Hostnames: e, f",
" |--OpenVPN selection:", " |--Custom port: 1",
" |--Protocol: udp",
" |--Custom port: 1",
}, },
}, },
} }
@@ -386,7 +362,7 @@ func Test_readProtocol(t *testing.T) {
t.Parallel() t.Parallel()
ctrl := gomock.NewController(t) ctrl := gomock.NewController(t)
env := mock_params.NewMockInterface(ctrl) env := mock_params.NewMockEnv(ctrl)
env.EXPECT(). env.EXPECT().
Inside("PROTOCOL", []string{"tcp", "udp"}, gomock.Any()). Inside("PROTOCOL", []string{"tcp", "udp"}, gomock.Any()).
Return(testCase.mockStr, testCase.mockErr) Return(testCase.mockStr, testCase.mockErr)

View File

@@ -6,34 +6,58 @@ import (
"github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/constants"
) )
func (settings *Provider) purevpnLines() (lines []string) {
if len(settings.ServerSelection.Regions) > 0 {
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
}
if len(settings.ServerSelection.Countries) > 0 {
lines = append(lines, lastIndent+"Countries: "+commaJoin(settings.ServerSelection.Countries))
}
if len(settings.ServerSelection.Cities) > 0 {
lines = append(lines, lastIndent+"Cities: "+commaJoin(settings.ServerSelection.Cities))
}
if len(settings.ServerSelection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
return lines
}
func (settings *Provider) readPurevpn(r reader) (err error) { func (settings *Provider) readPurevpn(r reader) (err error) {
settings.Name = constants.Purevpn settings.Name = constants.Purevpn
servers := r.servers.GetPurevpn()
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env) settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil { if err != nil {
return err return err
} }
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.PurevpnRegionChoices(servers)) settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.PurevpnRegionChoices())
if err != nil { if err != nil {
return fmt.Errorf("environment variable REGION: %w", err) return fmt.Errorf("environment variable REGION: %w", err)
} }
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.PurevpnCountryChoices(servers)) settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.PurevpnCountryChoices())
if err != nil { if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err) return fmt.Errorf("environment variable COUNTRY: %w", err)
} }
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.PurevpnCityChoices(servers)) settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.PurevpnCityChoices())
if err != nil { if err != nil {
return fmt.Errorf("environment variable CITY: %w", err) return fmt.Errorf("environment variable CITY: %w", err)
} }
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.PurevpnHostnameChoices(servers)) settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.PurevpnHostnameChoices())
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err) return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
} }
return settings.ServerSelection.OpenVPN.readProtocolOnly(r.env) return nil
} }

View File

@@ -7,26 +7,22 @@ import (
"strconv" "strconv"
"strings" "strings"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/golibs/logging" "github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/params" "github.com/qdm12/golibs/params"
"github.com/qdm12/golibs/verification" "github.com/qdm12/golibs/verification"
) )
type reader struct { type reader struct {
servers models.AllServers env params.Env
env params.Interface logger logging.Logger
logger logging.Logger regex verification.Regex
regex verification.Regex
} }
func newReader(env params.Interface, func newReader(env params.Env, logger logging.Logger) reader {
servers models.AllServers, logger logging.Logger) reader {
return reader{ return reader{
servers: servers, env: env,
env: env, logger: logger,
logger: logger, regex: verification.NewRegex(),
regex: verification.NewRegex(),
} }
} }
@@ -40,7 +36,7 @@ var (
ErrInvalidPort = errors.New("invalid port") ErrInvalidPort = errors.New("invalid port")
) )
func readCSVPorts(env params.Interface, key string) (ports []uint16, err error) { func readCSVPorts(env params.Env, key string) (ports []uint16, err error) {
s, err := env.Get(key) s, err := env.Get(key)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -67,7 +63,7 @@ var (
ErrInvalidIPNet = errors.New("invalid IP network") ErrInvalidIPNet = errors.New("invalid IP network")
) )
func readCSVIPNets(env params.Interface, key string, options ...params.OptionSetter) ( func readCSVIPNets(env params.Env, key string, options ...params.OptionSetter) (
ipNets []net.IPNet, err error) { ipNets []net.IPNet, err error) {
s, err := env.Get(key, options...) s, err := env.Get(key, options...)
if err != nil { if err != nil {
@@ -96,7 +92,7 @@ var (
ErrInvalidIP = errors.New("invalid IP address") ErrInvalidIP = errors.New("invalid IP address")
) )
func readIP(env params.Interface, key string) (ip net.IP, err error) { func readIP(env params.Env, key string) (ip net.IP, err error) {
s, err := env.Get(key) s, err := env.Get(key)
if s == "" { if s == "" {
return nil, nil return nil, nil
@@ -112,13 +108,13 @@ func readIP(env params.Interface, key string) (ip net.IP, err error) {
return ip, nil return ip, nil
} }
func readPortOrZero(env params.Interface, key string) (port uint16, err error) { func readPortOrZero(env params.Env, key string) (port uint16, err error) {
s, err := env.Get(key, params.Default("0")) s, err := env.Get(key)
if err != nil { if err != nil {
return 0, err return 0, err
} }
if s == "0" { if s == "" || s == "0" {
return 0, nil return 0, nil
} }

View File

@@ -1,23 +1,19 @@
package configuration package configuration
import ( import (
"fmt"
"net" "net"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/golibs/params"
) )
type ServerSelection struct { //nolint:maligned type ServerSelection struct { //nolint:maligned
// Common // Common
VPN string `json:"vpn"` // note: this is required TCP bool `json:"tcp"` // UDP if TCP is false
TargetIP net.IP `json:"target_ip,omitempty"` TargetIP net.IP `json:"target_ip,omitempty"`
// TODO comments // TODO comments
// Cyberghost, PIA, Protonvpn, Surfshark, Windscribe, Vyprvpn, NordVPN // Cyberghost, PIA, Protonvpn, Surfshark, Windscribe, Vyprvpn, NordVPN
Regions []string `json:"regions"` Regions []string `json:"regions"`
// Cyberghost // Cyberghost
Groups []string `json:"groups"` Group string `json:"group"`
// Fastestvpn, HideMyAss, IPVanish, IVPN, Mullvad, PrivateVPN, Protonvpn, PureVPN, VPNUnlimited // Fastestvpn, HideMyAss, IPVanish, IVPN, Mullvad, PrivateVPN, Protonvpn, PureVPN, VPNUnlimited
Countries []string `json:"countries"` Countries []string `json:"countries"`
@@ -31,133 +27,27 @@ type ServerSelection struct { //nolint:maligned
ISPs []string `json:"isps"` ISPs []string `json:"isps"`
Owned bool `json:"owned"` Owned bool `json:"owned"`
// Mullvad, Windscribe, PIA
CustomPort uint16 `json:"custom_port"`
// NordVPN // NordVPN
Numbers []uint16 `json:"numbers"` Numbers []uint16 `json:"numbers"`
// PIA
EncryptionPreset string `json:"encryption_preset"`
// ProtonVPN // ProtonVPN
FreeOnly bool `json:"free_only"` FreeOnly bool `json:"free_only"`
// VPNUnlimited // VPNUnlimited
StreamOnly bool `json:"stream_only"` StreamOnly bool `json:"stream_only"`
// Surfshark
MultiHopOnly bool `json:"multihop_only"`
OpenVPN OpenVPNSelection `json:"openvpn"`
Wireguard WireguardSelection `json:"wireguard"`
} }
func (selection ServerSelection) toLines() (lines []string) { type ExtraConfigOptions struct {
if selection.TargetIP != nil { ClientCertificate string `json:"-"` // Cyberghost
lines = append(lines, lastIndent+"Target IP address: "+selection.TargetIP.String()) ClientKey string `json:"-"` // Cyberghost, VPNUnlimited
} EncryptionPreset string `json:"encryption_preset"` // PIA
OpenVPNIPv6 bool `json:"openvpn_ipv6"` // Mullvad
if len(selection.Groups) > 0 {
lines = append(lines, lastIndent+"Server groups: "+commaJoin(selection.Groups))
}
if len(selection.Countries) > 0 {
lines = append(lines, lastIndent+"Countries: "+commaJoin(selection.Countries))
}
if len(selection.Regions) > 0 {
lines = append(lines, lastIndent+"Regions: "+commaJoin(selection.Regions))
}
if len(selection.Cities) > 0 {
lines = append(lines, lastIndent+"Cities: "+commaJoin(selection.Cities))
}
if len(selection.ISPs) > 0 {
lines = append(lines, lastIndent+"ISPs: "+commaJoin(selection.ISPs))
}
if selection.FreeOnly {
lines = append(lines, lastIndent+"Free servers only")
}
if selection.StreamOnly {
lines = append(lines, lastIndent+"Stream servers only")
}
if len(selection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(selection.Hostnames))
}
if len(selection.Names) > 0 {
lines = append(lines, lastIndent+"Names: "+commaJoin(selection.Names))
}
if len(selection.Numbers) > 0 {
numbersString := make([]string, len(selection.Numbers))
for i, numberUint16 := range selection.Numbers {
numbersString[i] = fmt.Sprint(numberUint16)
}
lines = append(lines, lastIndent+"Numbers: "+commaJoin(numbersString))
}
if selection.VPN == constants.OpenVPN {
lines = append(lines, selection.OpenVPN.lines()...)
} else { // wireguard
lines = append(lines, selection.Wireguard.lines()...)
}
return lines
}
type OpenVPNSelection struct {
TCP bool `json:"tcp"` // UDP if TCP is false
CustomPort uint16 `json:"custom_port"` // HideMyAss, Mullvad, PIA, ProtonVPN, Windscribe
EncPreset string `json:"encryption_preset"` // PIA - needed to get the port number
}
func (settings *OpenVPNSelection) lines() (lines []string) {
lines = append(lines, lastIndent+"OpenVPN selection:")
lines = append(lines, indent+lastIndent+"Protocol: "+protoToString(settings.TCP))
if settings.CustomPort != 0 {
lines = append(lines, indent+lastIndent+"Custom port: "+fmt.Sprint(settings.CustomPort))
}
if settings.EncPreset != "" {
lines = append(lines, indent+lastIndent+"PIA encryption preset: "+settings.EncPreset)
}
return lines
}
func (settings *OpenVPNSelection) readProtocolOnly(env params.Interface) (err error) {
settings.TCP, err = readProtocol(env)
return err
}
func (settings *OpenVPNSelection) readProtocolAndPort(env params.Interface) (err error) {
settings.TCP, err = readProtocol(env)
if err != nil {
return err
}
settings.CustomPort, err = readPortOrZero(env, "PORT")
if err != nil {
return fmt.Errorf("environment variable PORT: %w", err)
}
return nil
}
type WireguardSelection struct {
CustomPort uint16 `json:"custom_port"` // Mullvad
}
func (settings *WireguardSelection) lines() (lines []string) {
lines = append(lines, lastIndent+"Wireguard selection:")
if settings.CustomPort != 0 {
lines = append(lines, indent+lastIndent+"Custom port: "+fmt.Sprint(settings.CustomPort))
}
return lines
} }
// PortForwarding contains settings for port forwarding. // PortForwarding contains settings for port forwarding.

View File

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

View File

@@ -16,35 +16,23 @@ func Test_Settings_lines(t *testing.T) {
}{ }{
"default settings": { "default settings": {
settings: Settings{ settings: Settings{
VPN: VPN{ OpenVPN: OpenVPN{
Type: constants.OpenVPN, Version: constants.Openvpn25,
Provider: Provider{ Provider: Provider{
Name: constants.Mullvad, Name: constants.Mullvad,
ServerSelection: ServerSelection{
VPN: constants.OpenVPN,
},
},
OpenVPN: OpenVPN{
Version: constants.Openvpn25,
Interface: "tun",
}, },
}, },
}, },
lines: []string{ lines: []string{
"Settings summary below:", "Settings summary below:",
"|--VPN:", "|--OpenVPN:",
" |--Type: openvpn", " |--Version: 2.5",
" |--OpenVPN:", " |--Verbosity level: 0",
" |--Version: 2.5", " |--Provider:",
" |--Verbosity level: 0", " |--Mullvad settings:",
" |--Network interface: tun", " |--Network protocol: udp",
" |--Mullvad settings:",
" |--OpenVPN selection:",
" |--Protocol: udp",
"|--DNS:", "|--DNS:",
"|--Firewall: disabled ⚠️", "|--Firewall: disabled ⚠️",
"|--Log:",
" |--Level: DEBUG",
"|--System:", "|--System:",
" |--Process user ID: 0", " |--Process user ID: 0",
" |--Process group ID: 0", " |--Process group ID: 0",

View File

@@ -2,16 +2,19 @@ package configuration
import ( import (
"fmt" "fmt"
"strconv"
"strings" "strings"
"github.com/qdm12/golibs/params" "github.com/qdm12/golibs/params"
"github.com/qdm12/ss-server/pkg/tcpudp"
) )
// ShadowSocks contains settings to configure the Shadowsocks server. // ShadowSocks contains settings to configure the Shadowsocks server.
type ShadowSocks struct { type ShadowSocks struct {
Enabled bool Method string
tcpudp.Settings Password string
Port uint16
Enabled bool
Log bool
} }
func (settings *ShadowSocks) String() string { func (settings *ShadowSocks) String() string {
@@ -25,12 +28,12 @@ func (settings *ShadowSocks) lines() (lines []string) {
lines = append(lines, lastIndent+"Shadowsocks server:") lines = append(lines, lastIndent+"Shadowsocks server:")
lines = append(lines, indent+lastIndent+"Listening address: "+settings.Address) lines = append(lines, indent+lastIndent+"Listening port: "+strconv.Itoa(int(settings.Port)))
lines = append(lines, indent+lastIndent+"Cipher: "+settings.CipherName) lines = append(lines, indent+lastIndent+"Method: "+settings.Method)
if settings.LogAddresses { if settings.Log {
lines = append(lines, indent+lastIndent+"Log addresses: enabled") lines = append(lines, indent+lastIndent+"Logging: enabled")
} }
return lines return lines
@@ -49,61 +52,24 @@ func (settings *ShadowSocks) read(r reader) (err error) {
return err return err
} }
settings.LogAddresses, err = r.env.OnOff("SHADOWSOCKS_LOG", params.Default("off")) settings.Log, err = r.env.OnOff("SHADOWSOCKS_LOG", params.Default("off"))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SHADOWSOCKS_LOG: %w", err) return fmt.Errorf("environment variable SHADOWSOCKS_LOG: %w", err)
} }
settings.CipherName, err = r.env.Get("SHADOWSOCKS_CIPHER", params.Default("chacha20-ietf-poly1305"), settings.Method, err = r.env.Get("SHADOWSOCKS_METHOD", params.Default("chacha20-ietf-poly1305"))
params.RetroKeys([]string{"SHADOWSOCKS_METHOD"}, r.onRetroActive))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SHADOWSOCKS_CIPHER (or SHADOWSOCKS_METHOD): %w", err) return fmt.Errorf("environment variable SHADOWSOCKS_METHOD: %w", err)
} }
warning, err := settings.getAddress(r.env) var warning string
if warning != "" { settings.Port, warning, err = r.env.ListeningPort("SHADOWSOCKS_PORT", params.Default("8388"))
if len(warning) > 0 {
r.logger.Warn(warning) r.logger.Warn(warning)
} }
if err != nil { if err != nil {
return err return fmt.Errorf("environment variable SHADOWSOCKS_PORT: %w", err)
} }
return nil return nil
} }
func (settings *ShadowSocks) getAddress(env params.Interface) (
warning string, err error) {
address, err := env.Get("SHADOWSOCKS_LISTENING_ADDRESS")
if err != nil {
return "", fmt.Errorf("environment variable SHADOWSOCKS_LISTENING_ADDRESS: %w", err)
}
if address != "" {
address, warning, err := env.ListeningAddress("SHADOWSOCKS_LISTENING_ADDRESS")
if err != nil {
return "", fmt.Errorf("environment variable SHADOWSOCKS_LISTENING_ADDRESS: %w", err)
}
settings.Address = address
return warning, nil
}
// Retro-compatibility
const retroWarning = "You are using the old environment variable " +
"SHADOWSOCKS_PORT, please consider using " +
"SHADOWSOCKS_LISTENING_ADDRESS instead"
portStr, err := env.Get("SHADOWSOCKS_PORT")
if err != nil {
return retroWarning, fmt.Errorf("environment variable SHADOWSOCKS_PORT: %w", err)
} else if portStr != "" {
port, _, err := env.ListeningPort("SHADOWSOCKS_PORT")
if err != nil {
return retroWarning, fmt.Errorf("environment variable SHADOWSOCKS_PORT: %w", err)
}
settings.Address = ":" + fmt.Sprint(port)
return retroWarning, nil
}
// Default value
settings.Address = ":8388"
return "", nil
}

View File

@@ -2,101 +2,44 @@ package configuration
import ( import (
"fmt" "fmt"
"strings"
"github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/golibs/params"
) )
func (settings *Provider) surfsharkLines() (lines []string) {
if len(settings.ServerSelection.Regions) > 0 {
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
}
if len(settings.ServerSelection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
return lines
}
func (settings *Provider) readSurfshark(r reader) (err error) { func (settings *Provider) readSurfshark(r reader) (err error) {
settings.Name = constants.Surfshark settings.Name = constants.Surfshark
servers := r.servers.GetSurfshark()
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env) settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil { if err != nil {
return err return err
} }
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.SurfsharkCountryChoices(servers)) settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.SurfsharkRegionChoices())
if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err)
}
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 { if err != nil {
return fmt.Errorf("environment variable REGION: %w", err) return fmt.Errorf("environment variable REGION: %w", err)
} }
// Retro compatibility settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.SurfsharkHostnameChoices())
// TODO remove in v4
settings.ServerSelection = surfsharkRetroRegion(settings.ServerSelection)
settings.ServerSelection.MultiHopOnly, err = r.env.YesNo("MULTIHOP_ONLY", params.Default("no"))
if err != nil { if err != nil {
return fmt.Errorf("environment variable MULTIHOP_ONLY: %w", err) return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
} }
return settings.ServerSelection.OpenVPN.readProtocolOnly(r.env) return nil
}
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

@@ -1,305 +0,0 @@
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

@@ -6,30 +6,49 @@ import (
"github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/constants"
) )
func (settings *Provider) torguardLines() (lines []string) {
if len(settings.ServerSelection.Countries) > 0 {
lines = append(lines, lastIndent+"Countries: "+commaJoin(settings.ServerSelection.Countries))
}
if len(settings.ServerSelection.Cities) > 0 {
lines = append(lines, lastIndent+"Cities: "+commaJoin(settings.ServerSelection.Cities))
}
if len(settings.ServerSelection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
return lines
}
func (settings *Provider) readTorguard(r reader) (err error) { func (settings *Provider) readTorguard(r reader) (err error) {
settings.Name = constants.Torguard settings.Name = constants.Torguard
servers := r.servers.GetTorguard()
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env) settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil { if err != nil {
return err return err
} }
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.TorguardCountryChoices(servers)) settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.TorguardCountryChoices())
if err != nil { if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err) return fmt.Errorf("environment variable COUNTRY: %w", err)
} }
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.TorguardCityChoices(servers)) settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.TorguardCityChoices())
if err != nil { if err != nil {
return fmt.Errorf("environment variable CITY: %w", err) return fmt.Errorf("environment variable CITY: %w", err)
} }
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.TorguardHostnameChoices())
constants.TorguardHostnameChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err) return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
} }
return settings.ServerSelection.OpenVPN.readProtocolAndPort(r.env) return nil
} }

View File

@@ -59,7 +59,7 @@ var (
ErrInvalidDNSOverTLSProvider = errors.New("invalid DNS over TLS provider") ErrInvalidDNSOverTLSProvider = errors.New("invalid DNS over TLS provider")
) )
func (settings *DNS) readUnboundProviders(env params.Interface) (err error) { func (settings *DNS) readUnboundProviders(env params.Env) (err error) {
s, err := env.Get("DOT_PROVIDERS", params.Default("cloudflare")) s, err := env.Get("DOT_PROVIDERS", params.Default("cloudflare"))
if err != nil { if err != nil {
return fmt.Errorf("environment variable DOT_PROVIDERS: %w", err) return fmt.Errorf("environment variable DOT_PROVIDERS: %w", err)

View File

@@ -60,7 +60,7 @@ func Test_DNS_readUnboundProviders(t *testing.T) {
ctrl := gomock.NewController(t) ctrl := gomock.NewController(t)
env := mock_params.NewMockInterface(ctrl) env := mock_params.NewMockEnv(ctrl)
env.EXPECT().Get("DOT_PROVIDERS", gomock.Any()). env.EXPECT().Get("DOT_PROVIDERS", gomock.Any()).
Return(testCase.envValue, testCase.envErr) Return(testCase.envValue, testCase.envErr)

View File

@@ -1,97 +0,0 @@
package configuration
import (
"errors"
"fmt"
"strings"
"github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/golibs/params"
)
type VPN struct {
Type string `json:"type"`
OpenVPN OpenVPN `json:"openvpn"`
Wireguard Wireguard `json:"wireguard"`
Provider Provider `json:"provider"`
}
func (settings *VPN) String() string {
return strings.Join(settings.lines(), "\n")
}
func (settings *VPN) lines() (lines []string) {
lines = append(lines, lastIndent+"VPN:")
lines = append(lines, indent+lastIndent+"Type: "+settings.Type)
var vpnLines []string
switch settings.Type {
case constants.OpenVPN:
vpnLines = settings.OpenVPN.lines()
case constants.Wireguard:
vpnLines = settings.Wireguard.lines()
}
for _, line := range vpnLines {
lines = append(lines, indent+line)
}
for _, line := range settings.Provider.lines() {
lines = append(lines, indent+line)
}
return lines
}
var (
errReadProviderSettings = errors.New("cannot read provider settings")
errReadOpenVPNSettings = errors.New("cannot read OpenVPN settings")
errReadWireguardSettings = errors.New("cannot read Wireguard settings")
)
func (settings *VPN) read(r reader) (err error) {
vpnType, err := r.env.Inside("VPN_TYPE",
[]string{constants.OpenVPN, constants.Wireguard},
params.Default(constants.OpenVPN))
if err != nil {
return fmt.Errorf("environment variable VPN_TYPE: %w", err)
}
settings.Type = vpnType
if !settings.isOpenVPNCustomConfig(r.env) {
if err := settings.Provider.read(r, vpnType); err != nil {
return fmt.Errorf("%w: %s", errReadProviderSettings, err)
}
}
switch settings.Type {
case constants.OpenVPN:
err = settings.OpenVPN.read(r, settings.Provider.Name)
if err != nil {
return fmt.Errorf("%w: %s", errReadOpenVPNSettings, err)
}
case constants.Wireguard:
err = settings.Wireguard.read(r)
if err != nil {
return fmt.Errorf("%w: %s", errReadWireguardSettings, err)
}
}
return nil
}
func (settings VPN) isOpenVPNCustomConfig(env params.Interface) (ok bool) {
if settings.Type != constants.OpenVPN {
return false
}
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

@@ -7,27 +7,68 @@ import (
"github.com/qdm12/golibs/params" "github.com/qdm12/golibs/params"
) )
func (settings *Provider) vpnUnlimitedLines() (lines []string) {
if len(settings.ServerSelection.Countries) > 0 {
lines = append(lines, lastIndent+"Countries: "+commaJoin(settings.ServerSelection.Countries))
}
if len(settings.ServerSelection.Cities) > 0 {
lines = append(lines, lastIndent+"Cities: "+commaJoin(settings.ServerSelection.Cities))
}
if len(settings.ServerSelection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
if settings.ServerSelection.FreeOnly {
lines = append(lines, lastIndent+"Free servers only")
}
if settings.ServerSelection.StreamOnly {
lines = append(lines, lastIndent+"Stream servers only")
}
if settings.ExtraConfigOptions.ClientKey != "" {
lines = append(lines, lastIndent+"Client key is set")
}
return lines
}
func (settings *Provider) readVPNUnlimited(r reader) (err error) { func (settings *Provider) readVPNUnlimited(r reader) (err error) {
settings.Name = constants.VPNUnlimited settings.Name = constants.VPNUnlimited
servers := r.servers.GetVPNUnlimited()
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env) settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil { if err != nil {
return err return err
} }
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.VPNUnlimitedCountryChoices(servers)) settings.ExtraConfigOptions.ClientKey, err = readClientKey(r)
if err != nil {
return err
}
settings.ExtraConfigOptions.ClientCertificate, err = readClientCertificate(r)
if err != nil {
return err
}
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.VPNUnlimitedCountryChoices())
if err != nil { if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err) return fmt.Errorf("environment variable COUNTRY: %w", err)
} }
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.VPNUnlimitedCityChoices(servers)) settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.VPNUnlimitedCityChoices())
if err != nil { if err != nil {
return fmt.Errorf("environment variable CITY: %w", err) return fmt.Errorf("environment variable CITY: %w", err)
} }
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.VPNUnlimitedHostnameChoices())
constants.VPNUnlimitedHostnameChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err) return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
} }
@@ -42,19 +83,5 @@ func (settings *Provider) readVPNUnlimited(r reader) (err error) {
return fmt.Errorf("environment variable STREAM_ONLY: %w", err) return fmt.Errorf("environment variable STREAM_ONLY: %w", err)
} }
return settings.ServerSelection.OpenVPN.readProtocolOnly(r.env)
}
func (settings *OpenVPN) readVPNUnlimited(r reader) (err error) {
settings.ClientKey, err = readClientKey(r)
if err != nil {
return err
}
settings.ClientCrt, err = readClientCertificate(r)
if err != nil {
return err
}
return nil return nil
} }

View File

@@ -0,0 +1,42 @@
package configuration
import (
"testing"
"github.com/stretchr/testify/assert"
)
func Test_Provider_vpnUnlimitedLines(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
settings Provider
lines []string
}{
"empty settings": {},
"full settings": {
settings: Provider{
ServerSelection: ServerSelection{
Countries: []string{"A", "B"},
Cities: []string{"C", "D"},
Hostnames: []string{"E", "F"},
},
},
lines: []string{
"|--Countries: A, B",
"|--Cities: C, D",
"|--Hostnames: E, F",
},
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
lines := testCase.settings.vpnUnlimitedLines()
assert.Equal(t, testCase.lines, lines)
})
}
}

View File

@@ -6,16 +6,28 @@ import (
"github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/constants"
) )
func (settings *Provider) vyprvpnLines() (lines []string) {
if len(settings.ServerSelection.Regions) > 0 {
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
}
return lines
}
func (settings *Provider) readVyprvpn(r reader) (err error) { func (settings *Provider) readVyprvpn(r reader) (err error) {
settings.Name = constants.Vyprvpn settings.Name = constants.Vyprvpn
servers := r.servers.GetVyprvpn()
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env) settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil { if err != nil {
return err return err
} }
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.VyprvpnRegionChoices(servers)) settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.VyprvpnRegionChoices())
if err != nil { if err != nil {
return fmt.Errorf("environment variable REGION: %w", err) return fmt.Errorf("environment variable REGION: %w", err)
} }

View File

@@ -2,51 +2,60 @@ package configuration
import ( import (
"fmt" "fmt"
"strconv"
"github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/golibs/params"
) )
func (settings *Provider) windscribeLines() (lines []string) {
if len(settings.ServerSelection.Regions) > 0 {
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
}
if len(settings.ServerSelection.Cities) > 0 {
lines = append(lines, lastIndent+"Cities: "+commaJoin(settings.ServerSelection.Cities))
}
if len(settings.ServerSelection.Hostnames) > 0 {
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
}
if settings.ServerSelection.CustomPort > 0 {
lines = append(lines, lastIndent+"Custom port: "+strconv.Itoa(int(settings.ServerSelection.CustomPort)))
}
return lines
}
func (settings *Provider) readWindscribe(r reader) (err error) { func (settings *Provider) readWindscribe(r reader) (err error) {
settings.Name = constants.Windscribe settings.Name = constants.Windscribe
servers := r.servers.GetWindscribe()
settings.ServerSelection.TCP, err = readProtocol(r.env)
if err != nil {
return err
}
settings.ServerSelection.TargetIP, err = readTargetIP(r.env) settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
if err != nil { if err != nil {
return err return err
} }
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.WindscribeRegionChoices(servers)) settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.WindscribeRegionChoices())
if err != nil { if err != nil {
return fmt.Errorf("environment variable REGION: %w", err) return fmt.Errorf("environment variable REGION: %w", err)
} }
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.WindscribeCityChoices(servers)) settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.WindscribeCityChoices())
if err != nil { if err != nil {
return fmt.Errorf("environment variable CITY: %w", err) return fmt.Errorf("environment variable CITY: %w", err)
} }
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.WindscribeHostnameChoices())
constants.WindscribeHostnameChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err) return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
} }
err = settings.ServerSelection.OpenVPN.readWindscribe(r.env) settings.ServerSelection.CustomPort, err = readCustomPort(r.env, settings.ServerSelection.TCP,
if err != nil {
return err
}
return settings.ServerSelection.Wireguard.readWindscribe(r.env)
}
func (settings *OpenVPNSelection) readWindscribe(env params.Interface) (err error) {
settings.TCP, err = readProtocol(env)
if err != nil {
return err
}
settings.CustomPort, err = readOpenVPNCustomPort(env, settings.TCP,
[]uint16{21, 22, 80, 123, 143, 443, 587, 1194, 3306, 8080, 54783}, []uint16{21, 22, 80, 123, 143, 443, 587, 1194, 3306, 8080, 54783},
[]uint16{53, 80, 123, 443, 1194, 54783}) []uint16{53, 80, 123, 443, 1194, 54783})
if err != nil { if err != nil {
@@ -55,13 +64,3 @@ func (settings *OpenVPNSelection) readWindscribe(env params.Interface) (err erro
return nil return nil
} }
func (settings *WireguardSelection) readWindscribe(env params.Interface) (err error) {
settings.CustomPort, err = readWireguardCustomPort(env,
[]uint16{53, 80, 123, 443, 1194, 65142})
if err != nil {
return err
}
return nil
}

View File

@@ -1,88 +0,0 @@
package configuration
import (
"fmt"
"net"
"strings"
"github.com/qdm12/golibs/params"
)
// Wireguard contains settings to configure the Wireguard client.
type Wireguard struct {
PrivateKey string `json:"privatekey"`
PreSharedKey string `json:"presharedkey"`
Addresses []*net.IPNet `json:"addresses"`
Interface string `json:"interface"`
}
func (settings *Wireguard) String() string {
return strings.Join(settings.lines(), "\n")
}
func (settings *Wireguard) lines() (lines []string) {
lines = append(lines, lastIndent+"Wireguard:")
lines = append(lines, indent+lastIndent+"Network interface: "+settings.Interface)
if settings.PrivateKey != "" {
lines = append(lines, indent+lastIndent+"Private key is set")
}
if settings.PreSharedKey != "" {
lines = append(lines, indent+lastIndent+"Pre-shared key is set")
}
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
}
func (settings *Wireguard) read(r reader) (err error) {
settings.PrivateKey, err = r.env.Get("WIREGUARD_PRIVATE_KEY",
params.CaseSensitiveValue(), params.Unset(), params.Compulsory())
if err != nil {
return fmt.Errorf("environment variable WIREGUARD_PRIVATE_KEY: %w", err)
}
settings.PreSharedKey, err = r.env.Get("WIREGUARD_PRESHARED_KEY",
params.CaseSensitiveValue(), params.Unset())
if err != nil {
return fmt.Errorf("environment variable WIREGUARD_PRESHARED_KEY: %w", err)
}
err = settings.readAddresses(r.env)
if err != nil {
return err
}
settings.Interface, err = r.env.Get("WIREGUARD_INTERFACE", params.Default("wg0"))
if err != nil {
return fmt.Errorf("environment variable WIREGUARD_INTERFACE: %w", err)
}
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

@@ -1,8 +1,6 @@
package constants package constants
import ( import (
"sort"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
) )
@@ -11,7 +9,8 @@ const (
CyberghostCertificate = "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" CyberghostCertificate = "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"
) )
func CyberghostRegionChoices(servers []models.CyberghostServer) (choices []string) { func CyberghostRegionChoices() (choices []string) {
servers := CyberghostServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].Region choices[i] = servers[i].Region
@@ -19,27 +18,28 @@ func CyberghostRegionChoices(servers []models.CyberghostServer) (choices []strin
return makeUnique(choices) return makeUnique(choices)
} }
func CyberghostGroupChoices(servers []models.CyberghostServer) (choices []string) { func CyberghostGroupChoices() (choices []string) {
uniqueChoices := map[string]struct{}{} servers := CyberghostServers()
for _, server := range servers { choices = make([]string, len(servers))
uniqueChoices[server.Group] = struct{}{} for i := range servers {
choices[i] = servers[i].Group
} }
return makeUnique(choices)
choices = make([]string, 0, len(uniqueChoices))
for choice := range uniqueChoices {
choices = append(choices, choice)
}
sortable := sort.StringSlice(choices)
sortable.Sort()
return sortable
} }
func CyberghostHostnameChoices(servers []models.CyberghostServer) (choices []string) { func CyberghostHostnameChoices() (choices []string) {
servers := CyberghostServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))
for i := range servers { for i := range servers {
choices[i] = servers[i].Hostname choices[i] = servers[i].Hostname
} }
return makeUnique(choices) return makeUnique(choices)
} }
// CyberghostServers returns a slice with the server information for each
// of the Cyberghost server.
func CyberghostServers() (servers []models.CyberghostServer) {
servers = make([]models.CyberghostServer, len(allServers.Cyberghost.Servers))
copy(servers, allServers.Cyberghost.Servers)
return servers
}

View File

@@ -1,31 +0,0 @@
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(servers.GetCyberghost())
assert.Equal(t, expected, choices)
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,10 @@
package constants package constants
const (
TUN = "tun0"
TAP = "tap0"
)
const ( const (
AES128cbc = "aes-128-cbc" AES128cbc = "aes-128-cbc"
AES256cbc = "aes-256-cbc" AES256cbc = "aes-256-cbc"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,34 @@
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

@@ -1,4 +1,4 @@
package storage package constants
import ( import (
"crypto/sha256" "crypto/sha256"
@@ -12,10 +12,10 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func Test_parseHardcodedServers(t *testing.T) { func Test_parseAllServers(t *testing.T) {
t.Parallel() t.Parallel()
servers, err := parseHardcodedServers() servers, err := parseAllServers()
require.NoError(t, err) require.NoError(t, err)
require.NotEmpty(t, len(servers.Cyberghost.Servers)) require.NotEmpty(t, len(servers.Cyberghost.Servers))
@@ -38,10 +38,7 @@ func digestServerModelVersion(t *testing.T, server interface{}, version uint16)
func Test_versions(t *testing.T) { func Test_versions(t *testing.T) {
t.Parallel() t.Parallel()
allServers := GetAllServers()
allServers, err := parseHardcodedServers()
require.NoError(t, err)
const format = "you forgot to update the version for %s" const format = "you forgot to update the version for %s"
testCases := map[string]struct { testCases := map[string]struct {
model interface{} model interface{}
@@ -71,12 +68,12 @@ func Test_versions(t *testing.T) {
"Ivpn": { "Ivpn": {
model: models.IvpnServer{}, model: models.IvpnServer{},
version: allServers.Ivpn.Version, version: allServers.Ivpn.Version,
digest: "88074ceb", digest: "2eb80d28",
}, },
"Mullvad": { "Mullvad": {
model: models.MullvadServer{}, model: models.MullvadServer{},
version: allServers.Mullvad.Version, version: allServers.Mullvad.Version,
digest: "ec56f19d", digest: "2a009192",
}, },
"Nordvpn": { "Nordvpn": {
model: models.NordvpnServer{}, model: models.NordvpnServer{},
@@ -111,7 +108,7 @@ func Test_versions(t *testing.T) {
"Surfshark": { "Surfshark": {
model: models.SurfsharkServer{}, model: models.SurfsharkServer{},
version: allServers.Surfshark.Version, version: allServers.Surfshark.Version,
digest: "3ccaa772", digest: "58de06d8",
}, },
"Torguard": { "Torguard": {
model: models.TorguardServer{}, model: models.TorguardServer{},
@@ -131,7 +128,7 @@ func Test_versions(t *testing.T) {
"Windscribe": { "Windscribe": {
model: models.WindscribeServer{}, model: models.WindscribeServer{},
version: allServers.Windscribe.Version, version: allServers.Windscribe.Version,
digest: "4bd0fc4f", digest: "6f6c16d6",
}, },
} }
for name, testCase := range testCases { for name, testCase := range testCases {

View File

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

View File

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

View File

@@ -1,10 +1,5 @@
package constants package constants
const (
OpenVPN = "openvpn"
Wireguard = "wireguard"
)
const ( const (
// Cyberghost is a VPN provider. // Cyberghost is a VPN provider.
Cyberghost = "cyberghost" Cyberghost = "cyberghost"

View File

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

View File

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

View File

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

View File

@@ -1,7 +1,6 @@
package dns package dns
import ( import (
"context"
"regexp" "regexp"
"strings" "strings"
@@ -9,24 +8,18 @@ import (
"github.com/qdm12/golibs/logging" "github.com/qdm12/golibs/logging"
) )
func (l *Loop) collectLines(ctx context.Context, done chan<- struct{}, func (l *Loop) collectLines(stdout, stderr <-chan string, done chan<- struct{}) {
stdout, stderr chan string) {
defer close(done) defer close(done)
var line string var line string
var ok bool
for { for {
select { select {
case <-ctx.Done(): case line, ok = <-stderr:
// Context should only be canceled after stdout and stderr are done case line, ok = <-stdout:
// being written to. }
close(stdout) if !ok {
close(stderr) return
return
case line = <-stderr:
case line = <-stdout:
} }
line, level := processLogLine(line) line, level := processLogLine(line)
switch level { switch level {
case logging.LevelDebug: case logging.LevelDebug:

View File

@@ -30,13 +30,12 @@ func (l *Loop) setupUnbound(ctx context.Context) (
return nil, nil, nil, err return nil, nil, nil, err
} }
linesCollectionCtx, linesCollectionCancel := context.WithCancel(context.Background()) collectLinesDone := make(chan struct{})
lineCollectionDone := make(chan struct{}) go l.collectLines(stdoutLines, stderrLines, collectLinesDone)
go l.collectLines(linesCollectionCtx, lineCollectionDone,
stdoutLines, stderrLines)
closeStreams = func() { closeStreams = func() {
linesCollectionCancel() close(stdoutLines)
<-lineCollectionDone close(stderrLines)
<-collectLinesDone
} }
// use Unbound // use Unbound

View File

@@ -4,6 +4,8 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"github.com/qdm12/gluetun/internal/constants"
) )
var ( var (
@@ -107,9 +109,9 @@ func (c *Config) enable(ctx context.Context) (err error) {
if err = c.acceptOutputTrafficToVPN(ctx, c.defaultInterface, c.vpnConnection, remove); err != nil { if err = c.acceptOutputTrafficToVPN(ctx, c.defaultInterface, c.vpnConnection, remove); err != nil {
return fmt.Errorf("cannot enable firewall: %w", err) return fmt.Errorf("cannot enable firewall: %w", err)
} }
if err = c.acceptOutputThroughInterface(ctx, c.vpnIntf, remove); err != nil { }
return fmt.Errorf("cannot enable firewall: %w", err) if err = c.acceptOutputThroughInterface(ctx, string(constants.TUN), remove); err != nil {
} return fmt.Errorf("cannot enable firewall: %w", err)
} }
for _, network := range c.localNetworks { for _, network := range c.localNetworks {

View File

@@ -39,8 +39,7 @@ type Config struct { //nolint:maligned
// State // State
enabled bool enabled bool
vpnConnection models.Connection vpnConnection models.OpenVPNConnection
vpnIntf string
outboundSubnets []net.IPNet outboundSubnets []net.IPNet
allowedInputPorts map[uint16]string // port to interface mapping allowedInputPorts map[uint16]string // port to interface mapping
stateMutex sync.Mutex stateMutex sync.Mutex

View File

@@ -150,7 +150,7 @@ func (c *Config) acceptEstablishedRelatedTraffic(ctx context.Context, remove boo
} }
func (c *Config) acceptOutputTrafficToVPN(ctx context.Context, func (c *Config) acceptOutputTrafficToVPN(ctx context.Context,
defaultInterface string, connection models.Connection, remove bool) error { defaultInterface string, connection models.OpenVPNConnection, remove bool) error {
instruction := fmt.Sprintf("%s OUTPUT -d %s -o %s -p %s -m %s --dport %d -j ACCEPT", instruction := fmt.Sprintf("%s OUTPUT -d %s -o %s -p %s -m %s --dport %d -j ACCEPT",
appendOrDelete(remove), connection.IP, defaultInterface, connection.Protocol, appendOrDelete(remove), connection.IP, defaultInterface, connection.Protocol,
connection.Protocol, connection.Port) connection.Protocol, connection.Port)

View File

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

View File

@@ -1,15 +1,9 @@
package subnet package firewall
import ( import (
"net" "net"
) )
func FindSubnetsToChange(oldSubnets, newSubnets []net.IPNet) (subnetsToAdd, subnetsToRemove []net.IPNet) {
subnetsToAdd = findSubnetsToAdd(oldSubnets, newSubnets)
subnetsToRemove = findSubnetsToRemove(oldSubnets, newSubnets)
return subnetsToAdd, subnetsToRemove
}
func findSubnetsToAdd(oldSubnets, newSubnets []net.IPNet) (subnetsToAdd []net.IPNet) { func findSubnetsToAdd(oldSubnets, newSubnets []net.IPNet) (subnetsToAdd []net.IPNet) {
for _, newSubnet := range newSubnets { for _, newSubnet := range newSubnets {
found := false found := false
@@ -42,7 +36,11 @@ func findSubnetsToRemove(oldSubnets, newSubnets []net.IPNet) (subnetsToRemove []
return subnetsToRemove return subnetsToRemove
} }
func RemoveSubnetFromSubnets(subnets []net.IPNet, subnet net.IPNet) []net.IPNet { func subnetsAreEqual(a, b net.IPNet) bool {
return a.IP.Equal(b.IP) && a.Mask.String() == b.Mask.String()
}
func removeSubnetFromSubnets(subnets []net.IPNet, subnet net.IPNet) []net.IPNet {
L := len(subnets) L := len(subnets)
for i := range subnets { for i := range subnets {
if subnetsAreEqual(subnet, subnets[i]) { if subnetsAreEqual(subnet, subnets[i]) {

View File

@@ -8,12 +8,10 @@ import (
) )
type VPNConnectionSetter interface { type VPNConnectionSetter interface {
SetVPNConnection(ctx context.Context, SetVPNConnection(ctx context.Context, connection models.OpenVPNConnection) error
connection models.Connection, vpnIntf string) error
} }
func (c *Config) SetVPNConnection(ctx context.Context, func (c *Config) SetVPNConnection(ctx context.Context, connection models.OpenVPNConnection) (err error) {
connection models.Connection, vpnIntf string) (err error) {
c.stateMutex.Lock() c.stateMutex.Lock()
defer c.stateMutex.Unlock() defer c.stateMutex.Unlock()
@@ -35,26 +33,11 @@ func (c *Config) SetVPNConnection(ctx context.Context,
c.logger.Error("cannot remove outdated VPN connection through firewall: " + err.Error()) c.logger.Error("cannot remove outdated VPN connection through firewall: " + err.Error())
} }
} }
c.vpnConnection = models.Connection{} c.vpnConnection = models.OpenVPNConnection{}
if c.vpnIntf != "" {
if err = c.acceptOutputThroughInterface(ctx, c.vpnIntf, remove); err != nil {
c.logger.Error("cannot remove outdated VPN interface from firewall: " + err.Error())
}
}
c.vpnIntf = ""
remove = false remove = false
if err := c.acceptOutputTrafficToVPN(ctx, c.defaultInterface, connection, remove); err != nil { if err := c.acceptOutputTrafficToVPN(ctx, c.defaultInterface, connection, remove); err != nil {
return fmt.Errorf("cannot set VPN connection through firewall: %w", err) return fmt.Errorf("cannot set VPN connection through firewall: %w", err)
} }
c.vpnConnection = connection c.vpnConnection = connection
if err = c.acceptOutputThroughInterface(ctx, vpnIntf, remove); err != nil {
return fmt.Errorf("cannot accept output traffic through interface %s: %w", vpnIntf, err)
}
c.vpnIntf = vpnIntf
return nil return nil
} }

View File

@@ -12,7 +12,7 @@ import (
func (s *Server) runHealthcheckLoop(ctx context.Context, done chan<- struct{}) { func (s *Server) runHealthcheckLoop(ctx context.Context, done chan<- struct{}) {
defer close(done) defer close(done)
s.vpn.healthyTimer = time.NewTimer(s.vpn.healthyWait) s.openvpn.healthyTimer = time.NewTimer(s.openvpn.healthyWait)
for { for {
previousErr := s.handler.getErr() previousErr := s.handler.getErr()
@@ -22,12 +22,12 @@ func (s *Server) runHealthcheckLoop(ctx context.Context, done chan<- struct{}) {
if previousErr != nil && err == nil { if previousErr != nil && err == nil {
s.logger.Info("healthy!") s.logger.Info("healthy!")
s.vpn.healthyTimer.Stop() s.openvpn.healthyTimer.Stop()
s.vpn.healthyWait = s.config.OpenVPN.Initial s.openvpn.healthyWait = s.config.OpenVPN.Initial
} else if previousErr == nil && err != nil { } else if previousErr == nil && err != nil {
s.logger.Info("unhealthy: " + err.Error()) s.logger.Info("unhealthy: " + err.Error())
s.vpn.healthyTimer.Stop() s.openvpn.healthyTimer.Stop()
s.vpn.healthyTimer = time.NewTimer(s.vpn.healthyWait) s.openvpn.healthyTimer = time.NewTimer(s.openvpn.healthyWait)
} }
if err != nil { // try again after 1 second if err != nil { // try again after 1 second
@@ -39,7 +39,7 @@ func (s *Server) runHealthcheckLoop(ctx context.Context, done chan<- struct{}) {
} }
return return
case <-timer.C: case <-timer.C:
case <-s.vpn.healthyTimer.C: case <-s.openvpn.healthyTimer.C:
s.onUnhealthyOpenvpn(ctx) s.onUnhealthyOpenvpn(ctx)
} }
continue continue

View File

@@ -5,20 +5,20 @@ import (
"time" "time"
"github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/vpn" "github.com/qdm12/gluetun/internal/openvpn"
) )
type vpnHealth struct { type openvpnHealth struct {
looper vpn.Looper looper openvpn.Looper
healthyWait time.Duration healthyWait time.Duration
healthyTimer *time.Timer healthyTimer *time.Timer
} }
func (s *Server) onUnhealthyOpenvpn(ctx context.Context) { func (s *Server) onUnhealthyOpenvpn(ctx context.Context) {
s.logger.Info("program has been unhealthy for " + s.logger.Info("program has been unhealthy for " +
s.vpn.healthyWait.String() + ": restarting OpenVPN") s.openvpn.healthyWait.String() + ": restarting OpenVPN")
_, _ = s.vpn.looper.ApplyStatus(ctx, constants.Stopped) _, _ = s.openvpn.looper.ApplyStatus(ctx, constants.Stopped)
_, _ = s.vpn.looper.ApplyStatus(ctx, constants.Running) _, _ = s.openvpn.looper.ApplyStatus(ctx, constants.Running)
s.vpn.healthyWait += s.config.OpenVPN.Addition s.openvpn.healthyWait += s.config.OpenVPN.Addition
s.vpn.healthyTimer = time.NewTimer(s.vpn.healthyWait) s.openvpn.healthyTimer = time.NewTimer(s.openvpn.healthyWait)
} }

View File

@@ -5,7 +5,7 @@ import (
"net" "net"
"github.com/qdm12/gluetun/internal/configuration" "github.com/qdm12/gluetun/internal/configuration"
"github.com/qdm12/gluetun/internal/vpn" "github.com/qdm12/gluetun/internal/openvpn"
"github.com/qdm12/golibs/logging" "github.com/qdm12/golibs/logging"
) )
@@ -20,18 +20,18 @@ type Server struct {
handler *handler handler *handler
resolver *net.Resolver resolver *net.Resolver
config configuration.Health config configuration.Health
vpn vpnHealth openvpn openvpnHealth
} }
func NewServer(config configuration.Health, func NewServer(config configuration.Health,
logger logging.Logger, vpnLooper vpn.Looper) *Server { logger logging.Logger, openvpnLooper openvpn.Looper) *Server {
return &Server{ return &Server{
logger: logger, logger: logger,
handler: newHandler(logger), handler: newHandler(logger),
resolver: net.DefaultResolver, resolver: net.DefaultResolver,
config: config, config: config,
vpn: vpnHealth{ openvpn: openvpnHealth{
looper: vpnLooper, looper: openvpnLooper,
healthyWait: config.OpenVPN.Initial, healthyWait: config.OpenVPN.Initial,
}, },
} }

View File

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

View File

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

View File

@@ -51,7 +51,6 @@ func (s *State) ApplyStatus(ctx context.Context, status models.LoopStatus) (
case <-ctx.Done(): case <-ctx.Done():
case newStatus = <-s.running: case newStatus = <-s.running:
} }
s.SetStatus(newStatus) s.SetStatus(newStatus)
return newStatus.String(), nil return newStatus.String(), nil

View File

@@ -1,51 +0,0 @@
package models
import (
"fmt"
"net"
)
type Connection struct {
// Type is the connection type and can be "openvpn" or "wireguard"
Type string `json:"type"`
// IP is the VPN server IP address.
IP net.IP `json:"ip"`
// Port is the VPN server port.
Port uint16 `json:"port"`
// Protocol can be "tcp" or "udp".
Protocol string `json:"protocol"`
// Hostname is used for IPVanish, IVPN, Privado
// and Windscribe for TLS verification.
Hostname string `json:"hostname"`
// PubKey is the public key of the VPN server,
// used only for Wireguard.
PubKey string `json:"pubkey"`
}
func (c *Connection) Equal(other Connection) bool {
return c.IP.Equal(other.IP) && c.Port == other.Port &&
c.Protocol == other.Protocol && c.Hostname == other.Hostname &&
c.PubKey == other.PubKey
}
func (c Connection) OpenVPNRemoteLine() (line string) {
return "remote " + c.IP.String() + " " + fmt.Sprint(c.Port)
}
func (c Connection) OpenVPNProtoLine() (line string) {
return "proto " + c.Protocol
}
// UpdateEmptyWith updates each field of the connection where the
// value is not set using the value given as arguments.
func (c *Connection) UpdateEmptyWith(ip net.IP, port uint16, protocol string) {
if c.IP == nil {
c.IP = ip
}
if c.Port == 0 {
c.Port = port
}
if c.Protocol == "" {
c.Protocol = protocol
}
}

View File

@@ -1,252 +0,0 @@
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

@@ -1,181 +0,0 @@
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

@@ -1,12 +0,0 @@
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

@@ -0,0 +1,28 @@
package models
import (
"net"
"strconv"
)
type OpenVPNConnection struct {
IP net.IP `json:"ip"`
Port uint16 `json:"port"`
Protocol string `json:"protocol"`
// Hostname is used for IPVanish, IVPN, Privado
// and Windscribe for TLS verification
Hostname string `json:"hostname"`
}
func (o *OpenVPNConnection) Equal(other OpenVPNConnection) bool {
return o.IP.Equal(other.IP) && o.Port == other.Port && o.Protocol == other.Protocol &&
o.Hostname == other.Hostname
}
func (o OpenVPNConnection) RemoteLine() (line string) {
return "remote " + o.IP.String() + " " + strconv.Itoa(int(o.Port))
}
func (o OpenVPNConnection) ProtoLine() (line string) {
return "proto " + o.Protocol
}

View File

@@ -39,19 +39,15 @@ type IpvanishServer struct {
} }
type IvpnServer struct { type IvpnServer struct {
VPN string `json:"vpn"`
Country string `json:"country"` Country string `json:"country"`
City string `json:"city"` City string `json:"city"`
ISP string `json:"isp"`
Hostname string `json:"hostname"` Hostname string `json:"hostname"`
WgPubKey string `json:"wgpubkey,omitempty"`
TCP bool `json:"tcp"` TCP bool `json:"tcp"`
UDP bool `json:"udp"` UDP bool `json:"udp"`
IPs []net.IP `json:"ips"` IPs []net.IP `json:"ips"`
} }
type MullvadServer struct { type MullvadServer struct {
VPN string `json:"vpn"`
IPs []net.IP `json:"ips"` IPs []net.IP `json:"ips"`
IPsV6 []net.IP `json:"ipsv6"` IPsV6 []net.IP `json:"ipsv6"`
Country string `json:"country"` Country string `json:"country"`
@@ -59,7 +55,6 @@ type MullvadServer struct {
Hostname string `json:"hostname"` Hostname string `json:"hostname"`
ISP string `json:"isp"` ISP string `json:"isp"`
Owned bool `json:"owned"` Owned bool `json:"owned"`
WgPubKey string `json:"wgpubkey,omitempty"`
} }
type NordvpnServer struct { //nolint:maligned type NordvpnServer struct { //nolint:maligned
@@ -119,11 +114,7 @@ type PurevpnServer struct {
type SurfsharkServer struct { type SurfsharkServer struct {
Region string `json:"region"` Region string `json:"region"`
Country string `json:"country"` // Country is also used for multi-hop
City string `json:"city"`
RetroLoc string `json:"retroloc"` // TODO remove in v4
Hostname string `json:"hostname"` Hostname string `json:"hostname"`
MultiHop bool `json:"multihop"`
TCP bool `json:"tcp"` TCP bool `json:"tcp"`
UDP bool `json:"udp"` UDP bool `json:"udp"`
IPs []net.IP `json:"ips"` IPs []net.IP `json:"ips"`
@@ -158,11 +149,9 @@ type VyprvpnServer struct {
} }
type WindscribeServer struct { type WindscribeServer struct {
VPN string `json:"vpn"`
Region string `json:"region"` Region string `json:"region"`
City string `json:"city"` City string `json:"city"`
Hostname string `json:"hostname"` Hostname string `json:"hostname"`
OvpnX509 string `json:"x509,omitempty"` OvpnX509 string `json:"x509"`
WgPubKey string `json:"wgpubkey,omitempty"`
IPs []net.IP `json:"ips"` IPs []net.IP `json:"ips"`
} }

View File

@@ -1,22 +0,0 @@
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

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

View File

@@ -1,12 +0,0 @@
package netlink
//go:generate mockgen -destination=mock_$GOPACKAGE/$GOFILE . NetLinker
var _ NetLinker = (*NetLink)(nil)
type NetLinker interface {
Addresser
Linker
Router
Ruler
}

View File

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

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