VPN Unlimited support (#499)
- Fixes #420 - Revert to docker/build-push-action@v2.4.0
This commit is contained in:
3
.github/labels.yml
vendored
3
.github/labels.yml
vendored
@@ -53,6 +53,9 @@
|
||||
- name: ":cloud: Torguard"
|
||||
color: "cfe8d4"
|
||||
description: ""
|
||||
- name: ":cloud: VPNUnlimited"
|
||||
color: "cfe8d4"
|
||||
description: ""
|
||||
- name: ":cloud: Vyprvpn"
|
||||
color: "cfe8d4"
|
||||
description: ""
|
||||
|
||||
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -86,7 +86,7 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Build and push final image
|
||||
uses: docker/build-push-action@v2
|
||||
uses: docker/build-push-action@v2.4.0
|
||||
with:
|
||||
platforms: ${{ steps.vars.outputs.platforms }}
|
||||
build-args: |
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
*Lightweight swiss-knife-like VPN client to tunnel to Cyberghost, FastestVPN,
|
||||
HideMyAss, IVPN, Mullvad, NordVPN, Privado, Private Internet Access, PrivateVPN,
|
||||
ProtonVPN, PureVPN, Surfshark, TorGuard, VyprVPN and Windscribe VPN servers
|
||||
ProtonVPN, PureVPN, Surfshark, TorGuard, VPNUnlimited, VyprVPN and Windscribe VPN servers
|
||||
using Go, OpenVPN, iptables, DNS over TLS, ShadowSocks and an HTTP proxy*
|
||||
|
||||
**ANNOUNCEMENT**:
|
||||
@@ -39,7 +39,7 @@ using Go, OpenVPN, iptables, DNS over TLS, ShadowSocks and an HTTP proxy*
|
||||
## Features
|
||||
|
||||
- Based on Alpine 3.13 for a small Docker image of 54MB
|
||||
- Supports: **Cyberghost**, **FastestVPN**, **HideMyAss**, **IVPN**, **Mullvad**, **NordVPN**, **Privado**, **Private Internet Access**, **PrivateVPN**, **ProtonVPN**, **PureVPN**, **Surfshark**, **TorGuard**, **Vyprvpn**, **Windscribe** servers
|
||||
- Supports: **Cyberghost**, **FastestVPN**, **HideMyAss**, **IVPN**, **Mullvad**, **NordVPN**, **Privado**, **Private Internet Access**, **PrivateVPN**, **ProtonVPN**, **PureVPN**, **Surfshark**, **TorGuard**, **VPNUnlimited**, **Vyprvpn**, **Windscribe** 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
|
||||
|
||||
@@ -43,6 +43,7 @@ func (c *cli) Update(ctx context.Context, args []string, os os.OS, logger loggin
|
||||
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.VPNUnlimited, "vpnunlimited", false, "Update VPN Unlimited 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 {
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
package configuration
|
||||
|
||||
import (
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
"github.com/qdm12/golibs/params"
|
||||
)
|
||||
@@ -44,12 +40,12 @@ func (settings *Provider) readCyberghost(r reader) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
settings.ExtraConfigOptions.ClientKey, err = readCyberghostClientKey(r)
|
||||
settings.ExtraConfigOptions.ClientKey, err = readClientKey(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
settings.ExtraConfigOptions.ClientCertificate, err = readCyberghostClientCertificate(r)
|
||||
settings.ExtraConfigOptions.ClientCertificate, err = readClientCertificate(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -72,49 +68,3 @@ func (settings *Provider) readCyberghost(r reader) (err error) {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func readCyberghostClientKey(r reader) (clientKey string, err error) {
|
||||
b, err := r.getFromFileOrSecretFile("OPENVPN_CLIENTKEY", constants.ClientKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return extractClientKey(b)
|
||||
}
|
||||
|
||||
var errDecodePEMBlockClientKey = errors.New("cannot decode PEM block from client key")
|
||||
|
||||
func extractClientKey(b []byte) (key string, err error) {
|
||||
pemBlock, _ := pem.Decode(b)
|
||||
if pemBlock == nil {
|
||||
return "", errDecodePEMBlockClientKey
|
||||
}
|
||||
parsedBytes := pem.EncodeToMemory(pemBlock)
|
||||
s := string(parsedBytes)
|
||||
s = strings.ReplaceAll(s, "\n", "")
|
||||
s = strings.TrimPrefix(s, "-----BEGIN PRIVATE KEY-----")
|
||||
s = strings.TrimSuffix(s, "-----END PRIVATE KEY-----")
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func readCyberghostClientCertificate(r reader) (clientCertificate string, err error) {
|
||||
b, err := r.getFromFileOrSecretFile("OPENVPN_CLIENTCRT", constants.ClientCertificate)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return extractClientCertificate(b)
|
||||
}
|
||||
|
||||
var errDecodePEMBlockClientCert = errors.New("cannot decode PEM block from client certificate")
|
||||
|
||||
func extractClientCertificate(b []byte) (certificate string, err error) {
|
||||
pemBlock, _ := pem.Decode(b)
|
||||
if pemBlock == nil {
|
||||
return "", errDecodePEMBlockClientCert
|
||||
}
|
||||
parsedBytes := pem.EncodeToMemory(pemBlock)
|
||||
s := string(parsedBytes)
|
||||
s = strings.ReplaceAll(s, "\n", "")
|
||||
s = strings.TrimPrefix(s, "-----BEGIN CERTIFICATE-----")
|
||||
s = strings.TrimSuffix(s, "-----END CERTIFICATE-----")
|
||||
return s, nil
|
||||
}
|
||||
|
||||
55
internal/configuration/keys.go
Normal file
55
internal/configuration/keys.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package configuration
|
||||
|
||||
import (
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
)
|
||||
|
||||
func readClientKey(r reader) (clientKey string, err error) {
|
||||
b, err := r.getFromFileOrSecretFile("OPENVPN_CLIENTKEY", constants.ClientKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return extractClientKey(b)
|
||||
}
|
||||
|
||||
var errDecodePEMBlockClientKey = errors.New("cannot decode PEM block from client key")
|
||||
|
||||
func extractClientKey(b []byte) (key string, err error) {
|
||||
pemBlock, _ := pem.Decode(b)
|
||||
if pemBlock == nil {
|
||||
return "", errDecodePEMBlockClientKey
|
||||
}
|
||||
parsedBytes := pem.EncodeToMemory(pemBlock)
|
||||
s := string(parsedBytes)
|
||||
s = strings.ReplaceAll(s, "\n", "")
|
||||
s = strings.TrimPrefix(s, "-----BEGIN PRIVATE KEY-----")
|
||||
s = strings.TrimSuffix(s, "-----END PRIVATE KEY-----")
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func readClientCertificate(r reader) (clientCertificate string, err error) {
|
||||
b, err := r.getFromFileOrSecretFile("OPENVPN_CLIENTCRT", constants.ClientCertificate)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return extractClientCertificate(b)
|
||||
}
|
||||
|
||||
var errDecodePEMBlockClientCert = errors.New("cannot decode PEM block from client certificate")
|
||||
|
||||
func extractClientCertificate(b []byte) (certificate string, err error) {
|
||||
pemBlock, _ := pem.Decode(b)
|
||||
if pemBlock == nil {
|
||||
return "", errDecodePEMBlockClientCert
|
||||
}
|
||||
parsedBytes := pem.EncodeToMemory(pemBlock)
|
||||
s := string(parsedBytes)
|
||||
s = strings.ReplaceAll(s, "\n", "")
|
||||
s = strings.TrimPrefix(s, "-----BEGIN CERTIFICATE-----")
|
||||
s = strings.TrimSuffix(s, "-----END CERTIFICATE-----")
|
||||
return s, nil
|
||||
}
|
||||
@@ -70,7 +70,7 @@ func (settings *OpenVPN) read(r reader) (err error) {
|
||||
vpnsp, err := r.env.Inside("VPNSP", []string{
|
||||
"cyberghost", "fastestvpn", "hidemyass", "ivpn", "mullvad", "nordvpn",
|
||||
"privado", "pia", "private internet access", "privatevpn", "protonvpn",
|
||||
"purevpn", "surfshark", "torguard", "vyprvpn", "windscribe"},
|
||||
"purevpn", "surfshark", "torguard", constants.VPNUnlimited, "vyprvpn", "windscribe"},
|
||||
params.Default("private internet access"))
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -86,12 +86,13 @@ func (settings *OpenVPN) read(r reader) (err error) {
|
||||
return err
|
||||
}
|
||||
customConfig := settings.Config != ""
|
||||
credentialsRequired := true
|
||||
|
||||
if customConfig {
|
||||
credentialsRequired = false
|
||||
settings.Provider.Name = ""
|
||||
}
|
||||
|
||||
credentialsRequired := !customConfig && settings.Provider.Name != constants.VPNUnlimited
|
||||
|
||||
settings.User, err = r.getFromEnvOrSecretFile("OPENVPN_USER", credentialsRequired, []string{"USER"})
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -139,7 +140,10 @@ func (settings *OpenVPN) read(r reader) (err error) {
|
||||
return err
|
||||
}
|
||||
settings.MSSFix = uint16(mssFix)
|
||||
return settings.readProvider(r)
|
||||
}
|
||||
|
||||
func (settings *OpenVPN) readProvider(r reader) error {
|
||||
var readProvider func(r reader) error
|
||||
switch settings.Provider.Name {
|
||||
case "": // custom config
|
||||
@@ -170,6 +174,8 @@ func (settings *OpenVPN) read(r reader) (err error) {
|
||||
readProvider = settings.Provider.readSurfshark
|
||||
case constants.Torguard:
|
||||
readProvider = settings.Provider.readTorguard
|
||||
case constants.VPNUnlimited:
|
||||
readProvider = settings.Provider.readVPNUnlimited
|
||||
case constants.Vyprvpn:
|
||||
readProvider = settings.Provider.readVyprvpn
|
||||
case constants.Windscribe:
|
||||
@@ -177,6 +183,5 @@ func (settings *OpenVPN) read(r reader) (err error) {
|
||||
default:
|
||||
return fmt.Errorf("%w: %s", ErrInvalidVPNProvider, settings.Provider.Name)
|
||||
}
|
||||
|
||||
return readProvider(r)
|
||||
}
|
||||
|
||||
@@ -41,7 +41,8 @@ func Test_OpenVPN_JSON(t *testing.T) {
|
||||
"custom_port": 0,
|
||||
"numbers": null,
|
||||
"encryption_preset": "",
|
||||
"free_only": false
|
||||
"free_only": false,
|
||||
"stream_only": false
|
||||
},
|
||||
"extra_config": {
|
||||
"encryption_preset": "",
|
||||
|
||||
@@ -56,6 +56,8 @@ func (settings *Provider) lines() (lines []string) {
|
||||
providerLines = settings.surfsharkLines()
|
||||
case "torguard":
|
||||
providerLines = settings.torguardLines()
|
||||
case strings.ToLower(constants.VPNUnlimited):
|
||||
providerLines = settings.vpnUnlimitedLines()
|
||||
case "vyprvpn":
|
||||
providerLines = settings.vyprvpnLines()
|
||||
case "windscribe":
|
||||
|
||||
@@ -249,6 +249,31 @@ func Test_Provider_lines(t *testing.T) {
|
||||
" |--Hostnames: e",
|
||||
},
|
||||
},
|
||||
constants.VPNUnlimited: {
|
||||
settings: Provider{
|
||||
Name: constants.VPNUnlimited,
|
||||
ServerSelection: ServerSelection{
|
||||
Countries: []string{"a", "b"},
|
||||
Cities: []string{"c", "d"},
|
||||
Hostnames: []string{"e", "f"},
|
||||
FreeOnly: true,
|
||||
StreamOnly: true,
|
||||
},
|
||||
ExtraConfigOptions: ExtraConfigOptions{
|
||||
ClientKey: "a",
|
||||
},
|
||||
},
|
||||
lines: []string{
|
||||
"|--Vpn Unlimited settings:",
|
||||
" |--Network protocol: udp",
|
||||
" |--Countries: a, b",
|
||||
" |--Cities: c, d",
|
||||
" |--Hostnames: e, f",
|
||||
" |--Free servers only",
|
||||
" |--Stream servers only",
|
||||
" |--Client key is set",
|
||||
},
|
||||
},
|
||||
"vyprvpn": {
|
||||
settings: Provider{
|
||||
Name: constants.Vyprvpn,
|
||||
|
||||
@@ -15,9 +15,12 @@ type ServerSelection struct { //nolint:maligned
|
||||
// Cyberghost
|
||||
Group string `json:"group"`
|
||||
|
||||
Countries []string `json:"countries"` // Fastestvpn, HideMyAss, IVPN, Mullvad, PrivateVPN, Protonvpn, PureVPN
|
||||
Cities []string `json:"cities"` // HideMyAss, IVPN, Mullvad, PrivateVPN, Protonvpn, PureVPN, Windscribe
|
||||
Hostnames []string `json:"hostnames"` // Fastestvpn, HideMyAss, IVPN, PrivateVPN, Windscribe, Privado, Protonvpn
|
||||
// Fastestvpn, HideMyAss, IVPN, Mullvad, PrivateVPN, Protonvpn, PureVPN, VPNUnlimited
|
||||
Countries []string `json:"countries"`
|
||||
// HideMyAss, IVPN, Mullvad, PrivateVPN, Protonvpn, PureVPN, VPNUnlimited, Windscribe
|
||||
Cities []string `json:"cities"`
|
||||
// Fastestvpn, HideMyAss, IVPN, PrivateVPN, Windscribe, Privado, Protonvpn, VPNUnlimited
|
||||
Hostnames []string `json:"hostnames"`
|
||||
Names []string `json:"names"` // Protonvpn
|
||||
|
||||
// Mullvad
|
||||
@@ -35,11 +38,14 @@ type ServerSelection struct { //nolint:maligned
|
||||
|
||||
// ProtonVPN
|
||||
FreeOnly bool `json:"free_only"`
|
||||
|
||||
// VPNUnlimited
|
||||
StreamOnly bool `json:"stream_only"`
|
||||
}
|
||||
|
||||
type ExtraConfigOptions struct {
|
||||
ClientCertificate string `json:"-"` // Cyberghost
|
||||
ClientKey string `json:"-"` // Cyberghost
|
||||
ClientKey string `json:"-"` // Cyberghost, VPNUnlimited
|
||||
EncryptionPreset string `json:"encryption_preset"` // PIA
|
||||
OpenVPNIPv6 bool `json:"openvpn_ipv6"` // Mullvad
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ func (settings *Provider) readTorguard(r reader) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.TorguardHostnamesChoices())
|
||||
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.TorguardHostnameChoices())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ type Updater struct {
|
||||
Purevpn bool `json:"purevpn"`
|
||||
Surfshark bool `json:"surfshark"`
|
||||
Torguard bool `json:"torguard"`
|
||||
VPNUnlimited bool `json:"vpnunlimited"`
|
||||
Vyprvpn bool `json:"vyprvpn"`
|
||||
Windscribe bool `json:"windscribe"`
|
||||
// The two below should be used in CLI mode only
|
||||
@@ -60,6 +61,7 @@ func (settings *Updater) read(r reader) (err error) {
|
||||
settings.Purevpn = true
|
||||
settings.Surfshark = true
|
||||
settings.Torguard = true
|
||||
settings.VPNUnlimited = true
|
||||
settings.Vyprvpn = true
|
||||
settings.Windscribe = true
|
||||
settings.Stdout = false
|
||||
|
||||
85
internal/configuration/vpnunlimited.go
Normal file
85
internal/configuration/vpnunlimited.go
Normal file
@@ -0,0 +1,85 @@
|
||||
package configuration
|
||||
|
||||
import (
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
"github.com/qdm12/golibs/params"
|
||||
)
|
||||
|
||||
func (settings *Provider) vpnUnlimitedLines() (lines []string) {
|
||||
if len(settings.ServerSelection.Countries) > 0 {
|
||||
lines = append(lines, lastIndent+"Countries: "+commaJoin(settings.ServerSelection.Countries))
|
||||
}
|
||||
|
||||
if len(settings.ServerSelection.Cities) > 0 {
|
||||
lines = append(lines, lastIndent+"Cities: "+commaJoin(settings.ServerSelection.Cities))
|
||||
}
|
||||
|
||||
if len(settings.ServerSelection.Hostnames) > 0 {
|
||||
lines = append(lines, lastIndent+"Hostnames: "+commaJoin(settings.ServerSelection.Hostnames))
|
||||
}
|
||||
|
||||
if settings.ServerSelection.FreeOnly {
|
||||
lines = append(lines, lastIndent+"Free servers only")
|
||||
}
|
||||
|
||||
if settings.ServerSelection.StreamOnly {
|
||||
lines = append(lines, lastIndent+"Stream servers only")
|
||||
}
|
||||
|
||||
if settings.ExtraConfigOptions.ClientKey != "" {
|
||||
lines = append(lines, lastIndent+"Client key is set")
|
||||
}
|
||||
|
||||
return lines
|
||||
}
|
||||
|
||||
func (settings *Provider) readVPNUnlimited(r reader) (err error) {
|
||||
settings.Name = constants.VPNUnlimited
|
||||
|
||||
settings.ServerSelection.TCP, err = readProtocol(r.env)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
settings.ServerSelection.TargetIP, err = readTargetIP(r.env)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
settings.ExtraConfigOptions.ClientKey, err = readClientKey(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
settings.ExtraConfigOptions.ClientCertificate, err = readClientCertificate(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.IvpnCountryChoices())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.IvpnCityChoices())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.IvpnHostnameChoices())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
settings.ServerSelection.FreeOnly, err = r.env.YesNo("FREE_ONLY", params.Default("no"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
settings.ServerSelection.StreamOnly, err = r.env.YesNo("STREAM_ONLY", params.Default("no"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
42
internal/configuration/vpnunlimited_test.go
Normal file
42
internal/configuration/vpnunlimited_test.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package configuration
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_Provider_vpnUnlimitedLines(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
testCases := map[string]struct {
|
||||
settings Provider
|
||||
lines []string
|
||||
}{
|
||||
"empty settings": {},
|
||||
"full settings": {
|
||||
settings: Provider{
|
||||
ServerSelection: ServerSelection{
|
||||
Countries: []string{"A", "B"},
|
||||
Cities: []string{"C", "D"},
|
||||
Hostnames: []string{"E", "F"},
|
||||
},
|
||||
},
|
||||
lines: []string{
|
||||
"|--Countries: A, B",
|
||||
"|--Cities: C, D",
|
||||
"|--Hostnames: E, F",
|
||||
},
|
||||
},
|
||||
}
|
||||
for name, testCase := range testCases {
|
||||
testCase := testCase
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
lines := testCase.settings.vpnUnlimitedLines()
|
||||
|
||||
assert.Equal(t, testCase.lines, lines)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -136,6 +136,7 @@ func CountryCodes() map[string]string {
|
||||
"mg": "Madagascar",
|
||||
"mw": "Malawi",
|
||||
"my": "Malaysia",
|
||||
"mys": "Kuala Lumpur",
|
||||
"mv": "Maldives",
|
||||
"ml": "Mali",
|
||||
"mt": "Malta",
|
||||
|
||||
@@ -71,6 +71,11 @@ func GetAllServers() (allServers models.AllServers) {
|
||||
Timestamp: 1620611129,
|
||||
Servers: TorguardServers(),
|
||||
},
|
||||
VPNUnlimited: models.VPNUnlimitedServers{
|
||||
Version: 1,
|
||||
Timestamp: 1623950304,
|
||||
Servers: VPNUnlimitedServers(),
|
||||
},
|
||||
Vyprvpn: models.VyprvpnServers{
|
||||
Version: 2,
|
||||
Timestamp: 1620612506,
|
||||
|
||||
@@ -100,6 +100,11 @@ func Test_versions(t *testing.T) {
|
||||
version: allServers.Torguard.Version,
|
||||
digest: "6eb9028e",
|
||||
},
|
||||
"VPN Unlimited": {
|
||||
model: models.VPNUnlimitedServer{},
|
||||
version: allServers.VPNUnlimited.Version,
|
||||
digest: "5cb51319",
|
||||
},
|
||||
"Vyprvpn": {
|
||||
model: models.VyprvpnServer{},
|
||||
version: allServers.Vyprvpn.Version,
|
||||
@@ -212,6 +217,11 @@ func Test_timestamps(t *testing.T) {
|
||||
timestamp: allServers.Torguard.Timestamp,
|
||||
digest: "af54b9e8",
|
||||
},
|
||||
"VPN Unlimited": {
|
||||
servers: allServers.VPNUnlimited.Servers,
|
||||
timestamp: allServers.VPNUnlimited.Timestamp,
|
||||
digest: "f6ddb84c",
|
||||
},
|
||||
"Vyprvpn": {
|
||||
servers: allServers.Vyprvpn.Servers,
|
||||
timestamp: allServers.Vyprvpn.Timestamp,
|
||||
|
||||
@@ -30,7 +30,7 @@ func TorguardCityChoices() (choices []string) {
|
||||
return makeUnique(choices)
|
||||
}
|
||||
|
||||
func TorguardHostnamesChoices() (choices []string) {
|
||||
func TorguardHostnameChoices() (choices []string) {
|
||||
servers := TorguardServers()
|
||||
choices = make([]string, len(servers))
|
||||
for i := range servers {
|
||||
|
||||
@@ -27,6 +27,8 @@ const (
|
||||
Surfshark = "surfshark"
|
||||
// Torguard is a VPN provider.
|
||||
Torguard = "torguard"
|
||||
// VPNUnlimited is a VPN provider.
|
||||
VPNUnlimited = "vpn unlimited"
|
||||
// Vyprvpn is a VPN provider.
|
||||
Vyprvpn = "vyprvpn"
|
||||
// Windscribe is a VPN provider.
|
||||
|
||||
124
internal/constants/vpnunlimited.go
Normal file
124
internal/constants/vpnunlimited.go
Normal file
@@ -0,0 +1,124 @@
|
||||
package constants
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
)
|
||||
|
||||
//nolint:lll
|
||||
const (
|
||||
VPNUnlimitedCertificateAuthority = "MIIEjjCCA/egAwIBAgIJAKsVbHBdakXuMA0GCSqGSIb3DQEBBQUAMIHgMQswCQYDVQQGEwJVUzELMAkGA1UECBMCTlkxETAPBgNVBAcTCE5ldyBZb3JrMR8wHQYDVQQKExZTaW1wbGV4IFNvbHV0aW9ucyBJbmMuMRYwFAYDVQQLEw1WcG4gVW5saW1pdGVkMSMwIQYDVQQDExpzZXJ2ZXIudnBudW5saW1pdGVkYXBwLmNvbTEjMCEGA1UEKRMac2VydmVyLnZwbnVubGltaXRlZGFwcC5jb20xLjAsBgkqhkiG9w0BCQEWH3N1cHBvcnRAc2ltcGxleHNvbHV0aW9uc2luYy5jb20wHhcNMTMxMjE2MTM1OTQ0WhcNMjMxMjE0MTM1OTQ0WjCB4DELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAk5ZMREwDwYDVQQHEwhOZXcgWW9yazEfMB0GA1UEChMWU2ltcGxleCBTb2x1dGlvbnMgSW5jLjEWMBQGA1UECxMNVnBuIFVubGltaXRlZDEjMCEGA1UEAxMac2VydmVyLnZwbnVubGltaXRlZGFwcC5jb20xIzAhBgNVBCkTGnNlcnZlci52cG51bmxpbWl0ZWRhcHAuY29tMS4wLAYJKoZIhvcNAQkBFh9zdXBwb3J0QHNpbXBsZXhzb2x1dGlvbnNpbmMuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDADUzS8QWGvdhLFKsEzAiq5+b0ukKjBza0k6/dmCeYTvCVqGKg/h1IAtQdVVLAkmEp0zvGH7PuOhXm7zZrCouBr/RiG4tEcoRHwc6AJmowkYERlY7+xGx3OuNrD00QceNTsan0bn78jwt0zhFNmHdoTtFjgK3oqmQYSAtbEVWYgwIDAQABo4IBTDCCAUgwHQYDVR0OBBYEFKClmYP+tMNgWagUJCCHjtaui2k+MIIBFwYDVR0jBIIBDjCCAQqAFKClmYP+tMNgWagUJCCHjtaui2k+oYHmpIHjMIHgMQswCQYDVQQGEwJVUzELMAkGA1UECBMCTlkxETAPBgNVBAcTCE5ldyBZb3JrMR8wHQYDVQQKExZTaW1wbGV4IFNvbHV0aW9ucyBJbmMuMRYwFAYDVQQLEw1WcG4gVW5saW1pdGVkMSMwIQYDVQQDExpzZXJ2ZXIudnBudW5saW1pdGVkYXBwLmNvbTEjMCEGA1UEKRMac2VydmVyLnZwbnVubGltaXRlZGFwcC5jb20xLjAsBgkqhkiG9w0BCQEWH3N1cHBvcnRAc2ltcGxleHNvbHV0aW9uc2luYy5jb22CCQCrFWxwXWpF7jAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBALkWhfw7SSV7it+ZYZmT+cQbExjlYgQ40zae2J2CqIYACRcfsDHvh7Q+fiwSocevv2NE0dWi6WB2H6SiudYjvDvubAX/QbzfBxtbxCCoAIlfPCm8xOnWFN7TUJAzWwHJkKgEnu29GZHu2x8J+7VeDbKH5RTMHHe8FkSxh6Zz/BMN"
|
||||
)
|
||||
|
||||
func VPNUnlimitedCountryChoices() (choices []string) {
|
||||
servers := VPNUnlimitedServers()
|
||||
choices = make([]string, len(servers))
|
||||
for i := range servers {
|
||||
choices[i] = servers[i].Country
|
||||
}
|
||||
return makeUnique(choices)
|
||||
}
|
||||
|
||||
func VPNUnlimitedCityChoices() (choices []string) {
|
||||
servers := VPNUnlimitedServers()
|
||||
choices = make([]string, len(servers))
|
||||
for i := range servers {
|
||||
choices[i] = servers[i].City
|
||||
}
|
||||
return makeUnique(choices)
|
||||
}
|
||||
|
||||
func VPNUnlimitedHostnameChoices() (choices []string) {
|
||||
servers := VPNUnlimitedServers()
|
||||
choices = make([]string, len(servers))
|
||||
for i := range servers {
|
||||
choices[i] = servers[i].Hostname
|
||||
}
|
||||
return makeUnique(choices)
|
||||
}
|
||||
|
||||
//nolint:lll
|
||||
// VPNUnlimitedServers returns a slice of all the server information for VPNUnlimited.
|
||||
func VPNUnlimitedServers() []models.VPNUnlimitedServer {
|
||||
return []models.VPNUnlimitedServer{
|
||||
{Country: "Argentina", City: "", Hostname: "ar.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{131, 255, 4, 187}, {131, 255, 4, 235}}},
|
||||
{Country: "Australia", City: "Sydney", Hostname: "au-syd.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{139, 99, 131, 38}, {139, 99, 130, 220}}},
|
||||
{Country: "Austria", City: "", Hostname: "at.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{50, 7, 115, 19}, {185, 210, 219, 194}, {185, 210, 219, 198}}},
|
||||
{Country: "Belarus", City: "", Hostname: "by.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{185, 66, 68, 23}, {185, 66, 71, 108}, {185, 66, 71, 115}, {185, 66, 68, 84}, {185, 66, 71, 22}, {185, 66, 71, 122}, {185, 66, 71, 8}, {185, 66, 68, 18}}},
|
||||
{Country: "Belgium", City: "", Hostname: "be.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{37, 120, 143, 178}, {82, 102, 19, 110}}},
|
||||
{Country: "Bosnia and Herzegovina", City: "", Hostname: "ba.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{185, 164, 35, 37}}},
|
||||
{Country: "Brazil", City: "", Hostname: "br.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{66, 90, 70, 7}, {177, 67, 82, 222}, {177, 67, 82, 218}, {177, 67, 82, 228}, {177, 67, 82, 221}, {177, 67, 82, 219}, {177, 67, 82, 210}, {177, 67, 82, 223}}},
|
||||
{Country: "Bulgaria", City: "", Hostname: "bg.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{37, 120, 152, 26}}},
|
||||
{Country: "Canada", City: "", Hostname: "ca.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{192, 99, 7, 170}, {192, 99, 6, 164}, {192, 99, 14, 158}, {192, 99, 37, 199}, {192, 99, 6, 166}}},
|
||||
{Country: "Canada", City: "Toronto", Hostname: "ca-tr.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{184, 75, 213, 154}, {162, 253, 131, 106}, {162, 219, 176, 163}, {104, 254, 90, 58}, {104, 254, 92, 42}, {184, 75, 213, 194}, {204, 187, 100, 82}, {104, 254, 90, 34}}},
|
||||
{Country: "Canada", City: "Vancouver", Hostname: "ca-vn.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{162, 221, 202, 138}, {162, 221, 202, 134}, {162, 221, 202, 17}}},
|
||||
{Country: "Costa Rica", City: "", Hostname: "cr.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{138, 59, 19, 8}}},
|
||||
{Country: "Croatia", City: "", Hostname: "hr.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{85, 10, 56, 3}, {85, 10, 56, 100}, {85, 10, 51, 3}, {85, 10, 56, 4}}},
|
||||
{Country: "Cyprus", City: "", Hostname: "cy.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{194, 30, 136, 123}, {194, 30, 136, 102}}},
|
||||
{Country: "Czech Republic", City: "", Hostname: "cz.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{185, 216, 35, 46}}},
|
||||
{Country: "Denmark", City: "", Hostname: "dk.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{37, 120, 194, 174}, {89, 45, 7, 90}}},
|
||||
{Country: "Estonia", City: "", Hostname: "ee.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{185, 155, 96, 132}}},
|
||||
{Country: "Finland", City: "", Hostname: "fi.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{185, 112, 82, 26}, {91, 233, 116, 44}}},
|
||||
{Country: "France", City: "", Hostname: "fr.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{195, 154, 189, 85}, {195, 154, 204, 36}, {195, 154, 211, 84}, {62, 210, 38, 83}, {195, 154, 169, 66}, {195, 154, 166, 20}, {62, 210, 204, 161}, {62, 210, 205, 16}, {62, 210, 139, 124}, {195, 154, 221, 54}, {195, 154, 199, 175}, {195, 154, 189, 212}, {195, 154, 180, 96}, {195, 154, 199, 155}, {195, 154, 209, 149}, {195, 154, 222, 168}}},
|
||||
{Country: "France", City: "Roubaix", Hostname: "fr-rbx.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{151, 80, 27, 199}, {51, 255, 71, 16}, {147, 135, 137, 107}}},
|
||||
{Country: "Germany", City: "", Hostname: "de.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{84, 16, 236, 168}, {178, 162, 205, 77}, {178, 162, 205, 115}, {178, 162, 205, 82}, {84, 16, 238, 220}}},
|
||||
{Country: "Germany", City: "Düsseldorf", Hostname: "de-dus.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{146, 0, 42, 77}, {146, 0, 32, 121}, {85, 14, 243, 42}}},
|
||||
{Country: "Greece", City: "", Hostname: "gr.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{154, 57, 3, 36}}},
|
||||
{Country: "Hungary", City: "", Hostname: "hu.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{91, 219, 238, 174}}},
|
||||
{Country: "Iceland", City: "", Hostname: "is.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{37, 235, 49, 75}, {151, 236, 24, 114}, {37, 235, 49, 42}, {37, 235, 49, 244}, {37, 235, 49, 70}, {37, 235, 49, 15}, {151, 236, 24, 142}}},
|
||||
{Country: "India", City: "", Hostname: "in.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{103, 26, 204, 46}, {103, 26, 204, 84}}},
|
||||
{Country: "India", City: "Karnataka", Hostname: "in-ka.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{139, 59, 67, 224}, {139, 59, 65, 136}, {139, 59, 24, 197}, {139, 59, 24, 198}, {139, 59, 24, 199}, {139, 59, 24, 191}, {139, 59, 16, 78}}},
|
||||
{Country: "Ireland", City: "Dublin", Hostname: "ie-dub.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{188, 241, 178, 118}}},
|
||||
{Country: "Isle of Man", City: "", Hostname: "im.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{192, 71, 211, 15}, {37, 235, 55, 62}, {37, 235, 55, 68}, {37, 235, 55, 14}, {37, 235, 55, 45}}},
|
||||
{Country: "Israel", City: "", Hostname: "il.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{193, 182, 144, 170}, {193, 182, 144, 151}, {193, 182, 144, 23}}},
|
||||
{Country: "Italy", City: "Milan", Hostname: "it-mil.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{91, 193, 5, 50}}},
|
||||
{Country: "Japan", City: "", Hostname: "jp.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{172, 104, 67, 80}, {172, 104, 75, 121}, {161, 202, 97, 109}, {172, 104, 71, 35}, {172, 104, 126, 64}, {172, 104, 64, 213}, {85, 208, 110, 122}, {172, 104, 78, 67}, {172, 104, 83, 34}, {172, 104, 99, 172}, {161, 202, 97, 101}, {172, 104, 83, 13}, {172, 104, 87, 163}, {172, 104, 99, 14}}},
|
||||
{Country: "Korea", City: "", Hostname: "kr.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{160, 202, 162, 60}, {160, 202, 163, 122}}},
|
||||
{Country: "Kuala Lumpur", City: "", Hostname: "mys.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{111, 90, 141, 34}, {111, 90, 158, 159}}},
|
||||
{Country: "Latvia", City: "", Hostname: "lv.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{195, 123, 213, 172}, {195, 123, 209, 168}}},
|
||||
{Country: "Libya", City: "", Hostname: "ly.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{41, 208, 71, 39}}},
|
||||
{Country: "Lithuania", City: "", Hostname: "lt.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{185, 64, 105, 93}, {185, 64, 104, 142}, {185, 25, 48, 81}}},
|
||||
{Country: "Luxembourg", City: "", Hostname: "lu.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{94, 242, 246, 45}, {94, 242, 246, 46}, {94, 242, 246, 77}, {94, 242, 246, 38}, {94, 242, 246, 47}}},
|
||||
{Country: "Mexico", City: "", Hostname: "mx.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{169, 57, 35, 104}}},
|
||||
{Country: "Moldova", City: "", Hostname: "md.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{185, 163, 46, 141}}},
|
||||
{Country: "Netherlands", City: "", Hostname: "nl.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{190, 2, 132, 115}, {190, 2, 132, 16}, {190, 2, 132, 52}}},
|
||||
{Country: "New Zealand", City: "", Hostname: "nz.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{103, 75, 119, 109}, {103, 75, 119, 107}, {103, 75, 119, 105}, {103, 75, 119, 102}, {103, 75, 119, 103}, {103, 75, 119, 104}, {103, 75, 119, 106}, {103, 75, 119, 108}, {103, 75, 119, 101}, {103, 75, 119, 11}}},
|
||||
{Country: "Norway", City: "", Hostname: "no.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{185, 90, 61, 21}, {185, 90, 61, 74}}},
|
||||
{Country: "Oman", City: "", Hostname: "om.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{185, 226, 124, 110}}},
|
||||
{Country: "Poland", City: "", Hostname: "pl.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{37, 120, 156, 234}}},
|
||||
{Country: "Portugal", City: "", Hostname: "pt.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{185, 31, 159, 110}}},
|
||||
{Country: "Romania", City: "", Hostname: "ro.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{185, 144, 83, 11}, {93, 115, 92, 207}, {185, 144, 83, 13}, {77, 81, 98, 70}, {93, 115, 92, 208}}},
|
||||
{Country: "Serbia", City: "", Hostname: "rs.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{152, 89, 160, 142}}},
|
||||
{Country: "Singapore", City: "", Hostname: "sg-free.vpnunlimitedapp.com", Free: true, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{178, 128, 48, 177}, {188, 166, 177, 236}, {206, 189, 80, 158}, {178, 128, 117, 139}}},
|
||||
{Country: "Singapore", City: "", Hostname: "sg.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{50, 7, 60, 52}, {117, 20, 41, 9}, {117, 20, 41, 10}, {139, 99, 62, 61}}},
|
||||
{Country: "Slovakia", City: "", Hostname: "sk.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{46, 29, 2, 131}}},
|
||||
{Country: "Slovenia", City: "", Hostname: "si.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{192, 71, 244, 38}, {192, 71, 244, 28}}},
|
||||
{Country: "South Africa", City: "", Hostname: "za.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{129, 232, 130, 187}, {129, 232, 219, 196}, {129, 232, 133, 41}, {129, 232, 129, 157}, {129, 232, 219, 195}, {129, 232, 134, 122}, {129, 232, 130, 186}, {129, 232, 130, 179}}},
|
||||
{Country: "Spain", City: "", Hostname: "es.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{194, 99, 104, 58}, {195, 206, 107, 134}}},
|
||||
{Country: "Sweden", City: "", Hostname: "se.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{91, 132, 138, 174}}},
|
||||
{Country: "Switzerland", City: "", Hostname: "ch.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{152, 89, 162, 90}, {152, 89, 162, 86}}},
|
||||
{Country: "Thailand", City: "", Hostname: "th.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{103, 159, 3, 121}}},
|
||||
{Country: "Turkey", City: "", Hostname: "tr.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{185, 17, 115, 62}, {185, 73, 202, 218}}},
|
||||
{Country: "United Arab Emirates", City: "", Hostname: "ae.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{45, 9, 249, 201}, {45, 9, 249, 209}, {45, 9, 250, 147}, {45, 9, 250, 249}, {45, 9, 250, 138}, {45, 9, 249, 211}, {45, 9, 250, 144}, {45, 9, 249, 216}, {45, 9, 250, 145}, {45, 9, 250, 143}, {45, 9, 250, 134}, {45, 9, 250, 146}, {45, 9, 249, 202}}},
|
||||
{Country: "United Kingdom", City: "", Hostname: "uk.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{176, 227, 198, 122}, {77, 245, 65, 2}, {109, 73, 77, 18}, {5, 152, 213, 186}, {88, 150, 224, 74}, {88, 150, 180, 82}}},
|
||||
{Country: "United Kingdom", City: "London", Hostname: "uk-cv.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{5, 101, 169, 146}, {5, 101, 143, 66}, {178, 159, 10, 78}}},
|
||||
{Country: "United Kingdom", City: "London", Hostname: "uk-lon.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{78, 110, 160, 6}, {50, 7, 114, 220}, {5, 101, 136, 154}, {23, 106, 33, 89}}},
|
||||
{Country: "United States", City: "", Hostname: "us-stream.vpnunlimitedapp.com", Free: false, Stream: true, TCP: false, UDP: true, IPs: []net.IP{{167, 99, 96, 113}, {198, 199, 114, 155}, {192, 241, 194, 208}, {165, 227, 49, 171}, {159, 89, 128, 183}, {138, 197, 203, 142}, {64, 227, 111, 143}, {143, 110, 225, 79}, {164, 90, 144, 44}, {198, 199, 103, 243}, {198, 199, 96, 52}, {128, 199, 9, 51}, {143, 110, 156, 9}, {178, 128, 79, 75}, {198, 199, 97, 247}, {198, 199, 105, 87}, {198, 199, 103, 88}, {206, 189, 211, 205}, {206, 189, 165, 44}, {161, 35, 236, 242}}},
|
||||
{Country: "United States", City: "", Hostname: "us.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{199, 115, 117, 81}, {199, 115, 115, 139}, {207, 244, 72, 212}, {199, 115, 115, 160}, {199, 115, 117, 73}}},
|
||||
{Country: "United States", City: "Chicago", Hostname: "us-chi.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{66, 23, 205, 226}, {108, 62, 202, 211}, {173, 234, 41, 130}, {174, 34, 184, 130}}},
|
||||
{Country: "United States", City: "Dallas", Hostname: "us-dal.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{172, 241, 115, 99}, {172, 241, 113, 19}, {172, 241, 112, 92}}},
|
||||
{Country: "United States", City: "Denver", Hostname: "us-den.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{23, 237, 26, 131}, {23, 237, 26, 67}}},
|
||||
{Country: "United States", City: "Houston", Hostname: "us-hou.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{162, 218, 228, 194}, {162, 218, 228, 196}, {66, 187, 75, 122}, {162, 218, 229, 106}}},
|
||||
{Country: "United States", City: "Las Vegas", Hostname: "us-lv.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{185, 242, 5, 18}, {185, 242, 5, 22}}},
|
||||
{Country: "United States", City: "Los Angeles", Hostname: "us-la.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{64, 31, 33, 154}, {192, 184, 48, 10}, {23, 83, 37, 209}, {23, 83, 37, 213}, {45, 136, 131, 40}, {69, 162, 99, 70}}},
|
||||
{Country: "United States", City: "Miami", Hostname: "us-mia.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{186, 233, 185, 29}, {186, 233, 185, 79}, {186, 233, 185, 80}, {186, 233, 184, 187}, {186, 233, 184, 188}, {186, 233, 184, 37}, {186, 233, 184, 31}}},
|
||||
{Country: "United States", City: "New York", Hostname: "us-ny-free.vpnunlimitedapp.com", Free: true, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{64, 94, 215, 66}, {64, 94, 215, 162}, {64, 94, 215, 170}}},
|
||||
{Country: "United States", City: "New York", Hostname: "us-ny.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{64, 42, 178, 202}, {23, 105, 134, 162}, {64, 42, 178, 226}, {23, 237, 58, 112}, {23, 108, 31, 122}}},
|
||||
{Country: "United States", City: "Saint Louis", Hostname: "us-sl.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{69, 64, 58, 110}, {69, 64, 58, 255}, {209, 239, 123, 77}, {209, 239, 123, 107}}},
|
||||
{Country: "United States", City: "Salt Lake City", Hostname: "us-slc.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{209, 95, 53, 223}, {209, 95, 53, 225}}},
|
||||
{Country: "United States", City: "San Francisco", Hostname: "us-sf.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{209, 58, 139, 34}, {209, 58, 135, 72}, {209, 58, 130, 210}, {209, 58, 135, 106}, {209, 58, 135, 108}, {209, 58, 135, 74}, {209, 58, 139, 35}, {209, 58, 137, 94}, {172, 241, 251, 164}, {209, 58, 135, 120}}},
|
||||
{Country: "United States", City: "Seattle", Hostname: "us-sea.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{216, 244, 82, 50}, {23, 81, 209, 137}, {216, 244, 82, 210}}},
|
||||
{Country: "Vietnam", City: "", Hostname: "vn.vpnunlimitedapp.com", Free: false, Stream: false, TCP: false, UDP: true, IPs: []net.IP{{103, 9, 78, 84}, {146, 196, 67, 7}}},
|
||||
}
|
||||
}
|
||||
@@ -188,6 +188,22 @@ func (s *TorguardServer) String() string {
|
||||
s.Country, s.City, s.Hostname, s.TCP, s.UDP, goStringifyIPs(s.IPs))
|
||||
}
|
||||
|
||||
type VPNUnlimitedServer struct {
|
||||
Country string `json:"country"`
|
||||
City string `json:"city"`
|
||||
Hostname string `json:"hostname"`
|
||||
Free bool `json:"free"`
|
||||
Stream bool `json:"stream"`
|
||||
TCP bool `json:"tcp"`
|
||||
UDP bool `json:"udp"`
|
||||
IPs []net.IP `json:"ips"`
|
||||
}
|
||||
|
||||
func (s *VPNUnlimitedServer) String() string {
|
||||
return fmt.Sprintf("{Country: %q, City: %q, Hostname: %q, Free: %t, Stream: %t, TCP: %t, UDP: %t, IPs: %s}",
|
||||
s.Country, s.City, s.Hostname, s.Free, s.Stream, s.TCP, s.UDP, goStringifyIPs(s.IPs))
|
||||
}
|
||||
|
||||
type VyprvpnServer struct {
|
||||
Region string `json:"region"`
|
||||
Hostname string `json:"hostname"`
|
||||
|
||||
@@ -15,6 +15,7 @@ type AllServers struct {
|
||||
Purevpn PurevpnServers `json:"purevpn"`
|
||||
Surfshark SurfsharkServers `json:"surfshark"`
|
||||
Torguard TorguardServers `json:"torguard"`
|
||||
VPNUnlimited VPNUnlimitedServers `json:"vpnunlimited"`
|
||||
Vyprvpn VyprvpnServers `json:"vyprvpn"`
|
||||
Windscribe WindscribeServers `json:"windscribe"`
|
||||
}
|
||||
@@ -33,6 +34,7 @@ func (a *AllServers) Count() int {
|
||||
len(a.Purevpn.Servers) +
|
||||
len(a.Surfshark.Servers) +
|
||||
len(a.Torguard.Servers) +
|
||||
len(a.VPNUnlimited.Servers) +
|
||||
len(a.Vyprvpn.Servers) +
|
||||
len(a.Windscribe.Servers)
|
||||
}
|
||||
@@ -102,6 +104,11 @@ type TorguardServers struct {
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
Servers []TorguardServer `json:"servers"`
|
||||
}
|
||||
type VPNUnlimitedServers struct {
|
||||
Version uint16 `json:"version"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
Servers []VPNUnlimitedServer `json:"servers"`
|
||||
}
|
||||
type VyprvpnServers struct {
|
||||
Version uint16 `json:"version"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"github.com/qdm12/gluetun/internal/provider/purevpn"
|
||||
"github.com/qdm12/gluetun/internal/provider/surfshark"
|
||||
"github.com/qdm12/gluetun/internal/provider/torguard"
|
||||
"github.com/qdm12/gluetun/internal/provider/vpnunlimited"
|
||||
"github.com/qdm12/gluetun/internal/provider/vyprvpn"
|
||||
"github.com/qdm12/gluetun/internal/provider/windscribe"
|
||||
"github.com/qdm12/golibs/logging"
|
||||
@@ -69,6 +70,8 @@ func New(provider string, allServers models.AllServers, timeNow func() time.Time
|
||||
return surfshark.New(allServers.Surfshark.Servers, randSource)
|
||||
case constants.Torguard:
|
||||
return torguard.New(allServers.Torguard.Servers, randSource)
|
||||
case constants.VPNUnlimited:
|
||||
return vpnunlimited.New(allServers.VPNUnlimited.Servers, randSource)
|
||||
case constants.Vyprvpn:
|
||||
return vyprvpn.New(allServers.Vyprvpn.Servers, randSource)
|
||||
case constants.Windscribe:
|
||||
|
||||
44
internal/provider/vpnunlimited/connection.go
Normal file
44
internal/provider/vpnunlimited/connection.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package vpnunlimited
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration"
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
"github.com/qdm12/gluetun/internal/provider/utils"
|
||||
)
|
||||
|
||||
var ErrProtocolUnsupported = errors.New("network protocol is not supported")
|
||||
|
||||
func (p *Provider) GetOpenVPNConnection(selection configuration.ServerSelection) (
|
||||
connection models.OpenVPNConnection, err error) {
|
||||
const port = 1194
|
||||
const protocol = constants.UDP
|
||||
if selection.TCP {
|
||||
return connection, ErrProtocolUnsupported
|
||||
}
|
||||
|
||||
servers, err := p.filterServers(selection)
|
||||
if err != nil {
|
||||
return connection, err
|
||||
}
|
||||
|
||||
var connections []models.OpenVPNConnection
|
||||
for _, server := range servers {
|
||||
for _, IP := range server.IPs {
|
||||
connection := models.OpenVPNConnection{
|
||||
IP: IP,
|
||||
Port: port,
|
||||
Protocol: protocol,
|
||||
}
|
||||
connections = append(connections, connection)
|
||||
}
|
||||
}
|
||||
|
||||
if selection.TargetIP != nil {
|
||||
return utils.GetTargetIPConnection(connections, selection.TargetIP)
|
||||
}
|
||||
|
||||
return utils.PickRandomConnection(connections, p.randSource), nil
|
||||
}
|
||||
31
internal/provider/vpnunlimited/filter.go
Normal file
31
internal/provider/vpnunlimited/filter.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package vpnunlimited
|
||||
|
||||
import (
|
||||
"github.com/qdm12/gluetun/internal/configuration"
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
"github.com/qdm12/gluetun/internal/provider/utils"
|
||||
)
|
||||
|
||||
func (p *Provider) filterServers(selection configuration.ServerSelection) (
|
||||
servers []models.VPNUnlimitedServer, err error) {
|
||||
for _, server := range p.servers {
|
||||
switch {
|
||||
case
|
||||
utils.FilterByPossibilities(server.Country, selection.Countries),
|
||||
utils.FilterByPossibilities(server.City, selection.Cities),
|
||||
utils.FilterByPossibilities(server.Hostname, selection.Hostnames),
|
||||
selection.FreeOnly && !server.Free,
|
||||
selection.StreamOnly && !server.Stream,
|
||||
selection.TCP && !server.TCP,
|
||||
!selection.TCP && !server.UDP:
|
||||
default:
|
||||
servers = append(servers, server)
|
||||
}
|
||||
}
|
||||
|
||||
if len(servers) == 0 {
|
||||
return nil, utils.NoServerFoundError(selection)
|
||||
}
|
||||
|
||||
return servers, nil
|
||||
}
|
||||
69
internal/provider/vpnunlimited/openvpnconf.go
Normal file
69
internal/provider/vpnunlimited/openvpnconf.go
Normal file
@@ -0,0 +1,69 @@
|
||||
package vpnunlimited
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration"
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
"github.com/qdm12/gluetun/internal/provider/utils"
|
||||
)
|
||||
|
||||
func (p *Provider) BuildConf(connection models.OpenVPNConnection,
|
||||
username string, settings configuration.OpenVPN) (lines []string) {
|
||||
lines = []string{
|
||||
"client",
|
||||
"dev tun",
|
||||
"nobind",
|
||||
"persist-key",
|
||||
"tls-exit",
|
||||
"remote-cert-tls server",
|
||||
|
||||
// VPNUnlimited specific
|
||||
"reneg-sec 0",
|
||||
"ping 5",
|
||||
"ping-exit 30",
|
||||
"comp-lzo no",
|
||||
"route-metric 1",
|
||||
|
||||
// Added constant values
|
||||
"auth-nocache",
|
||||
"mute-replay-warnings",
|
||||
"pull-filter ignore \"auth-token\"", // prevent auth failed loops
|
||||
"auth-retry nointeract",
|
||||
"suppress-timestamps",
|
||||
|
||||
// Modified variables
|
||||
"verb " + strconv.Itoa(settings.Verbosity),
|
||||
// "auth-user-pass " + constants.OpenVPNAuthConf,
|
||||
connection.ProtoLine(),
|
||||
connection.RemoteLine(),
|
||||
}
|
||||
|
||||
if settings.Cipher != "" {
|
||||
lines = append(lines, utils.CipherLines(settings.Cipher, settings.Version)...)
|
||||
}
|
||||
|
||||
if settings.Auth != "" {
|
||||
lines = append(lines, "auth "+settings.Auth)
|
||||
}
|
||||
|
||||
if settings.MSSFix > 0 {
|
||||
lines = append(lines, "mssfix "+strconv.Itoa(int(settings.MSSFix)))
|
||||
}
|
||||
|
||||
if !settings.Root {
|
||||
lines = append(lines, "user "+username)
|
||||
}
|
||||
|
||||
lines = append(lines, utils.WrapOpenvpnCA(
|
||||
constants.VPNUnlimitedCertificateAuthority)...)
|
||||
lines = append(lines, utils.WrapOpenvpnCert(
|
||||
settings.Provider.ExtraConfigOptions.ClientCertificate)...)
|
||||
lines = append(lines, utils.WrapOpenvpnKey(
|
||||
settings.Provider.ExtraConfigOptions.ClientKey)...)
|
||||
|
||||
lines = append(lines, "")
|
||||
|
||||
return lines
|
||||
}
|
||||
17
internal/provider/vpnunlimited/portforward.go
Normal file
17
internal/provider/vpnunlimited/portforward.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package vpnunlimited
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/http"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/firewall"
|
||||
"github.com/qdm12/golibs/logging"
|
||||
"github.com/qdm12/golibs/os"
|
||||
)
|
||||
|
||||
func (p *Provider) 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 VPN Unlimited")
|
||||
}
|
||||
19
internal/provider/vpnunlimited/provider.go
Normal file
19
internal/provider/vpnunlimited/provider.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package vpnunlimited
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
)
|
||||
|
||||
type Provider struct {
|
||||
servers []models.VPNUnlimitedServer
|
||||
randSource rand.Source
|
||||
}
|
||||
|
||||
func New(servers []models.VPNUnlimitedServer, randSource rand.Source) *Provider {
|
||||
return &Provider{
|
||||
servers: servers,
|
||||
randSource: randSource,
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
)
|
||||
|
||||
@@ -45,6 +46,7 @@ func (s *storage) mergeServers(hardcoded, persisted models.AllServers) models.Al
|
||||
Purevpn: s.mergePureVPN(hardcoded.Purevpn, persisted.Purevpn),
|
||||
Surfshark: s.mergeSurfshark(hardcoded.Surfshark, persisted.Surfshark),
|
||||
Torguard: s.mergeTorguard(hardcoded.Torguard, persisted.Torguard),
|
||||
VPNUnlimited: s.mergeVPNUnlimited(hardcoded.VPNUnlimited, persisted.VPNUnlimited),
|
||||
Vyprvpn: s.mergeVyprvpn(hardcoded.Vyprvpn, persisted.Vyprvpn),
|
||||
Windscribe: s.mergeWindscribe(hardcoded.Windscribe, persisted.Windscribe),
|
||||
}
|
||||
@@ -234,6 +236,20 @@ func (s *storage) mergeTorguard(hardcoded, persisted models.TorguardServers) mod
|
||||
return persisted
|
||||
}
|
||||
|
||||
func (s *storage) mergeVPNUnlimited(hardcoded, persisted models.VPNUnlimitedServers) models.VPNUnlimitedServers {
|
||||
if persisted.Timestamp <= hardcoded.Timestamp {
|
||||
return hardcoded
|
||||
}
|
||||
versionDiff := hardcoded.Version - persisted.Version
|
||||
if versionDiff > 0 {
|
||||
s.logVersionDiff(constants.VPNUnlimited, versionDiff)
|
||||
return hardcoded
|
||||
}
|
||||
|
||||
s.logTimeDiff(constants.VPNUnlimited, persisted.Timestamp, hardcoded.Timestamp)
|
||||
return persisted
|
||||
}
|
||||
|
||||
func (s *storage) mergeVyprvpn(hardcoded, persisted models.VyprvpnServers) models.VyprvpnServers {
|
||||
if persisted.Timestamp <= hardcoded.Timestamp {
|
||||
return hardcoded
|
||||
|
||||
@@ -31,6 +31,7 @@ func countServers(allServers models.AllServers) int {
|
||||
len(allServers.Purevpn.Servers) +
|
||||
len(allServers.Surfshark.Servers) +
|
||||
len(allServers.Torguard.Servers) +
|
||||
len(allServers.VPNUnlimited.Servers) +
|
||||
len(allServers.Vyprvpn.Servers) +
|
||||
len(allServers.Windscribe.Servers)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
"github.com/qdm12/gluetun/internal/updater/providers/cyberghost"
|
||||
"github.com/qdm12/gluetun/internal/updater/providers/fastestvpn"
|
||||
"github.com/qdm12/gluetun/internal/updater/providers/hidemyass"
|
||||
@@ -17,6 +18,7 @@ import (
|
||||
"github.com/qdm12/gluetun/internal/updater/providers/purevpn"
|
||||
"github.com/qdm12/gluetun/internal/updater/providers/surfshark"
|
||||
"github.com/qdm12/gluetun/internal/updater/providers/torguard"
|
||||
"github.com/qdm12/gluetun/internal/updater/providers/vpnunlimited"
|
||||
"github.com/qdm12/gluetun/internal/updater/providers/vyprvpn"
|
||||
"github.com/qdm12/gluetun/internal/updater/providers/windscribe"
|
||||
)
|
||||
@@ -261,6 +263,26 @@ func (u *updater) updateTorguard(ctx context.Context) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *updater) updateVPNUnlimited(ctx context.Context) (err error) {
|
||||
minServers := getMinServers(len(u.servers.VPNUnlimited.Servers))
|
||||
servers, warnings, err := vpnunlimited.GetServers(
|
||||
ctx, u.unzipper, u.presolver, minServers)
|
||||
if u.options.CLI {
|
||||
for _, warning := range warnings {
|
||||
u.logger.Warn(constants.VPNUnlimited + ": " + warning)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if u.options.Stdout {
|
||||
u.println(vpnunlimited.Stringify(servers))
|
||||
}
|
||||
u.servers.VPNUnlimited.Timestamp = u.timeNow().Unix()
|
||||
u.servers.VPNUnlimited.Servers = servers
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *updater) updateVyprvpn(ctx context.Context) (err error) {
|
||||
minServers := getMinServers(len(u.servers.Vyprvpn.Servers))
|
||||
servers, warnings, err := vyprvpn.GetServers(
|
||||
|
||||
160
internal/updater/providers/vpnunlimited/constants.go
Normal file
160
internal/updater/providers/vpnunlimited/constants.go
Normal file
@@ -0,0 +1,160 @@
|
||||
package vpnunlimited
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
)
|
||||
|
||||
func getHostToServer() (hts hostToServer, warnings []string) {
|
||||
shortHTS := map[string]models.VPNUnlimitedServer{
|
||||
"ae": {},
|
||||
"ar": {},
|
||||
"at": {},
|
||||
"au-syd": {
|
||||
City: "Sydney",
|
||||
},
|
||||
"ba": {},
|
||||
"be": {},
|
||||
"bg": {},
|
||||
"br": {},
|
||||
"by": {},
|
||||
"ca-tr": {
|
||||
City: "Toronto",
|
||||
},
|
||||
"ca-vn": {
|
||||
City: "Vancouver",
|
||||
},
|
||||
"ca": {},
|
||||
"ch": {},
|
||||
"cr": {},
|
||||
"cy": {},
|
||||
"cz": {},
|
||||
"de-dus": {
|
||||
City: "Düsseldorf",
|
||||
},
|
||||
"de": {},
|
||||
"dk": {},
|
||||
"ee": {},
|
||||
"es": {},
|
||||
"fi": {},
|
||||
"fr-rbx": {
|
||||
City: "Roubaix",
|
||||
},
|
||||
"fr": {},
|
||||
"gr": {},
|
||||
"hr": {},
|
||||
"hu": {},
|
||||
"ie-dub": {
|
||||
City: "Dublin",
|
||||
},
|
||||
"il": {},
|
||||
"im": {},
|
||||
"in-ka": {
|
||||
City: "Karnataka",
|
||||
},
|
||||
"in": {},
|
||||
"is": {},
|
||||
"it-mil": {
|
||||
City: "Milan",
|
||||
},
|
||||
"jp": {},
|
||||
"kr": {},
|
||||
"lt": {},
|
||||
"lu": {},
|
||||
"lv": {},
|
||||
"ly": {},
|
||||
"md": {},
|
||||
"mx": {},
|
||||
"mys": {},
|
||||
"nl": {},
|
||||
"no": {},
|
||||
"nz": {},
|
||||
"om": {},
|
||||
"pl": {},
|
||||
"pt": {},
|
||||
"ro": {},
|
||||
"rs": {},
|
||||
"se": {},
|
||||
"sg-free": {
|
||||
Free: true,
|
||||
},
|
||||
"sg": {},
|
||||
"si": {},
|
||||
"sk": {},
|
||||
"th": {},
|
||||
"tr": {},
|
||||
"uk-cv": {
|
||||
City: "London",
|
||||
},
|
||||
"uk-lon": {
|
||||
City: "London",
|
||||
},
|
||||
"uk": {},
|
||||
"us-chi": {
|
||||
City: "Chicago",
|
||||
},
|
||||
"us-dal": {
|
||||
City: "Dallas",
|
||||
},
|
||||
"us-den": {
|
||||
City: "Denver",
|
||||
},
|
||||
"us-hou": {
|
||||
City: "Houston",
|
||||
},
|
||||
"us-la": {
|
||||
City: "Los Angeles",
|
||||
},
|
||||
"us-lv": {
|
||||
City: "Las Vegas",
|
||||
},
|
||||
"us-mia": {
|
||||
City: "Miami",
|
||||
},
|
||||
"us-ny-free": {
|
||||
City: "New York",
|
||||
Free: true,
|
||||
},
|
||||
"us-ny": {
|
||||
City: "New York",
|
||||
},
|
||||
"us-sea": {
|
||||
City: "Seattle",
|
||||
},
|
||||
"us-sf": {
|
||||
City: "San Francisco",
|
||||
},
|
||||
"us-sl": {
|
||||
City: "Saint Louis",
|
||||
},
|
||||
"us-slc": {
|
||||
City: "Salt Lake City",
|
||||
},
|
||||
"us-stream": {
|
||||
Stream: true,
|
||||
},
|
||||
"us": {},
|
||||
"vn": {},
|
||||
"za": {},
|
||||
}
|
||||
|
||||
hts = make(hostToServer, len(shortHTS))
|
||||
|
||||
countryCodesMap := constants.CountryCodes()
|
||||
for shortHost, server := range shortHTS {
|
||||
server.UDP = true
|
||||
server.Hostname = shortHost + ".vpnunlimitedapp.com"
|
||||
countryCode := strings.Split(shortHost, "-")[0]
|
||||
country, ok := countryCodesMap[countryCode]
|
||||
if !ok {
|
||||
warnings = append(warnings, "country code not found: "+countryCode)
|
||||
continue
|
||||
}
|
||||
server.Country = country
|
||||
hts[server.Hostname] = server
|
||||
}
|
||||
|
||||
return hts, warnings
|
||||
}
|
||||
38
internal/updater/providers/vpnunlimited/hosttoserver.go
Normal file
38
internal/updater/providers/vpnunlimited/hosttoserver.go
Normal file
@@ -0,0 +1,38 @@
|
||||
package vpnunlimited
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
)
|
||||
|
||||
type hostToServer map[string]models.VPNUnlimitedServer
|
||||
|
||||
func (hts hostToServer) toHostsSlice() (hosts []string) {
|
||||
hosts = make([]string, 0, len(hts))
|
||||
for host := range hts {
|
||||
hosts = append(hosts, host)
|
||||
}
|
||||
return hosts
|
||||
}
|
||||
|
||||
func (hts hostToServer) adaptWithIPs(hostToIPs map[string][]net.IP) {
|
||||
for host, IPs := range hostToIPs {
|
||||
server := hts[host]
|
||||
server.IPs = IPs
|
||||
hts[host] = server
|
||||
}
|
||||
for host, server := range hts {
|
||||
if len(server.IPs) == 0 {
|
||||
delete(hts, host)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (hts hostToServer) toServersSlice() (servers []models.VPNUnlimitedServer) {
|
||||
servers = make([]models.VPNUnlimitedServer, 0, len(hts))
|
||||
for _, server := range hts {
|
||||
servers = append(servers, server)
|
||||
}
|
||||
return servers
|
||||
}
|
||||
32
internal/updater/providers/vpnunlimited/resolve.go
Normal file
32
internal/updater/providers/vpnunlimited/resolve.go
Normal file
@@ -0,0 +1,32 @@
|
||||
package vpnunlimited
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/updater/resolver"
|
||||
)
|
||||
|
||||
func resolveHosts(ctx context.Context, presolver resolver.Parallel,
|
||||
hosts []string, minServers int) (hostToIPs map[string][]net.IP,
|
||||
warnings []string, err error) {
|
||||
const (
|
||||
maxFailRatio = 0.1
|
||||
maxDuration = 20 * time.Second
|
||||
betweenDuration = time.Second
|
||||
maxNoNew = 2
|
||||
maxFails = 2
|
||||
)
|
||||
settings := resolver.ParallelSettings{
|
||||
MaxFailRatio: maxFailRatio,
|
||||
MinFound: minServers,
|
||||
Repeat: resolver.RepeatSettings{
|
||||
MaxDuration: maxDuration,
|
||||
BetweenDuration: betweenDuration,
|
||||
MaxNoNew: maxNoNew,
|
||||
MaxFails: maxFails,
|
||||
},
|
||||
}
|
||||
return presolver.Resolve(ctx, hosts, settings)
|
||||
}
|
||||
42
internal/updater/providers/vpnunlimited/servers.go
Normal file
42
internal/updater/providers/vpnunlimited/servers.go
Normal file
@@ -0,0 +1,42 @@
|
||||
// Package vpnunlimited contains code to obtain the server information
|
||||
// for the VPNUnlimited provider.
|
||||
package vpnunlimited
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
"github.com/qdm12/gluetun/internal/updater/resolver"
|
||||
"github.com/qdm12/gluetun/internal/updater/unzip"
|
||||
)
|
||||
|
||||
var ErrNotEnoughServers = errors.New("not enough servers found")
|
||||
|
||||
func GetServers(ctx context.Context, unzipper unzip.Unzipper,
|
||||
presolver resolver.Parallel, minServers int) (
|
||||
servers []models.VPNUnlimitedServer, warnings []string, err error) {
|
||||
// Hardcoded data from a user provided ZIP file since it's behind a login wall
|
||||
hts, warnings := getHostToServer()
|
||||
|
||||
hosts := hts.toHostsSlice()
|
||||
hostToIPs, newWarnings, err := resolveHosts(ctx, presolver, hosts, minServers)
|
||||
warnings = append(warnings, newWarnings...)
|
||||
if err != nil {
|
||||
return nil, warnings, err
|
||||
}
|
||||
|
||||
hts.adaptWithIPs(hostToIPs)
|
||||
|
||||
servers = hts.toServersSlice()
|
||||
|
||||
if len(servers) < minServers {
|
||||
return nil, warnings, fmt.Errorf("%w: %d and expected at least %d",
|
||||
ErrNotEnoughServers, len(servers), minServers)
|
||||
}
|
||||
|
||||
sortServers(servers)
|
||||
|
||||
return servers, warnings, nil
|
||||
}
|
||||
19
internal/updater/providers/vpnunlimited/sort.go
Normal file
19
internal/updater/providers/vpnunlimited/sort.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package vpnunlimited
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
)
|
||||
|
||||
func sortServers(servers []models.VPNUnlimitedServer) {
|
||||
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
|
||||
})
|
||||
}
|
||||
14
internal/updater/providers/vpnunlimited/string.go
Normal file
14
internal/updater/providers/vpnunlimited/string.go
Normal file
@@ -0,0 +1,14 @@
|
||||
package vpnunlimited
|
||||
|
||||
import "github.com/qdm12/gluetun/internal/models"
|
||||
|
||||
func Stringify(servers []models.VPNUnlimitedServer) (s string) {
|
||||
s = "func VPNUnlimitedServers() []models.VPNUnlimitedServer {\n"
|
||||
s += " return []models.VPNUnlimitedServer{\n"
|
||||
for _, server := range servers {
|
||||
s += " " + server.String() + ",\n"
|
||||
}
|
||||
s += " }\n"
|
||||
s += "}"
|
||||
return s
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration"
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
"github.com/qdm12/gluetun/internal/updater/resolver"
|
||||
"github.com/qdm12/gluetun/internal/updater/unzip"
|
||||
@@ -186,6 +187,16 @@ func (u *updater) UpdateServers(ctx context.Context) (allServers models.AllServe
|
||||
}
|
||||
}
|
||||
|
||||
if u.options.VPNUnlimited {
|
||||
u.logger.Info("updating " + constants.VPNUnlimited + " servers...")
|
||||
if err := u.updateVPNUnlimited(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 {
|
||||
|
||||
Reference in New Issue
Block a user