Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1748a2ae12 | ||
|
|
eff46aa97a | ||
|
|
9fb186af75 | ||
|
|
f1b1001863 | ||
|
|
c5af536299 | ||
|
|
b9b2f691a5 | ||
|
|
bdc8817672 | ||
|
|
a55acb2816 | ||
|
|
d686c76db3 | ||
|
|
30c1ae651e | ||
|
|
adaad62fbd |
8
.github/ISSUE_TEMPLATE/bug.md
vendored
8
.github/ISSUE_TEMPLATE/bug.md
vendored
@@ -7,9 +7,11 @@ assignees: qdm12
|
||||
|
||||
---
|
||||
|
||||
**Is this urgent?**: No
|
||||
|
||||
**Host OS** (approximate answer is fine too): Ubuntu 18
|
||||
|
||||
**Is this urgent?**: No
|
||||
**CPU arch** or **device name**: amd64
|
||||
|
||||
**What VPN provider are you using**:
|
||||
|
||||
@@ -25,9 +27,7 @@ Running version latest built on 2020-03-13T01:30:06Z (commit d0f678c)
|
||||
|
||||
That feature doesn't work
|
||||
|
||||
**Share your logs...**
|
||||
|
||||
...*careful to remove i.e. token information with PIA port forwarding*
|
||||
**Share your logs... (careful to remove in example tokens)**
|
||||
|
||||
```log
|
||||
|
||||
|
||||
8
.github/ISSUE_TEMPLATE/help.md
vendored
8
.github/ISSUE_TEMPLATE/help.md
vendored
@@ -7,9 +7,11 @@ assignees:
|
||||
|
||||
---
|
||||
|
||||
**Is this urgent?**: No
|
||||
|
||||
**Host OS** (approximate answer is fine too): Ubuntu 18
|
||||
|
||||
**Is this urgent?**: No
|
||||
**CPU arch** or **device name**: amd64
|
||||
|
||||
**What VPN provider are you using**:
|
||||
|
||||
@@ -23,9 +25,7 @@ Running version latest built on 2020-03-13T01:30:06Z (commit d0f678c)
|
||||
|
||||
That feature doesn't work
|
||||
|
||||
**Share your logs...**
|
||||
|
||||
...*careful to remove i.e. token information with PIA port forwarding*
|
||||
**Share your logs... (careful to remove in example tokens)**
|
||||
|
||||
```log
|
||||
|
||||
|
||||
16
.github/ISSUE_TEMPLATE/provider.md
vendored
Normal file
16
.github/ISSUE_TEMPLATE/provider.md
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
---
|
||||
name: Support a VPN provider
|
||||
about: Suggest a VPN provider to be supported
|
||||
title: 'VPN provider support: NAME OF THE PROVIDER'
|
||||
labels: ":bulb: New provider"
|
||||
|
||||
---
|
||||
|
||||
One of the following is required:
|
||||
|
||||
- Publicly accessible URL to a zip file containing the Openvpn configuration files
|
||||
- Publicly accessible URL to a structured (JSON etc.) list of servers **and attach** an example Openvpn configuration file for both TCP and UDP
|
||||
- Publicly accessible URL to the list of servers **and attach** an example Openvpn configuration file for both TCP and UDP
|
||||
|
||||
If the list of servers requires to login **or** is hidden behind an interactive configurator,
|
||||
it's not possible to support the provider yet. Please instead subscribe to issue #223 which would solve it.
|
||||
3
.github/labels.yml
vendored
3
.github/labels.yml
vendored
@@ -36,6 +36,9 @@
|
||||
- name: ":cloud: Surfshark"
|
||||
color: "cfe8d4"
|
||||
description: ""
|
||||
- name: ":cloud: Torguard"
|
||||
color: "cfe8d4"
|
||||
description: ""
|
||||
- name: ":cloud: Vyprvpn"
|
||||
color: "cfe8d4"
|
||||
description: ""
|
||||
|
||||
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@@ -72,10 +72,10 @@ jobs:
|
||||
echo ::set-output name=build_date::$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
||||
if [ "$TAG" != "$GITHUB_REF" ]; then
|
||||
echo ::set-output name=version::$TAG
|
||||
echo ::set-output name=platforms::linux/amd64,linux/386,linux/arm64,linux/arm/v6,linux/arm/v7,linux/s390x,linux/ppc64le
|
||||
echo ::set-output name=platforms::linux/amd64,linux/386,linux/arm64,linux/arm/v6,linux/arm/v7,linux/ppc64le
|
||||
elif [ "$BRANCH" = "master" ]; then
|
||||
echo ::set-output name=version::latest
|
||||
echo ::set-output name=platforms::linux/amd64,linux/386,linux/arm64,linux/arm/v6,linux/arm/v7,linux/s390x,linux/ppc64le
|
||||
echo ::set-output name=platforms::linux/amd64,linux/386,linux/arm64,linux/arm/v6,linux/arm/v7,linux/ppc64le
|
||||
else
|
||||
echo ::set-output name=version::$BRANCH
|
||||
echo ::set-output name=platforms::linux/amd64
|
||||
|
||||
@@ -13,6 +13,9 @@ issues:
|
||||
- path: internal/server/
|
||||
linters:
|
||||
- dupl
|
||||
- path: internal/configuration/
|
||||
linters:
|
||||
- dupl
|
||||
linters:
|
||||
disable-all: true
|
||||
enable:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Gluetun VPN client
|
||||
|
||||
*Lightweight swiss-knife-like VPN client to tunnel to Private Internet Access,
|
||||
Mullvad, Windscribe, Surfshark Cyberghost, VyprVPN, NordVPN, PureVPN and Privado VPN servers, using Go, OpenVPN, iptables, DNS over TLS, ShadowSocks and an HTTP proxy*
|
||||
Mullvad, Windscribe, Surfshark Cyberghost, VyprVPN, NordVPN, PureVPN, Privado and TorGuard VPN servers, using Go, OpenVPN, iptables, DNS over TLS, ShadowSocks and an HTTP proxy*
|
||||
|
||||
**ANNOUNCEMENT**: *New Docker image name `qmcgaw/gluetun`*
|
||||
|
||||
@@ -37,7 +37,7 @@ Mullvad, Windscribe, Surfshark Cyberghost, VyprVPN, NordVPN, PureVPN and Privado
|
||||
## Features
|
||||
|
||||
- Based on Alpine 3.12 for a small Docker image of 52MB
|
||||
- Supports **Private Internet Access**, **Mullvad**, **Windscribe**, **Surfshark**, **Cyberghost**, **Vyprvpn**, **NordVPN**, **PureVPN** and **Privado** servers
|
||||
- Supports **Private Internet Access**, **Mullvad**, **Windscribe**, **Surfshark**, **Cyberghost**, **Vyprvpn**, **NordVPN**, **PureVPN**, **Privado** and **TorGuard** servers
|
||||
- Supports Openvpn only for now
|
||||
- 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
|
||||
@@ -47,7 +47,7 @@ Mullvad, Windscribe, Surfshark Cyberghost, VyprVPN, NordVPN, PureVPN and Privado
|
||||
- Built in HTTP proxy (tunnels HTTP and HTTPS through TCP)
|
||||
- [Connect other containers to it](https://github.com/qdm12/gluetun/wiki/Connect-to-gluetun)
|
||||
- [Connect LAN devices to it](https://github.com/qdm12/gluetun/wiki/Connect-to-gluetun)
|
||||
- Compatible with amd64, i686 (32 bit), **ARM** 64 bit, ARM 32 bit v6 and v7, and even s390x as well as ppc64le 🎆
|
||||
- Compatible with amd64, i686 (32 bit), **ARM** 64 bit, ARM 32 bit v6 and v7, and even ppc64le 🎆
|
||||
- VPN server side port forwarding for Private Internet Access and Vyprvpn
|
||||
- Possibility of split horizon DNS by selecting multiple DNS over TLS providers
|
||||
- Subprograms all drop root privileges once launched
|
||||
|
||||
@@ -29,6 +29,7 @@ func (c *cli) Update(ctx context.Context, args []string, os os.OS) error {
|
||||
flagSet.BoolVar(&options.Privado, "privado", false, "Update Privado servers")
|
||||
flagSet.BoolVar(&options.Purevpn, "purevpn", false, "Update Purevpn servers")
|
||||
flagSet.BoolVar(&options.Surfshark, "surfshark", false, "Update Surfshark servers")
|
||||
flagSet.BoolVar(&options.Torguard, "torguard", false, "Update Torguard servers")
|
||||
flagSet.BoolVar(&options.Vyprvpn, "vyprvpn", false, "Update Vyprvpn servers")
|
||||
flagSet.BoolVar(&options.Windscribe, "windscribe", false, "Update Windscribe servers")
|
||||
if err := flagSet.Parse(args); err != nil {
|
||||
|
||||
@@ -82,7 +82,7 @@ func (settings *Firewall) readVPNInputPorts(env params.Env) (err error) {
|
||||
}
|
||||
|
||||
func (settings *Firewall) readInputPorts(env params.Env) (err error) {
|
||||
settings.VPNInputPorts, err = readCSVPorts(env, "FIREWALL_INPUT_PORTS")
|
||||
settings.InputPorts, err = readCSVPorts(env, "FIREWALL_INPUT_PORTS")
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ func (settings *HTTPProxy) read(r reader) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
settings.Password, err = r.getFromEnvOrSecretFile("HTTPPROXY_USER", false,
|
||||
settings.Password, err = r.getFromEnvOrSecretFile("HTTPPROXY_PASSWORD", false,
|
||||
[]string{"TINYPROXY_PASSWORD", "PROXY_PASSWORD"})
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -95,10 +95,10 @@ func (settings *HTTPProxy) readLog(r reader) error {
|
||||
|
||||
switch strings.ToLower(s) {
|
||||
case "on":
|
||||
settings.Enabled = true
|
||||
settings.Log = true
|
||||
// Retro compatibility
|
||||
case "info", "connect", "notice":
|
||||
settings.Enabled = true
|
||||
settings.Log = true
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -56,7 +56,7 @@ var (
|
||||
|
||||
func (settings *OpenVPN) read(r reader) (err error) {
|
||||
vpnsp, err := r.env.Inside("VPNSP", []string{
|
||||
"pia", "private internet access", "mullvad", "windscribe", "surfshark",
|
||||
"pia", "private internet access", "mullvad", "windscribe", "surfshark", "torguard",
|
||||
"cyberghost", "vyprvpn", "nordvpn", "purevpn", "privado"},
|
||||
params.Default("private internet access"))
|
||||
if err != nil {
|
||||
@@ -130,6 +130,8 @@ func (settings *OpenVPN) read(r reader) (err error) {
|
||||
readProvider = settings.Provider.readPurevpn
|
||||
case constants.Privado:
|
||||
readProvider = settings.Provider.readPrivado
|
||||
case constants.Torguard:
|
||||
readProvider = settings.Provider.readTorguard
|
||||
default:
|
||||
return fmt.Errorf("%w: %s", ErrInvalidVPNProvider, settings.Provider.Name)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package configuration
|
||||
|
||||
import (
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
"github.com/qdm12/golibs/params"
|
||||
)
|
||||
|
||||
func (settings *Provider) privadoLines() (lines []string) {
|
||||
@@ -26,8 +25,7 @@ func (settings *Provider) readPrivado(r reader) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME",
|
||||
constants.PrivadoHostnameChoices(), params.RetroKeys([]string{"HOSTNAME"}, r.onRetroActive))
|
||||
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.PrivadoHostnameChoices())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -43,6 +43,8 @@ func (settings *Provider) lines() (lines []string) {
|
||||
providerLines = settings.purevpnLines()
|
||||
case "surfshark":
|
||||
providerLines = settings.surfsharkLines()
|
||||
case "torguard":
|
||||
providerLines = settings.torguardLines()
|
||||
case "vyprvpn":
|
||||
providerLines = settings.vyprvpnLines()
|
||||
case "windscribe":
|
||||
|
||||
@@ -152,6 +152,24 @@ func Test_Provider_lines(t *testing.T) {
|
||||
" |--Regions: a, b",
|
||||
},
|
||||
},
|
||||
"torguard": {
|
||||
settings: Provider{
|
||||
Name: constants.Torguard,
|
||||
ServerSelection: ServerSelection{
|
||||
Protocol: constants.UDP,
|
||||
Countries: []string{"a", "b"},
|
||||
Cities: []string{"c", "d"},
|
||||
Hostnames: []string{"e"},
|
||||
},
|
||||
},
|
||||
lines: []string{
|
||||
"|--Torguard settings:",
|
||||
" |--Network protocol: udp",
|
||||
" |--Countries: a, b",
|
||||
" |--Cities: c, d",
|
||||
" |--Hostnames: e",
|
||||
},
|
||||
},
|
||||
"vyprvpn": {
|
||||
settings: Provider{
|
||||
Name: constants.Vyprvpn,
|
||||
|
||||
@@ -8,7 +8,7 @@ type ServerSelection struct {
|
||||
// Common
|
||||
Protocol string `json:"network_protocol"`
|
||||
TargetIP net.IP `json:"target_ip,omitempty"`
|
||||
|
||||
// TODO comments
|
||||
// Cyberghost, PIA, Surfshark, Windscribe, Vyprvpn, NordVPN
|
||||
Regions []string `json:"regions"`
|
||||
|
||||
|
||||
52
internal/configuration/torguard.go
Normal file
52
internal/configuration/torguard.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package configuration
|
||||
|
||||
import (
|
||||
"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) {
|
||||
settings.Name = constants.Torguard
|
||||
|
||||
settings.ServerSelection.Protocol, err = readProtocol(r.env)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.TorguardCountryChoices())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.TorguardCityChoices())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.TorguardHostnamesChoices())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -17,6 +17,7 @@ type Updater struct {
|
||||
Privado bool `json:"privado"`
|
||||
Purevpn bool `json:"purevpn"`
|
||||
Surfshark bool `json:"surfshark"`
|
||||
Torguard bool `json:"torguard"`
|
||||
Vyprvpn bool `json:"vyprvpn"`
|
||||
Windscribe bool `json:"windscribe"`
|
||||
// The two below should be used in CLI mode only
|
||||
@@ -47,6 +48,7 @@ func (settings *Updater) read(r reader) (err error) {
|
||||
settings.PIA = true
|
||||
settings.Purevpn = true
|
||||
settings.Surfshark = true
|
||||
settings.Torguard = true
|
||||
settings.Vyprvpn = true
|
||||
settings.Windscribe = true
|
||||
settings.Stdout = false
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -22,8 +22,8 @@ func GetAllServers() (allServers models.AllServers) {
|
||||
Servers: NordvpnServers(),
|
||||
},
|
||||
Pia: models.PiaServers{
|
||||
Version: 3,
|
||||
Timestamp: 1611877630,
|
||||
Version: 4,
|
||||
Timestamp: 1613480675,
|
||||
Servers: PIAServers(),
|
||||
},
|
||||
Purevpn: models.PurevpnServers{
|
||||
@@ -41,6 +41,11 @@ func GetAllServers() (allServers models.AllServers) {
|
||||
Timestamp: 1612031135,
|
||||
Servers: SurfsharkServers(),
|
||||
},
|
||||
Torguard: models.TorguardServers{
|
||||
Version: 1,
|
||||
Timestamp: 1613357861,
|
||||
Servers: TorguardServers(),
|
||||
},
|
||||
Vyprvpn: models.VyprvpnServers{
|
||||
Version: 1,
|
||||
Timestamp: 1612031135,
|
||||
|
||||
@@ -52,7 +52,7 @@ func Test_versions(t *testing.T) {
|
||||
"Private Internet Access": {
|
||||
model: models.PIAServer{},
|
||||
version: allServers.Pia.Version,
|
||||
digest: "b90147aa",
|
||||
digest: "3e6066ec",
|
||||
},
|
||||
"Privado": {
|
||||
model: models.PrivadoServer{},
|
||||
@@ -69,6 +69,11 @@ func Test_versions(t *testing.T) {
|
||||
version: allServers.Surfshark.Version,
|
||||
digest: "042bef64",
|
||||
},
|
||||
"Torguard": {
|
||||
model: models.TorguardServer{},
|
||||
version: allServers.Torguard.Version,
|
||||
digest: "752702f3",
|
||||
},
|
||||
"Vyprvpn": {
|
||||
model: models.VyprvpnServer{},
|
||||
version: allServers.Vyprvpn.Version,
|
||||
@@ -133,7 +138,7 @@ func Test_timestamps(t *testing.T) {
|
||||
"Private Internet Access": {
|
||||
servers: allServers.Pia.Servers,
|
||||
timestamp: allServers.Pia.Timestamp,
|
||||
digest: "1d2938a1",
|
||||
digest: "e0f95a01",
|
||||
},
|
||||
"Purevpn": {
|
||||
servers: allServers.Purevpn.Servers,
|
||||
@@ -150,6 +155,11 @@ func Test_timestamps(t *testing.T) {
|
||||
timestamp: allServers.Surfshark.Timestamp,
|
||||
digest: "1a7f38bb",
|
||||
},
|
||||
"Torguard": {
|
||||
servers: allServers.Torguard.Servers,
|
||||
timestamp: allServers.Torguard.Timestamp,
|
||||
digest: "dffab93e",
|
||||
},
|
||||
"Vyprvpn": {
|
||||
servers: allServers.Vyprvpn.Servers,
|
||||
timestamp: allServers.Vyprvpn.Timestamp,
|
||||
|
||||
101
internal/constants/torguard.go
Normal file
101
internal/constants/torguard.go
Normal file
@@ -0,0 +1,101 @@
|
||||
package constants
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
)
|
||||
|
||||
//nolint:lll
|
||||
const (
|
||||
TorguardCertificate = "MIIDMTCCAhmgAwIBAgIJAKnGGJK6qLqSMA0GCSqGSIb3DQEBCwUAMBQxEjAQBgNVBAMMCVRHLVZQTi1DQTAgFw0xOTA1MjExNDIzMTFaGA8yMDU5MDUxMTE0MjMxMVowFDESMBAGA1UEAwwJVEctVlBOLUNBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlv0UgPD3xVAvhhP6q1HCmeAWbH+9HPkyQ2P6qM5oHY5dntjmq8YT48FZGHWv7+s9O47v6Bv7rEc4UwQx15cc2LByivX2JwmE8JACvNfwEnZXYAPq9WU3ZgRrAGvA09ItuLqK2fQ4A7h8bFhmyxCbSzP1sSIT/zJY6ebuh5rDQSMJRMaoI0t1zorEZ7PlEmh+o0w5GPs0D0vY50UcnEzB4GOdWC9pJREwEqppWYLN7RRdG8JyIqmA59mhARCnQFUo38HWic4trxFe71jtD7YInNV7ShQtg0S0sXo36Rqfz72Jo08qqI70dNs5DN1aGNkQ/tRK9DhL5DLmTkaCw7mEFQIDAQABo4GDMIGAMB0GA1UdDgQWBBR7DcymXBp6u/jAaZOPUjUhEyhXfjBEBgNVHSMEPTA7gBR7DcymXBp6u/jAaZOPUjUhEyhXfqEYpBYwFDESMBAGA1UEAwwJVEctVlBOLUNBggkAqcYYkrqoupIwDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYwDQYJKoZIhvcNAQELBQADggEBAE79ngbdSlP7IBbfnJ+2Ju7vqt9/GyhcsYtjibp6gsMUxKlD8HuvlSGj5kNO5wiwN7XXqsjYtJfdhmzzVbXksi8Fnbnfa8GhFl4IAjLJ5cxaWOxjr6wx2AhIs+BVVARjaU7iTK91RXJnl6u7UDHTkQylBTl7wgpMeG6GjhaHfcOL1t7D2w8x23cTO+p+n53P3cBq+9TiAUORdzXJvbCxlPMDSDArsgBjC57W7dtdnZo7gTfQG77JTDFBeSwPwLF7PjBB4S6rzU/4fcYwy83XKP6zDn9tgUJDnpFb/7jJ/PbNkK4BWYJp3XytOtt66v9SEKw+v/fJ+VkjU16vE/9Q3h4="
|
||||
TorguardOpenvpnStaticKeyV1 = "770e8de5fc56e0248cc7b5aab56be80d0e19cbf003c1b3ed68efbaf08613c3a1a019dac6a4b84f13a6198f73229ffc21fa512394e288f82aa2cf0180f01fb3eb1a71e00a077a20f6d7a83633f5b4f47f27e30617eaf8485dd8c722a8606d56b3c183f65da5d3c9001a8cbdb96c793d936251098b24fe52a6dd2472e98cfccbc466e63520d63ade7a0eacc36208c3142a1068236a52142fbb7b3ed83d785e12a28261bccfb3bcb62a8d2f6d18f5df5f3652e59c5627d8d9c8f7877c4d7b08e19a5c363556ba68d392be78b75152dd55ba0f74d45089e84f77f4492d886524ea6c82b9f4dd83d46528d4f5c3b51cfeaf2838d938bd0597c426b0e440434f2c451f"
|
||||
)
|
||||
|
||||
func TorguardCountryChoices() (choices []string) {
|
||||
servers := TorguardServers()
|
||||
choices = make([]string, len(servers))
|
||||
for i := range servers {
|
||||
choices[i] = servers[i].Country
|
||||
}
|
||||
return choices
|
||||
}
|
||||
|
||||
func TorguardCityChoices() (choices []string) {
|
||||
servers := TorguardServers()
|
||||
choices = make([]string, len(servers))
|
||||
for i := range servers {
|
||||
choices[i] = servers[i].City
|
||||
}
|
||||
return choices
|
||||
}
|
||||
|
||||
func TorguardHostnamesChoices() (choices []string) {
|
||||
servers := TorguardServers()
|
||||
choices = make([]string, len(servers))
|
||||
for i := range servers {
|
||||
choices[i] = servers[i].Hostname
|
||||
}
|
||||
return choices
|
||||
}
|
||||
|
||||
//nolint:lll
|
||||
// TorguardServers returns a slice of all the server information for Torguard.
|
||||
func TorguardServers() []models.TorguardServer {
|
||||
return []models.TorguardServer{
|
||||
{Country: "Australia", City: "Sydney", Hostname: "au.torguardvpnaccess.com", IP: net.IP{168, 1, 210, 188}},
|
||||
{Country: "Austria", City: "", Hostname: "aus.torguardvpnaccess.com", IP: net.IP{37, 120, 155, 18}},
|
||||
{Country: "Belarus", City: "", Hostname: "bl.torguardvpnaccess.com", IP: net.IP{95, 47, 99, 12}},
|
||||
{Country: "Belgium", City: "", Hostname: "bg.torguardvpnaccess.com", IP: net.IP{89, 249, 73, 250}},
|
||||
{Country: "Brazil", City: "", Hostname: "br.torguardvpnaccess.com", IP: net.IP{169, 57, 191, 3}},
|
||||
{Country: "Bulgaria", City: "", Hostname: "bul.torguardvpnaccess.com", IP: net.IP{82, 102, 23, 186}},
|
||||
{Country: "Canada", City: "Toronto", Hostname: "ca.torguardvpnaccess.com", IP: net.IP{184, 75, 209, 250}},
|
||||
{Country: "Canada", City: "Vancouver", Hostname: "vanc.ca.west.torguardvpnaccess.com", IP: net.IP{107, 181, 189, 48}},
|
||||
{Country: "Chile", City: "", Hostname: "chil.torguardvpnaccess.com", IP: net.IP{37, 235, 52, 19}},
|
||||
{Country: "Cyprus", City: "", Hostname: "cp.torguardvpnaccess.com", IP: net.IP{185, 173, 226, 49}},
|
||||
{Country: "Czech", City: "", Hostname: "czech.torguardvpnaccess.com", IP: net.IP{185, 189, 115, 103}},
|
||||
{Country: "Denmark", City: "", Hostname: "den.torguardvpnaccess.com", IP: net.IP{37, 120, 131, 29}},
|
||||
{Country: "Finland", City: "", Hostname: "fin.torguardvpnaccess.com", IP: net.IP{91, 233, 116, 228}},
|
||||
{Country: "France", City: "", Hostname: "fr.torguardvpnaccess.com", IP: net.IP{93, 177, 75, 90}},
|
||||
{Country: "Germany", City: "", Hostname: "gr.torguardvpnaccess.com", IP: net.IP{93, 177, 73, 90}},
|
||||
{Country: "Greece", City: "", Hostname: "gre.torguardvpnaccess.com", IP: net.IP{45, 92, 33, 10}},
|
||||
{Country: "Hong", City: "Kong", Hostname: "hk.torguardvpnaccess.com", IP: net.IP{45, 133, 181, 158}},
|
||||
{Country: "Hungary", City: "", Hostname: "hg.torguardvpnaccess.com", IP: net.IP{37, 120, 144, 106}},
|
||||
{Country: "Iceland", City: "", Hostname: "ice.torguardvpnaccess.com", IP: net.IP{82, 221, 111, 11}},
|
||||
{Country: "India", City: "Bangalore", Hostname: "in.torguardvpnaccess.com", IP: net.IP{139, 59, 62, 109}},
|
||||
{Country: "Ireland", City: "", Hostname: "ire.torguardvpnaccess.com", IP: net.IP{81, 17, 246, 108}},
|
||||
{Country: "Israel", City: "", Hostname: "isr.torguardvpnaccess.com", IP: net.IP{91, 223, 106, 201}},
|
||||
{Country: "Italy", City: "", Hostname: "it.torguardvpnaccess.com", IP: net.IP{192, 145, 127, 190}},
|
||||
{Country: "Japan", City: "", Hostname: "jp.torguardvpnaccess.com", IP: net.IP{91, 207, 174, 50}},
|
||||
{Country: "Latvia", City: "", Hostname: "lv.torguardvpnaccess.com", IP: net.IP{109, 248, 149, 167}},
|
||||
{Country: "Luxembourg", City: "", Hostname: "lux.torguardvpnaccess.com", IP: net.IP{94, 242, 238, 66}},
|
||||
{Country: "Mexico", City: "", Hostname: "mx.torguardvpnaccess.com", IP: net.IP{45, 133, 180, 34}},
|
||||
{Country: "Moldova", City: "", Hostname: "md.torguardvpnaccess.com", IP: net.IP{178, 175, 128, 74}},
|
||||
{Country: "Netherlands", City: "", Hostname: "nl.torguardvpnaccess.com", IP: net.IP{88, 202, 177, 181}},
|
||||
{Country: "New", City: "Zealand", Hostname: "nz.torguardvpnaccess.com", IP: net.IP{103, 108, 94, 58}},
|
||||
{Country: "Norway", City: "", Hostname: "no.torguardvpnaccess.com", IP: net.IP{185, 125, 169, 31}},
|
||||
{Country: "Poland", City: "", Hostname: "pl.torguardvpnaccess.com", IP: net.IP{37, 120, 156, 194}},
|
||||
{Country: "Portugal", City: "", Hostname: "por.torguardvpnaccess.com", IP: net.IP{94, 46, 179, 75}},
|
||||
{Country: "Romania", City: "", Hostname: "ro.torguardvpnaccess.com", IP: net.IP{93, 120, 27, 162}},
|
||||
{Country: "Singapore", City: "", Hostname: "singp.torguardvpnaccess.com", IP: net.IP{206, 189, 43, 152}},
|
||||
{Country: "Slovakia", City: "", Hostname: "slk.torguardvpnaccess.com", IP: net.IP{46, 29, 2, 113}},
|
||||
{Country: "South", City: "Korea", Hostname: "sk.torguardvpnaccess.com", IP: net.IP{169, 56, 83, 216}},
|
||||
{Country: "Spain", City: "", Hostname: "sp.torguardvpnaccess.com", IP: net.IP{192, 145, 124, 242}},
|
||||
{Country: "Sweden", City: "", Hostname: "swe.torguardvpnaccess.com", IP: net.IP{37, 120, 153, 72}},
|
||||
{Country: "Switzerland", City: "", Hostname: "swiss.torguardvpnaccess.com", IP: net.IP{195, 206, 105, 37}},
|
||||
{Country: "Taiwan", City: "", Hostname: "tw.torguardvpnaccess.com", IP: net.IP{61, 216, 159, 176}},
|
||||
{Country: "Thailand", City: "", Hostname: "thai.torguardvpnaccess.com", IP: net.IP{202, 129, 16, 104}},
|
||||
{Country: "UAE", City: "", Hostname: "uae.secureconnect.me", IP: net.IP{45, 9, 250, 10}},
|
||||
{Country: "UK", City: "London", Hostname: "uk.torguardvpnaccess.com", IP: net.IP{109, 123, 118, 13}},
|
||||
{Country: "USA", City: "Atlanta", Hostname: "atl.east.usa.torguardvpnaccess.com", IP: net.IP{104, 223, 95, 50}},
|
||||
{Country: "USA", City: "Chicago", Hostname: "chi.central.usa.torguardvpnaccess.com", IP: net.IP{167, 160, 172, 106}},
|
||||
{Country: "USA", City: "Dallas", Hostname: "dal.central.usa.torguardvpnaccess.com", IP: net.IP{96, 44, 145, 26}},
|
||||
{Country: "USA", City: "Las Vegas", Hostname: "lv.west.usa.torguardvpnaccess.com", IP: net.IP{76, 164, 203, 130}},
|
||||
{Country: "USA", City: "Los Angeles", Hostname: "la.west.usa.torguardvpnaccess.com", IP: net.IP{67, 215, 236, 58}},
|
||||
{Country: "USA", City: "Miami", Hostname: "fl.east.usa.torguardvpnaccess.com", IP: net.IP{96, 47, 226, 42}},
|
||||
{Country: "USA", City: "New Jersey", Hostname: "nj.east.usa.torguardvpnaccess.com", IP: net.IP{23, 226, 128, 146}},
|
||||
{Country: "USA", City: "New York", Hostname: "ny.east.usa.torguardvpnaccess.com", IP: net.IP{209, 95, 50, 116}},
|
||||
{Country: "USA", City: "San Francisco", Hostname: "sf.west.usa.torguardvpnaccess.com", IP: net.IP{206, 189, 218, 238}},
|
||||
{Country: "USA", City: "Seattle", Hostname: "sa.west.usa.torguardvpnaccess.com", IP: net.IP{199, 229, 250, 38}},
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,8 @@ const (
|
||||
Purevpn = "purevpn"
|
||||
// Privado is a VPN provider.
|
||||
Privado = "privado"
|
||||
// Torguard is a VPN provider.
|
||||
Torguard = "torguard"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -13,9 +13,11 @@ func newHandler(ctx context.Context, wg *sync.WaitGroup, logger logging.Logger,
|
||||
stealth, verbose bool, username, password string) http.Handler {
|
||||
const httpTimeout = 24 * time.Hour
|
||||
return &handler{
|
||||
ctx: ctx,
|
||||
wg: wg,
|
||||
client: &http.Client{Timeout: httpTimeout},
|
||||
ctx: ctx,
|
||||
wg: wg,
|
||||
client: &http.Client{
|
||||
Timeout: httpTimeout,
|
||||
CheckRedirect: returnRedirect},
|
||||
logger: logger,
|
||||
verbose: verbose,
|
||||
stealth: stealth,
|
||||
@@ -62,3 +64,8 @@ var hopHeaders = [...]string{ //nolint:gochecknoglobals
|
||||
"Transfer-Encoding",
|
||||
"Upgrade",
|
||||
}
|
||||
|
||||
// Do not follow redirect, but directly return the redirect response.
|
||||
func returnRedirect(req *http.Request, via []*http.Request) error {
|
||||
return http.ErrUseLastResponse
|
||||
}
|
||||
|
||||
@@ -10,14 +10,15 @@ import (
|
||||
type PIAServer struct {
|
||||
Region string `json:"region"`
|
||||
ServerName string `json:"server_name"`
|
||||
Protocol string `json:"protocol"`
|
||||
TCP bool `json:"tcp"`
|
||||
UDP bool `json:"udp"`
|
||||
PortForward bool `json:"port_forward"`
|
||||
IP net.IP `json:"ip"`
|
||||
}
|
||||
|
||||
func (p *PIAServer) String() string {
|
||||
return fmt.Sprintf("{Region: %q, ServerName: %q, Protocol: %q, PortForward: %t, IP: %s}",
|
||||
p.Region, p.ServerName, p.Protocol, p.PortForward, goStringifyIP(p.IP))
|
||||
return fmt.Sprintf("{Region: %q, ServerName: %q, TCP: %t, UDP: %t, PortForward: %t, IP: %s}",
|
||||
p.Region, p.ServerName, p.TCP, p.UDP, p.PortForward, goStringifyIP(p.IP))
|
||||
}
|
||||
|
||||
type MullvadServer struct {
|
||||
@@ -55,6 +56,18 @@ func (s *SurfsharkServer) String() string {
|
||||
return fmt.Sprintf("{Region: %q, IPs: %s}", s.Region, goStringifyIPs(s.IPs))
|
||||
}
|
||||
|
||||
type TorguardServer struct {
|
||||
Country string `json:"country"`
|
||||
City string `json:"city"`
|
||||
Hostname string `json:"hostname"`
|
||||
IP net.IP `json:"ip"`
|
||||
}
|
||||
|
||||
func (s *TorguardServer) String() string {
|
||||
return fmt.Sprintf("{Country: %q, City: %q, Hostname: %q, IP: %s}",
|
||||
s.Country, s.City, s.Hostname, goStringifyIP(s.IP))
|
||||
}
|
||||
|
||||
type CyberghostServer struct {
|
||||
Region string `json:"region"`
|
||||
Group string `json:"group"`
|
||||
|
||||
@@ -9,6 +9,7 @@ type AllServers struct {
|
||||
Privado PrivadoServers `json:"privado"`
|
||||
Purevpn PurevpnServers `json:"purevpn"`
|
||||
Surfshark SurfsharkServers `json:"surfshark"`
|
||||
Torguard TorguardServers `json:"torguard"`
|
||||
Vyprvpn VyprvpnServers `json:"vyprvpn"`
|
||||
Windscribe WindscribeServers `json:"windscribe"`
|
||||
}
|
||||
@@ -21,6 +22,7 @@ func (a *AllServers) Count() int {
|
||||
len(a.Privado.Servers) +
|
||||
len(a.Purevpn.Servers) +
|
||||
len(a.Surfshark.Servers) +
|
||||
len(a.Torguard.Servers) +
|
||||
len(a.Vyprvpn.Servers) +
|
||||
len(a.Windscribe.Servers)
|
||||
}
|
||||
@@ -60,6 +62,11 @@ type SurfsharkServers struct {
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
Servers []SurfsharkServer `json:"servers"`
|
||||
}
|
||||
type TorguardServers struct {
|
||||
Version uint16 `json:"version"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
Servers []TorguardServer `json:"servers"`
|
||||
}
|
||||
type VyprvpnServers struct {
|
||||
Version uint16 `json:"version"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
|
||||
@@ -2,5 +2,6 @@ package provider
|
||||
|
||||
const (
|
||||
aes256cbc = "aes-256-cbc"
|
||||
aes256gcm = "aes-256-gcm"
|
||||
sha256 = "sha256"
|
||||
)
|
||||
|
||||
@@ -341,8 +341,9 @@ func filterPIAServers(servers []models.PIAServer, regions []string, protocol str
|
||||
filtered []models.PIAServer) {
|
||||
for _, server := range servers {
|
||||
switch {
|
||||
case filterByPossibilities(server.Region, regions):
|
||||
case server.Protocol != protocol:
|
||||
case filterByPossibilities(server.Region, regions),
|
||||
protocol == constants.TCP && !server.TCP,
|
||||
protocol == constants.UDP && !server.UDP:
|
||||
default:
|
||||
filtered = append(filtered, server)
|
||||
}
|
||||
|
||||
@@ -43,6 +43,8 @@ func New(provider string, allServers models.AllServers, timeNow timeNowFunc) Pro
|
||||
return newPurevpn(allServers.Purevpn.Servers, timeNow)
|
||||
case constants.Privado:
|
||||
return newPrivado(allServers.Privado.Servers, timeNow)
|
||||
case constants.Torguard:
|
||||
return newTorguard(allServers.Torguard.Servers, timeNow)
|
||||
default:
|
||||
return nil // should never occur
|
||||
}
|
||||
|
||||
173
internal/provider/torguard.go
Normal file
173
internal/provider/torguard.go
Normal file
@@ -0,0 +1,173 @@
|
||||
package provider
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration"
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
"github.com/qdm12/gluetun/internal/firewall"
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
"github.com/qdm12/golibs/logging"
|
||||
"github.com/qdm12/golibs/os"
|
||||
)
|
||||
|
||||
type torguard struct {
|
||||
servers []models.TorguardServer
|
||||
randSource rand.Source
|
||||
}
|
||||
|
||||
func newTorguard(servers []models.TorguardServer, timeNow timeNowFunc) *torguard {
|
||||
return &torguard{
|
||||
servers: servers,
|
||||
randSource: rand.NewSource(timeNow().UnixNano()),
|
||||
}
|
||||
}
|
||||
|
||||
func (t *torguard) filterServers(countries, cities, hostnames []string) (servers []models.TorguardServer) {
|
||||
for _, server := range t.servers {
|
||||
switch {
|
||||
case filterByPossibilities(server.Country, countries):
|
||||
case filterByPossibilities(server.City, cities):
|
||||
case filterByPossibilities(server.Hostname, hostnames):
|
||||
default:
|
||||
servers = append(servers, server)
|
||||
}
|
||||
}
|
||||
return servers
|
||||
}
|
||||
|
||||
func (t *torguard) notFoundErr(selection configuration.ServerSelection) error {
|
||||
message := "no server found for protocol " + selection.Protocol
|
||||
|
||||
if len(selection.Countries) > 0 {
|
||||
message += " + countries " + commaJoin(selection.Countries)
|
||||
}
|
||||
|
||||
if len(selection.Cities) > 0 {
|
||||
message += " + cities " + commaJoin(selection.Cities)
|
||||
}
|
||||
|
||||
if len(selection.Hostnames) > 0 {
|
||||
message += " + hostnames " + commaJoin(selection.Hostnames)
|
||||
}
|
||||
|
||||
return fmt.Errorf(message)
|
||||
}
|
||||
|
||||
func (t *torguard) GetOpenVPNConnection(selection configuration.ServerSelection) (
|
||||
connection models.OpenVPNConnection, err error) {
|
||||
var port uint16 = 1912
|
||||
if selection.CustomPort > 0 {
|
||||
port = selection.CustomPort
|
||||
}
|
||||
|
||||
if selection.TargetIP != nil {
|
||||
return models.OpenVPNConnection{IP: selection.TargetIP, Port: port, Protocol: selection.Protocol}, nil
|
||||
}
|
||||
|
||||
servers := t.filterServers(selection.Countries, selection.Cities, selection.Hostnames)
|
||||
if len(servers) == 0 {
|
||||
return connection, t.notFoundErr(selection)
|
||||
}
|
||||
|
||||
connections := make([]models.OpenVPNConnection, len(servers))
|
||||
for i := range servers {
|
||||
connections[i] = models.OpenVPNConnection{
|
||||
IP: servers[i].IP,
|
||||
Port: port,
|
||||
Protocol: selection.Protocol,
|
||||
}
|
||||
}
|
||||
|
||||
return pickRandomConnection(connections, t.randSource), nil
|
||||
}
|
||||
|
||||
func (t *torguard) BuildConf(connection models.OpenVPNConnection,
|
||||
username string, settings configuration.OpenVPN) (lines []string) {
|
||||
if len(settings.Cipher) == 0 {
|
||||
settings.Cipher = aes256gcm
|
||||
}
|
||||
if len(settings.Auth) == 0 {
|
||||
settings.Auth = sha256
|
||||
}
|
||||
|
||||
const defaultMSSFix = 1450
|
||||
if settings.MSSFix == 0 {
|
||||
settings.MSSFix = defaultMSSFix
|
||||
}
|
||||
|
||||
lines = []string{
|
||||
"client",
|
||||
"dev tun",
|
||||
"nobind",
|
||||
"persist-key",
|
||||
"remote-cert-tls server",
|
||||
"tls-exit",
|
||||
|
||||
// Torguard specific
|
||||
"tun-mtu 1500",
|
||||
"tun-mtu-extra 32",
|
||||
"mssfix " + strconv.Itoa(int(settings.MSSFix)),
|
||||
"reneg-sec 0",
|
||||
"fast-io",
|
||||
"key-direction 1",
|
||||
"script-security 2",
|
||||
"ncp-disable",
|
||||
"compress",
|
||||
"keepalive 5 30",
|
||||
"sndbuf 393216",
|
||||
"rcvbuf 393216",
|
||||
// "up /etc/openvpn/update-resolv-conf",
|
||||
// "down /etc/openvpn/update-resolv-conf",
|
||||
|
||||
// Added constant values
|
||||
"auth-nocache",
|
||||
"mute-replay-warnings",
|
||||
"pull-filter ignore \"auth-token\"", // prevent auth failed loops
|
||||
"pull-filter ignore \"block-outside-dns\"",
|
||||
"auth-retry nointeract",
|
||||
"suppress-timestamps",
|
||||
|
||||
// Modified variables
|
||||
"verb " + strconv.Itoa(settings.Verbosity),
|
||||
"auth-user-pass " + constants.OpenVPNAuthConf,
|
||||
"proto " + connection.Protocol,
|
||||
"remote " + connection.IP.String() + " " + strconv.Itoa(int(connection.Port)),
|
||||
"cipher " + settings.Cipher,
|
||||
"auth " + settings.Auth,
|
||||
}
|
||||
|
||||
if !settings.Root {
|
||||
lines = append(lines, "user "+username)
|
||||
}
|
||||
|
||||
lines = append(lines, []string{
|
||||
"<ca>",
|
||||
"-----BEGIN CERTIFICATE-----",
|
||||
constants.TorguardCertificate,
|
||||
"-----END CERTIFICATE-----",
|
||||
"</ca>",
|
||||
}...)
|
||||
|
||||
lines = append(lines, []string{
|
||||
"<tls-auth>",
|
||||
"-----BEGIN OpenVPN Static key V1-----",
|
||||
constants.TorguardOpenvpnStaticKeyV1,
|
||||
"-----END OpenVPN Static key V1-----",
|
||||
"</tls-auth>",
|
||||
"",
|
||||
}...)
|
||||
|
||||
return lines
|
||||
}
|
||||
|
||||
func (t *torguard) PortForward(ctx context.Context, client *http.Client,
|
||||
openFile os.OpenFileFunc, pfLogger logging.Logger, gateway net.IP, fw firewall.Configurator,
|
||||
syncState func(port uint16) (pfFilepath string)) {
|
||||
panic("port forwarding is not supported for torguard")
|
||||
}
|
||||
@@ -24,6 +24,7 @@ func (s *storage) mergeServers(hardcoded, persisted models.AllServers) models.Al
|
||||
Privado: s.mergePrivado(hardcoded.Privado, persisted.Privado),
|
||||
Purevpn: s.mergePureVPN(hardcoded.Purevpn, persisted.Purevpn),
|
||||
Surfshark: s.mergeSurfshark(hardcoded.Surfshark, persisted.Surfshark),
|
||||
Torguard: s.mergeTorguard(hardcoded.Torguard, persisted.Torguard),
|
||||
Vyprvpn: s.mergeVyprvpn(hardcoded.Vyprvpn, persisted.Vyprvpn),
|
||||
Windscribe: s.mergeWindscribe(hardcoded.Windscribe, persisted.Windscribe),
|
||||
}
|
||||
@@ -106,6 +107,22 @@ func (s *storage) mergeSurfshark(hardcoded, persisted models.SurfsharkServers) m
|
||||
return persisted
|
||||
}
|
||||
|
||||
func (s *storage) mergeTorguard(hardcoded, persisted models.TorguardServers) models.TorguardServers {
|
||||
if persisted.Timestamp <= hardcoded.Timestamp {
|
||||
return hardcoded
|
||||
}
|
||||
versionDiff := hardcoded.Version - persisted.Version
|
||||
if versionDiff > 0 {
|
||||
s.logger.Info(
|
||||
"Torguard servers from file discarded because they are %d versions behind",
|
||||
versionDiff)
|
||||
return hardcoded
|
||||
}
|
||||
s.logger.Info("Using Torguard servers from file (%s more recent)",
|
||||
getUnixTimeDifference(persisted.Timestamp, hardcoded.Timestamp))
|
||||
return persisted
|
||||
}
|
||||
|
||||
func (s *storage) mergeVyprvpn(hardcoded, persisted models.VyprvpnServers) models.VyprvpnServers {
|
||||
if persisted.Timestamp <= hardcoded.Timestamp {
|
||||
return hardcoded
|
||||
|
||||
@@ -24,6 +24,7 @@ func countServers(allServers models.AllServers) int {
|
||||
len(allServers.Privado.Servers) +
|
||||
len(allServers.Purevpn.Servers) +
|
||||
len(allServers.Surfshark.Servers) +
|
||||
len(allServers.Torguard.Servers) +
|
||||
len(allServers.Vyprvpn.Servers) +
|
||||
len(allServers.Windscribe.Servers)
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
)
|
||||
|
||||
@@ -48,34 +47,62 @@ func (u *updater) updatePIA(ctx context.Context) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
servers := make([]models.PIAServer, 0, len(data.Regions))
|
||||
// Deduplicate servers with the same IP address
|
||||
type NetProtocols struct {
|
||||
tcp, udp bool
|
||||
}
|
||||
ipToProtocols := make(map[string]NetProtocols)
|
||||
|
||||
for _, region := range data.Regions {
|
||||
for _, udpServer := range region.Servers.UDP {
|
||||
protocols := ipToProtocols[udpServer.IP.String()]
|
||||
protocols.udp = true
|
||||
ipToProtocols[udpServer.IP.String()] = protocols
|
||||
}
|
||||
for _, tcpServer := range region.Servers.TCP {
|
||||
protocols := ipToProtocols[tcpServer.IP.String()]
|
||||
protocols.tcp = true
|
||||
ipToProtocols[tcpServer.IP.String()] = protocols
|
||||
}
|
||||
}
|
||||
|
||||
servers := make([]models.PIAServer, 0, len(ipToProtocols)) // set the capacity, not the length of the slice
|
||||
for _, region := range data.Regions {
|
||||
for _, udpServer := range region.Servers.UDP {
|
||||
protocols, ok := ipToProtocols[udpServer.IP.String()]
|
||||
if !ok { // already added that IP for a server
|
||||
continue
|
||||
}
|
||||
server := models.PIAServer{
|
||||
Region: region.Name,
|
||||
ServerName: udpServer.CN,
|
||||
Protocol: constants.UDP,
|
||||
TCP: protocols.tcp,
|
||||
UDP: protocols.udp,
|
||||
PortForward: region.PortForward,
|
||||
IP: udpServer.IP,
|
||||
}
|
||||
delete(ipToProtocols, udpServer.IP.String())
|
||||
servers = append(servers, server)
|
||||
}
|
||||
for _, tcpServer := range region.Servers.TCP {
|
||||
protocols, ok := ipToProtocols[tcpServer.IP.String()]
|
||||
if !ok { // already added that IP for a server
|
||||
continue
|
||||
}
|
||||
server := models.PIAServer{
|
||||
Region: region.Name,
|
||||
ServerName: tcpServer.CN,
|
||||
Protocol: constants.UDP,
|
||||
TCP: protocols.tcp,
|
||||
UDP: protocols.udp,
|
||||
PortForward: region.PortForward,
|
||||
IP: tcpServer.IP,
|
||||
}
|
||||
delete(ipToProtocols, tcpServer.IP.String())
|
||||
servers = append(servers, server)
|
||||
}
|
||||
}
|
||||
sort.Slice(servers, func(i, j int) bool {
|
||||
if servers[i].Region == servers[j].Region {
|
||||
if servers[i].ServerName == servers[j].ServerName {
|
||||
return servers[i].Protocol < servers[j].Protocol
|
||||
}
|
||||
return servers[i].ServerName < servers[j].ServerName
|
||||
}
|
||||
return servers[i].Region < servers[j].Region
|
||||
|
||||
@@ -86,7 +86,8 @@ func resolveRepeat(ctx context.Context, lookupIP lookupIPFunc, host string,
|
||||
repetition int, timeBetween time.Duration) (ips []net.IP, err error) {
|
||||
uniqueIPs := make(map[string]struct{})
|
||||
|
||||
for i := 0; i < repetition; i++ {
|
||||
i := 0
|
||||
for {
|
||||
newIPs, err := lookupIP(ctx, host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -95,6 +96,12 @@ func resolveRepeat(ctx context.Context, lookupIP lookupIPFunc, host string,
|
||||
key := ip.String()
|
||||
uniqueIPs[key] = struct{}{}
|
||||
}
|
||||
|
||||
i++
|
||||
if i == repetition {
|
||||
break
|
||||
}
|
||||
|
||||
timer := time.NewTimer(timeBetween)
|
||||
select {
|
||||
case <-timer.C:
|
||||
|
||||
114
internal/updater/torguard.go
Normal file
114
internal/updater/torguard.go
Normal file
@@ -0,0 +1,114 @@
|
||||
package updater
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
"github.com/qdm12/golibs/network"
|
||||
)
|
||||
|
||||
func (u *updater) updateTorguard(ctx context.Context) (err error) {
|
||||
servers, warnings, err := findTorguardServersFromZip(ctx, u.client)
|
||||
if u.options.CLI {
|
||||
for _, warning := range warnings {
|
||||
u.logger.Warn("Torguard: %s", warning)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot update Torguard servers: %w", err)
|
||||
}
|
||||
if u.options.Stdout {
|
||||
u.println(stringifyTorguardServers(servers))
|
||||
}
|
||||
u.servers.Torguard.Timestamp = u.timeNow().Unix()
|
||||
u.servers.Torguard.Servers = servers
|
||||
return nil
|
||||
}
|
||||
|
||||
func findTorguardServersFromZip(ctx context.Context, client network.Client) (
|
||||
servers []models.TorguardServer, warnings []string, err error) {
|
||||
// Note: all servers do both TCP and UDP
|
||||
const zipURL = "https://torguard.net/downloads/OpenVPN-TCP-Linux.zip"
|
||||
|
||||
contents, err := fetchAndExtractFiles(ctx, client, zipURL)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
for fileName, content := range contents {
|
||||
var server models.TorguardServer
|
||||
|
||||
const prefix = "TorGuard."
|
||||
const suffix = ".ovpn"
|
||||
s := strings.TrimPrefix(fileName, prefix)
|
||||
s = strings.TrimSuffix(s, suffix)
|
||||
|
||||
switch {
|
||||
case strings.Count(s, ".") == 1 && !strings.HasPrefix(s, "USA"):
|
||||
parts := strings.Split(s, ".")
|
||||
server.Country = parts[0]
|
||||
server.City = parts[1]
|
||||
case strings.HasPrefix(s, "USA"):
|
||||
server.Country = "USA"
|
||||
s = strings.TrimPrefix(s, "USA-")
|
||||
s = strings.ReplaceAll(s, "-", " ")
|
||||
s = strings.ReplaceAll(s, ".", " ")
|
||||
s = strings.ToLower(s)
|
||||
s = strings.Title(s)
|
||||
server.City = s
|
||||
default:
|
||||
server.Country = s
|
||||
}
|
||||
|
||||
hostnames := extractRemoteHostsFromOpenvpn(content, true, false)
|
||||
if len(hostnames) != 1 {
|
||||
warning := "found " + strconv.Itoa(len(hostnames)) +
|
||||
" hostname(s) instead of 1 in " + fileName
|
||||
warnings = append(warnings, warning)
|
||||
continue
|
||||
}
|
||||
server.Hostname = hostnames[0]
|
||||
|
||||
IPs := extractRemoteHostsFromOpenvpn(content, false, true)
|
||||
if len(IPs) != 1 {
|
||||
warning := "found " + strconv.Itoa(len(IPs)) +
|
||||
" IP(s) instead of 1 in " + fileName
|
||||
warnings = append(warnings, warning)
|
||||
continue
|
||||
}
|
||||
server.IP = net.ParseIP(IPs[0])
|
||||
if server.IP == nil {
|
||||
warnings = append(warnings, "IP address "+IPs[0]+" is not valid in file "+fileName)
|
||||
}
|
||||
|
||||
servers = append(servers, server)
|
||||
}
|
||||
|
||||
sort.Slice(servers, func(i, j int) bool {
|
||||
if servers[i].Country == servers[j].Country {
|
||||
if servers[i].City == servers[j].City {
|
||||
return servers[i].Hostname < servers[j].Hostname
|
||||
}
|
||||
return servers[i].City < servers[j].City
|
||||
}
|
||||
return servers[i].Country < servers[j].Country
|
||||
})
|
||||
|
||||
return servers, warnings, nil
|
||||
}
|
||||
|
||||
func stringifyTorguardServers(servers []models.TorguardServer) (s string) {
|
||||
s = "func TorguardServers() []models.TorguardServer {\n"
|
||||
s += " return []models.TorguardServer{\n"
|
||||
for _, server := range servers {
|
||||
s += " " + server.String() + ",\n"
|
||||
}
|
||||
s += " }\n"
|
||||
s += "}"
|
||||
return s
|
||||
}
|
||||
@@ -50,8 +50,8 @@ func New(settings configuration.Updater, httpClient *http.Client,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO parallelize DNS resolution.
|
||||
func (u *updater) UpdateServers(ctx context.Context) (allServers models.AllServers, err error) { //nolint:gocognit
|
||||
//nolint:gocognit,gocyclo
|
||||
func (u *updater) UpdateServers(ctx context.Context) (allServers models.AllServers, err error) {
|
||||
if u.options.Cyberghost {
|
||||
u.logger.Info("updating Cyberghost servers...")
|
||||
if err := u.updateCyberghost(ctx); err != nil {
|
||||
@@ -124,6 +124,16 @@ func (u *updater) UpdateServers(ctx context.Context) (allServers models.AllServe
|
||||
}
|
||||
}
|
||||
|
||||
if u.options.Torguard {
|
||||
u.logger.Info("updating Torguard servers...")
|
||||
if err := u.updateTorguard(ctx); err != nil {
|
||||
if ctxErr := ctx.Err(); ctxErr != nil {
|
||||
return allServers, ctxErr
|
||||
}
|
||||
u.logger.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
if u.options.Vyprvpn {
|
||||
u.logger.Info("updating Vyprvpn servers...")
|
||||
if err := u.updateVyprvpn(ctx); err != nil {
|
||||
|
||||
@@ -14,7 +14,7 @@ func (u *updater) updateVyprvpn(ctx context.Context) (err error) {
|
||||
servers, warnings, err := findVyprvpnServers(ctx, u.client, u.lookupIP)
|
||||
if u.options.CLI {
|
||||
for _, warning := range warnings {
|
||||
u.logger.Warn("Privado: %s", warning)
|
||||
u.logger.Warn("Vyprvpn: %s", warning)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user