mirror of
https://github.com/qdm12/gluetun.git
synced 2026-06-30 07:47:35 +02:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 93cc5a4b2c |
@@ -56,7 +56,6 @@ body:
|
|||||||
- IVPN
|
- IVPN
|
||||||
- Mullvad
|
- Mullvad
|
||||||
- NordVPN
|
- NordVPN
|
||||||
- OVPN
|
|
||||||
- Privado
|
- Privado
|
||||||
- Private Internet Access
|
- Private Internet Access
|
||||||
- PrivateVPN
|
- PrivateVPN
|
||||||
|
|||||||
@@ -64,8 +64,6 @@
|
|||||||
color: "cfe8d4"
|
color: "cfe8d4"
|
||||||
- name: "☁️ NordVPN"
|
- name: "☁️ NordVPN"
|
||||||
color: "cfe8d4"
|
color: "cfe8d4"
|
||||||
- name: "☁️ OVPN"
|
|
||||||
color: "cfe8d4"
|
|
||||||
- name: "☁️ Perfect Privacy"
|
- name: "☁️ Perfect Privacy"
|
||||||
color: "cfe8d4"
|
color: "cfe8d4"
|
||||||
- name: "☁️ PIA"
|
- name: "☁️ PIA"
|
||||||
|
|||||||
+1
-4
@@ -186,14 +186,12 @@ ENV VPN_SERVICE_PROVIDER=pia \
|
|||||||
# # ProtonVPN only:
|
# # ProtonVPN only:
|
||||||
SECURE_CORE_ONLY= \
|
SECURE_CORE_ONLY= \
|
||||||
TOR_ONLY= \
|
TOR_ONLY= \
|
||||||
# # Surfshark and ovpn only:
|
# # Surfshark only:
|
||||||
MULTIHOP_ONLY= \
|
MULTIHOP_ONLY= \
|
||||||
# # VPN Secure only:
|
# # VPN Secure only:
|
||||||
PREMIUM_ONLY= \
|
PREMIUM_ONLY= \
|
||||||
# # PIA and ProtonVPN only:
|
# # PIA and ProtonVPN only:
|
||||||
PORT_FORWARD_ONLY= \
|
PORT_FORWARD_ONLY= \
|
||||||
# # Ovpn only:
|
|
||||||
SERVER_DEDICATED=no \
|
|
||||||
# Firewall
|
# Firewall
|
||||||
FIREWALL_ENABLED_DISABLING_IT_SHOOTS_YOU_IN_YOUR_FOOT=on \
|
FIREWALL_ENABLED_DISABLING_IT_SHOOTS_YOU_IN_YOUR_FOOT=on \
|
||||||
FIREWALL_VPN_INPUT_PORTS= \
|
FIREWALL_VPN_INPUT_PORTS= \
|
||||||
@@ -220,7 +218,6 @@ ENV VPN_SERVICE_PROVIDER=pia \
|
|||||||
DNS_CACHING=on \
|
DNS_CACHING=on \
|
||||||
DNS_UPSTREAM_IPV6=off \
|
DNS_UPSTREAM_IPV6=off \
|
||||||
BLOCK_MALICIOUS=on \
|
BLOCK_MALICIOUS=on \
|
||||||
BLOCK_SURVEILLANCE=off \
|
|
||||||
BLOCK_ADS=off \
|
BLOCK_ADS=off \
|
||||||
DNS_UNBLOCK_HOSTNAMES= \
|
DNS_UNBLOCK_HOSTNAMES= \
|
||||||
DNS_REBINDING_PROTECTION_EXEMPT_HOSTNAMES= \
|
DNS_REBINDING_PROTECTION_EXEMPT_HOSTNAMES= \
|
||||||
|
|||||||
@@ -60,16 +60,16 @@ Lightweight swiss-army-knife-like VPN client to multiple VPN service providers
|
|||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Based on Alpine 3.23 for a small Docker image of 43.1MB
|
- Based on Alpine 3.23 for a small Docker image of 43.1MB
|
||||||
- Supports: **AirVPN**, **Cyberghost**, **ExpressVPN**, **FastestVPN**, **Giganews**, **HideMyAss**, **IPVanish**, **IVPN**, **Mullvad** (Wireguard only), **NordVPN**, **Ovpn**, **Perfect Privacy**, **Privado**, **Private Internet Access**, **PrivateVPN**, **ProtonVPN**, **PureVPN**, **SlickVPN**, **Surfshark**, **TorGuard**, **VPNSecure.me**, **VPNUnlimited**, **Vyprvpn**, **Windscribe** servers
|
- Supports: **AirVPN**, **Cyberghost**, **ExpressVPN**, **FastestVPN**, **Giganews**, **HideMyAss**, **IPVanish**, **IVPN**, **Mullvad** (Wireguard only), **NordVPN**, **Perfect Privacy**, **Privado**, **Private Internet Access**, **PrivateVPN**, **ProtonVPN**, **PureVPN**, **SlickVPN**, **Surfshark**, **TorGuard**, **VPNSecure.me**, **VPNUnlimited**, **Vyprvpn**, **Windscribe** servers
|
||||||
- Supports OpenVPN for all providers listed
|
- Supports OpenVPN for all providers listed
|
||||||
- Supports Wireguard both kernelspace and userspace
|
- Supports Wireguard both kernelspace and userspace
|
||||||
- For **AirVPN**, **FastestVPN**, **Ivpn**, **Mullvad**, **NordVPN**, **Ovpn**, **Perfect privacy**, **ProtonVPN**, **Surfshark** and **Windscribe**
|
- For **AirVPN**, **FastestVPN**, **Ivpn**, **Mullvad**, **NordVPN**, **Perfect privacy**, **ProtonVPN**, **Surfshark** and **Windscribe**
|
||||||
- For **Cyberghost**, **Private Internet Access**, **PrivateVPN**, **PureVPN**, **Torguard**, **VPN Unlimited** and **VyprVPN** using [the custom provider](https://github.com/qdm12/gluetun-wiki/blob/main/setup/providers/custom.md)
|
- For **Cyberghost**, **Private Internet Access**, **PrivateVPN**, **PureVPN**, **Torguard**, **VPN Unlimited** and **VyprVPN** using [the custom provider](https://github.com/qdm12/gluetun-wiki/blob/main/setup/providers/custom.md)
|
||||||
- For custom Wireguard configurations using [the custom provider](https://github.com/qdm12/gluetun-wiki/blob/main/setup/providers/custom.md)
|
- For custom Wireguard configurations using [the custom provider](https://github.com/qdm12/gluetun-wiki/blob/main/setup/providers/custom.md)
|
||||||
- More in progress, see [#134](https://github.com/passteque/gluetun/issues/134)
|
- More in progress, see [#134](https://github.com/passteque/gluetun/issues/134)
|
||||||
- Supports AmneziaWG only with the custom provider for now
|
- Supports AmneziaWG only with the custom provider for now
|
||||||
- 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 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`
|
||||||
- Built in firewall kill switch to allow traffic only with needed the VPN servers and LAN devices
|
- Built in firewall kill switch to allow traffic only with needed the VPN servers and LAN devices
|
||||||
- Built in Shadowsocks proxy server (protocol based on SOCKS5 with an encryption layer, tunnels TCP+UDP)
|
- Built in Shadowsocks proxy server (protocol based on SOCKS5 with an encryption layer, tunnels TCP+UDP)
|
||||||
|
|||||||
@@ -13,10 +13,9 @@ require (
|
|||||||
github.com/klauspost/pgzip v1.2.6
|
github.com/klauspost/pgzip v1.2.6
|
||||||
github.com/mdlayher/genetlink v1.4.0
|
github.com/mdlayher/genetlink v1.4.0
|
||||||
github.com/mdlayher/netlink v1.9.0
|
github.com/mdlayher/netlink v1.9.0
|
||||||
github.com/miekg/dns v1.1.62
|
|
||||||
github.com/pelletier/go-toml/v2 v2.2.4
|
github.com/pelletier/go-toml/v2 v2.2.4
|
||||||
github.com/qdm12/dns/v2 v2.0.0-rc9.0.20260421173011-9de8e7fdbe3a
|
github.com/qdm12/dns/v2 v2.0.0-rc9.0.20260421173011-9de8e7fdbe3a
|
||||||
github.com/qdm12/gluetun-servers v0.1.1-0.20260522005421-14277e92ce82
|
github.com/qdm12/gluetun-servers v0.1.0
|
||||||
github.com/qdm12/goservices v0.1.1-0.20251104135713-6bee97bd4978
|
github.com/qdm12/goservices v0.1.1-0.20251104135713-6bee97bd4978
|
||||||
github.com/qdm12/gosettings v0.4.4
|
github.com/qdm12/gosettings v0.4.4
|
||||||
github.com/qdm12/goshutdown v0.3.0
|
github.com/qdm12/goshutdown v0.3.0
|
||||||
@@ -48,6 +47,7 @@ require (
|
|||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mdlayher/socket v0.6.0 // indirect
|
github.com/mdlayher/socket v0.6.0 // indirect
|
||||||
|
github.com/miekg/dns v1.1.62 // indirect
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
|||||||
@@ -76,8 +76,8 @@ github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0leargg
|
|||||||
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||||
github.com/qdm12/dns/v2 v2.0.0-rc9.0.20260421173011-9de8e7fdbe3a h1:TE157yPQmAbVruH0MWCQzs0vTT/6t96DkoWUXd6PVuc=
|
github.com/qdm12/dns/v2 v2.0.0-rc9.0.20260421173011-9de8e7fdbe3a h1:TE157yPQmAbVruH0MWCQzs0vTT/6t96DkoWUXd6PVuc=
|
||||||
github.com/qdm12/dns/v2 v2.0.0-rc9.0.20260421173011-9de8e7fdbe3a/go.mod h1:98foWgXJZ+g8gJIuO+fdO+oWpFei5WShMFTeN4Im2lE=
|
github.com/qdm12/dns/v2 v2.0.0-rc9.0.20260421173011-9de8e7fdbe3a/go.mod h1:98foWgXJZ+g8gJIuO+fdO+oWpFei5WShMFTeN4Im2lE=
|
||||||
github.com/qdm12/gluetun-servers v0.1.1-0.20260522005421-14277e92ce82 h1:tE44IEW7o9yPQaO8HBeoO9RxtTTxqhboIypegrQlVt8=
|
github.com/qdm12/gluetun-servers v0.1.0 h1:w9JLghKZwI0Gzpp9p5rNANgEYUUZ1dxdxsG6NKIojaY=
|
||||||
github.com/qdm12/gluetun-servers v0.1.1-0.20260522005421-14277e92ce82/go.mod h1:acttuyHyoFDu6GTbf3kAV+QXeiX8oJeh0MBic67/9z8=
|
github.com/qdm12/gluetun-servers v0.1.0/go.mod h1:acttuyHyoFDu6GTbf3kAV+QXeiX8oJeh0MBic67/9z8=
|
||||||
github.com/qdm12/goservices v0.1.1-0.20251104135713-6bee97bd4978 h1:TRGpCU1l0lNwtogEUSs5U+RFceYxkAJUmrGabno7J5c=
|
github.com/qdm12/goservices v0.1.1-0.20251104135713-6bee97bd4978 h1:TRGpCU1l0lNwtogEUSs5U+RFceYxkAJUmrGabno7J5c=
|
||||||
github.com/qdm12/goservices v0.1.1-0.20251104135713-6bee97bd4978/go.mod h1:D1Po4CRQLYjccnAR2JsVlN1sBMgQrcNLONbvyuzcdTg=
|
github.com/qdm12/goservices v0.1.1-0.20251104135713-6bee97bd4978/go.mod h1:D1Po4CRQLYjccnAR2JsVlN1sBMgQrcNLONbvyuzcdTg=
|
||||||
github.com/qdm12/gosettings v0.4.4 h1:SM6tOZDf6k8qbjWU8KWyBF4mWIixfsKCfh9DGRLHlj4=
|
github.com/qdm12/gosettings v0.4.4 h1:SM6tOZDf6k8qbjWU8KWyBF4mWIixfsKCfh9DGRLHlj4=
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ func readObsolete(r *reader.Reader) (warnings []string) {
|
|||||||
"HEALTH_VPN_DURATION_ADDITION": "HEALTH_VPN_DURATION_ADDITION is obsolete",
|
"HEALTH_VPN_DURATION_ADDITION": "HEALTH_VPN_DURATION_ADDITION is obsolete",
|
||||||
"DNS_KEEP_NAMESERVER": "DNS_KEEP_NAMESERVER is obsolete because you should use the built-in server which now " +
|
"DNS_KEEP_NAMESERVER": "DNS_KEEP_NAMESERVER is obsolete because you should use the built-in server which now " +
|
||||||
"forwards local names to private DNS resolvers found in /etc/resolv.conf at container start",
|
"forwards local names to private DNS resolvers found in /etc/resolv.conf at container start",
|
||||||
|
"BLOCK_SURVEILLANCE": "BLOCK_SURVEILLANCE is obsolete because its DNS block lists are not longer maintained",
|
||||||
}
|
}
|
||||||
sortedKeys := slices.Collect(maps.Keys(keyToMessage))
|
sortedKeys := slices.Collect(maps.Keys(keyToMessage))
|
||||||
slices.Sort(sortedKeys)
|
slices.Sort(sortedKeys)
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import (
|
|||||||
type DNSBlacklist struct {
|
type DNSBlacklist struct {
|
||||||
BlockMalicious *bool
|
BlockMalicious *bool
|
||||||
BlockAds *bool
|
BlockAds *bool
|
||||||
BlockSurveillance *bool
|
|
||||||
AllowedHosts []string
|
AllowedHosts []string
|
||||||
AddBlockedHosts []string
|
AddBlockedHosts []string
|
||||||
AddBlockedIPs []netip.Addr
|
AddBlockedIPs []netip.Addr
|
||||||
@@ -31,7 +30,6 @@ type DNSBlacklist struct {
|
|||||||
func (b *DNSBlacklist) setDefaults() {
|
func (b *DNSBlacklist) setDefaults() {
|
||||||
b.BlockMalicious = gosettings.DefaultPointer(b.BlockMalicious, true)
|
b.BlockMalicious = gosettings.DefaultPointer(b.BlockMalicious, true)
|
||||||
b.BlockAds = gosettings.DefaultPointer(b.BlockAds, false)
|
b.BlockAds = gosettings.DefaultPointer(b.BlockAds, false)
|
||||||
b.BlockSurveillance = gosettings.DefaultPointer(b.BlockSurveillance, true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var hostRegex = regexp.MustCompile(`^([a-zA-Z0-9]|[a-zA-Z0-9_][a-zA-Z0-9\-_]{0,61}[a-zA-Z0-9_])(\.([a-zA-Z0-9]|[a-zA-Z0-9_][a-zA-Z0-9\-_]{0,61}[a-zA-Z0-9]))*$`) //nolint:lll
|
var hostRegex = regexp.MustCompile(`^([a-zA-Z0-9]|[a-zA-Z0-9_][a-zA-Z0-9\-_]{0,61}[a-zA-Z0-9_])(\.([a-zA-Z0-9]|[a-zA-Z0-9_][a-zA-Z0-9\-_]{0,61}[a-zA-Z0-9]))*$`) //nolint:lll
|
||||||
@@ -65,7 +63,6 @@ func (b DNSBlacklist) copy() (copied DNSBlacklist) {
|
|||||||
return DNSBlacklist{
|
return DNSBlacklist{
|
||||||
BlockMalicious: gosettings.CopyPointer(b.BlockMalicious),
|
BlockMalicious: gosettings.CopyPointer(b.BlockMalicious),
|
||||||
BlockAds: gosettings.CopyPointer(b.BlockAds),
|
BlockAds: gosettings.CopyPointer(b.BlockAds),
|
||||||
BlockSurveillance: gosettings.CopyPointer(b.BlockSurveillance),
|
|
||||||
AllowedHosts: gosettings.CopySlice(b.AllowedHosts),
|
AllowedHosts: gosettings.CopySlice(b.AllowedHosts),
|
||||||
AddBlockedHosts: gosettings.CopySlice(b.AddBlockedHosts),
|
AddBlockedHosts: gosettings.CopySlice(b.AddBlockedHosts),
|
||||||
AddBlockedIPs: gosettings.CopySlice(b.AddBlockedIPs),
|
AddBlockedIPs: gosettings.CopySlice(b.AddBlockedIPs),
|
||||||
@@ -77,7 +74,6 @@ func (b DNSBlacklist) copy() (copied DNSBlacklist) {
|
|||||||
func (b *DNSBlacklist) overrideWith(other DNSBlacklist) {
|
func (b *DNSBlacklist) overrideWith(other DNSBlacklist) {
|
||||||
b.BlockMalicious = gosettings.OverrideWithPointer(b.BlockMalicious, other.BlockMalicious)
|
b.BlockMalicious = gosettings.OverrideWithPointer(b.BlockMalicious, other.BlockMalicious)
|
||||||
b.BlockAds = gosettings.OverrideWithPointer(b.BlockAds, other.BlockAds)
|
b.BlockAds = gosettings.OverrideWithPointer(b.BlockAds, other.BlockAds)
|
||||||
b.BlockSurveillance = gosettings.OverrideWithPointer(b.BlockSurveillance, other.BlockSurveillance)
|
|
||||||
b.AllowedHosts = gosettings.OverrideWithSlice(b.AllowedHosts, other.AllowedHosts)
|
b.AllowedHosts = gosettings.OverrideWithSlice(b.AllowedHosts, other.AllowedHosts)
|
||||||
b.AddBlockedHosts = gosettings.OverrideWithSlice(b.AddBlockedHosts, other.AddBlockedHosts)
|
b.AddBlockedHosts = gosettings.OverrideWithSlice(b.AddBlockedHosts, other.AddBlockedHosts)
|
||||||
b.AddBlockedIPs = gosettings.OverrideWithSlice(b.AddBlockedIPs, other.AddBlockedIPs)
|
b.AddBlockedIPs = gosettings.OverrideWithSlice(b.AddBlockedIPs, other.AddBlockedIPs)
|
||||||
@@ -93,7 +89,6 @@ func (b DNSBlacklist) ToBlockBuilderSettings(client *http.Client) (
|
|||||||
Client: client,
|
Client: client,
|
||||||
BlockMalicious: b.BlockMalicious,
|
BlockMalicious: b.BlockMalicious,
|
||||||
BlockAds: b.BlockAds,
|
BlockAds: b.BlockAds,
|
||||||
BlockSurveillance: b.BlockSurveillance,
|
|
||||||
AllowedHosts: b.AllowedHosts,
|
AllowedHosts: b.AllowedHosts,
|
||||||
AddBlockedHosts: b.AddBlockedHosts,
|
AddBlockedHosts: b.AddBlockedHosts,
|
||||||
AddBlockedIPs: b.AddBlockedIPs,
|
AddBlockedIPs: b.AddBlockedIPs,
|
||||||
@@ -110,7 +105,6 @@ func (b DNSBlacklist) toLinesNode() (node *gotree.Node) {
|
|||||||
|
|
||||||
node.Appendf("Block malicious: %s", gosettings.BoolToYesNo(b.BlockMalicious))
|
node.Appendf("Block malicious: %s", gosettings.BoolToYesNo(b.BlockMalicious))
|
||||||
node.Appendf("Block ads: %s", gosettings.BoolToYesNo(b.BlockAds))
|
node.Appendf("Block ads: %s", gosettings.BoolToYesNo(b.BlockAds))
|
||||||
node.Appendf("Block surveillance: %s", gosettings.BoolToYesNo(b.BlockSurveillance))
|
|
||||||
|
|
||||||
if len(b.AllowedHosts) > 0 {
|
if len(b.AllowedHosts) > 0 {
|
||||||
allowedHostsNode := node.Append("Allowed hosts:")
|
allowedHostsNode := node.Append("Allowed hosts:")
|
||||||
@@ -156,12 +150,6 @@ func (b *DNSBlacklist) read(r *reader.Reader) (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
b.BlockSurveillance, err = r.BoolPtr("BLOCK_SURVEILLANCE",
|
|
||||||
reader.RetroKeys("BLOCK_NSA"))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
b.BlockAds, err = r.BoolPtr("BLOCK_ADS")
|
b.BlockAds, err = r.BoolPtr("BLOCK_ADS")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ func (o OpenVPNSelection) validate(vpnProvider string) (err error) {
|
|||||||
switch vpnProvider {
|
switch vpnProvider {
|
||||||
// no restriction on port
|
// no restriction on port
|
||||||
case providers.Custom, providers.Cyberghost, providers.HideMyAss,
|
case providers.Custom, providers.Cyberghost, providers.HideMyAss,
|
||||||
providers.Ovpn, providers.Privatevpn, providers.Torguard:
|
providers.Privatevpn, providers.Torguard:
|
||||||
// no custom port allowed
|
// no custom port allowed
|
||||||
case providers.Expressvpn, providers.Fastestvpn,
|
case providers.Expressvpn, providers.Fastestvpn,
|
||||||
providers.Giganews, providers.Ipvanish,
|
providers.Giganews, providers.Ipvanish,
|
||||||
|
|||||||
@@ -49,7 +49,6 @@ func (p *Provider) validate(vpnType string, filterChoicesGetter FilterChoicesGet
|
|||||||
providers.Ivpn,
|
providers.Ivpn,
|
||||||
providers.Mullvad,
|
providers.Mullvad,
|
||||||
providers.Nordvpn,
|
providers.Nordvpn,
|
||||||
providers.Ovpn,
|
|
||||||
providers.Protonvpn,
|
providers.Protonvpn,
|
||||||
providers.Surfshark,
|
providers.Surfshark,
|
||||||
providers.Windscribe,
|
providers.Windscribe,
|
||||||
|
|||||||
@@ -63,9 +63,6 @@ type ServerSelection struct {
|
|||||||
// TorOnly is true if VPN servers without tor should
|
// TorOnly is true if VPN servers without tor should
|
||||||
// be filtered. This is used with ProtonVPN.
|
// be filtered. This is used with ProtonVPN.
|
||||||
TorOnly *bool `json:"tor_only"`
|
TorOnly *bool `json:"tor_only"`
|
||||||
// Dedicated is true if dedicated VPN servers should be chosen only.
|
|
||||||
// This is used with OVPN.
|
|
||||||
Dedicated *bool `json:"dedicated"`
|
|
||||||
// OpenVPN contains settings to select OpenVPN servers
|
// OpenVPN contains settings to select OpenVPN servers
|
||||||
// and the final connection.
|
// and the final connection.
|
||||||
OpenVPN OpenVPNSelection `json:"openvpn"`
|
OpenVPN OpenVPNSelection `json:"openvpn"`
|
||||||
@@ -275,8 +272,6 @@ func validateFeatureFilters(settings ServerSelection, vpnServiceProvider string)
|
|||||||
return errors.New("secure core only filter is not supported")
|
return errors.New("secure core only filter is not supported")
|
||||||
case *settings.TorOnly && vpnServiceProvider != providers.Protonvpn:
|
case *settings.TorOnly && vpnServiceProvider != providers.Protonvpn:
|
||||||
return errors.New("tor only filter is not supported")
|
return errors.New("tor only filter is not supported")
|
||||||
case *settings.Dedicated && vpnServiceProvider != providers.Ovpn:
|
|
||||||
return errors.New("dedicated filter is not supported")
|
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -301,7 +296,6 @@ func (ss *ServerSelection) copy() (copied ServerSelection) {
|
|||||||
TorOnly: gosettings.CopyPointer(ss.TorOnly),
|
TorOnly: gosettings.CopyPointer(ss.TorOnly),
|
||||||
PortForwardOnly: gosettings.CopyPointer(ss.PortForwardOnly),
|
PortForwardOnly: gosettings.CopyPointer(ss.PortForwardOnly),
|
||||||
MultiHopOnly: gosettings.CopyPointer(ss.MultiHopOnly),
|
MultiHopOnly: gosettings.CopyPointer(ss.MultiHopOnly),
|
||||||
Dedicated: gosettings.CopyPointer(ss.Dedicated),
|
|
||||||
OpenVPN: ss.OpenVPN.copy(),
|
OpenVPN: ss.OpenVPN.copy(),
|
||||||
Wireguard: ss.Wireguard.copy(),
|
Wireguard: ss.Wireguard.copy(),
|
||||||
}
|
}
|
||||||
@@ -325,7 +319,6 @@ func (ss *ServerSelection) overrideWith(other ServerSelection) {
|
|||||||
ss.TorOnly = gosettings.OverrideWithPointer(ss.TorOnly, other.TorOnly)
|
ss.TorOnly = gosettings.OverrideWithPointer(ss.TorOnly, other.TorOnly)
|
||||||
ss.MultiHopOnly = gosettings.OverrideWithPointer(ss.MultiHopOnly, other.MultiHopOnly)
|
ss.MultiHopOnly = gosettings.OverrideWithPointer(ss.MultiHopOnly, other.MultiHopOnly)
|
||||||
ss.PortForwardOnly = gosettings.OverrideWithPointer(ss.PortForwardOnly, other.PortForwardOnly)
|
ss.PortForwardOnly = gosettings.OverrideWithPointer(ss.PortForwardOnly, other.PortForwardOnly)
|
||||||
ss.Dedicated = gosettings.OverrideWithPointer(ss.Dedicated, other.Dedicated)
|
|
||||||
ss.OpenVPN.overrideWith(other.OpenVPN)
|
ss.OpenVPN.overrideWith(other.OpenVPN)
|
||||||
ss.Wireguard.overrideWith(other.Wireguard)
|
ss.Wireguard.overrideWith(other.Wireguard)
|
||||||
}
|
}
|
||||||
@@ -342,7 +335,6 @@ func (ss *ServerSelection) setDefaults(vpnProvider string, portForwardingEnabled
|
|||||||
defaultPortForwardOnly := portForwardingEnabled &&
|
defaultPortForwardOnly := portForwardingEnabled &&
|
||||||
helpers.IsOneOf(vpnProvider, providers.PrivateInternetAccess, providers.Protonvpn)
|
helpers.IsOneOf(vpnProvider, providers.PrivateInternetAccess, providers.Protonvpn)
|
||||||
ss.PortForwardOnly = gosettings.DefaultPointer(ss.PortForwardOnly, defaultPortForwardOnly)
|
ss.PortForwardOnly = gosettings.DefaultPointer(ss.PortForwardOnly, defaultPortForwardOnly)
|
||||||
ss.Dedicated = gosettings.DefaultPointer(ss.Dedicated, false)
|
|
||||||
ss.OpenVPN.setDefaults(vpnProvider)
|
ss.OpenVPN.setDefaults(vpnProvider)
|
||||||
ss.Wireguard.setDefaults()
|
ss.Wireguard.setDefaults()
|
||||||
}
|
}
|
||||||
@@ -418,10 +410,6 @@ func (ss ServerSelection) toLinesNode() (node *gotree.Node) {
|
|||||||
node.Appendf("Multi-hop only servers: yes")
|
node.Appendf("Multi-hop only servers: yes")
|
||||||
}
|
}
|
||||||
|
|
||||||
if *ss.Dedicated {
|
|
||||||
node.Appendf("Dedicated servers: yes")
|
|
||||||
}
|
|
||||||
|
|
||||||
if *ss.PortForwardOnly {
|
if *ss.PortForwardOnly {
|
||||||
node.Appendf("Port forwarding only servers: yes")
|
node.Appendf("Port forwarding only servers: yes")
|
||||||
}
|
}
|
||||||
@@ -513,12 +501,6 @@ func (ss *ServerSelection) read(r *reader.Reader,
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ovpn only
|
|
||||||
ss.Dedicated, err = r.BoolPtr("SERVER_DEDICATED")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = ss.OpenVPN.read(r)
|
err = ss.OpenVPN.read(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -59,8 +59,7 @@ func Test_Settings_String(t *testing.T) {
|
|||||||
| ├── Update period: every 24h0m0s
|
| ├── Update period: every 24h0m0s
|
||||||
| └── DNS filtering settings:
|
| └── DNS filtering settings:
|
||||||
| ├── Block malicious: yes
|
| ├── Block malicious: yes
|
||||||
| ├── Block ads: no
|
| └── Block ads: no
|
||||||
| └── Block surveillance: yes
|
|
||||||
├── Firewall settings:
|
├── Firewall settings:
|
||||||
| ├── Enabled: yes
|
| ├── Enabled: yes
|
||||||
| └── Iptables settings:
|
| └── Iptables settings:
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings/helpers"
|
|
||||||
"github.com/qdm12/gluetun/internal/constants/providers"
|
"github.com/qdm12/gluetun/internal/constants/providers"
|
||||||
"github.com/qdm12/gosettings"
|
"github.com/qdm12/gosettings"
|
||||||
"github.com/qdm12/gosettings/reader"
|
"github.com/qdm12/gosettings/reader"
|
||||||
@@ -23,7 +22,7 @@ type WireguardSelection struct {
|
|||||||
// It can never be the zero value in the internal state.
|
// It can never be the zero value in the internal state.
|
||||||
EndpointIP netip.Addr `json:"endpoint_ip"`
|
EndpointIP netip.Addr `json:"endpoint_ip"`
|
||||||
// EndpointPort is a the server port to use for the VPN server.
|
// EndpointPort is a the server port to use for the VPN server.
|
||||||
// It is optional for VPN providers IVPN, Mullvad, Ovpn, Surfshark
|
// It is optional for VPN providers IVPN, Mullvad, Surfshark
|
||||||
// and Windscribe, and compulsory for the others.
|
// and Windscribe, and compulsory for the others.
|
||||||
// When optional, it can be set to 0 to indicate not use
|
// When optional, it can be set to 0 to indicate not use
|
||||||
// a custom endpoint port. It cannot be nil in the internal
|
// a custom endpoint port. It cannot be nil in the internal
|
||||||
@@ -41,9 +40,8 @@ func (w WireguardSelection) validate(vpnProvider string) (err error) {
|
|||||||
// Validate EndpointIP
|
// Validate EndpointIP
|
||||||
switch vpnProvider {
|
switch vpnProvider {
|
||||||
case providers.Airvpn, providers.Fastestvpn, providers.Ivpn,
|
case providers.Airvpn, providers.Fastestvpn, providers.Ivpn,
|
||||||
providers.Mullvad, providers.Nordvpn, providers.Ovpn,
|
providers.Mullvad, providers.Nordvpn, providers.Protonvpn,
|
||||||
providers.Protonvpn, providers.Surfshark,
|
providers.Surfshark, providers.Windscribe:
|
||||||
providers.Windscribe:
|
|
||||||
// endpoint IP addresses are baked in
|
// endpoint IP addresses are baked in
|
||||||
case providers.Custom:
|
case providers.Custom:
|
||||||
if !w.EndpointIP.IsValid() || w.EndpointIP.IsUnspecified() {
|
if !w.EndpointIP.IsValid() || w.EndpointIP.IsUnspecified() {
|
||||||
@@ -65,16 +63,12 @@ func (w WireguardSelection) validate(vpnProvider string) (err error) {
|
|||||||
if *w.EndpointPort != 0 {
|
if *w.EndpointPort != 0 {
|
||||||
return errors.New("endpoint port is set")
|
return errors.New("endpoint port is set")
|
||||||
}
|
}
|
||||||
case providers.Airvpn, providers.Ivpn, providers.Mullvad,
|
case providers.Airvpn, providers.Ivpn, providers.Mullvad, providers.Windscribe:
|
||||||
providers.Ovpn, providers.Windscribe:
|
|
||||||
// EndpointPort is optional and can be 0
|
// EndpointPort is optional and can be 0
|
||||||
if *w.EndpointPort == 0 {
|
if *w.EndpointPort == 0 {
|
||||||
break // no custom endpoint port set
|
break // no custom endpoint port set
|
||||||
}
|
}
|
||||||
if helpers.IsOneOf(vpnProvider,
|
if vpnProvider == providers.Mullvad {
|
||||||
providers.Mullvad,
|
|
||||||
providers.Ovpn,
|
|
||||||
) {
|
|
||||||
break // no restriction on custom endpoint port value
|
break // no restriction on custom endpoint port value
|
||||||
}
|
}
|
||||||
var allowed []uint16
|
var allowed []uint16
|
||||||
@@ -98,7 +92,7 @@ func (w WireguardSelection) validate(vpnProvider string) (err error) {
|
|||||||
// Validate PublicKey
|
// Validate PublicKey
|
||||||
switch vpnProvider {
|
switch vpnProvider {
|
||||||
case providers.Fastestvpn, providers.Ivpn, providers.Mullvad,
|
case providers.Fastestvpn, providers.Ivpn, providers.Mullvad,
|
||||||
providers.Ovpn, providers.Surfshark, providers.Windscribe:
|
providers.Surfshark, providers.Windscribe:
|
||||||
// public keys are baked in
|
// public keys are baked in
|
||||||
case providers.Custom:
|
case providers.Custom:
|
||||||
if w.PublicKey == "" {
|
if w.PublicKey == "" {
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ const (
|
|||||||
Ivpn = "ivpn"
|
Ivpn = "ivpn"
|
||||||
Mullvad = "mullvad"
|
Mullvad = "mullvad"
|
||||||
Nordvpn = "nordvpn"
|
Nordvpn = "nordvpn"
|
||||||
Ovpn = "ovpn"
|
|
||||||
Perfectprivacy = "perfect privacy"
|
Perfectprivacy = "perfect privacy"
|
||||||
Privado = "privado"
|
Privado = "privado"
|
||||||
PrivateInternetAccess = "private internet access"
|
PrivateInternetAccess = "private internet access"
|
||||||
@@ -44,7 +43,6 @@ func All() []string {
|
|||||||
Ivpn,
|
Ivpn,
|
||||||
Mullvad,
|
Mullvad,
|
||||||
Nordvpn,
|
Nordvpn,
|
||||||
Ovpn,
|
|
||||||
Perfectprivacy,
|
Perfectprivacy,
|
||||||
Privado,
|
Privado,
|
||||||
PrivateInternetAccess,
|
PrivateInternetAccess,
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"math"
|
"math"
|
||||||
"math/rand/v2"
|
"math/rand/v2"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -85,14 +84,11 @@ func triggerDNSQuery(ctx context.Context, client *http.Client, session string) (
|
|||||||
IP map[string]uint `json:"ip"`
|
IP map[string]uint `json:"ip"`
|
||||||
}
|
}
|
||||||
|
|
||||||
rawData, err := io.ReadAll(response.Body)
|
decoder := json.NewDecoder(response.Body)
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("reading response body: %w", err)
|
|
||||||
}
|
|
||||||
var data ipLeakData
|
var data ipLeakData
|
||||||
err = json.Unmarshal(rawData, &data)
|
err = decoder.Decode(&data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("decoding response %q: %w", rawData, err)
|
return nil, fmt.Errorf("decoding response: %w", err)
|
||||||
} else if data.Session != session {
|
} else if data.Session != session {
|
||||||
return nil, fmt.Errorf("ipleak.net session mismatch: expected %s, got %s", session, data.Session)
|
return nil, fmt.Errorf("ipleak.net session mismatch: expected %s, got %s", session, data.Session)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,11 +34,8 @@ type Server struct {
|
|||||||
SecureCore bool `json:"secure_core,omitempty"`
|
SecureCore bool `json:"secure_core,omitempty"`
|
||||||
Tor bool `json:"tor,omitempty"`
|
Tor bool `json:"tor,omitempty"`
|
||||||
PortForward bool `json:"port_forward,omitempty"`
|
PortForward bool `json:"port_forward,omitempty"`
|
||||||
Dedicated bool `json:"dedicated,omitempty"`
|
|
||||||
Keep bool `json:"keep,omitempty"`
|
Keep bool `json:"keep,omitempty"`
|
||||||
IPs []netip.Addr `json:"ips,omitempty"`
|
IPs []netip.Addr `json:"ips,omitempty"`
|
||||||
PortsTCP []uint16 `json:"ports_tcp,omitempty"`
|
|
||||||
PortsUDP []uint16 `json:"ports_udp,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) HasMinimumInformation() (err error) {
|
func (s *Server) HasMinimumInformation() (err error) {
|
||||||
|
|||||||
@@ -1,15 +0,0 @@
|
|||||||
package ovpn
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
|
||||||
"github.com/qdm12/gluetun/internal/models"
|
|
||||||
"github.com/qdm12/gluetun/internal/provider/utils"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (p *Provider) GetConnection(selection settings.ServerSelection, ipv6Supported bool) (
|
|
||||||
connection models.Connection, err error,
|
|
||||||
) {
|
|
||||||
defaults := utils.NewConnectionDefaults(443, 1194, 9929) //nolint:mnd
|
|
||||||
return utils.GetConnection(p.Name(),
|
|
||||||
p.storage, selection, defaults, ipv6Supported, p.connPicker)
|
|
||||||
}
|
|
||||||
@@ -1,126 +0,0 @@
|
|||||||
package ovpn
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"net/http"
|
|
||||||
"net/netip"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/golang/mock/gomock"
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
|
||||||
"github.com/qdm12/gluetun/internal/constants"
|
|
||||||
"github.com/qdm12/gluetun/internal/constants/providers"
|
|
||||||
"github.com/qdm12/gluetun/internal/constants/vpn"
|
|
||||||
"github.com/qdm12/gluetun/internal/models"
|
|
||||||
"github.com/qdm12/gluetun/internal/provider/common"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Test_Provider_GetConnection(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
const provider = providers.Ovpn
|
|
||||||
|
|
||||||
errTest := errors.New("test error")
|
|
||||||
|
|
||||||
testCases := map[string]struct {
|
|
||||||
filteredServers []models.Server
|
|
||||||
storageErr error
|
|
||||||
selection settings.ServerSelection
|
|
||||||
ipv6Supported bool
|
|
||||||
connection models.Connection
|
|
||||||
errWrapped error
|
|
||||||
errMessage string
|
|
||||||
}{
|
|
||||||
"error": {
|
|
||||||
storageErr: errTest,
|
|
||||||
errWrapped: errTest,
|
|
||||||
errMessage: "filtering servers: test error",
|
|
||||||
},
|
|
||||||
"default_openvpn_tcp_port": {
|
|
||||||
filteredServers: []models.Server{
|
|
||||||
{IPs: []netip.Addr{netip.AddrFrom4([4]byte{1, 1, 1, 1})}},
|
|
||||||
},
|
|
||||||
selection: settings.ServerSelection{
|
|
||||||
OpenVPN: settings.OpenVPNSelection{
|
|
||||||
Protocol: constants.TCP,
|
|
||||||
},
|
|
||||||
}.WithDefaults(provider),
|
|
||||||
connection: models.Connection{
|
|
||||||
Type: vpn.OpenVPN,
|
|
||||||
IP: netip.AddrFrom4([4]byte{1, 1, 1, 1}),
|
|
||||||
Port: 443,
|
|
||||||
Protocol: constants.TCP,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"default_openvpn_udp_port": {
|
|
||||||
filteredServers: []models.Server{
|
|
||||||
{IPs: []netip.Addr{netip.AddrFrom4([4]byte{1, 1, 1, 1})}},
|
|
||||||
},
|
|
||||||
selection: settings.ServerSelection{
|
|
||||||
OpenVPN: settings.OpenVPNSelection{
|
|
||||||
Protocol: constants.UDP,
|
|
||||||
},
|
|
||||||
}.WithDefaults(provider),
|
|
||||||
connection: models.Connection{
|
|
||||||
Type: vpn.OpenVPN,
|
|
||||||
IP: netip.AddrFrom4([4]byte{1, 1, 1, 1}),
|
|
||||||
Port: 1194,
|
|
||||||
Protocol: constants.UDP,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"default_wireguard_port": {
|
|
||||||
filteredServers: []models.Server{
|
|
||||||
{IPs: []netip.Addr{netip.AddrFrom4([4]byte{1, 1, 1, 1})}, WgPubKey: "x"},
|
|
||||||
},
|
|
||||||
selection: settings.ServerSelection{
|
|
||||||
VPN: vpn.Wireguard,
|
|
||||||
}.WithDefaults(provider),
|
|
||||||
connection: models.Connection{
|
|
||||||
Type: vpn.Wireguard,
|
|
||||||
IP: netip.AddrFrom4([4]byte{1, 1, 1, 1}),
|
|
||||||
Port: 9929,
|
|
||||||
Protocol: constants.UDP,
|
|
||||||
PubKey: "x",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"default_multihop_port": {
|
|
||||||
filteredServers: []models.Server{
|
|
||||||
{IPs: []netip.Addr{netip.AddrFrom4([4]byte{1, 1, 1, 1})}, WgPubKey: "x", PortsUDP: []uint16{30044}},
|
|
||||||
},
|
|
||||||
selection: settings.ServerSelection{
|
|
||||||
VPN: vpn.Wireguard,
|
|
||||||
}.WithDefaults(provider),
|
|
||||||
connection: models.Connection{
|
|
||||||
Type: vpn.Wireguard,
|
|
||||||
IP: netip.AddrFrom4([4]byte{1, 1, 1, 1}),
|
|
||||||
Port: 30044,
|
|
||||||
Protocol: constants.UDP,
|
|
||||||
PubKey: "x",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for name, testCase := range testCases {
|
|
||||||
t.Run(name, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
ctrl := gomock.NewController(t)
|
|
||||||
|
|
||||||
storage := common.NewMockStorage(ctrl)
|
|
||||||
storage.EXPECT().FilterServers(provider, testCase.selection).
|
|
||||||
Return(testCase.filteredServers, testCase.storageErr)
|
|
||||||
|
|
||||||
client := (*http.Client)(nil)
|
|
||||||
provider := New(storage, client)
|
|
||||||
|
|
||||||
connection, err := provider.GetConnection(testCase.selection, testCase.ipv6Supported)
|
|
||||||
|
|
||||||
assert.ErrorIs(t, err, testCase.errWrapped)
|
|
||||||
if testCase.errWrapped != nil {
|
|
||||||
assert.EqualError(t, err, testCase.errMessage)
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Equal(t, testCase.connection, connection)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
package ovpn
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
|
||||||
"github.com/qdm12/gluetun/internal/constants/openvpn"
|
|
||||||
"github.com/qdm12/gluetun/internal/models"
|
|
||||||
"github.com/qdm12/gluetun/internal/provider/utils"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (p *Provider) OpenVPNConfig(connection models.Connection,
|
|
||||||
settings settings.OpenVPN, ipv6Supported bool,
|
|
||||||
) (lines []string) {
|
|
||||||
providerSettings := utils.OpenVPNProviderSettings{
|
|
||||||
AuthUserPass: true,
|
|
||||||
RemoteCertTLS: true,
|
|
||||||
Ciphers: []string{
|
|
||||||
openvpn.AES256gcm,
|
|
||||||
openvpn.AES256cbc,
|
|
||||||
openvpn.AES128gcm,
|
|
||||||
openvpn.Chacha20Poly1305,
|
|
||||||
},
|
|
||||||
CAs: []string{
|
|
||||||
"MIIEfTCCA2WgAwIBAgIJAK2aIWqpLj1/MA0GCSqGSIb3DQEBBQUAMIGFMQswCQYDVQQGEwJTRTESMBAGA1UECBMJU3RvY2tob2xtMRIwEAYDVQQHEwlTdG9ja2hvbG0xHDAaBgNVBAsTE0Zpcm1hIERhdmlkIFdpYmVyZ2gxEzARBgNVBAMTCm92cG4uc2UgY2ExGzAZBgkqhkiG9w0BCQEWDGluZm9Ab3Zwbi5zZTAeFw0xNDA4MTcxODIxMjlaFw0zNDA4MTIxODIxMjlaMIGFMQswCQYDVQQGEwJTRTESMBAGA1UECBMJU3RvY2tob2xtMRIwEAYDVQQHEwlTdG9ja2hvbG0xHDAaBgNVBAsTE0Zpcm1hIERhdmlkIFdpYmVyZ2gxEzARBgNVBAMTCm92cG4uc2UgY2ExGzAZBgkqhkiG9w0BCQEWDGluZm9Ab3Zwbi5zZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMR+aP4GTuZwurZuOA2NYzMfqKyZi/TJcLEPlGTB/b4CWA9bTd8f0pHPrDAZsXIEayxxB58BIFNDNiybnbO15JN/QwlsqmA+aZX6mCSkScs/rRwasM6LDo8iGx+KmYEqAgzziONGbCMnlO+OaarXte7LhZ9X6Z/bryu4xq/i1v3raak13kXsrogtu4iDzxqJE/QhbNOi0yhCdlm5RYQjmlKGdPB9pNTgcakVI4HcngRYMzBlrGin0YkvWCdpx5FrDNeld7BSWrJMNYyvd+buaid0Fu1T9/P/Srj/8AiabKoaDyiGFbZdTnGfK+04lWRvwAmvazpqbUt5Omw634jJDuMCAwEAAaOB7TCB6jAdBgNVHQ4EFgQUEvJcHHcTiDtu7bAyZw+xaqg+xdIwgboGA1UdIwSBsjCBr4AUEvJcHHcTiDtu7bAyZw+xaqg+xdKhgYukgYgwgYUxCzAJBgNVBAYTAlNFMRIwEAYDVQQIEwlTdG9ja2hvbG0xEjAQBgNVBAcTCVN0b2NraG9sbTEcMBoGA1UECxMTRmlybWEgRGF2aWQgV2liZXJnaDETMBEGA1UEAxMKb3Zwbi5zZSBjYTEbMBkGCSqGSIb3DQEJARYMaW5mb0BvdnBuLnNlggkArZohaqkuPX8wDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAJmID6OyBJbV7ayPPgquojF+FICuDdOfGVKP828cyISxcbVA04VpD0QLYVb0k9pFUx0NbgX2SvRTiFhP7LcyS1HV9s+XLCb2WItPPsrdRTwtqU2n3TlCEzWA3WOcOCtT6JSkv1eelmx1JnP0gYJrDvDvRYBFctwWhtE0bineSQkZwN6980zkknADLAiHpeZSu/AMx7CGTwA6SmoFvpNBmHXDcfe/9ZqbbYfUfyPNe+0JbMrcv1elKi+6wlEkHFaEBphiZwGEbOX1CjUMcQFgW/cIp3n50Eiyx6ktuqimhyb59P4Nw8gqH452tTtE4MM/brA5y0Q0WFBRBojfZIbGWWQ==", //nolint:lll
|
|
||||||
},
|
|
||||||
TLSAuth: "81782767e4d59c4464cc5d1896f1cf6015017d53ac62e2e3b94b889e00b2c69ddc01944fe1c6d895b4d80540502eb71910b8d785c9efa9e3182343532adffe1cfbb7bb6eae39c502da2748edf0fb89b8a20b0a1085cc1f06135037881bc0c4ad8f2c0f4f72d2ab466fb54af3d8264c5fddeb0f21aa0ca41863678f5fc4c44de4ca0926b36dfddc42c6f2fabd1694bdc8215b2d223b9c21dc6734c2c778093187afb8c33403b228b9af68b540c284f6d183bcc88bd41d47bd717996e499ce1cbbfa768a9723c19c58314c4d19cfed82e543ee92e73d38ad26d4fbec231c0f9f3b30773a5c87792e9bc7c34e8d7611002ebedd044e48a0f1f96527bfdcc940aa09", //nolint:lll
|
|
||||||
KeyDirection: "1",
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.HasSuffix(connection.Hostname, "singapore.ovpn.com") {
|
|
||||||
providerSettings.TLSCrypt = providerSettings.TLSAuth
|
|
||||||
providerSettings.TLSAuth = ""
|
|
||||||
providerSettings.KeyDirection = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
return utils.OpenVPNConfig(providerSettings, connection, settings, ipv6Supported)
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
package ovpn
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/constants/providers"
|
|
||||||
"github.com/qdm12/gluetun/internal/provider/common"
|
|
||||||
"github.com/qdm12/gluetun/internal/provider/ovpn/updater"
|
|
||||||
"github.com/qdm12/gluetun/internal/provider/utils"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Provider struct {
|
|
||||||
storage common.Storage
|
|
||||||
connPicker *utils.ConnectionPicker
|
|
||||||
common.Fetcher
|
|
||||||
}
|
|
||||||
|
|
||||||
func New(storage common.Storage, client *http.Client) *Provider {
|
|
||||||
return &Provider{
|
|
||||||
storage: storage,
|
|
||||||
connPicker: utils.NewConnectionPicker(),
|
|
||||||
Fetcher: updater.New(client),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Provider) Name() string {
|
|
||||||
return providers.Ovpn
|
|
||||||
}
|
|
||||||
@@ -1,153 +0,0 @@
|
|||||||
package updater
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"net/netip"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type apiData struct {
|
|
||||||
Success bool `json:"success"`
|
|
||||||
DataCenters []apiDataCenter `json:"datacenters"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type apiDataCenter struct {
|
|
||||||
City string `json:"city"`
|
|
||||||
CountryName string `json:"country_name"`
|
|
||||||
Servers []apiServer `json:"servers"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type apiServer struct {
|
|
||||||
IP netip.Addr `json:"ip"`
|
|
||||||
Ptr string `json:"ptr"` // hostname
|
|
||||||
Online bool `json:"online"`
|
|
||||||
// PublicKey is for the Standard Shared Entry Point
|
|
||||||
PublicKey string `json:"public_key"`
|
|
||||||
// PublicKeyIPv4 is for the Public / Dedicated IP Entry Point
|
|
||||||
PublicKeyIPv4 string `json:"public_key_ipv4"`
|
|
||||||
WireguardPorts []uint16 `json:"wireguard_ports"`
|
|
||||||
MultiHopOpenvpnPort uint16 `json:"multihop_openvpn_port"`
|
|
||||||
MultiHopWireguardPort uint16 `json:"multihop_wireguard_port"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func fetchAPI(ctx context.Context, client *http.Client) (
|
|
||||||
data apiData, err error,
|
|
||||||
) {
|
|
||||||
const url = "https://www.ovpn.com/v2/api/client/entry"
|
|
||||||
|
|
||||||
request, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
|
||||||
if err != nil {
|
|
||||||
return data, err
|
|
||||||
}
|
|
||||||
|
|
||||||
response, err := client.Do(request)
|
|
||||||
if err != nil {
|
|
||||||
return data, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if response.StatusCode != http.StatusOK {
|
|
||||||
_ = response.Body.Close()
|
|
||||||
return data, fmt.Errorf("HTTP response status code is not OK: %d %s",
|
|
||||||
response.StatusCode, response.Status)
|
|
||||||
}
|
|
||||||
|
|
||||||
decoder := json.NewDecoder(response.Body)
|
|
||||||
err = decoder.Decode(&data)
|
|
||||||
if err != nil {
|
|
||||||
_ = response.Body.Close()
|
|
||||||
return data, fmt.Errorf("decoding response body: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = response.Body.Close()
|
|
||||||
if err != nil {
|
|
||||||
return data, fmt.Errorf("closing response body: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return data, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *apiDataCenter) validate() (err error) {
|
|
||||||
conditionalErrors := []conditionalError{
|
|
||||||
{err: "city is not set", condition: a.City == ""},
|
|
||||||
{err: "country name is not set", condition: a.CountryName == ""},
|
|
||||||
{err: "servers array is not set", condition: len(a.Servers) == 0},
|
|
||||||
}
|
|
||||||
err = collectErrors(conditionalErrors)
|
|
||||||
if err != nil {
|
|
||||||
var dataCenterSetFields []string
|
|
||||||
if a.CountryName != "" {
|
|
||||||
dataCenterSetFields = append(dataCenterSetFields, a.CountryName)
|
|
||||||
}
|
|
||||||
if a.City != "" {
|
|
||||||
dataCenterSetFields = append(dataCenterSetFields, a.City)
|
|
||||||
}
|
|
||||||
if len(dataCenterSetFields) == 0 {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return fmt.Errorf("data center %s: %w",
|
|
||||||
strings.Join(dataCenterSetFields, ", "), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, server := range a.Servers {
|
|
||||||
err = server.validate()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("datacenter %s, %s: server %d of %d: %w",
|
|
||||||
a.CountryName, a.City, i+1, len(a.Servers), err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *apiServer) validate() (err error) {
|
|
||||||
const defaultWireguardPort = 9929
|
|
||||||
conditionalErrors := []conditionalError{
|
|
||||||
{err: "ip address is not set", condition: !a.IP.IsValid()},
|
|
||||||
{err: "hostname field is not set", condition: a.Ptr == ""},
|
|
||||||
{err: "public key field is not set", condition: a.PublicKey == ""},
|
|
||||||
{err: "public key IPv4 field is not set", condition: a.PublicKeyIPv4 == ""},
|
|
||||||
{err: "wireguard ports array is not set", condition: len(a.WireguardPorts) == 0},
|
|
||||||
{
|
|
||||||
err: "wireguard port is not the default 9929",
|
|
||||||
condition: len(a.WireguardPorts) != 1 || a.WireguardPorts[0] != defaultWireguardPort,
|
|
||||||
},
|
|
||||||
{err: "multihop OpenVPN port is not set", condition: a.MultiHopOpenvpnPort == 0},
|
|
||||||
{err: "multihop WireGuard port is not set", condition: a.MultiHopWireguardPort == 0},
|
|
||||||
}
|
|
||||||
err = collectErrors(conditionalErrors)
|
|
||||||
switch {
|
|
||||||
case err == nil:
|
|
||||||
return nil
|
|
||||||
case a.Ptr != "":
|
|
||||||
return fmt.Errorf("server %s: %w", a.Ptr, err)
|
|
||||||
case a.IP.IsValid():
|
|
||||||
return fmt.Errorf("server %s: %w", a.IP.String(), err)
|
|
||||||
default:
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type conditionalError struct {
|
|
||||||
err string
|
|
||||||
condition bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func collectErrors(conditionalErrors []conditionalError) (err error) {
|
|
||||||
errs := make([]string, 0, len(conditionalErrors))
|
|
||||||
for _, conditionalError := range conditionalErrors {
|
|
||||||
if !conditionalError.condition {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
errs = append(errs, conditionalError.err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(errs) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return errors.New(strings.Join(errs, "; "))
|
|
||||||
}
|
|
||||||
@@ -1,118 +0,0 @@
|
|||||||
package updater
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"net/netip"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Test_fetchAPI(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
testCases := map[string]struct {
|
|
||||||
responseStatus int
|
|
||||||
responseBody io.ReadCloser
|
|
||||||
data apiData
|
|
||||||
err error
|
|
||||||
}{
|
|
||||||
"http response status not ok": {
|
|
||||||
responseStatus: http.StatusNoContent,
|
|
||||||
err: errors.New("HTTP response status code is not OK: 204 No Content"),
|
|
||||||
},
|
|
||||||
"nil body": {
|
|
||||||
responseStatus: http.StatusOK,
|
|
||||||
err: errors.New("decoding response body: EOF"),
|
|
||||||
},
|
|
||||||
"no server": {
|
|
||||||
responseStatus: http.StatusOK,
|
|
||||||
responseBody: io.NopCloser(strings.NewReader(`{}`)),
|
|
||||||
},
|
|
||||||
"success": {
|
|
||||||
responseStatus: http.StatusOK,
|
|
||||||
responseBody: io.NopCloser(strings.NewReader(`{
|
|
||||||
"success": true,
|
|
||||||
"datacenters": [
|
|
||||||
{
|
|
||||||
"slug": "vienna",
|
|
||||||
"city": "Vienna",
|
|
||||||
"country": "AT",
|
|
||||||
"country_name": "Austria",
|
|
||||||
"pools": [
|
|
||||||
"pool-1.prd.at.vienna.ovpn.com"
|
|
||||||
],
|
|
||||||
"ping_address": "37.120.212.227",
|
|
||||||
"servers": [
|
|
||||||
{
|
|
||||||
"ip": "37.120.212.227",
|
|
||||||
"ptr": "vpn44.prd.vienna.ovpn.com",
|
|
||||||
"name": "VPN44 - Vienna",
|
|
||||||
"online": true,
|
|
||||||
"load": 8,
|
|
||||||
"public_key": "r83LIc0Q2F8s3dY9x5y17Yz8wTADJc7giW1t5eSmoXc=",
|
|
||||||
"public_key_ipv4": "wFbSRyjSXBmkjJodlqz7DoYn3WNDPYFUIXyIUS2QU2A=",
|
|
||||||
"wireguard_ports": [
|
|
||||||
9929
|
|
||||||
],
|
|
||||||
"multihop_openvpn_port": 20044,
|
|
||||||
"multihop_wireguard_port": 30044
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}`)),
|
|
||||||
data: apiData{
|
|
||||||
Success: true,
|
|
||||||
DataCenters: []apiDataCenter{
|
|
||||||
{CountryName: "Austria", City: "Vienna", Servers: []apiServer{
|
|
||||||
{
|
|
||||||
IP: netip.MustParseAddr("37.120.212.227"),
|
|
||||||
Ptr: "vpn44.prd.vienna.ovpn.com",
|
|
||||||
Online: true,
|
|
||||||
PublicKey: "r83LIc0Q2F8s3dY9x5y17Yz8wTADJc7giW1t5eSmoXc=",
|
|
||||||
PublicKeyIPv4: "wFbSRyjSXBmkjJodlqz7DoYn3WNDPYFUIXyIUS2QU2A=",
|
|
||||||
WireguardPorts: []uint16{9929},
|
|
||||||
MultiHopOpenvpnPort: 20044,
|
|
||||||
MultiHopWireguardPort: 30044,
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for name, testCase := range testCases {
|
|
||||||
t.Run(name, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
client := &http.Client{
|
|
||||||
Transport: roundTripFunc(func(r *http.Request) (*http.Response, error) {
|
|
||||||
assert.Equal(t, http.MethodGet, r.Method)
|
|
||||||
assert.Equal(t, r.URL.String(), "https://www.ovpn.com/v2/api/client/entry")
|
|
||||||
return &http.Response{
|
|
||||||
StatusCode: testCase.responseStatus,
|
|
||||||
Status: http.StatusText(testCase.responseStatus),
|
|
||||||
Body: testCase.responseBody,
|
|
||||||
}, nil
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := fetchAPI(ctx, client)
|
|
||||||
|
|
||||||
assert.Equal(t, testCase.data, data)
|
|
||||||
if testCase.err != nil {
|
|
||||||
require.Error(t, err)
|
|
||||||
assert.Equal(t, testCase.err.Error(), err.Error())
|
|
||||||
} else {
|
|
||||||
assert.NoError(t, err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
package updater
|
|
||||||
|
|
||||||
import "net/http"
|
|
||||||
|
|
||||||
type roundTripFunc func(r *http.Request) (*http.Response, error)
|
|
||||||
|
|
||||||
func (f roundTripFunc) RoundTrip(r *http.Request) (*http.Response, error) {
|
|
||||||
return f(r)
|
|
||||||
}
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
package updater
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"net/netip"
|
|
||||||
"sort"
|
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/constants/vpn"
|
|
||||||
"github.com/qdm12/gluetun/internal/models"
|
|
||||||
"github.com/qdm12/gluetun/internal/provider/common"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (u *Updater) FetchServers(ctx context.Context, minServers int) (
|
|
||||||
servers []models.Server, err error,
|
|
||||||
) {
|
|
||||||
data, err := fetchAPI(ctx, u.client)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("fetching API: %w", err)
|
|
||||||
} else if !data.Success {
|
|
||||||
return nil, errors.New("response success field is false")
|
|
||||||
}
|
|
||||||
|
|
||||||
for dataCenterIndex, dataCenter := range data.DataCenters {
|
|
||||||
err = dataCenter.validate()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("validating data center %d of %d: %w",
|
|
||||||
dataCenterIndex+1, len(data.DataCenters), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, apiServer := range dataCenter.Servers {
|
|
||||||
if !apiServer.Online {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
baseServer := models.Server{
|
|
||||||
Country: dataCenter.CountryName,
|
|
||||||
City: dataCenter.City,
|
|
||||||
Hostname: apiServer.Ptr,
|
|
||||||
IPs: []netip.Addr{apiServer.IP},
|
|
||||||
}
|
|
||||||
openVPNServer := baseServer
|
|
||||||
openVPNServer.VPN = vpn.OpenVPN
|
|
||||||
openVPNServer.TCP = true
|
|
||||||
openVPNServer.UDP = true
|
|
||||||
multiHopOpenVPNServer := openVPNServer
|
|
||||||
multiHopOpenVPNServer.MultiHop = true
|
|
||||||
multiHopOpenVPNServer.PortsTCP = []uint16{apiServer.MultiHopOpenvpnPort}
|
|
||||||
multiHopOpenVPNServer.PortsUDP = []uint16{apiServer.MultiHopOpenvpnPort}
|
|
||||||
servers = append(servers, openVPNServer, multiHopOpenVPNServer)
|
|
||||||
|
|
||||||
wireguardServer := baseServer
|
|
||||||
wireguardServer.VPN = vpn.Wireguard
|
|
||||||
wireguardServer.WgPubKey = apiServer.PublicKey
|
|
||||||
multiHopWireguardServer := wireguardServer
|
|
||||||
multiHopWireguardServer.MultiHop = true
|
|
||||||
multiHopWireguardServer.PortsUDP = []uint16{apiServer.MultiHopWireguardPort}
|
|
||||||
dedicatedWireguardServer := wireguardServer
|
|
||||||
dedicatedWireguardServer.WgPubKey = apiServer.PublicKeyIPv4
|
|
||||||
dedicatedWireguardServer.Dedicated = true
|
|
||||||
dedicatedMultiHopWireguardServer := multiHopWireguardServer
|
|
||||||
dedicatedMultiHopWireguardServer.WgPubKey = apiServer.PublicKeyIPv4
|
|
||||||
dedicatedMultiHopWireguardServer.Dedicated = true
|
|
||||||
servers = append(servers,
|
|
||||||
wireguardServer,
|
|
||||||
multiHopWireguardServer,
|
|
||||||
dedicatedWireguardServer,
|
|
||||||
dedicatedMultiHopWireguardServer,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(servers) < minServers {
|
|
||||||
return nil, fmt.Errorf("%w: %d and expected at least %d",
|
|
||||||
common.ErrNotEnoughServers, len(servers), minServers)
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Sort(models.SortableServers(servers))
|
|
||||||
|
|
||||||
return servers, nil
|
|
||||||
}
|
|
||||||
@@ -1,228 +0,0 @@
|
|||||||
package updater
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"net/netip"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/constants/vpn"
|
|
||||||
"github.com/qdm12/gluetun/internal/models"
|
|
||||||
"github.com/qdm12/gluetun/internal/provider/common"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Test_Updater_FetchServers(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
testCases := map[string]struct {
|
|
||||||
// Inputs
|
|
||||||
minServers int
|
|
||||||
|
|
||||||
// From API
|
|
||||||
responseStatus int
|
|
||||||
responseBody string
|
|
||||||
|
|
||||||
// Output
|
|
||||||
servers []models.Server
|
|
||||||
errWrapped error
|
|
||||||
errMessage string
|
|
||||||
}{
|
|
||||||
"http_response_error": {
|
|
||||||
responseStatus: http.StatusNoContent,
|
|
||||||
errMessage: "fetching API: HTTP response status code is not OK: 204 No Content",
|
|
||||||
},
|
|
||||||
"success_field_false": {
|
|
||||||
responseStatus: http.StatusOK,
|
|
||||||
responseBody: `{"success": false}`,
|
|
||||||
errMessage: "response success field is false",
|
|
||||||
},
|
|
||||||
"validation_failed": {
|
|
||||||
responseStatus: http.StatusOK,
|
|
||||||
responseBody: `{
|
|
||||||
"success": true,
|
|
||||||
"datacenters": [
|
|
||||||
{
|
|
||||||
"city": "Vienna",
|
|
||||||
"servers": [
|
|
||||||
{}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}`,
|
|
||||||
errMessage: "validating data center 1 of 1: data center Vienna: country name is not set",
|
|
||||||
},
|
|
||||||
"not_enough_servers": {
|
|
||||||
minServers: 7,
|
|
||||||
responseStatus: http.StatusOK,
|
|
||||||
responseBody: `{
|
|
||||||
"success": true,
|
|
||||||
"datacenters": [
|
|
||||||
{
|
|
||||||
"city": "Vienna",
|
|
||||||
"country_name": "Austria",
|
|
||||||
"servers": [
|
|
||||||
{
|
|
||||||
"ip": "37.120.212.227",
|
|
||||||
"ptr": "vpn44.prd.vienna.ovpn.com",
|
|
||||||
"online": true,
|
|
||||||
"public_key": "r83LIc0Q2F8s3dY9x5y17Yz8wTADJc7giW1t5eSmoXc=",
|
|
||||||
"public_key_ipv4": "wFbSRyjSXBmkjJodlqz7DoYn3WNDPYFUIXyIUS2QU2A=",
|
|
||||||
"wireguard_ports": [9929],
|
|
||||||
"multihop_openvpn_port": 20044,
|
|
||||||
"multihop_wireguard_port": 30044
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}`,
|
|
||||||
errWrapped: common.ErrNotEnoughServers,
|
|
||||||
// Wireguard + dedicated Wireguard + Wireguard multi-hop +
|
|
||||||
// dedicated Wireguard multi-hop + OpenVPN + OpenVPN multi-hop
|
|
||||||
errMessage: "not enough servers found: 6 and expected at least 7",
|
|
||||||
},
|
|
||||||
"success": {
|
|
||||||
minServers: 4,
|
|
||||||
responseBody: `{
|
|
||||||
"success": true,
|
|
||||||
"datacenters": [
|
|
||||||
{
|
|
||||||
"slug": "vienna",
|
|
||||||
"city": "Vienna",
|
|
||||||
"country": "AT",
|
|
||||||
"country_name": "Austria",
|
|
||||||
"pools": [
|
|
||||||
"pool-1.prd.at.vienna.ovpn.com"
|
|
||||||
],
|
|
||||||
"ping_address": "37.120.212.227",
|
|
||||||
"servers": [
|
|
||||||
{
|
|
||||||
"ip": "37.120.212.227",
|
|
||||||
"ptr": "vpn44.prd.vienna.ovpn.com",
|
|
||||||
"name": "VPN44 - Vienna",
|
|
||||||
"online": true,
|
|
||||||
"load": 8,
|
|
||||||
"public_key": "r83LIc0Q2F8s3dY9x5y17Yz8wTADJc7giW1t5eSmoXc=",
|
|
||||||
"public_key_ipv4": "wFbSRyjSXBmkjJodlqz7DoYn3WNDPYFUIXyIUS2QU2A=",
|
|
||||||
"wireguard_ports": [
|
|
||||||
9929
|
|
||||||
],
|
|
||||||
"multihop_openvpn_port": 20044,
|
|
||||||
"multihop_wireguard_port": 30044
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"ip": "37.120.212.228",
|
|
||||||
"ptr": "vpn45.prd.vienna.ovpn.com",
|
|
||||||
"online": false,
|
|
||||||
"public_key": "r93LIc0Q2F8s3dY9x5y17Yz8wTADJc7giW1t5eSmoXc=",
|
|
||||||
"public_key_ipv4": "wGbSRyjSXBmkjJodlqz7DoYn3WNDPYFUIXyIUS2QU2A=",
|
|
||||||
"wireguard_ports": [9929],
|
|
||||||
"multihop_openvpn_port": 20045,
|
|
||||||
"multihop_wireguard_port": 30045
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}`,
|
|
||||||
responseStatus: http.StatusOK,
|
|
||||||
servers: []models.Server{
|
|
||||||
{
|
|
||||||
Country: "Austria",
|
|
||||||
City: "Vienna",
|
|
||||||
Hostname: "vpn44.prd.vienna.ovpn.com",
|
|
||||||
IPs: []netip.Addr{netip.MustParseAddr("37.120.212.227")},
|
|
||||||
VPN: vpn.OpenVPN,
|
|
||||||
UDP: true,
|
|
||||||
TCP: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Country: "Austria",
|
|
||||||
City: "Vienna",
|
|
||||||
Hostname: "vpn44.prd.vienna.ovpn.com",
|
|
||||||
IPs: []netip.Addr{netip.MustParseAddr("37.120.212.227")},
|
|
||||||
VPN: vpn.OpenVPN,
|
|
||||||
UDP: true,
|
|
||||||
TCP: true,
|
|
||||||
MultiHop: true,
|
|
||||||
PortsTCP: []uint16{20044},
|
|
||||||
PortsUDP: []uint16{20044},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Country: "Austria",
|
|
||||||
City: "Vienna",
|
|
||||||
Hostname: "vpn44.prd.vienna.ovpn.com",
|
|
||||||
IPs: []netip.Addr{netip.MustParseAddr("37.120.212.227")},
|
|
||||||
VPN: vpn.Wireguard,
|
|
||||||
WgPubKey: "r83LIc0Q2F8s3dY9x5y17Yz8wTADJc7giW1t5eSmoXc=",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Country: "Austria",
|
|
||||||
City: "Vienna",
|
|
||||||
Hostname: "vpn44.prd.vienna.ovpn.com",
|
|
||||||
IPs: []netip.Addr{netip.MustParseAddr("37.120.212.227")},
|
|
||||||
VPN: vpn.Wireguard,
|
|
||||||
WgPubKey: "r83LIc0Q2F8s3dY9x5y17Yz8wTADJc7giW1t5eSmoXc=",
|
|
||||||
MultiHop: true,
|
|
||||||
PortsUDP: []uint16{30044},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Country: "Austria",
|
|
||||||
City: "Vienna",
|
|
||||||
Hostname: "vpn44.prd.vienna.ovpn.com",
|
|
||||||
IPs: []netip.Addr{netip.MustParseAddr("37.120.212.227")},
|
|
||||||
VPN: vpn.Wireguard,
|
|
||||||
WgPubKey: "wFbSRyjSXBmkjJodlqz7DoYn3WNDPYFUIXyIUS2QU2A=",
|
|
||||||
Dedicated: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Country: "Austria",
|
|
||||||
City: "Vienna",
|
|
||||||
Hostname: "vpn44.prd.vienna.ovpn.com",
|
|
||||||
IPs: []netip.Addr{netip.MustParseAddr("37.120.212.227")},
|
|
||||||
VPN: vpn.Wireguard,
|
|
||||||
WgPubKey: "wFbSRyjSXBmkjJodlqz7DoYn3WNDPYFUIXyIUS2QU2A=",
|
|
||||||
MultiHop: true,
|
|
||||||
Dedicated: true,
|
|
||||||
PortsUDP: []uint16{30044},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for name, testCase := range testCases {
|
|
||||||
t.Run(name, func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
client := &http.Client{
|
|
||||||
Transport: roundTripFunc(func(r *http.Request) (*http.Response, error) {
|
|
||||||
assert.Equal(t, http.MethodGet, r.Method)
|
|
||||||
assert.Equal(t, r.URL.String(), "https://www.ovpn.com/v2/api/client/entry")
|
|
||||||
return &http.Response{
|
|
||||||
StatusCode: testCase.responseStatus,
|
|
||||||
Status: http.StatusText(testCase.responseStatus),
|
|
||||||
Body: io.NopCloser(strings.NewReader(testCase.responseBody)),
|
|
||||||
}, nil
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
|
|
||||||
updater := &Updater{
|
|
||||||
client: client,
|
|
||||||
}
|
|
||||||
|
|
||||||
servers, err := updater.FetchServers(ctx, testCase.minServers)
|
|
||||||
|
|
||||||
assert.Equal(t, testCase.servers, servers)
|
|
||||||
if testCase.errMessage == "" {
|
|
||||||
assert.NoError(t, err)
|
|
||||||
} else {
|
|
||||||
assert.Contains(t, err.Error(), testCase.errMessage)
|
|
||||||
}
|
|
||||||
if testCase.errWrapped != nil {
|
|
||||||
assert.ErrorIs(t, err, testCase.errWrapped)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
package updater
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Updater struct {
|
|
||||||
client *http.Client
|
|
||||||
}
|
|
||||||
|
|
||||||
func New(client *http.Client) *Updater {
|
|
||||||
return &Updater{
|
|
||||||
client: client,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -20,7 +20,6 @@ import (
|
|||||||
"github.com/qdm12/gluetun/internal/provider/ivpn"
|
"github.com/qdm12/gluetun/internal/provider/ivpn"
|
||||||
"github.com/qdm12/gluetun/internal/provider/mullvad"
|
"github.com/qdm12/gluetun/internal/provider/mullvad"
|
||||||
"github.com/qdm12/gluetun/internal/provider/nordvpn"
|
"github.com/qdm12/gluetun/internal/provider/nordvpn"
|
||||||
"github.com/qdm12/gluetun/internal/provider/ovpn"
|
|
||||||
"github.com/qdm12/gluetun/internal/provider/perfectprivacy"
|
"github.com/qdm12/gluetun/internal/provider/perfectprivacy"
|
||||||
"github.com/qdm12/gluetun/internal/provider/privado"
|
"github.com/qdm12/gluetun/internal/provider/privado"
|
||||||
"github.com/qdm12/gluetun/internal/provider/privateinternetaccess"
|
"github.com/qdm12/gluetun/internal/provider/privateinternetaccess"
|
||||||
@@ -68,7 +67,6 @@ func NewProviders(storage Storage, timeNow func() time.Time,
|
|||||||
providers.Ivpn: ivpn.New(storage, client, updaterWarner, parallelResolver),
|
providers.Ivpn: ivpn.New(storage, client, updaterWarner, parallelResolver),
|
||||||
providers.Mullvad: mullvad.New(storage, client),
|
providers.Mullvad: mullvad.New(storage, client),
|
||||||
providers.Nordvpn: nordvpn.New(storage, client, updaterWarner),
|
providers.Nordvpn: nordvpn.New(storage, client, updaterWarner),
|
||||||
providers.Ovpn: ovpn.New(storage, client),
|
|
||||||
providers.Perfectprivacy: perfectprivacy.New(storage, unzipper, updaterWarner),
|
providers.Perfectprivacy: perfectprivacy.New(storage, unzipper, updaterWarner),
|
||||||
providers.Privado: privado.New(storage, client, updaterWarner),
|
providers.Privado: privado.New(storage, client, updaterWarner),
|
||||||
providers.PrivateInternetAccess: privateinternetaccess.New(storage, timeNow, client),
|
providers.PrivateInternetAccess: privateinternetaccess.New(storage, timeNow, client),
|
||||||
|
|||||||
@@ -52,6 +52,8 @@ func GetConnection(provider string,
|
|||||||
})
|
})
|
||||||
|
|
||||||
protocol := getProtocol(selection)
|
protocol := getProtocol(selection)
|
||||||
|
port := getPort(selection, defaults.OpenVPNTCPPort,
|
||||||
|
defaults.OpenVPNUDPPort, defaults.WireguardPort)
|
||||||
|
|
||||||
connections := make([]models.Connection, 0, len(servers))
|
connections := make([]models.Connection, 0, len(servers))
|
||||||
for _, server := range servers {
|
for _, server := range servers {
|
||||||
@@ -67,9 +69,6 @@ func GetConnection(provider string,
|
|||||||
hostname = server.OvpnX509
|
hostname = server.OvpnX509
|
||||||
}
|
}
|
||||||
|
|
||||||
port := getPort(selection, server, defaults.OpenVPNTCPPort,
|
|
||||||
defaults.OpenVPNUDPPort, defaults.WireguardPort)
|
|
||||||
|
|
||||||
connection := models.Connection{
|
connection := models.Connection{
|
||||||
Type: selection.VPN,
|
Type: selection.VPN,
|
||||||
IP: ip,
|
IP: ip,
|
||||||
|
|||||||
@@ -6,44 +6,29 @@ import (
|
|||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||||
"github.com/qdm12/gluetun/internal/constants"
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
"github.com/qdm12/gluetun/internal/constants/vpn"
|
"github.com/qdm12/gluetun/internal/constants/vpn"
|
||||||
"github.com/qdm12/gluetun/internal/models"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func getPort(selection settings.ServerSelection, server models.Server,
|
func getPort(selection settings.ServerSelection,
|
||||||
defaultOpenVPNTCP, defaultOpenVPNUDP, defaultWireguard uint16,
|
defaultOpenVPNTCP, defaultOpenVPNUDP, defaultWireguard uint16,
|
||||||
) (port uint16) {
|
) (port uint16) {
|
||||||
switch selection.VPN {
|
switch selection.VPN {
|
||||||
case vpn.Wireguard:
|
case vpn.Wireguard:
|
||||||
customPort := *selection.Wireguard.EndpointPort
|
customPort := *selection.Wireguard.EndpointPort
|
||||||
if customPort > 0 {
|
if customPort > 0 {
|
||||||
// Note: servers filtering ensures the custom port is within the
|
|
||||||
// server ports defined if any is set.
|
|
||||||
return customPort
|
return customPort
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(server.PortsUDP) > 0 {
|
|
||||||
defaultWireguard = server.PortsUDP[0]
|
|
||||||
}
|
|
||||||
checkDefined("Wireguard", defaultWireguard)
|
checkDefined("Wireguard", defaultWireguard)
|
||||||
return defaultWireguard
|
return defaultWireguard
|
||||||
default: // OpenVPN
|
default: // OpenVPN
|
||||||
customPort := *selection.OpenVPN.CustomPort
|
customPort := *selection.OpenVPN.CustomPort
|
||||||
if customPort > 0 {
|
if customPort > 0 {
|
||||||
// Note: servers filtering ensures the custom port is within the
|
|
||||||
// server ports defined if any is set.
|
|
||||||
return customPort
|
return customPort
|
||||||
}
|
}
|
||||||
if selection.OpenVPN.Protocol == constants.TCP {
|
if selection.OpenVPN.Protocol == constants.TCP {
|
||||||
if len(server.PortsTCP) > 0 {
|
|
||||||
defaultOpenVPNTCP = server.PortsTCP[0]
|
|
||||||
}
|
|
||||||
checkDefined("OpenVPN TCP", defaultOpenVPNTCP)
|
checkDefined("OpenVPN TCP", defaultOpenVPNTCP)
|
||||||
return defaultOpenVPNTCP
|
return defaultOpenVPNTCP
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(server.PortsUDP) > 0 {
|
|
||||||
defaultOpenVPNUDP = server.PortsUDP[0]
|
|
||||||
}
|
|
||||||
checkDefined("OpenVPN UDP", defaultOpenVPNUDP)
|
checkDefined("OpenVPN UDP", defaultOpenVPNUDP)
|
||||||
return defaultOpenVPNUDP
|
return defaultOpenVPNUDP
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||||
"github.com/qdm12/gluetun/internal/constants"
|
"github.com/qdm12/gluetun/internal/constants"
|
||||||
"github.com/qdm12/gluetun/internal/constants/vpn"
|
"github.com/qdm12/gluetun/internal/constants/vpn"
|
||||||
"github.com/qdm12/gluetun/internal/models"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -23,7 +22,6 @@ func Test_GetPort(t *testing.T) {
|
|||||||
|
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
selection settings.ServerSelection
|
selection settings.ServerSelection
|
||||||
server models.Server
|
|
||||||
defaultOpenVPNTCP uint16
|
defaultOpenVPNTCP uint16
|
||||||
defaultOpenVPNUDP uint16
|
defaultOpenVPNUDP uint16
|
||||||
defaultWireguard uint16
|
defaultWireguard uint16
|
||||||
@@ -50,20 +48,6 @@ func Test_GetPort(t *testing.T) {
|
|||||||
defaultWireguard: defaultWireguard,
|
defaultWireguard: defaultWireguard,
|
||||||
port: defaultOpenVPNUDP,
|
port: defaultOpenVPNUDP,
|
||||||
},
|
},
|
||||||
"OpenVPN_server_port_udp": {
|
|
||||||
selection: settings.ServerSelection{
|
|
||||||
VPN: vpn.OpenVPN,
|
|
||||||
OpenVPN: settings.OpenVPNSelection{
|
|
||||||
CustomPort: uint16Ptr(0),
|
|
||||||
Protocol: constants.UDP,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
server: models.Server{
|
|
||||||
PortsUDP: []uint16{1234},
|
|
||||||
},
|
|
||||||
defaultOpenVPNUDP: defaultOpenVPNUDP,
|
|
||||||
port: 1234,
|
|
||||||
},
|
|
||||||
"OpenVPN UDP no default port defined": {
|
"OpenVPN UDP no default port defined": {
|
||||||
selection: settings.ServerSelection{
|
selection: settings.ServerSelection{
|
||||||
VPN: vpn.OpenVPN,
|
VPN: vpn.OpenVPN,
|
||||||
@@ -104,20 +88,6 @@ func Test_GetPort(t *testing.T) {
|
|||||||
},
|
},
|
||||||
port: 1234,
|
port: 1234,
|
||||||
},
|
},
|
||||||
"OpenVPN_server_port_tcp": {
|
|
||||||
selection: settings.ServerSelection{
|
|
||||||
VPN: vpn.OpenVPN,
|
|
||||||
OpenVPN: settings.OpenVPNSelection{
|
|
||||||
CustomPort: uint16Ptr(0),
|
|
||||||
Protocol: constants.TCP,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
server: models.Server{
|
|
||||||
PortsTCP: []uint16{1234},
|
|
||||||
},
|
|
||||||
defaultOpenVPNTCP: defaultOpenVPNTCP,
|
|
||||||
port: 1234,
|
|
||||||
},
|
|
||||||
"Wireguard": {
|
"Wireguard": {
|
||||||
selection: settings.ServerSelection{
|
selection: settings.ServerSelection{
|
||||||
VPN: vpn.Wireguard,
|
VPN: vpn.Wireguard,
|
||||||
@@ -135,19 +105,6 @@ func Test_GetPort(t *testing.T) {
|
|||||||
defaultWireguard: defaultWireguard,
|
defaultWireguard: defaultWireguard,
|
||||||
port: 1234,
|
port: 1234,
|
||||||
},
|
},
|
||||||
"Wireguard_server_port": {
|
|
||||||
selection: settings.ServerSelection{
|
|
||||||
VPN: vpn.Wireguard,
|
|
||||||
Wireguard: settings.WireguardSelection{
|
|
||||||
EndpointPort: uint16Ptr(0),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
server: models.Server{
|
|
||||||
PortsUDP: []uint16{1234},
|
|
||||||
},
|
|
||||||
defaultWireguard: defaultWireguard,
|
|
||||||
port: 1234,
|
|
||||||
},
|
|
||||||
"Wireguard no default port defined": {
|
"Wireguard no default port defined": {
|
||||||
selection: settings.ServerSelection{
|
selection: settings.ServerSelection{
|
||||||
VPN: vpn.Wireguard,
|
VPN: vpn.Wireguard,
|
||||||
@@ -163,7 +120,6 @@ func Test_GetPort(t *testing.T) {
|
|||||||
if testCase.panics != "" {
|
if testCase.panics != "" {
|
||||||
assert.PanicsWithValue(t, testCase.panics, func() {
|
assert.PanicsWithValue(t, testCase.panics, func() {
|
||||||
_ = getPort(testCase.selection,
|
_ = getPort(testCase.selection,
|
||||||
testCase.server,
|
|
||||||
testCase.defaultOpenVPNTCP,
|
testCase.defaultOpenVPNTCP,
|
||||||
testCase.defaultOpenVPNUDP,
|
testCase.defaultOpenVPNUDP,
|
||||||
testCase.defaultWireguard)
|
testCase.defaultWireguard)
|
||||||
@@ -172,7 +128,6 @@ func Test_GetPort(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
port := getPort(testCase.selection,
|
port := getPort(testCase.selection,
|
||||||
testCase.server,
|
|
||||||
testCase.defaultOpenVPNTCP,
|
testCase.defaultOpenVPNTCP,
|
||||||
testCase.defaultOpenVPNUDP,
|
testCase.defaultOpenVPNUDP,
|
||||||
testCase.defaultWireguard)
|
testCase.defaultWireguard)
|
||||||
|
|||||||
@@ -1,15 +1,23 @@
|
|||||||
package storage
|
package storage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"slices"
|
"net/netip"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/models"
|
"github.com/qdm12/gluetun/internal/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
func copyServer(server models.Server) (serverCopy models.Server) {
|
func copyServer(server models.Server) (serverCopy models.Server) {
|
||||||
serverCopy = server
|
serverCopy = server
|
||||||
serverCopy.IPs = slices.Clone(server.IPs)
|
serverCopy.IPs = copyIPs(server.IPs)
|
||||||
serverCopy.PortsTCP = slices.Clone(server.PortsTCP)
|
|
||||||
serverCopy.PortsUDP = slices.Clone(server.PortsUDP)
|
|
||||||
return serverCopy
|
return serverCopy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func copyIPs(toCopy []netip.Addr) (copied []netip.Addr) {
|
||||||
|
if toCopy == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
copied = make([]netip.Addr, len(toCopy))
|
||||||
|
copy(copied, toCopy)
|
||||||
|
return copied
|
||||||
|
}
|
||||||
|
|||||||
@@ -21,9 +21,43 @@ func Test_copyServer(t *testing.T) {
|
|||||||
assert.Equal(t, server, serverCopy)
|
assert.Equal(t, server, serverCopy)
|
||||||
// Check for mutation
|
// Check for mutation
|
||||||
serverCopy.IPs[0] = netip.AddrFrom4([4]byte{9, 9, 9, 9})
|
serverCopy.IPs[0] = netip.AddrFrom4([4]byte{9, 9, 9, 9})
|
||||||
serverCopy.PortsTCP = []uint16{80}
|
assert.NotEqual(t, server, serverCopy)
|
||||||
serverCopy.PortsUDP = []uint16{53}
|
}
|
||||||
assert.NotEqual(t, server.IPs, serverCopy.IPs)
|
|
||||||
assert.NotEqual(t, server.PortsTCP, serverCopy.PortsTCP)
|
func Test_copyIPs(t *testing.T) {
|
||||||
assert.NotEqual(t, server.PortsUDP, serverCopy.PortsUDP)
|
t.Parallel()
|
||||||
|
|
||||||
|
testCases := map[string]struct {
|
||||||
|
toCopy []netip.Addr
|
||||||
|
copied []netip.Addr
|
||||||
|
}{
|
||||||
|
"nil": {},
|
||||||
|
"empty": {
|
||||||
|
toCopy: []netip.Addr{},
|
||||||
|
copied: []netip.Addr{},
|
||||||
|
},
|
||||||
|
"single IP": {
|
||||||
|
toCopy: []netip.Addr{netip.AddrFrom4([4]byte{1, 1, 1, 1})},
|
||||||
|
copied: []netip.Addr{netip.AddrFrom4([4]byte{1, 1, 1, 1})},
|
||||||
|
},
|
||||||
|
"two IPs": {
|
||||||
|
toCopy: []netip.Addr{netip.AddrFrom4([4]byte{1, 1, 1, 1}), netip.AddrFrom4([4]byte{2, 2, 2, 2})},
|
||||||
|
copied: []netip.Addr{netip.AddrFrom4([4]byte{1, 1, 1, 1}), netip.AddrFrom4([4]byte{2, 2, 2, 2})},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, testCase := range testCases {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
copied := copyIPs(testCase.toCopy)
|
||||||
|
|
||||||
|
assert.Equal(t, testCase.copied, copied)
|
||||||
|
|
||||||
|
if len(copied) > 0 {
|
||||||
|
testCase.toCopy[0] = netip.AddrFrom4([4]byte{9, 9, 9, 9})
|
||||||
|
assert.NotEqual(t, testCase.toCopy[0], testCase.copied[0])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package storage
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"slices"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/qdm12/gluetun/internal/configuration/settings"
|
"github.com/qdm12/gluetun/internal/configuration/settings"
|
||||||
@@ -49,7 +48,6 @@ func (s *Storage) FilterServers(provider string, selection settings.ServerSelect
|
|||||||
return servers, nil
|
return servers, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
//nolint:gocognit,gocyclo
|
|
||||||
func filterServer(server models.Server,
|
func filterServer(server models.Server,
|
||||||
selection settings.ServerSelection,
|
selection settings.ServerSelection,
|
||||||
) (filtered bool) {
|
) (filtered bool) {
|
||||||
@@ -92,11 +90,6 @@ func filterServer(server models.Server,
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*selection.Dedicated && !server.Dedicated) ||
|
|
||||||
(!*selection.Dedicated && server.Dedicated) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if filterByPossibilities(server.Country, selection.Countries) {
|
if filterByPossibilities(server.Country, selection.Countries) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -129,14 +122,6 @@ func filterServer(server models.Server,
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
serverPorts := server.PortsUDP
|
|
||||||
if server.VPN == vpn.OpenVPN && server.TCP {
|
|
||||||
serverPorts = server.PortsTCP
|
|
||||||
}
|
|
||||||
if filterByPorts(selection, serverPorts) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO filter port forward server for PIA
|
// TODO filter port forward server for PIA
|
||||||
|
|
||||||
return false
|
return false
|
||||||
@@ -180,21 +165,3 @@ func filterByProtocol(selection settings.ServerSelection,
|
|||||||
return (wantTCP && !serverTCP) || (wantUDP && !serverUDP)
|
return (wantTCP && !serverTCP) || (wantUDP && !serverUDP)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func filterByPorts(selection settings.ServerSelection,
|
|
||||||
serverPorts []uint16,
|
|
||||||
) (filtered bool) {
|
|
||||||
if len(serverPorts) == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
customPort := *selection.OpenVPN.CustomPort
|
|
||||||
if selection.VPN == vpn.Wireguard {
|
|
||||||
customPort = *selection.Wireguard.EndpointPort
|
|
||||||
}
|
|
||||||
if customPort == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return !slices.Contains(serverPorts, customPort)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ func commaJoin(slice []string) string {
|
|||||||
return strings.Join(slice, ", ")
|
return strings.Join(slice, ", ")
|
||||||
}
|
}
|
||||||
|
|
||||||
func noServerFoundError(selection settings.ServerSelection) (err error) { //nolint:gocyclo
|
func noServerFoundError(selection settings.ServerSelection) (err error) {
|
||||||
var messageParts []string
|
var messageParts []string
|
||||||
|
|
||||||
messageParts = append(messageParts, "VPN "+selection.VPN)
|
messageParts = append(messageParts, "VPN "+selection.VPN)
|
||||||
@@ -155,15 +155,6 @@ func noServerFoundError(selection settings.ServerSelection) (err error) { //noli
|
|||||||
"target ip address "+targetIP.String())
|
"target ip address "+targetIP.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
customPort := *selection.OpenVPN.CustomPort
|
|
||||||
if selection.VPN == vpn.Wireguard {
|
|
||||||
customPort = *selection.Wireguard.EndpointPort
|
|
||||||
}
|
|
||||||
if customPort > 0 {
|
|
||||||
messageParts = append(messageParts,
|
|
||||||
fmt.Sprintf("%s endpoint port %d", selection.VPN, customPort))
|
|
||||||
}
|
|
||||||
|
|
||||||
message := "for " + strings.Join(messageParts, "; ")
|
message := "for " + strings.Join(messageParts, "; ")
|
||||||
|
|
||||||
return fmt.Errorf("no server found: %s", message)
|
return fmt.Errorf("no server found: %s", message)
|
||||||
|
|||||||
@@ -20,8 +20,7 @@ func parseHardcodedServers() (allServers models.AllServers) {
|
|||||||
filename := provider + ".json"
|
filename := provider + ".json"
|
||||||
providerFile, err := serversmodule.Files.Open(filename)
|
providerFile, err := serversmodule.Files.Open(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
const rootURL = "https://github.com/qdm12/gluetun-servers/blob/main/pkg/servers"
|
panic(fmt.Sprintf("reading embedded provider file %s for %s: %s", filename, provider, err))
|
||||||
panic(fmt.Sprintf("reading embedded provider file defined at %s/%s: %s", rootURL, filename, err))
|
|
||||||
}
|
}
|
||||||
defer providerFile.Close() // no-op
|
defer providerFile.Close() // no-op
|
||||||
|
|
||||||
|
|||||||
@@ -33,10 +33,7 @@ func Test_parseHardcodedServers(t *testing.T) {
|
|||||||
func Test_parseHardcodedServers_filepathsAndEmbeddedProviderFiles(t *testing.T) {
|
func Test_parseHardcodedServers_filepathsAndEmbeddedProviderFiles(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
var hardcodedServers models.AllServers
|
hardcodedServers := parseHardcodedServers()
|
||||||
require.NotPanics(t, func() {
|
|
||||||
hardcodedServers = parseHardcodedServers()
|
|
||||||
})
|
|
||||||
|
|
||||||
allProviders := providers.All()
|
allProviders := providers.All()
|
||||||
for _, provider := range allProviders {
|
for _, provider := range allProviders {
|
||||||
|
|||||||
Reference in New Issue
Block a user