Compare commits

...

94 Commits

Author SHA1 Message Date
Quentin McGaw (desktop)
6b6caa435f Fix: clear IP data when VPN is stopped 2021-09-06 13:28:05 +00:00
Quentin McGaw (desktop)
f9cb71027c Feat: location data at /v1/publicip/ip 2021-09-05 22:54:10 +00:00
Quentin McGaw (desktop)
82ac568ee3 Fix: wireguard cleanup preventing restarts 2021-09-04 22:29:04 +00:00
Quentin McGaw (desktop)
61afdce788 Hotfix: Wireguard WIREGUARD_ADDRESSES setting 2021-08-28 20:59:39 +00:00
Quentin McGaw (desktop)
119cac5a67 Feat: OPENVPN_TARGET_IP overrides IP
- Check target IP matches a server for Wireguard since we need the public key
- Streamline connection picking for all providers
2021-08-28 19:07:44 +00:00
Quentin McGaw (desktop)
c6fedd9214 Feat: support csv addresses in WIREGUARD_ADDRESS 2021-08-28 18:43:23 +00:00
Quentin McGaw (desktop)
da525e039d Fix: update Mullvad annoucement logged 2021-08-28 18:14:28 +00:00
Quentin McGaw (desktop)
29d92fd307 Fix: Surfshark REGION retro-compatibility 2021-08-28 18:14:21 +00:00
Quentin McGaw (desktop)
3863cc439e Maint: internal/storage rework
- No more global variables
- Inject merged servers to configuration package
- Fix #566: configuration parsing to use persisted servers.json
- Move server data files from `internal/constants` to `internal/storage`
2021-08-27 19:10:03 +00:00
Quentin McGaw (desktop)
b1cfc03fc5 Maint: internal/storage remove Windscribe debug logs 2021-08-27 12:10:49 +00:00
Quentin McGaw (desktop)
f706071048 Fix: FIREWALL_VPN_INPUT_PORTS for Wireguard 2021-08-26 19:54:48 +00:00
Quentin McGaw (desktop)
501ae2741b Fix: FIREWALL_OUTBOUND_SUBNETS ip rules 2021-08-26 15:46:19 +00:00
Quentin McGaw (desktop)
5b75635386 Maint: fix rules equality check for nil networks 2021-08-26 14:33:51 +00:00
Quentin McGaw (desktop)
2901db3cf3 Maint: internal/routing IP rules functions
- Take in `src` as `*net.IPNet` instead of `net.IP`
- Take `dst` IP network
- Debug logged `ip rule` dynamically built
- Add unit tests for all IP rules functions
2021-08-26 13:59:43 +00:00
Quentin McGaw (desktop)
6c2a3e36b5 Maint: rename outboundsubnets.go to outbound.go 2021-08-25 19:09:42 +00:00
Quentin McGaw (desktop)
8b125e6e95 Maint: internal/routing/inbound.go file 2021-08-25 19:08:55 +00:00
Quentin McGaw (desktop)
e1cc14e055 Fix: firewall inherits log level from LOG_LEVEL 2021-08-25 17:55:46 +00:00
Quentin McGaw (desktop)
d6659552df Maint: refactor internal/routing
- Split Go files better
- Reduce public API for exported errors
2021-08-25 17:52:05 +00:00
Quentin McGaw (desktop)
67001fa958 Maint: rename files in internal/subnet 2021-08-25 17:27:10 +00:00
Quentin McGaw (desktop)
ffeeae91ab Maint: merge subnet.FindSubnetsToAdd and subnet.FindSubnetsToRemove in subnet.FindSubnetsToChange 2021-08-25 17:25:36 +00:00
Quentin McGaw (desktop)
04fad1b781 Maint: internal/subnet package 2021-08-25 17:22:48 +00:00
Quentin McGaw (desktop)
dcaf952986 Maint: http proxy server constructor returns struct 2021-08-25 17:03:55 +00:00
Quentin McGaw (desktop)
ca3b9e892d Maint: http proxy HTTPS handling simplifications 2021-08-25 17:02:50 +00:00
Quentin McGaw (desktop)
9f12ffc069 Fix: MULTIHOP_ONLY defaults to no 2021-08-24 13:12:40 +00:00
Quentin McGaw (desktop)
0d6800a515 Fix: panic for certain no server found errors 2021-08-23 21:19:53 +00:00
Quentin McGaw (desktop)
b3d8b78205 Maint: only internal/netlink depends on github.com/vishvananda/netlink 2021-08-23 21:12:28 +00:00
Quentin McGaw (desktop)
ee82a85543 Maint: internal/routing uses internal/netlink 2021-08-23 20:56:10 +00:00
Quentin McGaw (desktop)
7907146aaf Maint: rework IPIsPrivate in internal/routing 2021-08-23 20:50:50 +00:00
Quentin McGaw (desktop)
1a677ce4f7 Maint: internal/routing returns *Routine struct 2021-08-23 20:50:32 +00:00
Quentin McGaw (desktop)
f1a6594474 Maint: utils.FilterByProtocol function 2021-08-23 20:16:29 +00:00
Quentin McGaw
f1a82d9d9c Feat: rework Surfshark servers data (#575)
- Feat: `MULTIHOP_ONLY` variable
- Feat: `COUNTRY` variable
- Feat: `CITY` variable
- Feat: `REGION` variable, with retro-compatibility
- Feat: merge servers from API, zip and hardcoded hostnames
- Fix: remove outdated and duplicate servers
- Maint: faster update with fully parallel DNS resolutions
2021-08-23 10:25:00 -07:00
Quentin McGaw (desktop)
8b52af0d03 Maint: common GetPort for OpenVPN+Wireguard providers 2021-08-23 16:13:20 +00:00
Quentin McGaw (desktop)
dbf5c569ea Maint: common GetProtocol for OpenVPN+Wireguard providers 2021-08-23 16:07:47 +00:00
Quentin McGaw (desktop)
06a2d79cb4 Feat: Wireguard support for Ivpn (#584) 2021-08-23 16:01:01 +00:00
Quentin McGaw (desktop)
eb6238ee52 Feat: WIREGUARD_PORT for Mullvad 2021-08-23 16:00:40 +00:00
Quentin McGaw (desktop)
f41fec57ed Feat: IVPN supports TCP and custom port 2021-08-23 13:34:00 +00:00
Quentin McGaw
c348343b22 IVPN server data update code and ISP filter (#578)
- Use IVPN's HTTP API instead of their .zip file
- Unit tests for API and GetServers
- Paves the way for Wireguard
- Update server information for IVPN
- Add `ISP` filter for IVPN
2021-08-22 20:11:56 -07:00
Quentin McGaw
b69dcb62e3 LOG_LEVEL variable (#577) 2021-08-22 18:57:10 -07:00
Quentin McGaw (laptop)
e4a260f148 Maint: upgrade qdm12/golibs 2021-08-22 20:44:14 +00:00
Quentin McGaw
614eb10d67 Wireguard support for Mullvad and Windscribe (#565)
- `internal/wireguard` client package with unit tests
- Implementation works with kernel space or user space if unavailable
- `WIREGUARD_PRIVATE_KEY`
- `WIREGUARD_ADDRESS`
- `WIREGUARD_PRESHARED_KEY`
- `WIREGUARD_PORT`
- `internal/netlink` package used by `internal/wireguard`
2021-08-22 14:58:39 -07:00
Quentin McGaw
0bfd58a3f5 Fix: sorted IP addresses for servers.json (#574)
- Reduce deltas between updates
- Applies to the following providers
  - IPVanish
  - IVPN
  - Surfshark
  - Torguard
  - VPNUnlimited
2021-08-21 16:03:18 -07:00
Quentin McGaw (desktop)
ff56857fc8 Fix: port forwarding VPN interface specification 2021-08-21 18:16:44 +00:00
Quentin McGaw (desktop)
8d258feff7 Hot fix: interface name set for openvpn configs 2021-08-20 01:13:04 +00:00
Quentin McGaw (desktop)
96ee1bbfb2 Maint: upgrade from Go 1.16 to Go 1.17 2021-08-20 00:07:41 +00:00
Quentin McGaw (desktop)
abaf688ad8 Doc: update readme
- Image size lowered to 34MB
- Using Alpine 3.14
- Beta wireguard support
2021-08-19 23:53:47 +00:00
Quentin McGaw (desktop)
bec8ff27ae Feat: OPENVPN_INTERFACE defaulting to tun0
- Fix: custom config with custom network interface name for firewall
- Keep VPN tunnel interface in firewall state
- Vul fix: only allow traffic through vpn interface when needed
- Adapt code to adapt to network interface name
- Remove outdated TUN and TAP constants
2021-08-19 23:22:55 +00:00
Quentin McGaw (desktop)
7191d4e911 Maint: upgrade golibs, fix logger settings inheritance 2021-08-19 19:29:50 +00:00
Quentin McGaw (desktop)
6f59bc3037 Maint: simplify provider configuration logging 2021-08-19 17:41:37 +00:00
Quentin McGaw (desktop)
5c2286f4e8 Maint: simplify settings code in internal/vpn 2021-08-19 14:57:11 +00:00
Quentin McGaw (desktop)
9218c7ef19 Maint: create OpenVPN runner in VPN run loop 2021-08-19 14:45:57 +00:00
Quentin McGaw (desktop)
3d8e61900b Maint: make VPN connection not specific to OpenVPN
- Add VPN field to ServerSelection struct
- Set VPN type to server selection at start using VPN_TYPE
- Change OpenVPNConnection to Connection with Type field
- Rename Provider GetOpenVPNConnection to GetConnection
- Rename GetTargetIPOpenVPNConnection to GetTargetIPConnection
- Rename PickRandomOpenVPNConnection to PickRandomConnection
- Add 'OpenVPN' prefix to OpenVPN specific methods on connection
2021-08-19 14:09:41 +00:00
Quentin McGaw (desktop)
105d81c018 Maint: move Openvpn package files
- Move internal/openvpn/config/*.go to internal/openvpn/
- Move internal/openvpn/setup.go to internal/vpn/openvpn.go
2021-08-19 13:31:12 +00:00
Quentin McGaw (desktop)
d4ca5cf257 Maint: internal/vpn package for vpn loop 2021-08-18 22:01:04 +00:00
Quentin McGaw (desktop)
05018ec971 Maint: use VPN settings instead of OpenVPN in loop 2021-08-18 21:27:09 +00:00
Quentin McGaw (desktop)
538bc72c3c Maint: better log when cathing an OS signal 2021-08-18 21:22:27 +00:00
Quentin McGaw (desktop)
0027a76c49 Maint: move OpenVPN streams processing to config package 2021-08-18 21:16:28 +00:00
Quentin McGaw (desktop)
a0cb6fabfd Maint: rename openvpn command.go to start.go 2021-08-18 20:47:03 +00:00
Quentin McGaw (desktop)
9e5400f52d Maint: split out OpenVPN version functions to openvpn/config/version.go 2021-08-18 20:46:20 +00:00
Quentin McGaw (desktop)
7a1d0ff3ec Maint: internal/openvpn setup.go file 2021-08-18 20:43:47 +00:00
Quentin McGaw (desktop)
d9fbecaa01 Maint: minor changes to openvpn/config package
- Constructor returns concrete struct instead of interface
- Rename conf to openvpnConf in openvpn loop
2021-08-18 20:28:42 +00:00
Quentin McGaw (desktop)
ecdf9396a5 Maint: move OpenVPN configurator to openvpn/config 2021-08-18 20:23:50 +00:00
Quentin McGaw (desktop)
df51aa40f4 Maint: split custom config files in openvpn/custom 2021-08-18 20:18:49 +00:00
Quentin McGaw (desktop)
996942af47 Maint: move custom config files to custom package 2021-08-18 20:14:02 +00:00
Quentin McGaw (desktop)
f17a4eae3e Maint: rework OpenVPN custom configuration code
- Refactor code and errors returned
- Add unit tests
- Make custom config code independent from loop
2021-08-18 20:12:26 +00:00
Quentin McGaw (desktop)
c515603d2f Fix: Openvpn custom config: remove user set 2021-08-18 17:41:53 +00:00
Quentin McGaw (desktop)
14c3b6429b Maint: openvpn process user in Openvpn settings 2021-08-18 16:16:47 +00:00
Quentin McGaw (desktop)
bd110b960b Maint: remove startPFCh from Openvpn loop 2021-08-18 16:07:35 +00:00
Quentin McGaw (desktop)
3ad4319163 Maint: minor Openvpn loop simplifications 2021-08-18 15:52:38 +00:00
Quentin McGaw (desktop)
97340ec70b Fix: chown openvpn configuration file 2021-08-18 15:47:11 +00:00
Quentin McGaw (desktop)
5140a7b010 Maint: set PUID and PGID in openvpn configurator 2021-08-18 15:44:58 +00:00
Quentin McGaw (desktop)
bd74879303 Maint: read all settings first 2021-08-18 15:42:19 +00:00
Quentin McGaw (desktop)
da30ae287f Maint: decouple OpenVPN config writer from loop 2021-08-18 15:35:07 +00:00
Quentin McGaw (desktop)
6a545aa088 Maint: tun package to handle tun device operations
- Moved from openvpn package to tun package
- TUN check verifies Rdev value
- TUN create
- Inject as interface to main function
- Add integration test
- Clearer log message for end users if tun device does not exist
- Remove unix package (unneeded for tests)
- Remove tun file opening at the end of tun file creation
- Do not mock unix.Mkdev (no OS operation)
- Remove Tun operations from OpenVPN configurator
2021-08-18 15:31:08 +00:00
Quentin McGaw (desktop)
384a4bae3a Hotfix: PIA: encryption preset reading 2021-08-17 19:35:57 +00:00
Quentin McGaw (desktop)
e65f924cd7 Maint: remove custom config readProvider constructor 2021-08-17 17:53:13 +00:00
Quentin McGaw (desktop)
9105b33e9f Maint: configuration Openvpn selection structure
- Move network protocol from ServerSelection to OpenVPNSelection child
- Move PIA encryption preset from ServerSelection to OpenVPNSelection child
- Move custom port from ServerSelection to OpenVPNSelection child
2021-08-17 16:54:22 +00:00
Quentin McGaw (desktop)
cc2235653a Maint: refactor VPN configuration structure
- Paves the way for Wireguard
- VPN struct contains Type, Openvpn and Provider configurations
- OpenVPN specific options (e.g. client key) moved from Provider to Openvpn configuration struct
- Move Provider configuration from OpenVPN configuration to VPN
- HTTP control server returns only openvpn settings (not provider settings)
2021-08-17 15:44:11 +00:00
Quentin McGaw (desktop)
a00de75f61 Maint: rename utility names to be Openvpn specific
- GetTargetIPConnection to GetTargetIPOpenVPNConnection
- PickRandomConnection to PickRandomOpenVPNConnection
2021-08-17 14:08:53 +00:00
Quentin McGaw (desktop)
836412b032 Maint: move routeReadyEvents to openvpn package 2021-08-16 19:19:41 +00:00
Quentin McGaw (desktop)
ba16270059 Maint: context aware collectLines functions 2021-08-16 19:19:33 +00:00
Quentin McGaw (desktop)
2c73672e64 Fix: restore PIA error if region does not support port forwarding 2021-08-16 19:16:05 +00:00
Quentin McGaw (desktop)
74b7c81195 Fix: apk-tools culnerability fix installation
- Install apk-tools before using apk
- Install latest apk-tools so it can be rebuilt in the future
2021-08-09 14:49:45 +00:00
Quentin McGaw (desktop)
a021ff6b22 Fix: loopstate mutex unlocking
- Fix #547
- Fix all run loops for restarts
2021-08-09 14:35:55 +00:00
Quentin McGaw (desktop)
6d1a90cac0 Fix: use apk-tools 2.12.7-r0
- valid for ppc64le
- additional security fix
2021-08-09 01:21:19 +00:00
Quentin McGaw (desktop)
1f47c16102 Fix: windscribe: only get openvpn IP addresses 2021-08-09 01:18:51 +00:00
Quentin McGaw (desktop)
abbcf60aed Fix: port forward get route, fixes #552 2021-08-01 15:01:28 +00:00
Quentin McGaw (desktop)
f339c882d7 Feat: updater cyberghost servers 2021-07-31 22:38:18 +00:00
Quentin McGaw (desktop)
982536e9e8 Fix & feat: Cyberghost server groups
- Allow multiple comma separated values for CYBERGHOST_GROUP
- Defaults to all UDP groups
- If TCP is enabled, defaults to all TCP groups
- Check groups specified match the protocol
- Default Cyberghost group to empty
- Adjust formatting and messages
2021-07-31 14:53:34 +00:00
Quentin McGaw (desktop)
c17b351efb Fix: cyberghost: explicit-exit-notify only for UDP 2021-07-31 14:02:02 +00:00
Quentin McGaw (desktop)
130bebf2c6 Doc: add unraid template link to issue templates 2021-07-30 19:48:42 +00:00
Quentin McGaw (desktop)
83c4ad2e59 Hotfix: fix shadowsocks config parsing, refix #548 2021-07-29 13:50:40 +00:00
Quentin McGaw (desktop)
0bcc6ed597 Fix: port forwarding deadlock bug, fix #547 2021-07-29 01:13:16 +00:00
Quentin McGaw (desktop)
c61f854edc Maint: upgrade ss-server to v0.3.0
- `SHADOWSOCKS_PORT` in retrocompatibility
- `SHADOWSOCKS_METHOD` in retrocompatibility
- `SHADOWSOCKS_ADDRESS` added
- `SHADOWSOCKS_CIPHER` added
- Shadowsocks config inherit from ss-server's Settings
- Log adapter removed as no longer needed
2021-07-29 00:48:46 +00:00
Quentin McGaw
2998cf5e48 Maint: port forwarding refactoring (#543)
- portforward package
- portforward run loop
- Less functional arguments and cycles
2021-07-28 08:35:44 -07:00
331 changed files with 26841 additions and 8587 deletions

View File

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

View File

@@ -4,6 +4,8 @@ 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
@@ -23,7 +25,8 @@ 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,6 +15,10 @@ 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,6 +70,9 @@
- 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.13 ARG GO_ALPINE_VERSION=3.14
ARG GO_VERSION=1.16 ARG GO_VERSION=1.17
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,6 +68,8 @@ 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 \
@@ -76,6 +78,12 @@ 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= \
@@ -147,17 +155,17 @@ ENV VPNSP=pia \
# Shadowsocks # Shadowsocks
SHADOWSOCKS=off \ SHADOWSOCKS=off \
SHADOWSOCKS_LOG=off \ SHADOWSOCKS_LOG=off \
SHADOWSOCKS_PORT=8388 \ SHADOWSOCKS_ADDRESS=":8388" \
SHADOWSOCKS_PASSWORD= \ SHADOWSOCKS_PASSWORD= \
SHADOWSOCKS_PASSWORD_SECRETFILE=/run/secrets/shadowsocks_password \ SHADOWSOCKS_PASSWORD_SECRETFILE=/run/secrets/shadowsocks_password \
SHADOWSOCKS_METHOD=chacha20-ietf-poly1305 \ SHADOWSOCKS_CIPHER=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
HEALTHCHECK --interval=5s --timeout=5s --start-period=10s --retries=1 CMD /entrypoint healthcheck HEALTHCHECK --interval=5s --timeout=5s --start-period=10s --retries=1 CMD /entrypoint healthcheck
ARG TARGETPLATFORM ARG TARGETPLATFORM
RUN apk add --no-cache --update -X "https://dl-cdn.alpinelinux.org/alpine/v3.12/main" openvpn==2.4.11-r0 && \ RUN apk add --no-cache --update -l apk-tools && \
if [ "${TARGETPLATFORM}" != "linux/ppc64le" ]; then apk add --no-cache --update apk-tools==2.12.6-r0; fi && \ apk add --no-cache --update -X "https://dl-cdn.alpinelinux.org/alpine/v3.12/main" openvpn==2.4.11-r0 && \
mv /usr/sbin/openvpn /usr/sbin/openvpn2.4 && \ mv /usr/sbin/openvpn /usr/sbin/openvpn2.4 && \
apk del openvpn && \ apk del openvpn && \
apk add --no-cache --update openvpn ca-certificates iptables ip6tables unbound tzdata && \ apk add --no-cache --update openvpn ca-certificates iptables ip6tables unbound tzdata && \

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**: **ANNOUNCEMENT**: Wireguard is now supported for all providers supporting it!
![Title image](https://raw.githubusercontent.com/qdm12/gluetun/master/title.svg) ![Title image](https://raw.githubusercontent.com/qdm12/gluetun/master/title.svg)
@@ -55,9 +55,10 @@ using Go, OpenVPN, iptables, DNS over TLS, ShadowSocks and an HTTP proxy*
## Features ## Features
- Based on Alpine 3.13 for a small Docker image of 54MB - Based on Alpine 3.14 for a small Docker image of 31MB
- Supports: **Cyberghost**, **FastestVPN**, **HideMyAss**, **IPVanish**, **IVPN**, **Mullvad**, **NordVPN**, **Privado**, **Private Internet Access**, **PrivateVPN**, **ProtonVPN**, **PureVPN**, **Surfshark**, **TorGuard**, **VPNUnlimited**, **Vyprvpn**, **Windscribe** servers - Supports: **Cyberghost**, **FastestVPN**, **HideMyAss**, **IPVanish**, **IVPN**, **Mullvad**, **NordVPN**, **Privado**, **Private Internet Access**, **PrivateVPN**, **ProtonVPN**, **PureVPN**, **Surfshark**, **TorGuard**, **VPNUnlimited**, **Vyprvpn**, **Windscribe** servers
- Supports Openvpn only for now - Supports OpenVPN
- 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`
@@ -110,6 +111,7 @@ 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,7 +4,6 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"net"
"net/http" "net/http"
"os" "os"
"os/signal" "os/signal"
@@ -23,15 +22,17 @@ 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/unix" "github.com/qdm12/gluetun/internal/tun"
"github.com/qdm12/gluetun/internal/updater" "github.com/qdm12/gluetun/internal/updater"
versionpkg "github.com/qdm12/gluetun/internal/version" "github.com/qdm12/gluetun/internal/vpn"
"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"
@@ -59,29 +60,32 @@ func main() {
Created: created, Created: created,
} }
ctx := context.Background() background := context.Background()
ctx, stop := signal.NotifyContext(ctx, syscall.SIGINT, syscall.SIGTERM, os.Interrupt) signalCtx, stop := signal.NotifyContext(background, syscall.SIGINT, syscall.SIGTERM, os.Interrupt)
ctx, cancel := context.WithCancel(ctx) ctx, cancel := context.WithCancel(background)
logger := logging.NewParent(logging.Settings{ logger := logging.New(logging.Settings{
Level: logging.LevelInfo, Level: logging.LevelInfo,
}) })
args := os.Args args := os.Args
unix := unix.New() tun := tun.New()
netLinker := netlink.New()
cli := cli.New() cli := cli.New()
env := params.NewEnv() env := params.New()
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, unix, cmder, cli) errorCh <- _main(ctx, buildInfo, args, logger, env, tun, netLinker, cmder, cli)
}() }()
select { select {
case <-ctx.Done(): case <-signalCtx.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)
@@ -113,8 +117,9 @@ 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.Env, args []string, logger logging.ParentLogger, env params.Interface,
unix unix.Unix, cmder command.RunStarter, cli cli.CLIer) error { tun tun.Interface, netLinker netlink.NetLinker, cmder command.RunStarter,
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":
@@ -122,7 +127,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) return cli.OpenvpnConfig(logger, env)
case "update": case "update":
return cli.Update(ctx, args[2:], logger) return cli.Update(ctx, args[2:], logger)
default: default:
@@ -130,19 +135,37 @@ 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.NewConfigurator( ovpnConf := openvpn.New(
logger.NewChild(logging.Settings{Prefix: "openvpn configurator: "}), logger.NewChild(logging.Settings{Prefix: "openvpn configurator: "}),
unix, cmder) cmder, puid, pgid)
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-07-22T00:00:00Z") announcementExp, err := time.Parse(time.RFC3339, "2021-10-02T00:00:00Z")
if err != nil { if err != nil {
return err return err
} }
@@ -153,7 +176,7 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
Version: buildInfo.Version, Version: buildInfo.Version,
Commit: buildInfo.Commit, Commit: buildInfo.Commit,
BuildDate: buildInfo.Created, BuildDate: buildInfo.Created,
Announcement: "", Announcement: "Wireguard is now supported for Mullvad, IVPN and Windscribe!",
AnnounceExp: announcementExp, AnnounceExp: announcementExp,
// Sponsor information // Sponsor information
PaypalUser: "qmcgaw", PaypalUser: "qmcgaw",
@@ -176,12 +199,6 @@ 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 {
@@ -191,18 +208,6 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
return err return err
} }
// TODO run this in a loop or in openvpn to reload from file without restarting
storage := storage.New(
logger.NewChild(logging.Settings{Prefix: "storage: "}),
constants.ServersData)
allServers, err := storage.SyncServers(constants.GetAllServers())
if err != nil {
return err
}
// 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 {
@@ -214,12 +219,13 @@ 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 := logging.LevelInfo firewallLogLevel := allSettings.Log.Level
if allSettings.Firewall.Debug { if allSettings.Firewall.Debug {
firewallLogLevel = logging.LevelDebug firewallLogLevel = logging.LevelDebug
} }
@@ -227,7 +233,7 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
Prefix: "routing: ", Prefix: "routing: ",
Level: firewallLogLevel, Level: firewallLogLevel,
}) })
routingConf := routing.NewRouting(routingLogger) routingConf := routing.New(netLinker, routingLogger)
defaultInterface, defaultGateway, err := routingConf.DefaultRoute() defaultInterface, defaultGateway, err := routingConf.DefaultRoute()
if err != nil { if err != nil {
@@ -271,17 +277,14 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
return err return err
} }
if err := ovpnConf.CheckTUN(); err != nil { if err := tun.Check(constants.TunnelDevice); err != nil {
logger.Warn(err.Error()) logger.Info(err.Error() + "; creating it...")
err = ovpnConf.CreateTUN() err = tun.Create(constants.TunnelDevice)
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 {
@@ -290,7 +293,8 @@ func _main(ctx context.Context, buildInfo models.BuildInformation,
} }
for _, vpnPort := range allSettings.Firewall.VPNInputPorts { for _, vpnPort := range allSettings.Firewall.VPNInputPorts {
err = firewallConf.SetAllowedPort(ctx, vpnPort, string(constants.TUN)) vpnIntf := allSettings.VPN.VPNInterface()
err = firewallConf.SetAllowedPort(ctx, vpnPort, vpnIntf)
if err != nil { if err != nil {
return err return err
} }
@@ -321,21 +325,12 @@ 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)
openvpnLooper := openvpn.NewLoop(allSettings.OpenVPN, nonRootUsername, puid, pgid, allServers, portForwardLogger := logger.NewChild(logging.Settings{Prefix: "port forwarding: "})
ovpnConf, firewallConf, logger, httpClient, tunnelReadyCh) portForwardLooper := portforward.NewLoop(allSettings.VPN.Provider.PortForwarding,
openvpnHandler, openvpnCtx, openvpnDone := goshutdown.NewGoRoutineHandler( httpClient, firewallConf, portForwardLogger)
"openvpn", goshutdown.GoRoutineSettings{Timeout: time.Second}) portForwardHandler, portForwardCtx, portForwardDone := goshutdown.NewGoRoutineHandler(
// wait for restartOpenvpn "port forwarding", goshutdown.GoRoutineSettings{Timeout: time.Second})
go openvpnLooper.Run(openvpnCtx, openvpnDone) go portForwardLooper.Run(portForwardCtx, portForwardDone)
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,
@@ -346,6 +341,11 @@ 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,6 +359,29 @@ 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)
@@ -374,26 +397,18 @@ 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, openvpnLooper, unboundLooper, updaterLooper, publicIPLooper) buildInfo, vpnLooper, portForwardLooper, 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, openvpnLooper) healthcheckServer := healthcheck.NewServer(allSettings.Health, healthLogger, vpnLooper)
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)
@@ -406,21 +421,14 @@ 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,
openvpnHandler, otherGroupHandler) vpnHandler, portForwardHandler, otherGroupHandler)
// Start openvpn for the first time in a blocking call // Start VPN for the first time in a blocking call
// until openvpn is launched // until the VPN is launched
_, _ = openvpnLooper.ApplyStatus(ctx, constants.Running) // TODO option to disable with variable _, _ = vpnLooper.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())
} }
@@ -445,73 +453,3 @@ 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,10 +15,11 @@ services:
volumes: volumes:
- /yourpath:/gluetun - /yourpath:/gluetun
environment: environment:
# More variables are available, see the readme table # More variables are available, see the Wiki 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,18 +1,40 @@
module github.com/qdm12/gluetun module github.com/qdm12/gluetun
go 1.16 go 1.17
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-20210723175634-a75ca7fd74c2 github.com/qdm12/golibs v0.0.0-20210822203818-5c568b0777b6
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.2.0 github.com/qdm12/ss-server v0.3.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,11 +33,28 @@ 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=
@@ -51,8 +68,24 @@ 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=
@@ -66,14 +99,15 @@ 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.2.0 h1:+togLzeeLAJ68MD1JqOWvYi9rl9t/fx1Qh7wKzZhY1g= github.com/qdm12/ss-server v0.3.0 h1:BfKv4OU6dYb2KcDMYpTc7LIuO2jB73g3JCzy988GrLI=
github.com/qdm12/ss-server v0.2.0/go.mod h1:+1bWO1EfWNvsGM5Cuep6vneChK2OHniqtAsED9Fh1y0= github.com/qdm12/ss-server v0.3.0/go.mod h1:ug+nWfuzKw/h5fxL1B6e9/OhkVuWJX4i2V1Pf0pJU1o=
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=
@@ -105,6 +139,8 @@ 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=
@@ -112,38 +148,67 @@ 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=
@@ -152,7 +217,14 @@ 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.Env, logger logging.Logger) error HealthCheck(ctx context.Context, env params.Interface, logger logging.Logger) error
} }
func (c *CLI) HealthCheck(ctx context.Context, env params.Env, func (c *CLI) HealthCheck(ctx context.Context, env params.Interface,
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,26 +14,27 @@ import (
) )
type OpenvpnConfigMaker interface { type OpenvpnConfigMaker interface {
OpenvpnConfig(logger logging.Logger) error OpenvpnConfig(logger logging.Logger, env params.Interface) error
} }
func (c *CLI) OpenvpnConfig(logger logging.Logger) error { func (c *CLI) OpenvpnConfig(logger logging.Logger, env params.Interface) 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(params.NewEnv(), logger) err = allSettings.Read(env, allServers, logger)
if err != nil { if err != nil {
return err return err
} }
allServers, err := storage.New(logger, constants.ServersData). providerConf := provider.New(allSettings.VPN.Provider.Name, allServers, time.Now)
SyncServers(constants.GetAllServers()) connection, err := providerConf.GetConnection(allSettings.VPN.Provider.ServerSelection)
if err != nil { if err != nil {
return err return err
} }
providerConf := provider.New(allSettings.OpenVPN.Provider.Name, allServers, time.Now) lines := providerConf.BuildConf(connection, allSettings.VPN.OpenVPN)
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")
ErrSyncServers = errors.New("cannot sync hardcoded and persisted servers") ErrNewStorage = errors.New("cannot create storage")
ErrUpdateServerInformation = errors.New("cannot update server information") ErrUpdateServerInformation = errors.New("cannot update server information")
ErrWriteToFile = errors.New("cannot write updated information to file") ErrWriteToFile = errors.New("cannot write updated information to file")
) )
@@ -68,11 +68,13 @@ func (c *CLI) Update(ctx context.Context, args []string, logger logging.Logger)
const clientTimeout = 10 * time.Second const clientTimeout = 10 * time.Second
httpClient := &http.Client{Timeout: clientTimeout} httpClient := &http.Client{Timeout: clientTimeout}
storage := storage.New(logger, constants.ServersData)
currentServers, err := storage.SyncServers(constants.GetAllServers()) storage, err := storage.New(logger, constants.ServersData)
if err != nil { if err != nil {
return fmt.Errorf("%w: %s", ErrSyncServers, err) return fmt.Errorf("%w: %s", ErrNewStorage, err)
} }
currentServers := storage.GetServers()
updater := updater.New(options, httpClient, currentServers, logger) updater := updater.New(options, httpClient, currentServers, logger)
allServers, err := updater.UpdateServers(ctx) allServers, err := updater.UpdateServers(ctx)
if err != nil { if err != nil {

View File

@@ -4,69 +4,47 @@ 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.ExtraConfigOptions.ClientKey, err = readClientKey(r) settings.ServerSelection.Groups, err = r.env.CSVInside("CYBERGHOST_GROUP",
if err != nil { constants.CyberghostGroupChoices(servers))
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()) settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.CyberghostRegionChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable REGION: %w", err) return fmt.Errorf("environment variable REGION: %w", err)
} }
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.CyberghostHostnameChoices()) settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME",
constants.CyberghostHostnameChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err) return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
} }
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.Env) error { func (settings *DNS) readDNSPlaintext(env params.Interface) 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.Env) (err error) { func (settings *DNS) readPrivateAddresses(env params.Interface) (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,40 +6,25 @@ 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", constants.FastestvpnHostnameChoices()) settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME",
constants.FastestvpnHostnameChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err) return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
} }
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.FastestvpnCountriesChoices()) settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.FastestvpnCountriesChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err) return fmt.Errorf("environment variable COUNTRY: %w", err)
} }
return nil return settings.ServerSelection.OpenVPN.readProtocolOnly(r.env)
} }

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.Env) (err error) { func (settings *Firewall) readVPNInputPorts(env params.Interface) (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.Env) (err error) {
return nil return nil
} }
func (settings *Firewall) readInputPorts(env params.Env) (err error) { func (settings *Firewall) readInputPorts(env params.Interface) (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,6 +4,7 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/golibs/logging" "github.com/qdm12/golibs/logging"
"github.com/qdm12/golibs/params" "github.com/qdm12/golibs/params"
) )
@@ -32,8 +33,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.Env, logger logging.Logger) (err error) { func (settings *Health) Read(env params.Interface, logger logging.Logger) (err error) {
reader := newReader(env, logger) reader := newReader(env, models.AllServers{}, logger) // note: no need for servers data
return settings.read(reader) return settings.read(reader)
} }

View File

@@ -139,7 +139,7 @@ func Test_Health_read(t *testing.T) {
ctrl := gomock.NewController(t) ctrl := gomock.NewController(t)
env := mock_params.NewMockEnv(ctrl) env := mock_params.NewMockInterface(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,58 +6,35 @@ 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()) settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.HideMyAssCountryChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err) return fmt.Errorf("environment variable COUNTRY: %w", err)
} }
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.HideMyAssCountryChoices()) settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.HideMyAssCountryChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable REGION: %w", err) return fmt.Errorf("environment variable REGION: %w", err)
} }
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.HideMyAssCityChoices()) settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.HideMyAssCityChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable CITY: %w", err) return fmt.Errorf("environment variable CITY: %w", err)
} }
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.HideMyAssHostnameChoices()) settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME",
constants.HideMyAssHostnameChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err) return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
} }
return nil return settings.ServerSelection.OpenVPN.readProtocolAndPort(r.env)
} }

View File

@@ -6,49 +6,30 @@ 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()) settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.IpvanishCountryChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err) return fmt.Errorf("environment variable COUNTRY: %w", err)
} }
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.IpvanishCityChoices()) settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.IpvanishCityChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable CITY: %w", err) return fmt.Errorf("environment variable CITY: %w", err)
} }
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.IpvanishHostnameChoices()) settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME",
constants.IpvanishHostnameChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err) return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
} }
return nil return settings.ServerSelection.OpenVPN.readProtocolOnly(r.env)
} }

View File

@@ -7,46 +7,12 @@ 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()
@@ -65,23 +31,15 @@ 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,
@@ -89,7 +47,6 @@ 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{
@@ -98,7 +55,6 @@ 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},
@@ -108,7 +64,6 @@ 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},
@@ -118,26 +73,39 @@ 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"),
}, },
"default settings": { "protocol 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},
hostnames: 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": {
targetIP: singleStringCall{call: true},
countries: sliceStringCall{call: true},
cities: 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"},
@@ -152,29 +120,39 @@ func Test_Provider_readIpvanish(t *testing.T) {
t.Parallel() t.Parallel()
ctrl := gomock.NewController(t) ctrl := gomock.NewController(t)
env := mock_params.NewMockEnv(ctrl) servers := []models.IpvanishServer{{Hostname: "a"}}
if testCase.protocol.call { allServers := models.AllServers{
env.EXPECT().Inside("PROTOCOL", []string{constants.TCP, constants.UDP}, gomock.Any()). Ipvanish: models.IpvanishServers{
Return(testCase.protocol.value, testCase.protocol.err) Servers: servers,
},
} }
env := mock_params.NewMockInterface(ctrl)
if testCase.targetIP.call { if testCase.targetIP.call {
env.EXPECT().Get("OPENVPN_TARGET_IP"). env.EXPECT().Get("OPENVPN_TARGET_IP").
Return(testCase.targetIP.value, testCase.targetIP.err) Return(testCase.targetIP.value, testCase.targetIP.err)
} }
if testCase.countries.call { if testCase.countries.call {
env.EXPECT().CSVInside("COUNTRY", constants.IpvanishCountryChoices()). env.EXPECT().CSVInside("COUNTRY", constants.IpvanishCountryChoices(servers)).
Return(testCase.countries.values, testCase.countries.err) Return(testCase.countries.values, testCase.countries.err)
} }
if testCase.cities.call { if testCase.cities.call {
env.EXPECT().CSVInside("CITY", constants.IpvanishCityChoices()). env.EXPECT().CSVInside("CITY", constants.IpvanishCityChoices(servers)).
Return(testCase.cities.values, testCase.cities.err) Return(testCase.cities.values, testCase.cities.err)
} }
if testCase.hostnames.call { if testCase.hostnames.call {
env.EXPECT().CSVInside("SERVER_HOSTNAME", constants.IpvanishHostnameChoices()). env.EXPECT().CSVInside("SERVER_HOSTNAME", constants.IpvanishHostnameChoices(servers)).
Return(testCase.hostnames.values, testCase.hostnames.err) Return(testCase.hostnames.values, testCase.hostnames.err)
} }
if testCase.protocol.call {
env.EXPECT().Inside("PROTOCOL", []string{constants.TCP, constants.UDP}, gomock.Any()).
Return(testCase.protocol.value, testCase.protocol.err)
}
r := reader{env: env} r := reader{
servers: allServers,
env: env,
}
var settings Provider var settings Provider
err := settings.readIpvanish(r) err := settings.readIpvanish(r)

View File

@@ -4,51 +4,67 @@ import (
"fmt" "fmt"
"github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/golibs/params"
) )
func (settings *Provider) 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()) settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.IvpnCountryChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err) return fmt.Errorf("environment variable COUNTRY: %w", err)
} }
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.IvpnCityChoices()) settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.IvpnCityChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable CITY: %w", err) return fmt.Errorf("environment variable CITY: %w", err)
} }
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.IvpnHostnameChoices()) settings.ServerSelection.ISPs, err = r.env.CSVInside("ISP", constants.IvpnISPChoices(servers))
if err != nil {
return fmt.Errorf("environment variable ISP: %w", err)
}
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.IvpnHostnameChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err) return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
} }
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,47 +7,13 @@ import (
"github.com/golang/mock/gomock" "github.com/golang/mock/gomock"
"github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/golibs/params/mock_params" "github.com/qdm12/golibs/params/mock_params"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func Test_Provider_ivpnLines(t *testing.T) { func Test_Provider_readIvpn(t *testing.T) { //nolint:gocognit
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")
@@ -58,6 +24,15 @@ func Test_Provider_readIvpn(t *testing.T) {
err error err error
} }
type portCall struct {
getCall bool
getValue string // "" or "0"
getErr error
portCall bool
portValue uint16
portErr error
}
type sliceStringCall struct { type sliceStringCall struct {
call bool call bool
values []string values []string
@@ -65,23 +40,18 @@ func Test_Provider_readIvpn(t *testing.T) {
} }
testCases := map[string]struct { testCases := map[string]struct {
protocol singleStringCall
targetIP singleStringCall targetIP singleStringCall
countries sliceStringCall countries sliceStringCall
cities sliceStringCall cities sliceStringCall
isps sliceStringCall
hostnames sliceStringCall hostnames sliceStringCall
protocol singleStringCall
ovpnPort portCall
wgPort portCall
settings Provider settings Provider
err error err error
}{ }{
"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,
@@ -89,7 +59,6 @@ func Test_Provider_readIvpn(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{
@@ -98,7 +67,6 @@ func Test_Provider_readIvpn(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},
@@ -107,40 +75,102 @@ func Test_Provider_readIvpn(t *testing.T) {
}, },
err: errors.New("environment variable CITY: dummy test error"), err: errors.New("environment variable CITY: dummy test error"),
}, },
"hostnames error": { "isps 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, err: errDummy},
settings: Provider{
Name: constants.Ivpn,
},
err: errors.New("environment variable ISP: dummy test error"),
},
"hostnames error": {
targetIP: singleStringCall{call: true},
countries: sliceStringCall{call: true},
cities: sliceStringCall{call: true},
isps: sliceStringCall{call: true},
hostnames: sliceStringCall{call: true, err: errDummy}, 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"),
}, },
"default settings": { "openvpn protocol 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}, 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": {
targetIP: singleStringCall{call: true},
countries: sliceStringCall{call: true},
cities: sliceStringCall{call: true},
isps: sliceStringCall{call: true},
hostnames: sliceStringCall{call: true},
protocol: singleStringCall{call: true},
ovpnPort: portCall{getCall: true, getValue: "0"},
wgPort: portCall{getCall: true, getValue: "0"},
settings: Provider{ 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"},
}, },
}, },
@@ -152,29 +182,60 @@ func Test_Provider_readIvpn(t *testing.T) {
t.Parallel() t.Parallel()
ctrl := gomock.NewController(t) ctrl := gomock.NewController(t)
env := mock_params.NewMockEnv(ctrl) env := mock_params.NewMockInterface(ctrl)
if testCase.protocol.call {
env.EXPECT().Inside("PROTOCOL", []string{constants.TCP, constants.UDP}, gomock.Any()). servers := []models.IvpnServer{{Hostname: "a"}}
Return(testCase.protocol.value, testCase.protocol.err) allServers := models.AllServers{
Ivpn: models.IvpnServers{
Servers: servers,
},
} }
if testCase.targetIP.call { if testCase.targetIP.call {
env.EXPECT().Get("OPENVPN_TARGET_IP"). env.EXPECT().Get("OPENVPN_TARGET_IP").
Return(testCase.targetIP.value, testCase.targetIP.err) Return(testCase.targetIP.value, testCase.targetIP.err)
} }
if testCase.countries.call { if testCase.countries.call {
env.EXPECT().CSVInside("COUNTRY", constants.IvpnCountryChoices()). env.EXPECT().CSVInside("COUNTRY", constants.IvpnCountryChoices(servers)).
Return(testCase.countries.values, testCase.countries.err) Return(testCase.countries.values, testCase.countries.err)
} }
if testCase.cities.call { if testCase.cities.call {
env.EXPECT().CSVInside("CITY", constants.IvpnCityChoices()). env.EXPECT().CSVInside("CITY", constants.IvpnCityChoices(servers)).
Return(testCase.cities.values, testCase.cities.err) Return(testCase.cities.values, testCase.cities.err)
} }
if testCase.isps.call {
env.EXPECT().CSVInside("ISP", constants.IvpnISPChoices(servers)).
Return(testCase.isps.values, testCase.isps.err)
}
if testCase.hostnames.call { if testCase.hostnames.call {
env.EXPECT().CSVInside("SERVER_HOSTNAME", constants.IvpnHostnameChoices()). env.EXPECT().CSVInside("SERVER_HOSTNAME", constants.IvpnHostnameChoices(servers)).
Return(testCase.hostnames.values, testCase.hostnames.err) Return(testCase.hostnames.values, testCase.hostnames.err)
} }
if testCase.protocol.call {
env.EXPECT().Inside("PROTOCOL", []string{constants.TCP, constants.UDP}, gomock.Any()).
Return(testCase.protocol.value, testCase.protocol.err)
}
if testCase.ovpnPort.getCall {
env.EXPECT().Get("PORT", gomock.Any()).
Return(testCase.ovpnPort.getValue, testCase.ovpnPort.getErr)
}
if testCase.ovpnPort.portCall {
env.EXPECT().Port("PORT").
Return(testCase.ovpnPort.portValue, testCase.ovpnPort.portErr)
}
if testCase.wgPort.getCall {
env.EXPECT().Get("WIREGUARD_PORT", gomock.Any()).
Return(testCase.wgPort.getValue, testCase.wgPort.getErr)
}
if testCase.wgPort.portCall {
env.EXPECT().Port("WIREGUARD_PORT").
Return(testCase.wgPort.portValue, testCase.wgPort.portErr)
}
r := reader{env: env} r := reader{
servers: allServers,
env: env,
}
var settings Provider var settings Provider
err := settings.readIvpn(r) err := settings.readIvpn(r)

View File

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

View File

@@ -2,87 +2,72 @@ 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()) settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.MullvadCountryChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err) return fmt.Errorf("environment variable COUNTRY: %w", err)
} }
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.MullvadCityChoices()) settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.MullvadCityChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable CITY: %w", err) return fmt.Errorf("environment variable CITY: %w", err)
} }
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.MullvadHostnameChoices()) settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.MullvadHostnameChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err) return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
} }
settings.ServerSelection.ISPs, err = r.env.CSVInside("ISP", constants.MullvadISPChoices()) settings.ServerSelection.ISPs, err = r.env.CSVInside("ISP", constants.MullvadISPChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable ISP: %w", err) return fmt.Errorf("environment variable ISP: %w", err)
} }
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)
} }
settings.ExtraConfigOptions.OpenVPNIPv6, err = r.env.OnOff("OPENVPN_IPV6", params.Default("off")) err = settings.ServerSelection.OpenVPN.readMullvad(r.env)
if err != nil { if err != nil {
return fmt.Errorf("environment variable OPENVPN_IPV6: %w", err) return 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,54 +8,26 @@ 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()) settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.NordvpnRegionChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable REGION: %w", err) return fmt.Errorf("environment variable REGION: %w", err)
} }
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.NordvpnHostnameChoices()) settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.NordvpnHostnameChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err) return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
} }
settings.ServerSelection.Names, err = r.env.CSVInside("SERVER_NAME", constants.NordvpnHostnameChoices()) settings.ServerSelection.Names, err = r.env.CSVInside("SERVER_NAME", constants.NordvpnHostnameChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_NAME: %w", err) return fmt.Errorf("environment variable SERVER_NAME: %w", err)
} }
@@ -65,10 +37,10 @@ func (settings *Provider) readNordvpn(r reader) (err error) {
return err return err
} }
return nil return settings.ServerSelection.OpenVPN.readProtocolOnly(r.env)
} }
func readNordVPNServerNumbers(env params.Env) (numbers []uint16, err error) { func readNordVPNServerNumbers(env params.Interface) (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,6 +3,7 @@ package configuration
import ( import (
"errors" "errors"
"fmt" "fmt"
"regexp"
"strconv" "strconv"
"strings" "strings"
@@ -20,9 +21,14 @@ 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 {
@@ -36,6 +42,8 @@ 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, " "))
} }
@@ -55,48 +63,32 @@ 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.Provider.Name == "" { if settings.ClientKey != "" {
lines = append(lines, indent+lastIndent+"Provider: custom configuration") lines = append(lines, indent+lastIndent+"Client key is set")
} else {
lines = append(lines, indent+lastIndent+"Provider:")
for _, line := range settings.Provider.lines() {
lines = append(lines, indent+indent+line)
} }
if settings.ClientCrt != "" {
lines = append(lines, indent+lastIndent+"Client certificate is set")
}
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
} }
var ( func (settings *OpenVPN) read(r reader, serviceProvider string) (err error) {
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 != ""
if customConfig { credentialsRequired := settings.Config == "" && serviceProvider != constants.VPNUnlimited
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 {
@@ -105,7 +97,7 @@ func (settings *OpenVPN) read(r reader) (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 settings.Provider.Name == constants.Mullvad { if serviceProvider == 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"})
@@ -155,50 +147,58 @@ func (settings *OpenVPN) read(r reader) (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"))
if err != nil {
return fmt.Errorf("environment variable OPENVPN_IPV6: %w", err)
}
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:
err = settings.readCyberghost(r)
case constants.VPNUnlimited:
err = settings.readVPNUnlimited(r)
}
if err != nil {
return err
}
return nil
} }
func (settings *OpenVPN) readProvider(r reader) error { func readProtocol(env params.Interface) (tcp bool, err error) {
var readProvider func(r reader) error protocol, err := env.Inside("PROTOCOL", []string{constants.TCP, constants.UDP}, params.Default(constants.UDP))
switch settings.Provider.Name { if err != nil {
case "": // custom config return false, fmt.Errorf("environment variable PROTOCOL: %w", err)
readProvider = func(r reader) error { return nil }
case constants.Cyberghost:
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:
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)
} }
return readProvider(r) 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,9 +13,6 @@ 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)
@@ -28,35 +25,12 @@ 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,55 +6,31 @@ 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()) settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.PrivadoCountryChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err) return fmt.Errorf("environment variable COUNTRY: %w", err)
} }
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.PrivadoRegionChoices()) settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.PrivadoRegionChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable REGION: %w", err) return fmt.Errorf("environment variable REGION: %w", err)
} }
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.PrivadoCityChoices()) settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.PrivadoCityChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable CITY: %w", err) return fmt.Errorf("environment variable CITY: %w", err)
} }
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.PrivadoHostnameChoices()) settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.PrivadoHostnameChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err) return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
} }

View File

@@ -2,85 +2,35 @@ 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
} }
encryptionPreset, err := r.env.Inside("PIA_ENCRYPTION", settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.PIAGeoChoices(servers))
[]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()) settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.PIAHostnameChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err) return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
} }
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_NAME", constants.PIANameChoices()) settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_NAME", constants.PIANameChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_NAME: %w", err) return fmt.Errorf("environment variable SERVER_NAME: %w", err)
} }
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)
@@ -94,5 +44,32 @@ 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,49 +6,30 @@ 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()) settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.PrivatevpnCountryChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err) return fmt.Errorf("environment variable COUNTRY: %w", err)
} }
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.PrivatevpnCityChoices()) settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.PrivatevpnCityChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable CITY: %w", err) return fmt.Errorf("environment variable CITY: %w", err)
} }
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.PrivatevpnHostnameChoices()) settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME",
constants.PrivatevpnHostnameChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err) return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
} }
return nil return settings.ServerSelection.OpenVPN.readProtocolAndPort(r.env)
} }

View File

@@ -7,73 +7,37 @@ 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.CustomPort, err = readPortOrZero(r.env, "PORT") settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.ProtonvpnCountryChoices(servers))
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()) settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.ProtonvpnRegionChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable REGION: %w", err) return fmt.Errorf("environment variable REGION: %w", err)
} }
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.ProtonvpnCityChoices()) settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.ProtonvpnCityChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable CITY: %w", err) return fmt.Errorf("environment variable CITY: %w", err)
} }
settings.ServerSelection.Names, err = r.env.CSVInside("SERVER_NAME", constants.ProtonvpnNameChoices()) settings.ServerSelection.Names, err = r.env.CSVInside("SERVER_NAME", constants.ProtonvpnNameChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_NAME: %w", err) return fmt.Errorf("environment variable SERVER_NAME: %w", err)
} }
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.ProtonvpnHostnameChoices()) settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME",
constants.ProtonvpnHostnameChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err) return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
} }
@@ -83,5 +47,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 nil return settings.ServerSelection.OpenVPN.readProtocolAndPort(r.env)
} }

View File

@@ -1,6 +1,7 @@
package configuration package configuration
import ( import (
"errors"
"fmt" "fmt"
"net" "net"
"strings" "strings"
@@ -13,79 +14,114 @@ import (
type Provider struct { type Provider struct {
Name string `json:"name"` Name string `json:"name"`
ServerSelection ServerSelection `json:"server_selection"` ServerSelection ServerSelection `json:"server_selection"`
ExtraConfigOptions ExtraConfigOptions `json:"extra_config"`
PortForwarding PortForwarding `json:"port_forwarding"` 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:")
selection := settings.ServerSelection for _, line := range settings.ServerSelection.toLines() {
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())
}
var providerLines []string
switch strings.ToLower(settings.Name) {
case "cyberghost":
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) lines = append(lines, indent+line)
} }
if settings.PortForwarding.Enabled { // PIA
lines = append(lines, indent+lastIndent+"Port forwarding:")
for _, line := range settings.PortForwarding.lines() {
lines = append(lines, indent+indent+line)
}
}
return lines return lines
} }
func commaJoin(slice []string) string { var (
return strings.Join(slice, ", ") 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 readProtocol(env params.Env) (tcp bool, err error) { func (settings *Provider) readVPNServiceProvider(r reader, vpnType string) (err error) {
protocol, err := env.Inside("PROTOCOL", []string{constants.TCP, constants.UDP}, params.Default(constants.UDP)) var allowedVPNServiceProviders []string
if err != nil { switch vpnType {
return false, fmt.Errorf("environment variable PROTOCOL: %w", err) 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}
} }
return protocol == constants.TCP, nil
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 {
return strings.Join(slice, ", ")
} }
func protoToString(tcp bool) string { func protoToString(tcp bool) string {
@@ -95,7 +131,7 @@ func protoToString(tcp bool) string {
return constants.UDP return constants.UDP
} }
func readTargetIP(env params.Env) (targetIP net.IP, err error) { func readTargetIP(env params.Interface) (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)
@@ -103,7 +139,7 @@ func readTargetIP(env params.Env) (targetIP net.IP, err error) {
return targetIP, nil return targetIP, nil
} }
func readCustomPort(env params.Env, tcp bool, func readOpenVPNCustomPort(env params.Interface, 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 {
@@ -118,12 +154,48 @@ func readCustomPort(env params.Env, tcp bool,
return port, nil return port, nil
} }
} }
return 0, fmt.Errorf("environment variable PORT: %w: port %d for TCP protocol", ErrInvalidPort, port) return 0, fmt.Errorf(
"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("environment variable PORT: %w: port %d for UDP protocol", ErrInvalidPort, port) return 0, fmt.Errorf(
"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,42 +24,41 @@ func Test_Provider_lines(t *testing.T) {
settings: Provider{ settings: Provider{
Name: constants.Cyberghost, Name: constants.Cyberghost,
ServerSelection: ServerSelection{ ServerSelection: ServerSelection{
Group: "group", VPN: constants.OpenVPN,
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:",
" |--Network protocol: udp", " |--Server groups: group",
" |--Server group: group",
" |--Regions: a, El country", " |--Regions: a, El country",
" |--Client key is set", " |--OpenVPN selection:",
" |--Client certificate is set", " |--Protocol: udp",
}, },
}, },
"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:",
" |--Network protocol: udp",
" |--Hostnames: a, b",
" |--Countries: c, d", " |--Countries: c, d",
" |--Hostnames: a, b",
" |--OpenVPN selection:",
" |--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"},
@@ -67,16 +66,18 @@ 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"},
@@ -84,16 +85,18 @@ 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"},
@@ -101,67 +104,73 @@ 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"},
OpenVPN: OpenVPNSelection{
CustomPort: 1, 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:",
" |--Protocol: udp",
" |--Custom port: 1", " |--Custom port: 1",
" |--IPv6: enabled",
}, },
}, },
"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"},
@@ -169,16 +178,18 @@ 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"},
@@ -188,22 +199,25 @@ 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",
" |--Names: g, h",
" |--Hostnames: i, j", " |--Hostnames: i, j",
" |--Names: g, h",
" |--OpenVPN selection:",
" |--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,
Filepath: string("/here"), Filepath: string("/here"),
@@ -211,9 +225,9 @@ 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",
" |--Encryption preset: strong", " |--OpenVPN selection:",
" |--Protocol: udp",
" |--Custom port: 1", " |--Custom port: 1",
" |--Port forwarding:", " |--Port forwarding:",
" |--File path: /here", " |--File path: /here",
@@ -223,6 +237,7 @@ 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"},
@@ -230,29 +245,33 @@ func Test_Provider_lines(t *testing.T) {
}, },
lines: []string{ lines: []string{
"|--Purevpn settings:", "|--Purevpn settings:",
" |--Network protocol: udp",
" |--Regions: a, b",
" |--Countries: c, d", " |--Countries: c, d",
" |--Regions: a, b",
" |--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"},
@@ -260,66 +279,71 @@ 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",
" |--Client key is set", " |--Hostnames: e, f",
" |--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"},
OpenVPN: OpenVPNSelection{
CustomPort: 1, 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:",
" |--Protocol: udp",
" |--Custom port: 1", " |--Custom port: 1",
}, },
}, },
@@ -362,7 +386,7 @@ func Test_readProtocol(t *testing.T) {
t.Parallel() t.Parallel()
ctrl := gomock.NewController(t) ctrl := gomock.NewController(t)
env := mock_params.NewMockEnv(ctrl) env := mock_params.NewMockInterface(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,58 +6,34 @@ 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()) settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.PurevpnRegionChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable REGION: %w", err) return fmt.Errorf("environment variable REGION: %w", err)
} }
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.PurevpnCountryChoices()) settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.PurevpnCountryChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err) return fmt.Errorf("environment variable COUNTRY: %w", err)
} }
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.PurevpnCityChoices()) settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.PurevpnCityChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable CITY: %w", err) return fmt.Errorf("environment variable CITY: %w", err)
} }
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.PurevpnHostnameChoices()) settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.PurevpnHostnameChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err) return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
} }
return nil return settings.ServerSelection.OpenVPN.readProtocolOnly(r.env)
} }

View File

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

View File

@@ -1,19 +1,23 @@
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
TCP bool `json:"tcp"` // UDP if TCP is false VPN string `json:"vpn"` // note: this is required
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
Group string `json:"group"` Groups []string `json:"groups"`
// 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"`
@@ -27,27 +31,133 @@ 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"`
} }
type ExtraConfigOptions struct { func (selection ServerSelection) toLines() (lines []string) {
ClientCertificate string `json:"-"` // Cyberghost if selection.TargetIP != nil {
ClientKey string `json:"-"` // Cyberghost, VPNUnlimited lines = append(lines, lastIndent+"Target IP address: "+selection.TargetIP.String())
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,13 +5,14 @@ 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 {
OpenVPN OpenVPN VPN VPN
System System System System
DNS DNS DNS DNS
Firewall Firewall Firewall Firewall
@@ -22,6 +23,7 @@ type Settings struct {
VersionInformation bool VersionInformation bool
ControlServer ControlServer ControlServer ControlServer
Health Health Health Health
Log Log
} }
func (settings *Settings) String() string { func (settings *Settings) String() string {
@@ -30,9 +32,10 @@ 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.OpenVPN.lines()...) lines = append(lines, settings.VPN.lines()...)
lines = append(lines, settings.DNS.lines()...) lines = append(lines, settings.DNS.lines()...)
lines = append(lines, settings.Firewall.lines()...) lines = append(lines, settings.Firewall.lines()...)
lines = append(lines, settings.Log.lines()...)
lines = append(lines, settings.System.lines()...) lines = append(lines, settings.System.lines()...)
lines = append(lines, settings.HTTPProxy.lines()...) lines = append(lines, settings.HTTPProxy.lines()...)
lines = append(lines, settings.ShadowSocks.lines()...) lines = append(lines, settings.ShadowSocks.lines()...)
@@ -47,7 +50,7 @@ func (settings *Settings) lines() (lines []string) {
} }
var ( var (
ErrOpenvpn = errors.New("cannot read Openvpn settings") ErrVPN = errors.New("cannot read VPN 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")
@@ -57,20 +60,22 @@ 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.Env, logger logging.Logger) (err error) { func (settings *Settings) Read(env params.Interface, servers models.AllServers,
r := newReader(env, logger) logger logging.Logger) (err error) {
r := newReader(env, servers, logger)
settings.VersionInformation, err = r.env.OnOff("VERSION_INFORMATION", params.Default("on")) settings.VersionInformation, err = r.env.OnOff("VERSION_INFORMATION", params.Default("on"))
if err != nil { if err != nil {
return fmt.Errorf("environment variable VERSION_INFORMATION: %w", err) return fmt.Errorf("environment variable VERSION_INFORMATION: %w", err)
} }
if err := settings.OpenVPN.read(r); err != nil { if err := settings.VPN.read(r); err != nil {
return fmt.Errorf("%w: %s", ErrOpenvpn, err) return fmt.Errorf("%w: %s", ErrVPN, err)
} }
if err := settings.System.read(r); err != nil { if err := settings.System.read(r); err != nil {
@@ -113,5 +118,9 @@ func (settings *Settings) Read(env params.Env, logger logging.Logger) (err error
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,23 +16,35 @@ func Test_Settings_lines(t *testing.T) {
}{ }{
"default settings": { "default settings": {
settings: Settings{ settings: Settings{
OpenVPN: OpenVPN{ VPN: VPN{
Version: constants.Openvpn25, Type: constants.OpenVPN,
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:",
"|--OpenVPN:", "|--VPN:",
" |--Type: openvpn",
" |--OpenVPN:",
" |--Version: 2.5", " |--Version: 2.5",
" |--Verbosity level: 0", " |--Verbosity level: 0",
" |--Provider:", " |--Network interface: tun",
" |--Mullvad settings:", " |--Mullvad settings:",
" |--Network protocol: udp", " |--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,19 +2,16 @@ 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 {
Method string
Password string
Port uint16
Enabled bool Enabled bool
Log bool tcpudp.Settings
} }
func (settings *ShadowSocks) String() string { func (settings *ShadowSocks) String() string {
@@ -28,12 +25,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 port: "+strconv.Itoa(int(settings.Port))) lines = append(lines, indent+lastIndent+"Listening address: "+settings.Address)
lines = append(lines, indent+lastIndent+"Method: "+settings.Method) lines = append(lines, indent+lastIndent+"Cipher: "+settings.CipherName)
if settings.Log { if settings.LogAddresses {
lines = append(lines, indent+lastIndent+"Logging: enabled") lines = append(lines, indent+lastIndent+"Log addresses: enabled")
} }
return lines return lines
@@ -52,24 +49,61 @@ func (settings *ShadowSocks) read(r reader) (err error) {
return err return err
} }
settings.Log, err = r.env.OnOff("SHADOWSOCKS_LOG", params.Default("off")) settings.LogAddresses, 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.Method, err = r.env.Get("SHADOWSOCKS_METHOD", params.Default("chacha20-ietf-poly1305")) settings.CipherName, err = r.env.Get("SHADOWSOCKS_CIPHER", params.Default("chacha20-ietf-poly1305"),
params.RetroKeys([]string{"SHADOWSOCKS_METHOD"}, r.onRetroActive))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SHADOWSOCKS_METHOD: %w", err) return fmt.Errorf("environment variable SHADOWSOCKS_CIPHER (or SHADOWSOCKS_METHOD): %w", err)
} }
var warning string warning, err := settings.getAddress(r.env)
settings.Port, warning, err = r.env.ListeningPort("SHADOWSOCKS_PORT", params.Default("8388")) if warning != "" {
if len(warning) > 0 {
r.logger.Warn(warning) r.logger.Warn(warning)
} }
if err != nil { if err != nil {
return fmt.Errorf("environment variable SHADOWSOCKS_PORT: %w", err) return 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,44 +2,101 @@ package configuration
import ( import (
"fmt" "fmt"
"strings"
"github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/golibs/params"
) )
func (settings *Provider) 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.Regions, err = r.env.CSVInside("REGION", constants.SurfsharkRegionChoices()) settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.SurfsharkCountryChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable REGION: %w", err) return fmt.Errorf("environment variable COUNTRY: %w", err)
} }
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.SurfsharkHostnameChoices()) settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.SurfsharkCityChoices(servers))
if err != nil {
return fmt.Errorf("environment variable CITY: %w", err)
}
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME",
constants.SurfsharkHostnameChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err) return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
} }
return nil regionChoices := constants.SurfsharkRegionChoices(servers)
regionChoices = append(regionChoices, constants.SurfsharkRetroLocChoices(servers)...)
settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", regionChoices)
if err != nil {
return fmt.Errorf("environment variable REGION: %w", err)
}
// Retro compatibility
// TODO remove in v4
settings.ServerSelection = surfsharkRetroRegion(settings.ServerSelection)
settings.ServerSelection.MultiHopOnly, err = r.env.YesNo("MULTIHOP_ONLY", params.Default("no"))
if err != nil {
return fmt.Errorf("environment variable MULTIHOP_ONLY: %w", err)
}
return settings.ServerSelection.OpenVPN.readProtocolOnly(r.env)
}
func surfsharkRetroRegion(selection ServerSelection) (
updatedSelection ServerSelection) {
locationData := constants.SurfsharkLocationData()
retroToLocation := make(map[string]models.SurfsharkLocationData, len(locationData))
for _, data := range locationData {
if data.RetroLoc == "" {
continue
}
retroToLocation[strings.ToLower(data.RetroLoc)] = data
}
for i, region := range selection.Regions {
location, ok := retroToLocation[region]
if !ok {
continue
}
selection.Regions[i] = strings.ToLower(location.Region)
selection.Countries = append(selection.Countries, strings.ToLower(location.Country))
selection.Cities = append(selection.Cities, strings.ToLower(location.City)) // even empty string
selection.Hostnames = append(selection.Hostnames, location.Hostname)
}
selection.Regions = dedupSlice(selection.Regions)
selection.Countries = dedupSlice(selection.Countries)
selection.Cities = dedupSlice(selection.Cities)
selection.Hostnames = dedupSlice(selection.Hostnames)
return selection
}
func dedupSlice(slice []string) (deduped []string) {
if slice == nil {
return nil
}
deduped = make([]string, 0, len(slice))
seen := make(map[string]struct{}, len(slice))
for _, s := range slice {
if _, ok := seen[s]; !ok {
seen[s] = struct{}{}
deduped = append(deduped, s)
}
}
return deduped
} }

View File

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

View File

@@ -6,49 +6,30 @@ 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()) settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.TorguardCountryChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable COUNTRY: %w", err) return fmt.Errorf("environment variable COUNTRY: %w", err)
} }
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.TorguardCityChoices()) settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.TorguardCityChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable CITY: %w", err) return fmt.Errorf("environment variable CITY: %w", err)
} }
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.TorguardHostnameChoices()) settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME",
constants.TorguardHostnameChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err) return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
} }
return nil return settings.ServerSelection.OpenVPN.readProtocolAndPort(r.env)
} }

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.Env) (err error) { func (settings *DNS) readUnboundProviders(env params.Interface) (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.NewMockEnv(ctrl) env := mock_params.NewMockInterface(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

@@ -0,0 +1,97 @@
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,68 +7,27 @@ 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.ExtraConfigOptions.ClientKey, err = readClientKey(r) settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.VPNUnlimitedCountryChoices(servers))
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()) settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.VPNUnlimitedCityChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable CITY: %w", err) return fmt.Errorf("environment variable CITY: %w", err)
} }
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.VPNUnlimitedHostnameChoices()) settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME",
constants.VPNUnlimitedHostnameChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err) return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
} }
@@ -83,5 +42,19 @@ 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

@@ -1,42 +0,0 @@
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,28 +6,16 @@ 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()) settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.VyprvpnRegionChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable REGION: %w", err) return fmt.Errorf("environment variable REGION: %w", err)
} }

View File

@@ -2,60 +2,51 @@ 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()) settings.ServerSelection.Regions, err = r.env.CSVInside("REGION", constants.WindscribeRegionChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable REGION: %w", err) return fmt.Errorf("environment variable REGION: %w", err)
} }
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.WindscribeCityChoices()) settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.WindscribeCityChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable CITY: %w", err) return fmt.Errorf("environment variable CITY: %w", err)
} }
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.WindscribeHostnameChoices()) settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME",
constants.WindscribeHostnameChoices(servers))
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err) return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)
} }
settings.ServerSelection.CustomPort, err = readCustomPort(r.env, settings.ServerSelection.TCP, err = settings.ServerSelection.OpenVPN.readWindscribe(r.env)
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 {
@@ -64,3 +55,13 @@ func (settings *Provider) readWindscribe(r reader) (err error) {
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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,7 @@
package dns package dns
import ( import (
"context"
"regexp" "regexp"
"strings" "strings"
@@ -8,18 +9,24 @@ import (
"github.com/qdm12/golibs/logging" "github.com/qdm12/golibs/logging"
) )
func (l *Loop) collectLines(stdout, stderr <-chan string, done chan<- struct{}) { func (l *Loop) collectLines(ctx context.Context, 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 line, ok = <-stderr: case <-ctx.Done():
case line, ok = <-stdout: // Context should only be canceled after stdout and stderr are done
} // being written to.
if !ok { close(stdout)
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,12 +30,13 @@ func (l *Loop) setupUnbound(ctx context.Context) (
return nil, nil, nil, err return nil, nil, nil, err
} }
collectLinesDone := make(chan struct{}) linesCollectionCtx, linesCollectionCancel := context.WithCancel(context.Background())
go l.collectLines(stdoutLines, stderrLines, collectLinesDone) lineCollectionDone := make(chan struct{})
go l.collectLines(linesCollectionCtx, lineCollectionDone,
stdoutLines, stderrLines)
closeStreams = func() { closeStreams = func() {
close(stdoutLines) linesCollectionCancel()
close(stderrLines) <-lineCollectionDone
<-collectLinesDone
} }
// use Unbound // use Unbound

View File

@@ -4,8 +4,6 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"github.com/qdm12/gluetun/internal/constants"
) )
var ( var (
@@ -109,10 +107,10 @@ 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 {
if err = c.acceptOutputThroughInterface(ctx, string(constants.TUN), remove); err != nil {
return fmt.Errorf("cannot enable firewall: %w", err) return fmt.Errorf("cannot enable firewall: %w", err)
} }
}
for _, network := range c.localNetworks { for _, network := range c.localNetworks {
if err := c.acceptOutputFromIPToSubnet(ctx, network.InterfaceName, network.IP, *network.IPNet, remove); err != nil { if err := c.acceptOutputFromIPToSubnet(ctx, network.InterfaceName, network.IP, *network.IPNet, remove); err != nil {

View File

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

View File

@@ -1,53 +0,0 @@
package firewall
import (
"net"
)
func findSubnetsToAdd(oldSubnets, newSubnets []net.IPNet) (subnetsToAdd []net.IPNet) {
for _, newSubnet := range newSubnets {
found := false
for _, oldSubnet := range oldSubnets {
if subnetsAreEqual(oldSubnet, newSubnet) {
found = true
break
}
}
if !found {
subnetsToAdd = append(subnetsToAdd, newSubnet)
}
}
return subnetsToAdd
}
func findSubnetsToRemove(oldSubnets, newSubnets []net.IPNet) (subnetsToRemove []net.IPNet) {
for _, oldSubnet := range oldSubnets {
found := false
for _, newSubnet := range newSubnets {
if subnetsAreEqual(oldSubnet, newSubnet) {
found = true
break
}
}
if !found {
subnetsToRemove = append(subnetsToRemove, oldSubnet)
}
}
return subnetsToRemove
}
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)
for i := range subnets {
if subnetsAreEqual(subnet, subnets[i]) {
subnets[i] = subnets[L-1]
subnets = subnets[:L-1]
break
}
}
return subnets
}

View File

@@ -8,10 +8,12 @@ import (
) )
type VPNConnectionSetter interface { type VPNConnectionSetter interface {
SetVPNConnection(ctx context.Context, connection models.OpenVPNConnection) error SetVPNConnection(ctx context.Context,
connection models.Connection, vpnIntf string) error
} }
func (c *Config) SetVPNConnection(ctx context.Context, connection models.OpenVPNConnection) (err error) { func (c *Config) SetVPNConnection(ctx context.Context,
connection models.Connection, vpnIntf string) (err error) {
c.stateMutex.Lock() c.stateMutex.Lock()
defer c.stateMutex.Unlock() defer c.stateMutex.Unlock()
@@ -33,11 +35,26 @@ func (c *Config) SetVPNConnection(ctx context.Context, connection models.OpenVPN
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.OpenVPNConnection{} c.vpnConnection = models.Connection{}
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.openvpn.healthyTimer = time.NewTimer(s.openvpn.healthyWait) s.vpn.healthyTimer = time.NewTimer(s.vpn.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.openvpn.healthyTimer.Stop() s.vpn.healthyTimer.Stop()
s.openvpn.healthyWait = s.config.OpenVPN.Initial s.vpn.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.openvpn.healthyTimer.Stop() s.vpn.healthyTimer.Stop()
s.openvpn.healthyTimer = time.NewTimer(s.openvpn.healthyWait) s.vpn.healthyTimer = time.NewTimer(s.vpn.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.openvpn.healthyTimer.C: case <-s.vpn.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/openvpn" "github.com/qdm12/gluetun/internal/vpn"
) )
type openvpnHealth struct { type vpnHealth struct {
looper openvpn.Looper looper vpn.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.openvpn.healthyWait.String() + ": restarting OpenVPN") s.vpn.healthyWait.String() + ": restarting OpenVPN")
_, _ = s.openvpn.looper.ApplyStatus(ctx, constants.Stopped) _, _ = s.vpn.looper.ApplyStatus(ctx, constants.Stopped)
_, _ = s.openvpn.looper.ApplyStatus(ctx, constants.Running) _, _ = s.vpn.looper.ApplyStatus(ctx, constants.Running)
s.openvpn.healthyWait += s.config.OpenVPN.Addition s.vpn.healthyWait += s.config.OpenVPN.Addition
s.openvpn.healthyTimer = time.NewTimer(s.openvpn.healthyWait) s.vpn.healthyTimer = time.NewTimer(s.vpn.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/openvpn" "github.com/qdm12/gluetun/internal/vpn"
"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
openvpn openvpnHealth vpn vpnHealth
} }
func NewServer(config configuration.Health, func NewServer(config configuration.Health,
logger logging.Logger, openvpnLooper openvpn.Looper) *Server { logger logging.Logger, vpnLooper vpn.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,
openvpn: openvpnHealth{ vpn: vpnHealth{
looper: openvpnLooper, looper: vpnLooper,
healthyWait: config.OpenVPN.Initial, healthyWait: config.OpenVPN.Initial,
}, },
} }

View File

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

View File

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

View File

@@ -37,6 +37,7 @@ func (s *State) ApplyStatus(ctx context.Context, status models.LoopStatus) (
switch existingStatus { switch existingStatus {
case constants.Stopped, constants.Completed: case constants.Stopped, constants.Completed:
default: default:
s.statusMu.Unlock()
return "already " + existingStatus.String(), nil return "already " + existingStatus.String(), nil
} }
@@ -50,11 +51,13 @@ 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
case constants.Stopped: case constants.Stopped:
if existingStatus != constants.Running { if existingStatus != constants.Running {
s.statusMu.Unlock()
return "already " + existingStatus.String(), nil return "already " + existingStatus.String(), nil
} }
@@ -73,6 +76,7 @@ func (s *State) ApplyStatus(ctx context.Context, status models.LoopStatus) (
return newStatus.String(), nil return newStatus.String(), nil
default: default:
s.statusMu.Unlock()
return "", fmt.Errorf("%w: %s: it can only be one of: %s, %s", return "", fmt.Errorf("%w: %s: it can only be one of: %s, %s",
ErrInvalidStatus, status, constants.Running, constants.Stopped) ErrInvalidStatus, status, constants.Running, constants.Stopped)
} }

View File

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

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

View File

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

View File

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

View File

@@ -1,28 +0,0 @@
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,15 +39,19 @@ 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"`
@@ -55,6 +59,7 @@ 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
@@ -114,7 +119,11 @@ type PurevpnServer struct {
type SurfsharkServer struct { type SurfsharkServer struct {
Region string `json:"region"` Region string `json:"region"`
Country string `json:"country"` // Country is also used for multi-hop
City string `json:"city"`
RetroLoc string `json:"retroloc"` // TODO remove in v4
Hostname string `json:"hostname"` Hostname string `json:"hostname"`
MultiHop bool `json:"multihop"`
TCP bool `json:"tcp"` TCP bool `json:"tcp"`
UDP bool `json:"udp"` UDP bool `json:"udp"`
IPs []net.IP `json:"ips"` IPs []net.IP `json:"ips"`
@@ -149,9 +158,11 @@ 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"` OvpnX509 string `json:"x509,omitempty"`
WgPubKey string `json:"wgpubkey,omitempty"`
IPs []net.IP `json:"ips"` IPs []net.IP `json:"ips"`
} }

View File

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

View File

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

View File

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

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

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

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

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

View File

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

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