diff --git a/.github/labels.yml b/.github/labels.yml index 7e5c013b..5adb93ac 100644 --- a/.github/labels.yml +++ b/.github/labels.yml @@ -27,6 +27,9 @@ - name: ":cloud: IVPN" color: "cfe8d4" description: "" +- name: ":cloud: ExpressVPN" + color: "cfe8d4" + description: "" - name: ":cloud: FastestVPN" color: "cfe8d4" description: "" diff --git a/README.md b/README.md index 25ce57db..3db2a152 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Gluetun VPN client -*Lightweight swiss-knife-like VPN client to tunnel to Cyberghost, FastestVPN, +*Lightweight swiss-knife-like VPN client to tunnel to Cyberghost, ExpressVPN, FastestVPN, HideMyAss, IPVanish, IVPN, Mullvad, NordVPN, Privado, Private Internet Access, PrivateVPN, ProtonVPN, PureVPN, Surfshark, TorGuard, VPNUnlimited, VyprVPN, WeVPN and Windscribe VPN servers using Go, OpenVPN or Wireguard, iptables, DNS over TLS, ShadowSocks and an HTTP proxy* @@ -60,7 +60,7 @@ using Go, OpenVPN or Wireguard, iptables, DNS over TLS, ShadowSocks and an HTTP ## Features - Based on Alpine 3.14 for a small Docker image of 31MB -- Supports: **Cyberghost**, **FastestVPN**, **HideMyAss**, **IPVanish**, **IVPN**, **Mullvad**, **NordVPN**, **Privado**, **Private Internet Access**, **PrivateVPN**, **ProtonVPN**, **PureVPN**, **Surfshark**, **TorGuard**, **VPNUnlimited**, **Vyprvpn**, **WeVPN**, **Windscribe** servers +- Supports: **Cyberghost**, **ExpressVPN**, **FastestVPN**, **HideMyAss**, **IPVanish**, **IVPN**, **Mullvad**, **NordVPN**, **Privado**, **Private Internet Access**, **PrivateVPN**, **ProtonVPN**, **PureVPN**, **Surfshark**, **TorGuard**, **VPNUnlimited**, **Vyprvpn**, **WeVPN**, **Windscribe** servers - Supports OpenVPN for all providers listed - Supports Wireguard - For **Mullvad**, **Ivpn** and **Windscribe** diff --git a/internal/cli/formatservers.go b/internal/cli/formatservers.go index 508ec1a9..a0aba335 100644 --- a/internal/cli/formatservers.go +++ b/internal/cli/formatservers.go @@ -25,13 +25,14 @@ var ( func (c *CLI) FormatServers(args []string) error { var format, output string - var cyberghost, fastestvpn, hideMyAss, ipvanish, ivpn, mullvad, + var cyberghost, expressvpn, fastestvpn, hideMyAss, ipvanish, ivpn, mullvad, nordvpn, pia, privado, privatevpn, protonvpn, purevpn, surfshark, torguard, vpnUnlimited, vyprvpn, wevpn, windscribe bool flagSet := flag.NewFlagSet("markdown", flag.ExitOnError) flagSet.StringVar(&format, "format", "markdown", "Format to use which can be: 'markdown'") flagSet.StringVar(&output, "output", "/dev/stdout", "Output file to write the formatted data to") flagSet.BoolVar(&cyberghost, "cyberghost", false, "Format Cyberghost servers") + flagSet.BoolVar(&expressvpn, "expressvpn", false, "Format ExpressVPN servers") flagSet.BoolVar(&fastestvpn, "fastestvpn", false, "Format FastestVPN servers") flagSet.BoolVar(&hideMyAss, "hidemyass", false, "Format HideMyAss servers") flagSet.BoolVar(&ipvanish, "ipvanish", false, "Format IpVanish servers") @@ -68,6 +69,8 @@ func (c *CLI) FormatServers(args []string) error { switch { case cyberghost: formatted = currentServers.Cyberghost.ToMarkdown() + case expressvpn: + formatted = currentServers.Expressvpn.ToMarkdown() case fastestvpn: formatted = currentServers.Fastestvpn.ToMarkdown() case hideMyAss: diff --git a/internal/cli/update.go b/internal/cli/update.go index b8dc3c8e..682fa9ea 100644 --- a/internal/cli/update.go +++ b/internal/cli/update.go @@ -44,6 +44,7 @@ func (c *CLI) Update(ctx context.Context, args []string, logger UpdaterLogger) e flagSet.StringVar(&options.DNSAddress, "dns", "8.8.8.8", "DNS resolver address to use") flagSet.BoolVar(&updateAll, "all", false, "Update servers for all VPN providers") flagSet.BoolVar(&options.Cyberghost, "cyberghost", false, "Update Cyberghost servers") + flagSet.BoolVar(&options.Expressvpn, "expressvpn", false, "Update ExpressVPN servers") flagSet.BoolVar(&options.Fastestvpn, "fastestvpn", false, "Update FastestVPN servers") flagSet.BoolVar(&options.HideMyAss, "hidemyass", false, "Update HideMyAss servers") flagSet.BoolVar(&options.Ipvanish, "ipvanish", false, "Update IpVanish servers") diff --git a/internal/configuration/expressvpn.go b/internal/configuration/expressvpn.go new file mode 100644 index 00000000..031c047e --- /dev/null +++ b/internal/configuration/expressvpn.go @@ -0,0 +1,40 @@ +package configuration + +import ( + "fmt" + + "github.com/qdm12/gluetun/internal/constants" +) + +func (settings *Provider) readExpressvpn(r reader) (err error) { + settings.Name = constants.Expressvpn + servers := r.servers.GetExpressvpn() + + settings.ServerSelection.TargetIP, err = readTargetIP(r.env) + if err != nil { + return err + } + + settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", + constants.ExpressvpnHostnameChoices(servers)) + if err != nil { + return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err) + } + + settings.ServerSelection.Countries, err = r.env.CSVInside("COUNTRY", constants.ExpressvpnCountriesChoices(servers)) + if err != nil { + return fmt.Errorf("environment variable COUNTRY: %w", err) + } + + settings.ServerSelection.Cities, err = r.env.CSVInside("CITY", constants.ExpressvpnCityChoices(servers)) + if err != nil { + return fmt.Errorf("environment variable CITY: %w", err) + } + + settings.ServerSelection.OpenVPN.TCP, err = readOpenVPNProtocol(r) + if err != nil { + return err + } + + return nil +} diff --git a/internal/configuration/provider.go b/internal/configuration/provider.go index f681d1f7..b39fac52 100644 --- a/internal/configuration/provider.go +++ b/internal/configuration/provider.go @@ -53,6 +53,8 @@ func (settings *Provider) read(r reader, vpnType string) error { err = settings.readCustom(r, vpnType) case constants.Cyberghost: err = settings.readCyberghost(r) + case constants.Expressvpn: + err = settings.readExpressvpn(r) case constants.Fastestvpn: err = settings.readFastestvpn(r) case constants.HideMyAss: @@ -104,7 +106,8 @@ func (settings *Provider) readVPNServiceProvider(r reader, vpnType string) (err case constants.OpenVPN: allowedVPNServiceProviders = []string{ constants.Custom, - "cyberghost", "fastestvpn", "hidemyass", "ipvanish", "ivpn", "mullvad", "nordvpn", + "cyberghost", constants.Expressvpn, "fastestvpn", "hidemyass", "ipvanish", + "ivpn", "mullvad", "nordvpn", "privado", "pia", "private internet access", "privatevpn", "protonvpn", "purevpn", "surfshark", "torguard", constants.VPNUnlimited, "vyprvpn", constants.Wevpn, "windscribe"} diff --git a/internal/configuration/provider_test.go b/internal/configuration/provider_test.go index d301b360..f6a6a7c2 100644 --- a/internal/configuration/provider_test.go +++ b/internal/configuration/provider_test.go @@ -35,6 +35,25 @@ func Test_Provider_lines(t *testing.T) { " |--Protocol: udp", }, }, + "expressvpn": { + settings: Provider{ + Name: constants.Expressvpn, + ServerSelection: ServerSelection{ + VPN: constants.OpenVPN, + Hostnames: []string{"a", "b"}, + Countries: []string{"c", "d"}, + Cities: []string{"e", "f"}, + }, + }, + lines: []string{ + "|--Expressvpn settings:", + " |--Countries: c, d", + " |--Cities: e, f", + " |--Hostnames: a, b", + " |--OpenVPN selection:", + " |--Protocol: udp", + }, + }, "fastestvpn": { settings: Provider{ Name: constants.Fastestvpn, diff --git a/internal/configuration/selection.go b/internal/configuration/selection.go index 1db5c55d..d34b0445 100644 --- a/internal/configuration/selection.go +++ b/internal/configuration/selection.go @@ -15,11 +15,11 @@ type ServerSelection struct { //nolint:maligned // Cyberghost, PIA, Protonvpn, Surfshark, Windscribe, Vyprvpn, NordVPN Regions []string `json:"regions"` - // Fastestvpn, HideMyAss, IPVanish, IVPN, Mullvad, PrivateVPN, Protonvpn, PureVPN, VPNUnlimited + // Expressvpn, Fastestvpn, HideMyAss, IPVanish, IVPN, Mullvad, PrivateVPN, Protonvpn, PureVPN, VPNUnlimited Countries []string `json:"countries"` - // HideMyAss, IPVanish, IVPN, Mullvad, PrivateVPN, Protonvpn, PureVPN, VPNUnlimited, WeVPN, Windscribe + // Expressvpn, HideMyAss, IPVanish, IVPN, Mullvad, PrivateVPN, Protonvpn, PureVPN, VPNUnlimited, WeVPN, Windscribe Cities []string `json:"cities"` - // Fastestvpn, HideMyAss, IPVanish, IVPN, PrivateVPN, Windscribe, Privado, Protonvpn, VPNUnlimited, WeVPN + // Expressvpn, Fastestvpn, HideMyAss, IPVanish, IVPN, PrivateVPN, Windscribe, Privado, Protonvpn, VPNUnlimited, WeVPN Hostnames []string `json:"hostnames"` Names []string `json:"names"` // Protonvpn diff --git a/internal/configuration/updater.go b/internal/configuration/updater.go index de5867dc..340bfe36 100644 --- a/internal/configuration/updater.go +++ b/internal/configuration/updater.go @@ -12,6 +12,7 @@ type Updater struct { Period time.Duration `json:"period"` DNSAddress string `json:"dns_address"` Cyberghost bool `json:"cyberghost"` + Expressvpn bool `json:"expressvpn"` Fastestvpn bool `json:"fastestvpn"` HideMyAss bool `json:"hidemyass"` Ipvanish bool `json:"ipvanish"` diff --git a/internal/constants/expressvpn.go b/internal/constants/expressvpn.go new file mode 100644 index 00000000..96663b1d --- /dev/null +++ b/internal/constants/expressvpn.go @@ -0,0 +1,37 @@ +package constants + +import ( + "github.com/qdm12/gluetun/internal/models" +) + +//nolint:lll +const ( + ExpressvpnCert = "MIIDTjCCAregAwIBAgIDKzZvMA0GCSqGSIb3DQEBCwUAMIGFMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExFTATBgNVBAcTDFNhbkZyYW5jaXNjbzEVMBMGA1UEChMMRm9ydC1GdW5zdG9uMRgwFgYDVQQDEw9Gb3J0LUZ1bnN0b24gQ0ExITAfBgkqhkiG9w0BCQEWEm1lQG15aG9zdC5teWRvbWFpbjAgFw0xNjExMDMwMzA2MThaGA8yMDY2MTEwMzAzMDYxOFowgYoxCzAJBgNVBAYTAlZHMQwwCgYDVQQIDANCVkkxEzARBgNVBAoMCkV4cHJlc3NWUE4xEzARBgNVBAsMCkV4cHJlc3NWUE4xHDAaBgNVBAMME2V4cHJlc3N2cG5fY3VzdG9tZXIxJTAjBgkqhkiG9w0BCQEWFnN1cHBvcnRAZXhwcmVzc3Zwbi5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrOYt/KOi2uMDGev3pXg8j1SO4J/4EVWDF7vJcKr2jrZlqD/zuAFx2W1YWvwumPO6PKH4PU9621aNdiumaUkv/RplCfznnnxqobhJuTE2oA+rS1bOq+9OhHwF9jgNXNVk+XX4d0toST5uGE6Z3OdmPBur8o5AlCf78PDSAwpFOw5HrgLqOEU4hTweC1/czX2VsvsHv22HRI6JMZgP8gGQii/p9iukqfaJvGdPciL5p1QRBUQIi8P8pNvEp1pVIpxYj7/LOUqb2DxFvgmp2v1IQ0Yu88SWsFk84+xAYHzfkLyS31Sqj5uLRBnJqx3fIlOihQ50GI72fwPMwo+OippvVAgMBAAGjPzA9MAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgSwMB0GA1UdDgQWBBSkBM1TCX9kBgFsv2RmOzudMXa9njANBgkqhkiG9w0BAQsFAAOBgQA+2e4b+33zFmA+1ZQ46kWkfiB+fEeDyMwMLeYYyDS2d8mZhNZKdOw7dy4Ifz9Vqzp4aKuQ6j61c6k1UaQQL0tskqWVzslSFvs9NZyUAJLLdGUc5TT2MiLwiXQwd4UvH6bGeePdhvB4+ZbW7VMD7TE8hZhjhAL4F6yAP1EQvg3LDA==" + ExpressvpnRSAKey = "MIIEpAIBAAKCAQEAqzmLfyjotrjAxnr96V4PI9UjuCf+BFVgxe7yXCq9o62Zag/87gBcdltWFr8Lpjzujyh+D1PettWjXYrpmlJL/0aZQn85558aqG4SbkxNqAPq0tWzqvvToR8BfY4DVzVZPl1+HdLaEk+bhhOmdznZjwbq/KOQJQn+/Dw0gMKRTsOR64C6jhFOIU8Hgtf3M19lbL7B79th0SOiTGYD/IBkIov6fYrpKn2ibxnT3Ii+adUEQVECIvD/KTbxKdaVSKcWI+/yzlKm9g8Rb4Jqdr9SENGLvPElrBZPOPsQGB835C8kt9Uqo+bi0QZyasd3yJTooUOdBiO9n8DzMKPjoqab1QIDAQABAoIBAHgsekC0SKi+AOcNOZqJ3pxqophE0V7fQX2KWGXhxZnUZMFxGTc936deMYzjZ1y0lUa6x8cgOUcfqHol3hDmw9oWBckLHGv5Wi9umdb6DOLoZO62+FQATSdfaJ9jheq2Ub2YxsRN0apaXzB6KDKz0oM0+sZ4Udn9Kw6DfuIELRIWwEx4w0v3gKW7YLC4Jkc4AwLkPK03xEA/qImfkCmaMPLhrgVQt+IFfP8bXzL7CCC04rNU/IS8pyjex+iUolnQZlbXntF7Bm4V2mz0827ZVqrgAb/hEQRlsTW3rRkVh+rrdoUE7BCZRTFmRCbLoShjN6XuSf4sAus8ch4UEN12gN0CgYEA4o/tSvij1iPaXLmt4KOEuxqmSGB8MLKhFde8lBbNdrDgxiIH9bH7khKx15XRTX0qLDbs8b2/UJygZG0Aa1kIBqZTXTgeMAuxPRTesALJPdqQ/ROnbJcdFkI7gllrAG8VB0fH4wTRsRd0vWEB6YlCdE107u6LEsLAHxOj9Q5819cCgYEAwXjx9RkQ2qITBx5Ewib8YsltA0n3cmRomPicLlsnKV5DfvyCLpFIsZ1h3f9dUpfxRLwzp8wcoLiq9cCoOGdu1udw/yBTqmhaXWhUK/g77f9Ze2ZB1OEhuyKLYJ1vW/h/Z/a1aPCMxZqsDTPCePsuO8Cez5gqs8LjM3W7EyzRxDMCgYEAvhHrDFt975fSiLoJgo0MPIAGAnBXn+8sLwv3m/FpW+rWF8LTFK/Fku12H5wDpNOdvswxijkauIE+GiJMGMLvdcyx4WHECaC1h73reJRNykOEIZ0Md5BrCZJ1JEzp9Mo8RQhWTEFtvfkkqgApP4g0pSeaMx0StaGG1kt+4IbP+68CgYBrZdQKlquAck/Vt7u7eyDHRcE5/ilaWtqlb/xizz7h++3D5C/v4b5UumTFcyg+3RGVclPKZcfOgDSGzzeSd/hTW46iUTOgeOUQzQVMkzPRXdoyYgVRQtgSpY5xR3O1vjAbahwx8LZ0SvQPMBhYSDbV/Isr+fBacWjl/AipEEwxeQKBgQDdrAEnVlOFoCLw4sUjsPoxkLjhTAgI7CYk5NNxX67Rnj0tp+Y49+sGUhl5sCGfMKkLShiON5P2oxZa+B0aPtQjsdnsFPa1uaZkK4c++SS6AetzYRpVDLmLp7/1CulE0z3O0sBekpwiuaqLJ9ZccC81g4+2j8j6c50rIAct3hxIxw==" + ExpressvpnTLSAuthOpenvpnStaticKeyV1 = "48d9999bd71095b10649c7cb471c1051b1afdece597cea06909b99303a18c67401597b12c04a787e98cdb619ee960d90a0165529dc650f3a5c6fbe77c91c137dcf55d863fcbe314df5f0b45dbe974d9bde33ef5b4803c3985531c6c23ca6906d6cd028efc8585d1b9e71003566bd7891b9cc9212bcba510109922eed87f5c8e66d8e59cbd82575261f02777372b2cd4ca5214c4a6513ff26dd568f574fd40d6cd450fc788160ff68434ce2bf6afb00e710a3198538f14c4d45d84ab42637872e778a6b35a124e700920879f1d003ba93dccdb953cdf32bea03f365760b0ed8002098d4ce20d045b45a83a8432cc737677aed27125592a7148d25c87fdbe0a3f6" + ExpressvpnCA = "MIIF+DCCA+CgAwIBAgIBATANBgkqhkiG9w0BAQ0FADCBhDELMAkGA1UEBhMCVkcxDDAKBgNVBAgMA0JWSTETMBEGA1UECgwKRXhwcmVzc1ZQTjETMBEGA1UECwwKRXhwcmVzc1ZQTjEWMBQGA1UEAwwNRXhwcmVzc1ZQTiBDQTElMCMGCSqGSIb3DQEJARYWc3VwcG9ydEBleHByZXNzdnBuLmNvbTAeFw0xNTEwMjEwMDAwMDBaFw0yNjA0MDEyMTEyMDBaMIGEMQswCQYDVQQGEwJWRzEMMAoGA1UECAwDQlZJMRMwEQYDVQQKDApFeHByZXNzVlBOMRMwEQYDVQQLDApFeHByZXNzVlBOMRYwFAYDVQQDDA1FeHByZXNzVlBOIENBMSUwIwYJKoZIhvcNAQkBFhZzdXBwb3J0QGV4cHJlc3N2cG4uY29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAxzXvHZ25OsESKRMQFINHJNqE9kVRLWJS50oVB2jxobudPhCsWvJSApvar8CB2RrqkVMhXu2HT3FBtDL91INg070qAyjjRpzEbDPWqQ1+G0tk0sjiJt2mXPJK2IlNFnhe6rTs09Pkpcp8qRhfZay/dIlmagohQAr4JvYL1Ajg9A3sLb8JkY03H6GhOF8EKYTqhrEppCcg4sQKQhNSytRoQAm8Ta+tnTYIedwWpqjUXP9YXFOvljPaixfYug24eAkpTjeuWTcELSyfnuiBeK+z9+5OYunhqFt2QZMq33kLFZGMN2gHRCzngxxphurypsPRo7jiFgQI1yLt8uZsEZ+otGEK91jjKfOC+g9TBy2RUtxk1neWcQ6syXDuc3rBNrGA8iM0ZoEqQ1BC8xWr3NYlSjqN+1mgpTAX3/Dxze4GzHd7AmYaYJV8xnKBVNphlMlg1giCAu5QXjMxPbfCgZiEFq/uq0SOKQJeT3AI/uVPSvwCMWByjyMbDpKKAK8Hy3UT5m4bCNu8J7bxj+vdnq0A2HPwtF0FwBl/TIM3zNsyFrZZ0j6jLRT50mFsgDBKcD4L/J5rjdCsKPu5rodhxe38rCx2GknP1Zkov4yoVCcR48+CQwg3oBkq0/EflvWUvcYApzs9SomUM/g+8Q/V0WOfJmFWuxN9YntZlnzHRSRjrvMCAwEAAaNzMHEwHQYDVR0OBBYEFIzmQGj8xS+0LLklwqHD45VVOZRJMB8GA1UdIwQYMBaAFIzmQGj8xS+0LLklwqHD45VVOZRJMA8GA1UdEwEB/wQFMAMBAf8wCwYDVR0PBAQDAgEGMBEGCWCGSAGG+EIBAQQEAwIBFjANBgkqhkiG9w0BAQ0FAAOCAgEAbHfuMKtojm1NgX7qSU2Rm2B5L8G0FuFP0L40dj8O5WHt45j2z8coMK90vrUnQEZNQmRzot7v3XjVzVlxBWYSsCEApTsSDNi/4BNFP8H/BUUtJuy2GFTO4wDVJnqNkZOHBmyVD75s1Y+W8a+zB4jkMeDEhOHZdwQ0l1fJDDgXal5f1UT5F5WH6/RwHmWTwX4GxuCiIVtx70CjkXqhM8yZtTp1UtHLRNYcNSIes0vrAPHPgoA5z9B8UvsOjuP+mfcjzi0LGGrY+2pJu0BKO2dRnarIZZABETIisI3FokoTszx5jpRPyxyUTuRDKWHrvi0PPtOmC8nFahfugWFUi6uBsqCaSeuex+ahnTPCq0b1l0Ozpg0YeE8CW1TL9Y92b01up2c+PP6wZOIm3JyTH+L5smDFbh80V42dKyGNdPXMg5IcJhj3YfAy4k8h/qbWY57KFcIzKx40bFsoI7PeydbGtT/dIoFLSZRLW5bleXNgG9mXZp270UeEC6CpATCS6uVl8LVT1I02uulHUpFaRmTEOrmMxsXGt6UAwYTY55K/B8uuID341xKbeC0kzhuN2gsL5UJaocBHyWK/AqwbeBttdhOCLwoaj7+nSViPxICObKrg3qavGNCvtwy/fEegK9X/wlp2e2CFlIhFbadeXOBr9Fn8ypYPP17mTqe98OJYM04=" +) + +func ExpressvpnCountriesChoices(servers []models.ExpressvpnServer) (choices []string) { + choices = make([]string, len(servers)) + for i := range servers { + choices[i] = servers[i].Country + } + return makeUnique(choices) +} + +func ExpressvpnCityChoices(servers []models.ExpressvpnServer) (choices []string) { + choices = make([]string, len(servers)) + for i := range servers { + choices[i] = servers[i].City + } + return makeUnique(choices) +} + +func ExpressvpnHostnameChoices(servers []models.ExpressvpnServer) (choices []string) { + choices = make([]string, len(servers)) + for i := range servers { + choices[i] = servers[i].Hostname + } + return makeUnique(choices) +} diff --git a/internal/constants/fastestvpn.go b/internal/constants/fastestvpn.go index 492d3c43..47110d21 100644 --- a/internal/constants/fastestvpn.go +++ b/internal/constants/fastestvpn.go @@ -15,7 +15,7 @@ func FastestvpnCountriesChoices(servers []models.FastestvpnServer) (choices []st for i := range servers { choices[i] = servers[i].Country } - return choices + return makeUnique(choices) } func FastestvpnHostnameChoices(servers []models.FastestvpnServer) (choices []string) { @@ -23,5 +23,5 @@ func FastestvpnHostnameChoices(servers []models.FastestvpnServer) (choices []str for i := range servers { choices[i] = servers[i].Hostname } - return choices + return makeUnique(choices) } diff --git a/internal/constants/vpn.go b/internal/constants/vpn.go index 4369d207..ed9eb3c1 100644 --- a/internal/constants/vpn.go +++ b/internal/constants/vpn.go @@ -11,6 +11,8 @@ const ( Custom = "custom" // Cyberghost is a VPN provider. Cyberghost = "cyberghost" + // Expressvpn is a VPN provider. + Expressvpn = "expressvpn" // Fastestvpn is a VPN provider. Fastestvpn = "fastestvpn" // HideMyAss is a VPN provider. diff --git a/internal/models/getservers.go b/internal/models/getservers.go index 30b91898..4b5c6790 100644 --- a/internal/models/getservers.go +++ b/internal/models/getservers.go @@ -7,6 +7,7 @@ import ( func (a AllServers) GetCopy() (servers AllServers) { servers = a // copy versions and timestamps servers.Cyberghost.Servers = a.GetCyberghost() + servers.Expressvpn.Servers = a.GetExpressvpn() servers.Fastestvpn.Servers = a.GetFastestvpn() servers.HideMyAss.Servers = a.GetHideMyAss() servers.Ipvanish.Servers = a.GetIpvanish() @@ -38,6 +39,18 @@ func (a *AllServers) GetCyberghost() (servers []CyberghostServer) { return servers } +func (a *AllServers) GetExpressvpn() (servers []ExpressvpnServer) { + if a.Expressvpn.Servers == nil { + return nil + } + servers = make([]ExpressvpnServer, len(a.Expressvpn.Servers)) + for i, serverToCopy := range a.Expressvpn.Servers { + servers[i] = serverToCopy + servers[i].IPs = copyIPs(serverToCopy.IPs) + } + return servers +} + func (a *AllServers) GetFastestvpn() (servers []FastestvpnServer) { if a.Fastestvpn.Servers == nil { return nil diff --git a/internal/models/getservers_test.go b/internal/models/getservers_test.go index cabdef5f..87a2fcf5 100644 --- a/internal/models/getservers_test.go +++ b/internal/models/getservers_test.go @@ -16,6 +16,11 @@ func Test_AllServers_GetCopy(t *testing.T) { IPs: []net.IP{{1, 2, 3, 4}}, }}, }, + Expressvpn: ExpressvpnServers{ + Servers: []ExpressvpnServer{{ + IPs: []net.IP{{1, 2, 3, 4}}, + }}, + }, Fastestvpn: FastestvpnServers{ Servers: []FastestvpnServer{{ IPs: []net.IP{{1, 2, 3, 4}}, diff --git a/internal/models/markdown.go b/internal/models/markdown.go index 8a731976..47923615 100644 --- a/internal/models/markdown.go +++ b/internal/models/markdown.go @@ -30,6 +30,20 @@ func (s CyberghostServer) ToMarkdown() (markdown string) { boolToMarkdown(s.TCP), boolToMarkdown(s.UDP)) } +func (s *ExpressvpnServers) ToMarkdown() (markdown string) { + markdown = markdownTableHeading("Country", "City", "Hostname", "TCP", "UDP") + for _, server := range s.Servers { + markdown += server.ToMarkdown() + "\n" + } + return markdown +} + +func (s *ExpressvpnServer) ToMarkdown() (markdown string) { + return fmt.Sprintf("| %s | %s | `%s` | %s | %s |", + s.Country, s.City, s.Hostname, + boolToMarkdown(s.TCP), boolToMarkdown(s.UDP)) +} + func (s *FastestvpnServers) ToMarkdown() (markdown string) { markdown = markdownTableHeading("Country", "Hostname", "TCP", "UDP") for _, server := range s.Servers { diff --git a/internal/models/server.go b/internal/models/server.go index 10147fed..d1c9ab99 100644 --- a/internal/models/server.go +++ b/internal/models/server.go @@ -12,6 +12,15 @@ type CyberghostServer struct { IPs []net.IP `json:"ips"` } +type ExpressvpnServer struct { + Country string `json:"country"` + City string `json:"city,omitempty"` + Hostname string `json:"hostname"` + TCP bool `json:"tcp"` + UDP bool `json:"udp"` + IPs []net.IP `json:"ips"` +} + type FastestvpnServer struct { Hostname string `json:"hostname"` TCP bool `json:"tcp"` diff --git a/internal/models/servers.go b/internal/models/servers.go index 9f242ae8..665bfdf7 100644 --- a/internal/models/servers.go +++ b/internal/models/servers.go @@ -3,6 +3,7 @@ package models type AllServers struct { Version uint16 `json:"version"` // used for migration of the top level scheme Cyberghost CyberghostServers `json:"cyberghost"` + Expressvpn ExpressvpnServers `json:"expressvpn"` Fastestvpn FastestvpnServers `json:"fastestvpn"` HideMyAss HideMyAssServers `json:"hidemyass"` Ipvanish IpvanishServers `json:"ipvanish"` @@ -24,6 +25,7 @@ type AllServers struct { func (a *AllServers) Count() int { return len(a.Cyberghost.Servers) + + len(a.Expressvpn.Servers) + len(a.Fastestvpn.Servers) + len(a.HideMyAss.Servers) + len(a.Ipvanish.Servers) + @@ -48,6 +50,11 @@ type CyberghostServers struct { Timestamp int64 `json:"timestamp"` Servers []CyberghostServer `json:"servers"` } +type ExpressvpnServers struct { + Version uint16 `json:"version"` + Timestamp int64 `json:"timestamp"` + Servers []ExpressvpnServer `json:"servers"` +} type FastestvpnServers struct { Version uint16 `json:"version"` Timestamp int64 `json:"timestamp"` diff --git a/internal/provider/expressvpn/connection.go b/internal/provider/expressvpn/connection.go new file mode 100644 index 00000000..6cb24802 --- /dev/null +++ b/internal/provider/expressvpn/connection.go @@ -0,0 +1,44 @@ +package expressvpn + +import ( + "github.com/qdm12/gluetun/internal/configuration" + "github.com/qdm12/gluetun/internal/models" + "github.com/qdm12/gluetun/internal/provider/utils" +) + +func (p *Provider) GetConnection(selection configuration.ServerSelection) ( + connection models.Connection, err error) { + port := getPort(selection) + protocol := utils.GetProtocol(selection) + + servers, err := p.filterServers(selection) + if err != nil { + return connection, err + } + + var connections []models.Connection + for _, server := range servers { + for _, IP := range server.IPs { + connection := models.Connection{ + Type: selection.VPN, + IP: IP, + Port: port, + Protocol: protocol, + Hostname: server.Hostname, + } + connections = append(connections, connection) + } + } + + return utils.PickConnection(connections, selection, p.randSource) +} + +func getPort(selection configuration.ServerSelection) (port uint16) { + const ( + defaultOpenVPNTCP = 0 + defaultOpenVPNUDP = 1195 + defaultWireguard = 0 + ) + return utils.GetPort(selection, defaultOpenVPNTCP, + defaultOpenVPNUDP, defaultWireguard) +} diff --git a/internal/provider/expressvpn/connection_test.go b/internal/provider/expressvpn/connection_test.go new file mode 100644 index 00000000..3f1d97b4 --- /dev/null +++ b/internal/provider/expressvpn/connection_test.go @@ -0,0 +1,97 @@ +package expressvpn + +import ( + "errors" + "math/rand" + "net" + "testing" + + "github.com/qdm12/gluetun/internal/configuration" + "github.com/qdm12/gluetun/internal/constants" + "github.com/qdm12/gluetun/internal/models" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_Provider_GetConnection(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + servers []models.ExpressvpnServer + selection configuration.ServerSelection + connection models.Connection + err error + }{ + "no server available": { + selection: configuration.ServerSelection{ + VPN: constants.OpenVPN, + }, + err: errors.New("no server found: for VPN openvpn; protocol udp"), + }, + "no filter": { + servers: []models.ExpressvpnServer{ + {IPs: []net.IP{net.IPv4(1, 1, 1, 1)}, UDP: true}, + {IPs: []net.IP{net.IPv4(2, 2, 2, 2)}, UDP: true}, + {IPs: []net.IP{net.IPv4(3, 3, 3, 3)}, UDP: true}, + }, + connection: models.Connection{ + IP: net.IPv4(1, 1, 1, 1), + Port: 1195, + Protocol: constants.UDP, + }, + }, + "target IP": { + selection: configuration.ServerSelection{ + TargetIP: net.IPv4(2, 2, 2, 2), + }, + servers: []models.ExpressvpnServer{ + {IPs: []net.IP{net.IPv4(1, 1, 1, 1)}, UDP: true}, + {IPs: []net.IP{net.IPv4(2, 2, 2, 2)}, UDP: true}, + {IPs: []net.IP{net.IPv4(3, 3, 3, 3)}, UDP: true}, + }, + connection: models.Connection{ + IP: net.IPv4(2, 2, 2, 2), + Port: 1195, + Protocol: constants.UDP, + }, + }, + "with filter": { + selection: configuration.ServerSelection{ + Hostnames: []string{"b"}, + }, + servers: []models.ExpressvpnServer{ + {Hostname: "a", IPs: []net.IP{net.IPv4(1, 1, 1, 1)}, UDP: true}, + {Hostname: "b", IPs: []net.IP{net.IPv4(2, 2, 2, 2)}, UDP: true}, + {Hostname: "a", IPs: []net.IP{net.IPv4(3, 3, 3, 3)}, UDP: true}, + }, + connection: models.Connection{ + IP: net.IPv4(2, 2, 2, 2), + Port: 1195, + Protocol: constants.UDP, + Hostname: "b", + }, + }, + } + + for name, testCase := range testCases { + testCase := testCase + t.Run(name, func(t *testing.T) { + t.Parallel() + + randSource := rand.NewSource(0) + + m := New(testCase.servers, randSource) + + connection, err := m.GetConnection(testCase.selection) + + if testCase.err != nil { + require.Error(t, err) + assert.Equal(t, testCase.err.Error(), err.Error()) + } else { + assert.NoError(t, err) + } + + assert.Equal(t, testCase.connection, connection) + }) + } +} diff --git a/internal/provider/expressvpn/filter.go b/internal/provider/expressvpn/filter.go new file mode 100644 index 00000000..7d382e91 --- /dev/null +++ b/internal/provider/expressvpn/filter.go @@ -0,0 +1,28 @@ +package expressvpn + +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.ExpressvpnServer, 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), + utils.FilterByProtocol(selection, server.TCP, server.UDP): + default: + servers = append(servers, server) + } + } + + if len(servers) == 0 { + return nil, utils.NoServerFoundError(selection) + } + + return servers, nil +} diff --git a/internal/provider/expressvpn/filter_test.go b/internal/provider/expressvpn/filter_test.go new file mode 100644 index 00000000..13c2afa3 --- /dev/null +++ b/internal/provider/expressvpn/filter_test.go @@ -0,0 +1,119 @@ +package expressvpn + +import ( + "errors" + "math/rand" + "testing" + + "github.com/qdm12/gluetun/internal/configuration" + "github.com/qdm12/gluetun/internal/constants" + "github.com/qdm12/gluetun/internal/models" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_Expressvpn_filterServers(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + servers []models.ExpressvpnServer + selection configuration.ServerSelection + filtered []models.ExpressvpnServer + err error + }{ + "no server available": { + selection: configuration.ServerSelection{ + VPN: constants.OpenVPN, + }, + err: errors.New("no server found: for VPN openvpn; protocol udp"), + }, + "no filter": { + servers: []models.ExpressvpnServer{ + {Hostname: "a", UDP: true}, + {Hostname: "b", UDP: true}, + {Hostname: "c", UDP: true}, + }, + filtered: []models.ExpressvpnServer{ + {Hostname: "a", UDP: true}, + {Hostname: "b", UDP: true}, + {Hostname: "c", UDP: true}, + }, + }, + "filter by country": { + selection: configuration.ServerSelection{ + Countries: []string{"b"}, + }, + servers: []models.ExpressvpnServer{ + {Country: "a", UDP: true}, + {Country: "b", UDP: true}, + {Country: "c", UDP: true}, + }, + filtered: []models.ExpressvpnServer{ + {Country: "b", UDP: true}, + }, + }, + "filter by city": { + selection: configuration.ServerSelection{ + Cities: []string{"b"}, + }, + servers: []models.ExpressvpnServer{ + {City: "a", UDP: true}, + {City: "b", UDP: true}, + {City: "c", UDP: true}, + }, + filtered: []models.ExpressvpnServer{ + {City: "b", UDP: true}, + }, + }, + "filter by hostname": { + selection: configuration.ServerSelection{ + Hostnames: []string{"b"}, + }, + servers: []models.ExpressvpnServer{ + {Hostname: "a", UDP: true}, + {Hostname: "b", UDP: true}, + {Hostname: "c", UDP: true}, + }, + filtered: []models.ExpressvpnServer{ + {Hostname: "b", UDP: true}, + }, + }, + "filter by protocol": { + selection: configuration.ServerSelection{ + OpenVPN: configuration.OpenVPNSelection{ + TCP: true, + }, + }, + servers: []models.ExpressvpnServer{ + {Hostname: "a", UDP: true}, + {Hostname: "b", UDP: true, TCP: true}, + {Hostname: "c", UDP: true}, + }, + filtered: []models.ExpressvpnServer{ + {Hostname: "b", UDP: true, TCP: true}, + }, + }, + } + + for name, testCase := range testCases { + testCase := testCase + t.Run(name, func(t *testing.T) { + t.Parallel() + + randSource := rand.NewSource(0) + + m := New(testCase.servers, randSource) + + servers, err := m.filterServers(testCase.selection) + + if testCase.err != nil { + require.Error(t, err) + assert.Equal(t, testCase.err.Error(), err.Error()) + } else { + assert.NoError(t, err) + } + + assert.Equal(t, testCase.filtered, servers) + }) + } +} diff --git a/internal/provider/expressvpn/openvpnconf.go b/internal/provider/expressvpn/openvpnconf.go new file mode 100644 index 00000000..c4ba9d65 --- /dev/null +++ b/internal/provider/expressvpn/openvpnconf.go @@ -0,0 +1,85 @@ +package expressvpn + +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.Connection, + settings configuration.OpenVPN) (lines []string, err error) { + if settings.Cipher == "" { + settings.Cipher = constants.AES256cbc + } + if settings.Auth == "" { + settings.Auth = constants.SHA512 + } + + if settings.MSSFix == 0 { + settings.MSSFix = 1200 + } + + lines = []string{ + "client", + "nobind", + "tls-exit", + "dev " + settings.Interface, + "verb " + strconv.Itoa(settings.Verbosity), + + // Expressvpn specific + "fast-io", + "fragment 1300", + "mssfix " + strconv.Itoa(int(settings.MSSFix)), + "sndbuf 524288", + "rcvbuf 524288", + "verify-x509-name Server name-prefix", // security hole I guess? + "remote-cert-tls server", // updated name of ns-cert-type + "key-direction 1", + "auth-user-pass " + constants.OpenVPNAuthConf, + "auth " + settings.Auth, + + // Added constant values + "mute-replay-warnings", + "auth-nocache", + "pull-filter ignore \"auth-token\"", // prevent auth failed loops + "auth-retry nointeract", + "suppress-timestamps", + + // Modified variables + connection.OpenVPNProtoLine(), + connection.OpenVPNRemoteLine(), + } + + lines = append(lines, utils.CipherLines(settings.Cipher, settings.Version)...) + + if connection.Protocol == constants.UDP { + lines = append(lines, "explicit-exit-notify") + } + + if !settings.Root { + lines = append(lines, "user "+settings.ProcUser) + lines = append(lines, "persist-tun") + lines = append(lines, "persist-key") + } + + if !settings.IPv6 { + lines = append(lines, `pull-filter ignore "route-ipv6"`) + lines = append(lines, `pull-filter ignore "ifconfig-ipv6"`) + } + + lines = append(lines, utils.WrapOpenvpnCert( + constants.ExpressvpnCert)...) + lines = append(lines, utils.WrapOpenvpnRSAKey( + constants.ExpressvpnRSAKey)...) + lines = append(lines, utils.WrapOpenvpnTLSAuth( + constants.ExpressvpnTLSAuthOpenvpnStaticKeyV1)...) + lines = append(lines, utils.WrapOpenvpnCA( + constants.ExpressvpnCA)...) + + lines = append(lines, "") + + return lines, nil +} diff --git a/internal/provider/expressvpn/provider.go b/internal/provider/expressvpn/provider.go new file mode 100644 index 00000000..54ac4092 --- /dev/null +++ b/internal/provider/expressvpn/provider.go @@ -0,0 +1,23 @@ +package expressvpn + +import ( + "math/rand" + + "github.com/qdm12/gluetun/internal/constants" + "github.com/qdm12/gluetun/internal/models" + "github.com/qdm12/gluetun/internal/provider/utils" +) + +type Provider struct { + servers []models.ExpressvpnServer + randSource rand.Source + utils.NoPortForwarder +} + +func New(servers []models.ExpressvpnServer, randSource rand.Source) *Provider { + return &Provider{ + servers: servers, + randSource: randSource, + NoPortForwarder: utils.NewNoPortForwarding(constants.Expressvpn), + } +} diff --git a/internal/provider/provider.go b/internal/provider/provider.go index f18ea118..6ecd1f1e 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -13,6 +13,7 @@ import ( "github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/provider/custom" "github.com/qdm12/gluetun/internal/provider/cyberghost" + "github.com/qdm12/gluetun/internal/provider/expressvpn" "github.com/qdm12/gluetun/internal/provider/fastestvpn" "github.com/qdm12/gluetun/internal/provider/hidemyass" "github.com/qdm12/gluetun/internal/provider/ipvanish" @@ -55,6 +56,8 @@ func New(provider string, allServers models.AllServers, timeNow func() time.Time return custom.New() case constants.Cyberghost: return cyberghost.New(allServers.Cyberghost.Servers, randSource) + case constants.Expressvpn: + return expressvpn.New(allServers.Expressvpn.Servers, randSource) case constants.Fastestvpn: return fastestvpn.New(allServers.Fastestvpn.Servers, randSource) case constants.HideMyAss: diff --git a/internal/storage/hardcoded_test.go b/internal/storage/hardcoded_test.go index b771cab8..82831dcc 100644 --- a/internal/storage/hardcoded_test.go +++ b/internal/storage/hardcoded_test.go @@ -53,6 +53,11 @@ func Test_versions(t *testing.T) { version: allServers.Cyberghost.Version, digest: "9ce64729", }, + "Expressvpn": { + model: models.ExpressvpnServer{}, + version: allServers.Expressvpn.Version, + digest: "6e54a351", + }, "Fastestvpn": { model: models.FastestvpnServer{}, version: allServers.Fastestvpn.Version, diff --git a/internal/storage/merge.go b/internal/storage/merge.go index c5f91fb2..6ffe5b83 100644 --- a/internal/storage/merge.go +++ b/internal/storage/merge.go @@ -34,6 +34,7 @@ func (s *Storage) mergeServers(hardcoded, persisted models.AllServers) models.Al return models.AllServers{ Version: hardcoded.Version, Cyberghost: s.mergeCyberghost(hardcoded.Cyberghost, persisted.Cyberghost), + Expressvpn: s.mergeExpressvpn(hardcoded.Expressvpn, persisted.Expressvpn), Fastestvpn: s.mergeFastestvpn(hardcoded.Fastestvpn, persisted.Fastestvpn), HideMyAss: s.mergeHideMyAss(hardcoded.HideMyAss, persisted.HideMyAss), Ipvanish: s.mergeIpvanish(hardcoded.Ipvanish, persisted.Ipvanish), @@ -69,6 +70,19 @@ func (s *Storage) mergeCyberghost(hardcoded, persisted models.CyberghostServers) return persisted } +func (s *Storage) mergeExpressvpn(hardcoded, persisted models.ExpressvpnServers) models.ExpressvpnServers { + if persisted.Timestamp <= hardcoded.Timestamp { + return hardcoded + } + versionDiff := int(hardcoded.Version) - int(persisted.Version) + if versionDiff > 0 { + s.logVersionDiff("ExpressVPN", versionDiff) + return hardcoded + } + s.logTimeDiff("ExpressVPN", persisted.Timestamp, hardcoded.Timestamp) + return persisted +} + func (s *Storage) mergeFastestvpn(hardcoded, persisted models.FastestvpnServers) models.FastestvpnServers { if persisted.Timestamp <= hardcoded.Timestamp { return hardcoded diff --git a/internal/storage/servers.json b/internal/storage/servers.json index c1ebd849..a9743998 100644 --- a/internal/storage/servers.json +++ b/internal/storage/servers.json @@ -3351,6 +3351,1671 @@ } ] }, + "expressvpn": { + "version": 1, + "timestamp": 1631629451, + "servers": [ + { + "country": "Albania", + "hostname": "albania-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "31.171.152.38", + "31.171.152.54" + ] + }, + { + "country": "Algeria", + "hostname": "algeria-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "85.203.22.37", + "85.203.22.43" + ] + }, + { + "country": "Andorra", + "hostname": "andorra-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "85.203.22.18", + "85.203.22.30" + ] + }, + { + "country": "Argentina", + "hostname": "argentina-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "2.57.171.134", + "2.57.171.164", + "2.57.171.215" + ] + }, + { + "country": "Armenia", + "hostname": "armenia-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "85.203.22.52", + "85.203.22.55" + ] + }, + { + "country": "Australia", + "city": "Brisbane", + "hostname": "australia-brisbane-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "45.132.224.3", + "45.132.224.195", + "45.132.224.225" + ] + }, + { + "country": "Australia", + "city": "Melbourne", + "hostname": "australia-melbourne-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "45.132.225.3", + "45.132.225.12", + "45.132.225.205" + ] + }, + { + "country": "Australia", + "city": "Perth", + "hostname": "australia-perth-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "45.133.6.10", + "45.133.6.16", + "45.133.6.22" + ] + }, + { + "country": "Australia", + "city": "Sydney", + "hostname": "australia-sydney-2-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "45.133.4.5", + "45.133.4.13", + "45.133.4.21" + ] + }, + { + "country": "Australia", + "city": "Sydney", + "hostname": "australia-sydney-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "45.67.96.2", + "45.67.96.8", + "45.67.96.14", + "45.67.96.20", + "45.67.96.218", + "45.67.96.224" + ] + }, + { + "country": "Austria", + "hostname": "austria-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "86.107.21.195", + "185.183.107.211", + "185.183.107.216", + "185.183.107.217", + "185.183.107.229", + "185.183.107.240" + ] + }, + { + "country": "Bahamas", + "hostname": "bahamas-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "154.6.23.3", + "154.6.23.10" + ] + }, + { + "country": "Bangladesh", + "hostname": "bangladesh-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "84.17.39.45", + "84.17.39.80" + ] + }, + { + "country": "Belarus", + "hostname": "belarus-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "85.203.22.64", + "85.203.22.65" + ] + }, + { + "country": "Belgium", + "hostname": "belgium-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "194.110.115.108", + "194.110.115.116" + ] + }, + { + "country": "Bhutan", + "hostname": "bhutan-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "84.17.39.70", + "84.17.39.75" + ] + }, + { + "country": "Bosnia And Herzegovina", + "city": "Bosnia And Herzegovina", + "hostname": "bosniaandherzegovina-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "45.135.185.3", + "45.135.185.9" + ] + }, + { + "country": "Brazil", + "hostname": "brazil-2-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "191.101.252.94", + "191.101.252.116" + ] + }, + { + "country": "Brazil", + "hostname": "brazil-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "2.57.171.13", + "2.57.171.169" + ] + }, + { + "country": "Brunei", + "hostname": "brunei-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "84.17.39.60", + "84.17.39.65" + ] + }, + { + "country": "Cambodia", + "hostname": "cambodia-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "84.17.39.50", + "84.17.39.55" + ] + }, + { + "country": "Canada", + "city": "Montreal", + "hostname": "canada-montreal-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "45.91.23.26", + "45.91.23.34", + "144.168.166.3" + ] + }, + { + "country": "Canada", + "city": "Montreal", + "hostname": "canada-montreal-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "45.91.23.26", + "45.91.23.34", + "144.168.166.3" + ] + }, + { + "country": "Canada", + "city": "Toronto", + "hostname": "canada-toronto-2-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "64.57.129.219", + "185.92.26.109", + "185.92.26.115", + "185.92.26.116", + "185.92.26.217" + ] + }, + { + "country": "Canada", + "city": "Toronto", + "hostname": "canada-toronto-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "198.144.145.5", + "198.144.145.7", + "198.144.145.12", + "198.144.145.17", + "198.144.145.30", + "198.144.154.134", + "198.144.154.144", + "198.144.154.149" + ] + }, + { + "country": "Canada", + "city": "Vancouver", + "hostname": "canada-vancouver-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "71.19.252.124", + "71.19.252.142", + "71.19.252.146", + "71.19.252.149" + ] + }, + { + "country": "Chile", + "hostname": "chile-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "2.57.171.112", + "2.57.171.181" + ] + }, + { + "country": "Colombia", + "hostname": "colombia-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "2.57.171.85", + "2.57.171.95", + "2.57.171.186" + ] + }, + { + "country": "Costa Rica", + "city": "Costa Rica", + "hostname": "costarica-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "179.48.251.106", + "190.112.223.50" + ] + }, + { + "country": "Croatia", + "hostname": "croatia-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "85.10.56.232", + "85.10.56.237" + ] + }, + { + "country": "Cyprus", + "hostname": "cyprus-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "185.205.186.3", + "213.169.148.152" + ] + }, + { + "country": "Czech Republic", + "city": "Czech Republic", + "hostname": "czechrepublic-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "89.187.191.226", + "89.187.191.233", + "138.199.56.130", + "138.199.56.146", + "138.199.56.148", + "138.199.56.167", + "138.199.56.175", + "185.152.65.226", + "185.152.65.231" + ] + }, + { + "country": "Denmark", + "hostname": "denmark-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "157.97.120.108", + "193.29.107.4", + "193.29.107.11", + "193.29.107.19" + ] + }, + { + "country": "Ecuador", + "hostname": "ecuador-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "2.57.171.122", + "2.57.171.244" + ] + }, + { + "country": "Egypt", + "hostname": "egypt-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "45.135.184.10" + ] + }, + { + "country": "Estonia", + "hostname": "estonia-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "185.195.237.147", + "185.195.237.155" + ] + }, + { + "country": "Finland", + "hostname": "finland-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "196.244.192.2" + ] + }, + { + "country": "France", + "city": "Paris", + "hostname": "france-paris-1-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "45.91.22.2", + "45.91.22.7", + "45.91.22.29", + "45.91.22.50" + ] + }, + { + "country": "France", + "city": "Paris", + "hostname": "france-paris-2-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "84.17.43.199", + "84.17.43.204", + "84.17.43.209", + "84.17.43.217", + "84.17.43.222" + ] + }, + { + "country": "France", + "city": "Strasbourg", + "hostname": "france-strasbourg-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "191.96.180.122", + "191.96.180.173", + "191.96.180.249" + ] + }, + { + "country": "Georgia", + "hostname": "georgia-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "91.239.206.88", + "91.239.206.191" + ] + }, + { + "country": "Germany", + "city": "Frankfurt", + "hostname": "germany-darmstadt-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "45.86.202.7", + "45.86.202.13", + "45.86.202.61", + "156.146.33.113" + ] + }, + { + "country": "Germany", + "city": "Frankfurt", + "hostname": "germany-frankfurt-1-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "45.86.203.3", + "45.86.203.11", + "45.86.203.75", + "45.86.203.91", + "45.86.203.115", + "45.86.203.123", + "45.86.203.187", + "45.86.203.195" + ] + }, + { + "country": "Germany", + "city": "Frankfurt", + "hostname": "germany-frankfurt-2-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "194.195.91.26", + "194.195.91.51", + "194.195.91.59" + ] + }, + { + "country": "Germany", + "city": "Nuremberg", + "hostname": "germany-nuremberg-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "85.203.15.2", + "85.203.15.8", + "85.203.15.89" + ] + }, + { + "country": "Greece", + "hostname": "greece-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "185.51.134.218", + "185.51.134.226" + ] + }, + { + "country": "Guatemala", + "hostname": "guatemala-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "2.57.171.222", + "2.57.171.229" + ] + }, + { + "country": "Hong Kong", + "city": "Hong Kong", + "hostname": "hongkong-2-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "193.176.211.27", + "193.176.211.33", + "193.176.211.39", + "193.176.211.45", + "193.176.211.51" + ] + }, + { + "country": "Hong Kong", + "city": "Hong Kong", + "hostname": "hongkong4-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "45.41.184.192", + "45.61.69.49", + "45.61.69.205" + ] + }, + { + "country": "Hungary", + "hostname": "hungary-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "91.219.236.214", + "91.219.238.81", + "91.219.238.100", + "91.219.239.222" + ] + }, + { + "country": "Iceland", + "hostname": "iceland-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "45.133.193.136", + "45.133.193.147", + "45.133.193.152", + "45.133.193.155" + ] + }, + { + "country": "India", + "city": "Chennai", + "hostname": "india-chennai-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "45.137.126.2", + "45.137.126.8", + "45.137.126.14", + "45.137.126.172", + "45.137.126.198" + ] + }, + { + "country": "India", + "city": "Mumbai", + "hostname": "india-mumbai-1-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "180.149.242.253" + ] + }, + { + "country": "Indonesia", + "hostname": "indonesia-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "64.64.121.86", + "64.64.121.91" + ] + }, + { + "country": "Ireland", + "hostname": "ireland-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "188.241.178.132", + "188.241.178.140", + "188.241.178.148", + "188.241.178.156" + ] + }, + { + "country": "Isle Of Man", + "city": "Isle Of Man", + "hostname": "isleofman-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "85.203.22.153", + "185.80.220.148" + ] + }, + { + "country": "Israel", + "hostname": "israel-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "31.168.162.18", + "82.80.25.113", + "82.81.85.236" + ] + }, + { + "country": "Italy", + "city": "Cosenza", + "hostname": "italy-cosenza-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "45.134.22.228", + "212.102.55.12", + "212.102.55.32" + ] + }, + { + "country": "Italy", + "city": "Milan", + "hostname": "italy-milan-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "45.91.20.82", + "45.91.20.89", + "45.91.20.131", + "45.91.20.200", + "45.91.20.219" + ] + }, + { + "country": "Japan", + "city": "Kawasaki", + "hostname": "japan-kawasaki-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "194.5.48.19", + "194.5.48.47", + "194.5.48.84", + "194.5.48.108", + "194.5.48.130" + ] + }, + { + "country": "Japan", + "city": "Tokyo", + "hostname": "japan-tokyo-1-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "84.17.34.192", + "84.17.34.201", + "212.102.50.130" + ] + }, + { + "country": "Japan", + "city": "Tokyo", + "hostname": "japan-tokyo-2-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "45.8.68.17", + "45.8.68.22", + "45.8.68.26" + ] + }, + { + "country": "Jersey", + "hostname": "jersey-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "85.203.22.169", + "185.80.220.149" + ] + }, + { + "country": "Kazakhstan", + "hostname": "kazakhstan-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "85.203.21.183", + "85.203.21.204" + ] + }, + { + "country": "Kenya", + "hostname": "kenya-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "62.12.114.42", + "62.12.114.54" + ] + }, + { + "country": "Kyrgyzstan", + "hostname": "kyrgyzstan-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "91.213.233.57", + "91.213.233.146" + ] + }, + { + "country": "Laos", + "hostname": "laos-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "46.244.29.97", + "46.244.29.103" + ] + }, + { + "country": "Latvia", + "hostname": "latvia-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "80.233.134.134", + "80.233.134.168" + ] + }, + { + "country": "Liechtenstein", + "hostname": "liechtenstein-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "85.203.22.181", + "185.80.220.156" + ] + }, + { + "country": "Lithuania", + "hostname": "lithuania-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "85.206.166.87", + "85.206.175.220" + ] + }, + { + "country": "Luxembourg", + "hostname": "luxembourg-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "94.242.218.139", + "94.242.231.194", + "94.242.249.195" + ] + }, + { + "country": "Macau", + "hostname": "macau-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "84.17.39.35", + "84.17.39.40" + ] + }, + { + "country": "Malaysia", + "hostname": "malaysia-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "84.17.38.203", + "84.17.38.208" + ] + }, + { + "country": "Malta", + "hostname": "malta-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "85.203.22.5", + "85.203.22.12" + ] + }, + { + "country": "Mexico", + "hostname": "mexico-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "179.61.245.3", + "179.61.245.10", + "179.61.245.24" + ] + }, + { + "country": "Moldova", + "hostname": "moldova-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "178.17.168.130", + "178.17.168.138" + ] + }, + { + "country": "Monaco", + "hostname": "monaco-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "85.203.22.209", + "85.203.22.217" + ] + }, + { + "country": "Mongolia", + "hostname": "mongolia-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "85.203.21.122", + "85.203.21.190" + ] + }, + { + "country": "Montenegro", + "hostname": "montenegro-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "85.203.22.229", + "85.203.22.236" + ] + }, + { + "country": "Myanmar", + "hostname": "myanmar-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "84.17.39.25", + "84.17.39.30" + ] + }, + { + "country": "Nepal", + "hostname": "nepal-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "84.17.39.15", + "84.17.39.20" + ] + }, + { + "country": "Netherlands", + "city": "Amsterdam", + "hostname": "netherlands-amsterdam-2-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "45.86.200.13", + "45.86.200.19", + "45.86.200.55", + "212.102.34.106" + ] + }, + { + "country": "Netherlands", + "city": "Amsterdam", + "hostname": "netherlands-amsterdam-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "45.86.201.0", + "45.86.201.16", + "45.86.201.144", + "45.86.201.160", + "45.86.201.176", + "45.86.201.184", + "45.86.201.192", + "45.86.201.200", + "45.86.201.208", + "45.86.201.216" + ] + }, + { + "country": "Netherlands", + "city": "Rotterdam", + "hostname": "netherlands-rotterdam-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "31.204.150.71", + "31.204.151.90", + "31.204.154.117", + "31.204.154.121" + ] + }, + { + "country": "Netherlands", + "city": "The Hague", + "hostname": "netherlands-thehague-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "46.166.143.2", + "46.166.143.4", + "46.166.143.8", + "46.166.143.14", + "46.166.186.15", + "46.166.186.16", + "203.159.81.143", + "203.159.81.234" + ] + }, + { + "country": "New Zealand", + "city": "New Zealand", + "hostname": "newzealand-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "45.133.7.3", + "45.133.7.9", + "45.133.7.15", + "45.133.7.21" + ] + }, + { + "country": "North Macedonia", + "city": "North Macedonia", + "hostname": "macedonia-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "85.203.22.196", + "85.203.22.202" + ] + }, + { + "country": "Norway", + "hostname": "norway-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "45.92.228.3", + "45.92.228.10", + "45.92.228.18" + ] + }, + { + "country": "Pakistan", + "hostname": "pakistan-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "85.203.21.142", + "85.203.21.209" + ] + }, + { + "country": "Panama", + "hostname": "panama-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "2.57.171.73", + "2.57.171.115" + ] + }, + { + "country": "Peru", + "hostname": "peru-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "2.57.171.88", + "2.57.171.251" + ] + }, + { + "country": "Philippines Via Singapore", + "city": "Philippines Via Singapore", + "hostname": "ph-via-sing-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "45.56.146.28", + "45.56.146.34" + ] + }, + { + "country": "Poland", + "hostname": "poland-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "84.17.55.74" + ] + }, + { + "country": "Portugal", + "hostname": "portugal-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "94.46.13.79", + "94.46.167.74", + "94.46.167.214", + "109.71.41.132" + ] + }, + { + "country": "Romania", + "hostname": "romania-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "94.176.148.60", + "185.195.19.197", + "185.195.19.215" + ] + }, + { + "country": "Serbia", + "hostname": "serbia-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "89.38.224.156", + "89.38.224.163" + ] + }, + { + "country": "Singapore", + "city": "CBD", + "hostname": "singapore-cbd-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "193.37.33.3", + "193.37.33.9", + "193.37.33.24", + "193.37.33.42", + "193.37.33.66", + "193.37.33.131", + "193.37.33.226" + ] + }, + { + "country": "Singapore", + "city": "Jurong", + "hostname": "singapore-jurong-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "85.203.21.93", + "193.37.32.110", + "193.37.32.140", + "193.37.32.150" + ] + }, + { + "country": "Singapore", + "city": "Marina Bay", + "hostname": "singapore-marinabay-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "45.41.185.185", + "45.41.188.42", + "45.41.188.76", + "45.41.188.252", + "45.41.189.130" + ] + }, + { + "country": "Slovakia", + "hostname": "slovakia-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "193.37.255.214", + "193.37.255.220" + ] + }, + { + "country": "Slovenia", + "hostname": "slovenia-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "45.157.5.24", + "45.157.5.25" + ] + }, + { + "country": "South Africa", + "city": "South Africa", + "hostname": "southafrica-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "154.70.152.148", + "154.70.152.164" + ] + }, + { + "country": "South Korea", + "city": "South Korea", + "hostname": "southkorea2-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "185.92.24.16", + "185.92.24.113", + "185.92.24.144" + ] + }, + { + "country": "Spain", + "city": "Barcelona", + "hostname": "spain-barcelona-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "45.130.136.4", + "45.130.136.18", + "45.130.136.133", + "45.130.136.153", + "45.130.136.158" + ] + }, + { + "country": "Spain", + "city": "Madrid", + "hostname": "spain-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "194.99.104.135", + "194.99.104.147", + "194.99.104.152" + ] + }, + { + "country": "Sri Lanka", + "city": "Sri Lanka", + "hostname": "srilanka-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "85.203.21.196", + "193.37.32.105" + ] + }, + { + "country": "Sweden", + "hostname": "sweden-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "84.17.36.47", + "84.17.36.52", + "84.17.36.59", + "84.17.36.65" + ] + }, + { + "country": "Switzerland", + "hostname": "switzerland-2-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "85.203.45.2", + "85.203.45.8", + "85.203.45.32", + "85.203.45.37" + ] + }, + { + "country": "Switzerland", + "hostname": "switzerland-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "89.187.165.43", + "89.187.165.48", + "89.187.165.53", + "89.187.165.66", + "89.187.165.181", + "89.187.165.183", + "178.239.56.34", + "178.239.56.183" + ] + }, + { + "country": "Taiwan", + "hostname": "taiwan-2-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "194.5.52.1", + "194.5.52.7" + ] + }, + { + "country": "Thailand", + "hostname": "thailand-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "138.199.25.175", + "194.5.49.14", + "194.5.49.38" + ] + }, + { + "country": "Turkey", + "hostname": "turkey-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "87.251.20.164", + "87.251.20.201" + ] + }, + { + "country": "UK", + "city": "Docklands", + "hostname": "uk-berkshire-2-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "85.203.36.2", + "85.203.36.119", + "85.203.36.125", + "85.203.36.146", + "85.203.36.147", + "85.203.36.237", + "85.203.36.253", + "185.198.243.12", + "185.198.243.97", + "185.198.243.100", + "185.198.243.115", + "185.217.117.47", + "185.217.117.54" + ] + }, + { + "country": "UK", + "city": "London", + "hostname": "uk-east-london-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "45.80.158.37", + "45.80.158.186", + "85.203.34.58", + "85.203.34.64", + "85.203.34.80", + "85.203.34.88", + "85.203.34.104", + "185.244.10.29", + "185.244.10.53" + ] + }, + { + "country": "UK", + "city": "London", + "hostname": "uk-london-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "5.101.142.13", + "46.244.28.152", + "46.244.28.195", + "78.110.162.165", + "185.192.70.234" + ] + }, + { + "country": "USA", + "city": "Atlanta", + "hostname": "usa-atlanta-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "193.56.116.4", + "193.56.116.8", + "193.56.116.20" + ] + }, + { + "country": "USA", + "city": "Chicago", + "hostname": "usa-chicago-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "154.6.21.204", + "173.239.199.88", + "173.239.199.100" + ] + }, + { + "country": "USA", + "city": "Dallas", + "hostname": "usa-dallas-2-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "45.131.192.2", + "45.131.192.7", + "45.131.192.12", + "45.131.192.17", + "45.131.192.22" + ] + }, + { + "country": "USA", + "city": "Dallas", + "hostname": "usa-dallas-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "45.132.115.16", + "154.6.28.4", + "154.6.28.16", + "154.6.28.142" + ] + }, + { + "country": "USA", + "city": "Denver", + "hostname": "usa-denver-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "70.39.102.170", + "70.39.102.180" + ] + }, + { + "country": "USA", + "city": "Los Angeles", + "hostname": "usa-losangeles-1-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "104.237.51.205", + "104.237.53.171", + "136.144.33.240" + ] + }, + { + "country": "USA", + "city": "Los Angeles", + "hostname": "usa-losangeles-2-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "69.12.68.54", + "191.101.177.53", + "191.101.177.84" + ] + }, + { + "country": "USA", + "city": "Los Angeles", + "hostname": "usa-losangeles-3-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "199.101.192.221", + "199.101.192.231", + "199.101.192.240" + ] + }, + { + "country": "USA", + "city": "Los Angeles", + "hostname": "usa-losangeles-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "192.109.205.101", + "192.109.205.204", + "192.109.205.241", + "192.109.205.250" + ] + }, + { + "country": "USA", + "city": "Los Angeles", + "hostname": "usa-losangeles5-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "45.84.215.238", + "45.85.89.113", + "45.85.89.188", + "167.160.56.53" + ] + }, + { + "country": "USA", + "city": "Miami", + "hostname": "usa-miami-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "173.44.51.43", + "193.36.224.55", + "193.36.224.85" + ] + }, + { + "country": "USA", + "city": "New Jersey", + "hostname": "usa-newjersey-1-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "45.131.195.206", + "45.131.195.249", + "45.146.55.7", + "45.146.55.27", + "45.146.55.32", + "45.146.55.92", + "45.146.55.237", + "45.146.55.245", + "45.146.55.252" + ] + }, + { + "country": "USA", + "city": "New Jersey", + "hostname": "usa-newjersey-3-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "136.144.35.10", + "136.144.35.22", + "136.144.35.34", + "136.144.35.46" + ] + }, + { + "country": "USA", + "city": "New Jersey", + "hostname": "usa-newjersey2-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "191.101.174.194", + "191.101.174.216" + ] + }, + { + "country": "USA", + "city": "New York", + "hostname": "us-new-york-2-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "191.101.41.2", + "191.101.41.8", + "191.101.41.14" + ] + }, + { + "country": "USA", + "city": "New York", + "hostname": "usa-newyork-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "154.6.16.191", + "173.239.207.24", + "173.239.207.146" + ] + }, + { + "country": "USA", + "city": "Salt Lake City", + "hostname": "usa-saltlakecity-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "107.182.233.232", + "191.96.206.4", + "191.96.206.10" + ] + }, + { + "country": "USA", + "city": "San Francisco", + "hostname": "usa-sanfrancisco-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "173.239.198.41", + "173.239.198.70", + "173.239.198.76", + "191.96.120.75", + "191.96.120.192", + "191.96.120.193", + "191.96.120.222" + ] + }, + { + "country": "USA", + "city": "Seattle", + "hostname": "usa-seattle-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "104.140.17.147", + "104.140.17.180", + "104.140.21.19", + "104.140.21.123", + "104.140.148.196" + ] + }, + { + "country": "USA", + "city": "Tampa", + "hostname": "usa-tampa-1-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "191.96.170.33", + "191.96.170.38" + ] + }, + { + "country": "USA", + "city": "Washington DC", + "hostname": "usa-washingtondc-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "107.181.155.4", + "107.181.155.12", + "107.181.155.20" + ] + }, + { + "country": "Ukraine", + "hostname": "ukraine-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "95.214.235.51", + "95.214.235.58" + ] + }, + { + "country": "Uruguay", + "hostname": "uruguay-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "2.57.171.135", + "2.57.171.141" + ] + }, + { + "country": "Uzbekistan", + "hostname": "uzbekistan-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "85.203.22.83", + "85.203.22.86" + ] + }, + { + "country": "Venezuela", + "hostname": "venezuela-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "2.57.171.90", + "2.57.171.241" + ] + }, + { + "country": "Vietnam", + "hostname": "vietnam-ca-version-2.expressnetw.com", + "tcp": false, + "udp": true, + "ips": [ + "85.203.21.157", + "85.203.21.161" + ] + } + ] + }, "fastestvpn": { "version": 1, "timestamp": 1620435633, diff --git a/internal/storage/sync.go b/internal/storage/sync.go index b2e4db30..1d65740f 100644 --- a/internal/storage/sync.go +++ b/internal/storage/sync.go @@ -15,6 +15,7 @@ var ( func countServers(allServers models.AllServers) int { return len(allServers.Cyberghost.Servers) + + len(allServers.Expressvpn.Servers) + len(allServers.Fastestvpn.Servers) + len(allServers.HideMyAss.Servers) + len(allServers.Ipvanish.Servers) + diff --git a/internal/updater/providers.go b/internal/updater/providers.go index 81ef209c..b2b25eea 100644 --- a/internal/updater/providers.go +++ b/internal/updater/providers.go @@ -7,6 +7,7 @@ import ( "github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/updater/providers/cyberghost" + "github.com/qdm12/gluetun/internal/updater/providers/expressvpn" "github.com/qdm12/gluetun/internal/updater/providers/fastestvpn" "github.com/qdm12/gluetun/internal/updater/providers/hidemyass" "github.com/qdm12/gluetun/internal/updater/providers/ipvanish" @@ -42,6 +43,28 @@ func (u *updater) updateCyberghost(ctx context.Context) (err error) { return nil } +func (u *updater) updateExpressvpn(ctx context.Context) (err error) { + minServers := getMinServers(len(u.servers.Expressvpn.Servers)) + servers, warnings, err := expressvpn.GetServers( + ctx, u.unzipper, u.presolver, minServers) + if u.options.CLI { + for _, warning := range warnings { + u.logger.Warn("ExpressVPN: " + warning) + } + } + if err != nil { + return err + } + + if reflect.DeepEqual(u.servers.Expressvpn.Servers, servers) { + return nil + } + + u.servers.Expressvpn.Timestamp = u.timeNow().Unix() + u.servers.Expressvpn.Servers = servers + return nil +} + func (u *updater) updateFastestvpn(ctx context.Context) (err error) { minServers := getMinServers(len(u.servers.Fastestvpn.Servers)) servers, warnings, err := fastestvpn.GetServers( diff --git a/internal/updater/providers/expressvpn/hardcoded.go b/internal/updater/providers/expressvpn/hardcoded.go new file mode 100644 index 00000000..43693094 --- /dev/null +++ b/internal/updater/providers/expressvpn/hardcoded.go @@ -0,0 +1,152 @@ +package expressvpn + +import ( + "github.com/qdm12/gluetun/internal/models" +) + +//nolint:lll +func hardcodedServers() (servers []models.ExpressvpnServer) { + return []models.ExpressvpnServer{ + {Country: "Albania", Hostname: "albania-ca-version-2.expressnetw.com"}, + {Country: "Algeria", Hostname: "algeria-ca-version-2.expressnetw.com"}, + {Country: "Andorra", Hostname: "andorra-ca-version-2.expressnetw.com"}, + {Country: "Argentina", Hostname: "argentina-ca-version-2.expressnetw.com"}, + {Country: "Armenia", Hostname: "armenia-ca-version-2.expressnetw.com"}, + {Country: "Australia", City: "Brisbane", Hostname: "australia-brisbane-ca-version-2.expressnetw.com"}, + {Country: "Australia", City: "Melbourne", Hostname: "australia-melbourne-ca-version-2.expressnetw.com"}, + {Country: "Australia", City: "Perth", Hostname: "australia-perth-ca-version-2.expressnetw.com"}, + {Country: "Australia", City: "Sydney", Hostname: "australia-sydney-2-ca-version-2.expressnetw.com"}, + {Country: "Australia", City: "Sydney", Hostname: "australia-sydney-ca-version-2.expressnetw.com"}, + {Country: "Austria", Hostname: "austria-ca-version-2.expressnetw.com"}, + {Country: "Bahamas", Hostname: "bahamas-ca-version-2.expressnetw.com"}, + {Country: "Bangladesh", Hostname: "bangladesh-ca-version-2.expressnetw.com"}, + {Country: "Belarus", Hostname: "belarus-ca-version-2.expressnetw.com"}, + {Country: "Belgium", Hostname: "belgium-ca-version-2.expressnetw.com"}, + {Country: "Bhutan", Hostname: "bhutan-ca-version-2.expressnetw.com"}, + {Country: "Bosnia And Herzegovina", City: "Bosnia And Herzegovina", Hostname: "bosniaandherzegovina-ca-version-2.expressnetw.com"}, + {Country: "Brazil", Hostname: "brazil-2-ca-version-2.expressnetw.com"}, + {Country: "Brazil", Hostname: "brazil-ca-version-2.expressnetw.com"}, + {Country: "Brunei", Hostname: "brunei-ca-version-2.expressnetw.com"}, + {Country: "Cambodia", Hostname: "cambodia-ca-version-2.expressnetw.com"}, + {Country: "Canada", City: "Montreal", Hostname: "canada-montreal-ca-version-2.expressnetw.com"}, + {Country: "Canada", City: "Montreal", Hostname: "canada-montreal-ca-version-2.expressnetw.com"}, + {Country: "Canada", City: "Toronto", Hostname: "canada-toronto-2-ca-version-2.expressnetw.com"}, + {Country: "Canada", City: "Toronto", Hostname: "canada-toronto-ca-version-2.expressnetw.com"}, + {Country: "Canada", City: "Vancouver", Hostname: "canada-vancouver-ca-version-2.expressnetw.com"}, + {Country: "Chile", Hostname: "chile-ca-version-2.expressnetw.com"}, + {Country: "Colombia", Hostname: "colombia-ca-version-2.expressnetw.com"}, + {Country: "Costa Rica", City: "Costa Rica", Hostname: "costarica-ca-version-2.expressnetw.com"}, + {Country: "Croatia", Hostname: "croatia-ca-version-2.expressnetw.com"}, + {Country: "Cyprus", Hostname: "cyprus-ca-version-2.expressnetw.com"}, + {Country: "Czech Republic", City: "Czech Republic", Hostname: "czechrepublic-ca-version-2.expressnetw.com"}, + {Country: "Denmark", Hostname: "denmark-ca-version-2.expressnetw.com"}, + {Country: "Ecuador", Hostname: "ecuador-ca-version-2.expressnetw.com"}, + {Country: "Egypt", Hostname: "egypt-ca-version-2.expressnetw.com"}, + {Country: "Estonia", Hostname: "estonia-ca-version-2.expressnetw.com"}, + {Country: "Finland", Hostname: "finland-ca-version-2.expressnetw.com"}, + {Country: "France", City: "Paris", Hostname: "france-paris-1-ca-version-2.expressnetw.com"}, + {Country: "France", City: "Paris", Hostname: "france-paris-2-ca-version-2.expressnetw.com"}, + {Country: "France", City: "Strasbourg", Hostname: "france-strasbourg-ca-version-2.expressnetw.com"}, + {Country: "Georgia", Hostname: "georgia-ca-version-2.expressnetw.com"}, + {Country: "Germany", City: "Frankfurt", Hostname: "germany-frankfurt-1-ca-version-2.expressnetw.com"}, + {Country: "Germany", City: "Frankfurt", Hostname: "germany-frankfurt-2-ca-version-2.expressnetw.com"}, + {Country: "Germany", City: "Frankfurt", Hostname: "germany-darmstadt-ca-version-2.expressnetw.com"}, + {Country: "Germany", City: "Nuremberg", Hostname: "germany-nuremberg-ca-version-2.expressnetw.com"}, + {Country: "Greece", Hostname: "greece-ca-version-2.expressnetw.com"}, + {Country: "Guatemala", Hostname: "guatemala-ca-version-2.expressnetw.com"}, + {Country: "Hong Kong", City: "Hong Kong", Hostname: "hongkong-2-ca-version-2.expressnetw.com"}, + {Country: "Hong Kong", City: "Hong Kong", Hostname: "hongkong4-ca-version-2.expressnetw.com"}, + {Country: "Hungary", Hostname: "hungary-ca-version-2.expressnetw.com"}, + {Country: "Iceland", Hostname: "iceland-ca-version-2.expressnetw.com"}, + {Country: "India", City: "Chennai", Hostname: "india-chennai-ca-version-2.expressnetw.com"}, + {Country: "India", City: "Mumbai", Hostname: "india-mumbai-1-ca-version-2.expressnetw.com"}, + {Country: "Indonesia", Hostname: "indonesia-ca-version-2.expressnetw.com"}, + {Country: "Ireland", Hostname: "ireland-ca-version-2.expressnetw.com"}, + {Country: "Isle Of Man", City: "Isle Of Man", Hostname: "isleofman-ca-version-2.expressnetw.com"}, + {Country: "Israel", Hostname: "israel-ca-version-2.expressnetw.com"}, + {Country: "Italy", City: "Cosenza", Hostname: "italy-cosenza-ca-version-2.expressnetw.com"}, + {Country: "Italy", City: "Milan", Hostname: "italy-milan-ca-version-2.expressnetw.com"}, + {Country: "Japan", City: "Kawasaki", Hostname: "japan-kawasaki-ca-version-2.expressnetw.com"}, + {Country: "Japan", City: "Tokyo", Hostname: "japan-tokyo-1-ca-version-2.expressnetw.com"}, + {Country: "Japan", City: "Tokyo", Hostname: "japan-tokyo-2-ca-version-2.expressnetw.com"}, + {Country: "Jersey", Hostname: "jersey-ca-version-2.expressnetw.com"}, + {Country: "Kazakhstan", Hostname: "kazakhstan-ca-version-2.expressnetw.com"}, + {Country: "Kenya", Hostname: "kenya-ca-version-2.expressnetw.com"}, + {Country: "Kyrgyzstan", Hostname: "kyrgyzstan-ca-version-2.expressnetw.com"}, + {Country: "Laos", Hostname: "laos-ca-version-2.expressnetw.com"}, + {Country: "Latvia", Hostname: "latvia-ca-version-2.expressnetw.com"}, + {Country: "Liechtenstein", Hostname: "liechtenstein-ca-version-2.expressnetw.com"}, + {Country: "Lithuania", Hostname: "lithuania-ca-version-2.expressnetw.com"}, + {Country: "Luxembourg", Hostname: "luxembourg-ca-version-2.expressnetw.com"}, + {Country: "Macau", Hostname: "macau-ca-version-2.expressnetw.com"}, + {Country: "Malaysia", Hostname: "malaysia-ca-version-2.expressnetw.com"}, + {Country: "Malta", Hostname: "malta-ca-version-2.expressnetw.com"}, + {Country: "Mexico", Hostname: "mexico-ca-version-2.expressnetw.com"}, + {Country: "Moldova", Hostname: "moldova-ca-version-2.expressnetw.com"}, + {Country: "Monaco", Hostname: "monaco-ca-version-2.expressnetw.com"}, + {Country: "Mongolia", Hostname: "mongolia-ca-version-2.expressnetw.com"}, + {Country: "Montenegro", Hostname: "montenegro-ca-version-2.expressnetw.com"}, + {Country: "Myanmar", Hostname: "myanmar-ca-version-2.expressnetw.com"}, + {Country: "Nepal", Hostname: "nepal-ca-version-2.expressnetw.com"}, + {Country: "Netherlands", City: "Amsterdam", Hostname: "netherlands-amsterdam-2-ca-version-2.expressnetw.com"}, + {Country: "Netherlands", City: "Amsterdam", Hostname: "netherlands-amsterdam-ca-version-2.expressnetw.com"}, + {Country: "Netherlands", City: "Rotterdam", Hostname: "netherlands-rotterdam-ca-version-2.expressnetw.com"}, + {Country: "Netherlands", City: "The Hague", Hostname: "netherlands-thehague-ca-version-2.expressnetw.com"}, + {Country: "New Zealand", City: "New Zealand", Hostname: "newzealand-ca-version-2.expressnetw.com"}, + {Country: "North Macedonia", City: "North Macedonia", Hostname: "macedonia-ca-version-2.expressnetw.com"}, + {Country: "Norway", Hostname: "norway-ca-version-2.expressnetw.com"}, + {Country: "Pakistan", Hostname: "pakistan-ca-version-2.expressnetw.com"}, + {Country: "Panama", Hostname: "panama-ca-version-2.expressnetw.com"}, + {Country: "Peru", Hostname: "peru-ca-version-2.expressnetw.com"}, + {Country: "Philippines Via Singapore", City: "Philippines Via Singapore", Hostname: "ph-via-sing-ca-version-2.expressnetw.com"}, + {Country: "Poland", Hostname: "poland-ca-version-2.expressnetw.com"}, + {Country: "Portugal", Hostname: "portugal-ca-version-2.expressnetw.com"}, + {Country: "Romania", Hostname: "romania-ca-version-2.expressnetw.com"}, + {Country: "Serbia", Hostname: "serbia-ca-version-2.expressnetw.com"}, + {Country: "Singapore", City: "CBD", Hostname: "singapore-cbd-ca-version-2.expressnetw.com"}, + {Country: "Singapore", City: "Jurong", Hostname: "singapore-jurong-ca-version-2.expressnetw.com"}, + {Country: "Singapore", City: "Marina Bay", Hostname: "singapore-marinabay-ca-version-2.expressnetw.com"}, + {Country: "Slovakia", Hostname: "slovakia-ca-version-2.expressnetw.com"}, + {Country: "Slovenia", Hostname: "slovenia-ca-version-2.expressnetw.com"}, + {Country: "South Africa", City: "South Africa", Hostname: "southafrica-ca-version-2.expressnetw.com"}, + {Country: "South Korea", City: "South Korea", Hostname: "southkorea2-ca-version-2.expressnetw.com"}, + {Country: "Spain", City: "Barcelona", Hostname: "spain-barcelona-ca-version-2.expressnetw.com"}, + {Country: "Spain", City: "Madrid", Hostname: "spain-ca-version-2.expressnetw.com"}, + {Country: "Sri Lanka", City: "Sri Lanka", Hostname: "srilanka-ca-version-2.expressnetw.com"}, + {Country: "Sweden", Hostname: "sweden-ca-version-2.expressnetw.com"}, + {Country: "Switzerland", Hostname: "switzerland-2-ca-version-2.expressnetw.com"}, + {Country: "Switzerland", Hostname: "switzerland-ca-version-2.expressnetw.com"}, + {Country: "Taiwan", Hostname: "taiwan-2-ca-version-2.expressnetw.com"}, + {Country: "Thailand", Hostname: "thailand-ca-version-2.expressnetw.com"}, + {Country: "Turkey", Hostname: "turkey-ca-version-2.expressnetw.com"}, + {Country: "Ukraine", Hostname: "ukraine-ca-version-2.expressnetw.com"}, + {Country: "UK", City: "Docklands", Hostname: "uk-berkshire-2-ca-version-2.expressnetw.com"}, + {Country: "UK", City: "London", Hostname: "uk-east-london-ca-version-2.expressnetw.com"}, + {Country: "UK", City: "London", Hostname: "uk-london-ca-version-2.expressnetw.com"}, + {Country: "Uruguay", Hostname: "uruguay-ca-version-2.expressnetw.com"}, + {Country: "USA", City: "Atlanta", Hostname: "usa-atlanta-ca-version-2.expressnetw.com"}, + {Country: "USA", City: "Chicago", Hostname: "usa-chicago-ca-version-2.expressnetw.com"}, + {Country: "USA", City: "Dallas", Hostname: "usa-dallas-2-ca-version-2.expressnetw.com"}, + {Country: "USA", City: "Dallas", Hostname: "usa-dallas-ca-version-2.expressnetw.com"}, + {Country: "USA", City: "Denver", Hostname: "usa-denver-ca-version-2.expressnetw.com"}, + {Country: "USA", City: "Los Angeles", Hostname: "usa-losangeles-1-ca-version-2.expressnetw.com"}, + {Country: "USA", City: "Los Angeles", Hostname: "usa-losangeles-2-ca-version-2.expressnetw.com"}, + {Country: "USA", City: "Los Angeles", Hostname: "usa-losangeles-3-ca-version-2.expressnetw.com"}, + {Country: "USA", City: "Los Angeles", Hostname: "usa-losangeles5-ca-version-2.expressnetw.com"}, + {Country: "USA", City: "Los Angeles", Hostname: "usa-losangeles-ca-version-2.expressnetw.com"}, + {Country: "USA", City: "Miami", Hostname: "usa-miami-ca-version-2.expressnetw.com"}, + {Country: "USA", City: "New Jersey", Hostname: "usa-newjersey-1-ca-version-2.expressnetw.com"}, + {Country: "USA", City: "New Jersey", Hostname: "usa-newjersey2-ca-version-2.expressnetw.com"}, + {Country: "USA", City: "New Jersey", Hostname: "usa-newjersey-3-ca-version-2.expressnetw.com"}, + {Country: "USA", City: "New York", Hostname: "us-new-york-2-ca-version-2.expressnetw.com"}, + {Country: "USA", City: "New York", Hostname: "usa-newyork-ca-version-2.expressnetw.com"}, + {Country: "USA", City: "Salt Lake City", Hostname: "usa-saltlakecity-ca-version-2.expressnetw.com"}, + {Country: "USA", City: "San Francisco", Hostname: "usa-sanfrancisco-ca-version-2.expressnetw.com"}, + {Country: "USA", City: "Seattle", Hostname: "usa-seattle-ca-version-2.expressnetw.com"}, + {Country: "USA", City: "Tampa", Hostname: "usa-tampa-1-ca-version-2.expressnetw.com"}, + {Country: "USA", City: "Washington DC", Hostname: "usa-washingtondc-ca-version-2.expressnetw.com"}, + {Country: "Uzbekistan", Hostname: "uzbekistan-ca-version-2.expressnetw.com"}, + {Country: "Venezuela", Hostname: "venezuela-ca-version-2.expressnetw.com"}, + {Country: "Vietnam", Hostname: "vietnam-ca-version-2.expressnetw.com"}, + } +} diff --git a/internal/updater/providers/expressvpn/resolve.go b/internal/updater/providers/expressvpn/resolve.go new file mode 100644 index 00000000..959fa493 --- /dev/null +++ b/internal/updater/providers/expressvpn/resolve.go @@ -0,0 +1,30 @@ +package expressvpn + +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 + maxNoNew = 1 + maxFails = 2 + ) + settings := resolver.ParallelSettings{ + MaxFailRatio: maxFailRatio, + MinFound: minServers, + Repeat: resolver.RepeatSettings{ + MaxDuration: time.Second, + MaxNoNew: maxNoNew, + MaxFails: maxFails, + SortIPs: true, + }, + } + return presolver.Resolve(ctx, hosts, settings) +} diff --git a/internal/updater/providers/expressvpn/servers.go b/internal/updater/providers/expressvpn/servers.go new file mode 100644 index 00000000..cf05a65b --- /dev/null +++ b/internal/updater/providers/expressvpn/servers.go @@ -0,0 +1,54 @@ +// package expressvpn contains code to obtain the server information +// for the ExpressVPN provider. +package expressvpn + +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.ExpressvpnServer, warnings []string, err error) { + servers = hardcodedServers() + + hosts := make([]string, len(servers)) + for i := range servers { + hosts[i] = servers[i].Hostname + } + + hostToIPs, newWarnings, err := resolveHosts(ctx, presolver, hosts, minServers) + warnings = append(warnings, newWarnings...) + if err != nil { + return nil, warnings, err + } + + i := 0 + for _, server := range servers { + hostname := server.Hostname + server.IPs = hostToIPs[hostname] + if len(server.IPs) == 0 { + continue + } + server.UDP = true // no TCP support + servers[i] = server + i++ + } + servers = servers[:i] + + 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 +} diff --git a/internal/updater/providers/expressvpn/sort.go b/internal/updater/providers/expressvpn/sort.go new file mode 100644 index 00000000..4471bcdf --- /dev/null +++ b/internal/updater/providers/expressvpn/sort.go @@ -0,0 +1,19 @@ +package expressvpn + +import ( + "sort" + + "github.com/qdm12/gluetun/internal/models" +) + +func sortServers(servers []models.ExpressvpnServer) { + 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 + }) +} diff --git a/internal/updater/updater.go b/internal/updater/updater.go index 6c5bbbf0..54968a05 100644 --- a/internal/updater/updater.go +++ b/internal/updater/updater.go @@ -61,6 +61,16 @@ func (u *updater) UpdateServers(ctx context.Context) (allServers models.AllServe } } + if u.options.Expressvpn { + u.logger.Info("updating Expressvpn servers...") + if err := u.updateExpressvpn(ctx); err != nil { + u.logger.Error(err.Error()) + } + if err := ctx.Err(); err != nil { + return allServers, err + } + } + if u.options.Fastestvpn { u.logger.Info("updating Fastestvpn servers...") if err := u.updateFastestvpn(ctx); err != nil {