diff --git a/internal/provider/fastestvpn/provider.go b/internal/provider/fastestvpn/provider.go
index 017cccea..497aa2e1 100644
--- a/internal/provider/fastestvpn/provider.go
+++ b/internal/provider/fastestvpn/provider.go
@@ -2,6 +2,7 @@ package fastestvpn
import (
"math/rand"
+ "net/http"
"github.com/qdm12/gluetun/internal/constants/providers"
"github.com/qdm12/gluetun/internal/provider/common"
@@ -15,12 +16,12 @@ type Provider struct {
}
func New(storage common.Storage, randSource rand.Source,
- unzipper common.Unzipper, updaterWarner common.Warner,
+ client *http.Client, updaterWarner common.Warner,
parallelResolver common.ParallelResolver) *Provider {
return &Provider{
storage: storage,
randSource: randSource,
- Fetcher: updater.New(unzipper, updaterWarner, parallelResolver),
+ Fetcher: updater.New(client, updaterWarner, parallelResolver),
}
}
diff --git a/internal/provider/fastestvpn/updater/api.go b/internal/provider/fastestvpn/updater/api.go
new file mode 100644
index 00000000..39e35fc6
--- /dev/null
+++ b/internal/provider/fastestvpn/updater/api.go
@@ -0,0 +1,129 @@
+package updater
+
+import (
+ "bytes"
+ "context"
+ "errors"
+ "fmt"
+ "io"
+ "net/http"
+ "net/url"
+ "strings"
+
+ "github.com/qdm12/gluetun/internal/provider/common"
+)
+
+type apiServer struct {
+ country string
+ city string
+ hostname string
+}
+
+var (
+ ErrDataMalformed = errors.New("data is malformed")
+)
+
+const apiURL = "https://support.fastestvpn.com/wp-admin/admin-ajax.php"
+
+// The API URL and requests are shamelessly taken from network operations
+// done on the page https://support.fastestvpn.com/vpn-servers/
+func fetchAPIServers(ctx context.Context, client *http.Client, protocol string) (
+ servers []apiServer, err error) {
+ form := url.Values{
+ "action": []string{"vpn_servers"},
+ "protocol": []string{protocol},
+ }
+ body := strings.NewReader(form.Encode())
+
+ request, err := http.NewRequestWithContext(ctx, http.MethodPost, apiURL, body)
+ if err != nil {
+ return nil, fmt.Errorf("creating request: %w", err)
+ }
+ request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
+ // request.Header.Set("User-Agent", "curl/8.9.0")
+ // request.Header.Set("Accept", "*/*")
+
+ response, err := client.Do(request)
+ if err != nil {
+ return nil, fmt.Errorf("sending request: %w", err)
+ }
+
+ if response.StatusCode != http.StatusOK {
+ _ = response.Body.Close()
+ return nil, fmt.Errorf("%w: %d", common.ErrHTTPStatusCodeNotOK, response.StatusCode)
+ }
+
+ data, err := io.ReadAll(response.Body)
+ if err != nil {
+ _ = response.Body.Close()
+ return nil, fmt.Errorf("reading response body: %w", err)
+ }
+
+ err = response.Body.Close()
+ if err != nil {
+ return nil, fmt.Errorf("closing response body: %w", err)
+ }
+
+ const usualMaxNumber = 100
+ servers = make([]apiServer, 0, usualMaxNumber)
+
+ for {
+ trBlock := getNextTRBlock(data)
+ if trBlock == nil {
+ break
+ }
+ data = data[len(trBlock):]
+
+ var server apiServer
+
+ const numberOfTDBlocks = 3
+ for i := 0; i < numberOfTDBlocks; i++ {
+ tdBlock := getNextTDBlock(trBlock)
+ if tdBlock == nil {
+ return nil, fmt.Errorf("%w: expected 3
blocks in | block %q",
+ ErrDataMalformed, string(trBlock))
+ }
+ trBlock = trBlock[len(tdBlock):]
+
+ const startToken, endToken = "| ", " | "
+ tdBlockData := string(tdBlock[len(startToken) : len(tdBlock)-len(endToken)])
+ const countryIndex, cityIndex, hostnameIndex = 0, 1, 2
+ switch i {
+ case countryIndex:
+ server.country = tdBlockData
+ case cityIndex:
+ server.city = tdBlockData
+ case hostnameIndex:
+ server.hostname = tdBlockData
+ }
+ }
+ servers = append(servers, server)
+ }
+
+ return servers, nil
+}
+
+func getNextTRBlock(data []byte) (trBlock []byte) {
+ const startToken, endToken = "
", "
"
+ return getNextBlock(data, startToken, endToken)
+}
+
+func getNextTDBlock(data []byte) (tdBlock []byte) {
+ const startToken, endToken = "", " | "
+ return getNextBlock(data, startToken, endToken)
+}
+
+func getNextBlock(data []byte, startToken, endToken string) (nextBlock []byte) {
+ i := bytes.Index(data, []byte(startToken))
+ if i == -1 {
+ return nil
+ }
+
+ nextBlock = data[i:]
+ i = bytes.Index(nextBlock[len(startToken):], []byte(endToken))
+ if i == -1 {
+ return nil
+ }
+ nextBlock = nextBlock[:i+len(startToken)+len(endToken)]
+ return nextBlock
+}
diff --git a/internal/provider/fastestvpn/updater/api_test.go b/internal/provider/fastestvpn/updater/api_test.go
new file mode 100644
index 00000000..ec49a887
--- /dev/null
+++ b/internal/provider/fastestvpn/updater/api_test.go
@@ -0,0 +1,164 @@
+package updater
+
+import (
+ "context"
+ "errors"
+ "io"
+ "net/http"
+ "strings"
+ "testing"
+
+ "github.com/qdm12/gluetun/internal/provider/common"
+ "github.com/stretchr/testify/assert"
+)
+
+type roundTripFunc func(r *http.Request) (*http.Response, error)
+
+func (f roundTripFunc) RoundTrip(r *http.Request) (*http.Response, error) {
+ return f(r)
+}
+
+func Test_fechAPIServers(t *testing.T) {
+ t.Parallel()
+
+ errTest := errors.New("test error")
+
+ testCases := map[string]struct {
+ ctx context.Context
+ protocol string
+ requestBody string
+ responseStatus int
+ responseBody io.ReadCloser
+ transportErr error
+ servers []apiServer
+ errWrapped error
+ errMessage string
+ }{
+ "transport_error": {
+ ctx: context.Background(),
+ protocol: "tcp",
+ requestBody: "action=vpn_servers&protocol=tcp",
+ responseStatus: http.StatusOK,
+ transportErr: errTest,
+ errWrapped: errTest,
+ errMessage: `sending request: Post ` +
+ `"https://support.fastestvpn.com/wp-admin/admin-ajax.php": ` +
+ `test error`,
+ },
+ "not_found_status_code": {
+ ctx: context.Background(),
+ protocol: "tcp",
+ requestBody: "action=vpn_servers&protocol=tcp",
+ responseStatus: http.StatusNotFound,
+ errWrapped: common.ErrHTTPStatusCodeNotOK,
+ errMessage: "HTTP status code not OK: 404",
+ },
+ "empty_data": {
+ ctx: context.Background(),
+ protocol: "tcp",
+ requestBody: "action=vpn_servers&protocol=tcp",
+ responseStatus: http.StatusOK,
+ responseBody: io.NopCloser(strings.NewReader("")),
+ servers: []apiServer{},
+ },
+ "single_server": {
+ ctx: context.Background(),
+ protocol: "tcp",
+ requestBody: "action=vpn_servers&protocol=tcp",
+ responseStatus: http.StatusOK,
+ responseBody: io.NopCloser(strings.NewReader(
+ "irrelevant| Australia | Sydney | " +
+ "au-stream.jumptoserver.com |
irrelevant")),
+ servers: []apiServer{
+ {country: "Australia", city: "Sydney", hostname: "au-stream.jumptoserver.com"},
+ },
+ },
+ "two_servers": {
+ ctx: context.Background(),
+ protocol: "tcp",
+ requestBody: "action=vpn_servers&protocol=tcp",
+ responseStatus: http.StatusOK,
+ responseBody: io.NopCloser(strings.NewReader(
+ "| Australia | Sydney | au-stream.jumptoserver.com |
" +
+ "| Australia | Sydney | au-01.jumptoserver.com |
")),
+ servers: []apiServer{
+ {country: "Australia", city: "Sydney", hostname: "au-stream.jumptoserver.com"},
+ {country: "Australia", city: "Sydney", hostname: "au-01.jumptoserver.com"},
+ },
+ },
+ }
+
+ for name, testCase := range testCases {
+ testCase := testCase
+ t.Run(name, func(t *testing.T) {
+ t.Parallel()
+
+ client := &http.Client{
+ Transport: roundTripFunc(func(r *http.Request) (*http.Response, error) {
+ assert.Equal(t, apiURL, r.URL.String())
+ requestBody, err := io.ReadAll(r.Body)
+ assert.NoError(t, err)
+ assert.Equal(t, testCase.requestBody, string(requestBody))
+ if testCase.transportErr != nil {
+ return nil, testCase.transportErr
+ }
+ return &http.Response{
+ StatusCode: testCase.responseStatus,
+ Body: testCase.responseBody,
+ }, nil
+ }),
+ }
+
+ entries, err := fetchAPIServers(testCase.ctx, client, testCase.protocol)
+
+ assert.ErrorIs(t, err, testCase.errWrapped)
+ if testCase.errWrapped != nil {
+ assert.EqualError(t, err, testCase.errMessage)
+ }
+ assert.Equal(t, testCase.servers, entries)
+ })
+ }
+}
+
+func Test_getNextBlock(t *testing.T) {
+ t.Parallel()
+
+ testCases := map[string]struct {
+ data string
+ startToken string
+ endToken string
+ nextBlock []byte
+ }{
+ "empty_data": {
+ startToken: "",
+ endToken: "",
+ },
+ "start_token_not_found": {
+ data: "test",
+ startToken: "",
+ endToken: "",
+ },
+ "end_token_not_found": {
+ data: "test",
+ startToken: "",
+ endToken: "",
+ },
+ "block_found": {
+ data: "xytesttest2zx",
+ startToken: "",
+ endToken: "",
+ nextBlock: []byte("test"),
+ },
+ }
+
+ for name, testCase := range testCases {
+ testCase := testCase
+ t.Run(name, func(t *testing.T) {
+ t.Parallel()
+
+ nextBlock := getNextBlock([]byte(testCase.data), testCase.startToken, testCase.endToken)
+
+ assert.Equal(t, testCase.nextBlock, nextBlock)
+ })
+ }
+}
diff --git a/internal/provider/fastestvpn/updater/filename.go b/internal/provider/fastestvpn/updater/filename.go
deleted file mode 100644
index 2940a7c8..00000000
--- a/internal/provider/fastestvpn/updater/filename.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package updater
-
-import (
- "errors"
- "fmt"
- "regexp"
- "strings"
-)
-
-var errFilenameNoProtocolSuffix = errors.New("filename does not have a protocol suffix")
-
-var trailNumberExp = regexp.MustCompile(`[0-9]+$`)
-
-func parseFilename(fileName string) (
- country string, tcp, udp bool, err error,
-) {
- const (
- tcpSuffix = "-tcp.ovpn"
- udpSuffix = "-udp.ovpn"
- )
- var suffix string
- switch {
- case strings.HasSuffix(strings.ToLower(fileName), tcpSuffix):
- suffix = tcpSuffix
- tcp = true
- case strings.HasSuffix(strings.ToLower(fileName), udpSuffix):
- suffix = udpSuffix
- udp = true
- default:
- return "", false, false, fmt.Errorf("%w: %s",
- errFilenameNoProtocolSuffix, fileName)
- }
-
- countryWithNumber := strings.TrimSuffix(fileName, suffix)
- number := trailNumberExp.FindString(countryWithNumber)
- country = countryWithNumber[:len(countryWithNumber)-len(number)]
-
- return country, tcp, udp, nil
-}
diff --git a/internal/provider/fastestvpn/updater/hosttoserver.go b/internal/provider/fastestvpn/updater/hosttoserver.go
index 6deaf3bd..3f87b27c 100644
--- a/internal/provider/fastestvpn/updater/hosttoserver.go
+++ b/internal/provider/fastestvpn/updater/hosttoserver.go
@@ -9,12 +9,19 @@ import (
type hostToServer map[string]models.Server
-func (hts hostToServer) add(host, country string, tcp, udp bool) {
+func (hts hostToServer) add(host, country, city string, tcp, udp bool) {
server, ok := hts[host]
if !ok {
server.VPN = vpn.OpenVPN
server.Hostname = host
server.Country = country
+ server.City = city
+ }
+ if city != "" {
+ // some servers are listed without the city although
+ // they are also listed with the city described, so update
+ // the city field.
+ server.City = city
}
if tcp {
server.TCP = true
diff --git a/internal/provider/fastestvpn/updater/servers.go b/internal/provider/fastestvpn/updater/servers.go
index 41699c77..bdbff7b8 100644
--- a/internal/provider/fastestvpn/updater/servers.go
+++ b/internal/provider/fastestvpn/updater/servers.go
@@ -4,48 +4,26 @@ import (
"context"
"fmt"
"sort"
- "strings"
"github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/provider/common"
- "github.com/qdm12/gluetun/internal/updater/openvpn"
)
func (u *Updater) FetchServers(ctx context.Context, minServers int) (
servers []models.Server, err error) {
- const url = "https://support.fastestvpn.com/download/fastestvpn_ovpn"
- contents, err := u.unzipper.FetchAndExtract(ctx, url)
- if err != nil {
- return nil, err
- } else if len(contents) < minServers {
- return nil, fmt.Errorf("%w: %d and expected at least %d",
- common.ErrNotEnoughServers, len(contents), minServers)
- }
-
+ protocols := []string{"tcp", "udp"}
hts := make(hostToServer)
- for fileName, content := range contents {
- if !strings.HasSuffix(fileName, ".ovpn") {
- continue // not an OpenVPN file
- }
-
- country, tcp, udp, err := parseFilename(fileName)
+ for _, protocol := range protocols {
+ apiServers, err := fetchAPIServers(ctx, u.client, protocol)
if err != nil {
- u.warner.Warn(err.Error())
- continue
+ return nil, fmt.Errorf("fetching %s servers from API: %w", protocol, err)
}
-
- host, warning, err := openvpn.ExtractHost(content)
- if warning != "" {
- u.warner.Warn(warning)
+ for _, apiServer := range apiServers {
+ tcp := protocol == "tcp"
+ udp := protocol == "udp"
+ hts.add(apiServer.hostname, apiServer.country, apiServer.city, tcp, udp)
}
- if err != nil {
- // treat error as warning and go to next file
- u.warner.Warn(err.Error() + " in " + fileName)
- continue
- }
-
- hts.add(host, country, tcp, udp)
}
if len(hts) < minServers {
diff --git a/internal/provider/fastestvpn/updater/updater.go b/internal/provider/fastestvpn/updater/updater.go
index 3fbee059..f15a2f3a 100644
--- a/internal/provider/fastestvpn/updater/updater.go
+++ b/internal/provider/fastestvpn/updater/updater.go
@@ -1,19 +1,21 @@
package updater
import (
+ "net/http"
+
"github.com/qdm12/gluetun/internal/provider/common"
)
type Updater struct {
- unzipper common.Unzipper
+ client *http.Client
parallelResolver common.ParallelResolver
warner common.Warner
}
-func New(unzipper common.Unzipper, warner common.Warner,
+func New(client *http.Client, warner common.Warner,
parallelResolver common.ParallelResolver) *Updater {
return &Updater{
- unzipper: unzipper,
+ client: client,
parallelResolver: parallelResolver,
warner: warner,
}
diff --git a/internal/provider/providers.go b/internal/provider/providers.go
index 37c9e735..a689e149 100644
--- a/internal/provider/providers.go
+++ b/internal/provider/providers.go
@@ -62,7 +62,7 @@ func NewProviders(storage Storage, timeNow func() time.Time,
providers.Custom: custom.New(extractor),
providers.Cyberghost: cyberghost.New(storage, randSource, parallelResolver),
providers.Expressvpn: expressvpn.New(storage, randSource, unzipper, updaterWarner, parallelResolver),
- providers.Fastestvpn: fastestvpn.New(storage, randSource, unzipper, updaterWarner, parallelResolver),
+ providers.Fastestvpn: fastestvpn.New(storage, randSource, client, updaterWarner, parallelResolver),
providers.HideMyAss: hidemyass.New(storage, randSource, client, updaterWarner, parallelResolver),
providers.Ipvanish: ipvanish.New(storage, randSource, unzipper, updaterWarner, parallelResolver),
providers.Ivpn: ivpn.New(storage, randSource, client, updaterWarner, parallelResolver),
diff --git a/internal/storage/servers.json b/internal/storage/servers.json
index f4a2aae4..d531ecb1 100644
--- a/internal/storage/servers.json
+++ b/internal/storage/servers.json
@@ -23712,11 +23712,12 @@
},
"fastestvpn": {
"version": 2,
- "timestamp": 1669836047,
+ "timestamp": 1722350820,
"servers": [
{
"vpn": "openvpn",
- "country": "australia",
+ "country": "Australia",
+ "city": "Sydney",
"hostname": "au-01.jumptoserver.com",
"tcp": true,
"udp": true,
@@ -23726,7 +23727,8 @@
},
{
"vpn": "openvpn",
- "country": "australia",
+ "country": "Australia",
+ "city": "Sydney",
"hostname": "au-02.jumptoserver.com",
"tcp": true,
"udp": true,
@@ -23736,7 +23738,30 @@
},
{
"vpn": "openvpn",
- "country": "austria",
+ "country": "Australia",
+ "city": "Sydney",
+ "hostname": "au-stream.jumptoserver.com",
+ "tcp": true,
+ "udp": true,
+ "ips": [
+ "146.70.141.62"
+ ]
+ },
+ {
+ "vpn": "openvpn",
+ "country": "Austria",
+ "city": "Vienna",
+ "hostname": "at-stream.jumptoserver.com",
+ "tcp": true,
+ "udp": true,
+ "ips": [
+ "146.70.146.50"
+ ]
+ },
+ {
+ "vpn": "openvpn",
+ "country": "Austria",
+ "city": "Wien",
"hostname": "at-01.jumptoserver.com",
"tcp": true,
"udp": true,
@@ -23746,7 +23771,8 @@
},
{
"vpn": "openvpn",
- "country": "belgium",
+ "country": "Belgium",
+ "city": "Brussel",
"hostname": "bel-01.jumptoserver.com",
"tcp": true,
"udp": true,
@@ -23756,37 +23782,32 @@
},
{
"vpn": "openvpn",
- "country": "belgium",
- "hostname": "bel-02.jumptoserver.com",
+ "country": "Belgium",
+ "city": "Brussels",
+ "hostname": "bel-stream.jumptoserver.com",
"tcp": true,
"udp": true,
"ips": [
- "193.9.114.211"
+ "193.9.114.210"
]
},
{
"vpn": "openvpn",
- "country": "brazil",
- "hostname": "br.jumptoserver.com",
+ "country": "Brazil",
+ "hostname": "br-cf.jumptoserver.com",
"tcp": true,
"udp": true,
"ips": [
- "185.192.124.101"
+ "185.192.124.75",
+ "185.192.124.77",
+ "185.192.124.104",
+ "185.192.124.131"
]
},
{
"vpn": "openvpn",
- "country": "brazil",
- "hostname": "br2.jumptoserver.com",
- "tcp": true,
- "udp": true,
- "ips": [
- "185.192.124.121"
- ]
- },
- {
- "vpn": "openvpn",
- "country": "bulgaria",
+ "country": "Bulgaria",
+ "city": "Sofia",
"hostname": "bg-01.jumptoserver.com",
"tcp": true,
"udp": true,
@@ -23796,7 +23817,8 @@
},
{
"vpn": "openvpn",
- "country": "canada",
+ "country": "Canada",
+ "city": "Montreal",
"hostname": "ca-01.jumptoserver.com",
"tcp": true,
"udp": true,
@@ -23806,7 +23828,8 @@
},
{
"vpn": "openvpn",
- "country": "canada-stream",
+ "country": "Canada",
+ "city": "Montreal",
"hostname": "ca-stream.jumptoserver.com",
"tcp": true,
"udp": true,
@@ -23816,7 +23839,18 @@
},
{
"vpn": "openvpn",
- "country": "colombia",
+ "country": "Canada",
+ "city": "Montreal",
+ "hostname": "hk-dbl.jumptoserver.com",
+ "udp": true,
+ "ips": [
+ "193.239.86.4"
+ ]
+ },
+ {
+ "vpn": "openvpn",
+ "country": "Colombia",
+ "city": "Bogota",
"hostname": "clmb.jumptoserver.com",
"tcp": true,
"udp": true,
@@ -23826,7 +23860,8 @@
},
{
"vpn": "openvpn",
- "country": "czechia",
+ "country": "Czech Republic",
+ "city": "Prague",
"hostname": "cz-01.jumptoserver.com",
"tcp": true,
"udp": true,
@@ -23836,7 +23871,7 @@
},
{
"vpn": "openvpn",
- "country": "denmark",
+ "country": "Denmark",
"hostname": "dk-01.jumptoserver.com",
"tcp": true,
"udp": true,
@@ -23846,17 +23881,19 @@
},
{
"vpn": "openvpn",
- "country": "finland",
- "hostname": "fi.jumptoserver.com",
+ "country": "Finland",
+ "city": "Helsinki",
+ "hostname": "fi-p2p.jumptoserver.com",
"tcp": true,
"udp": true,
"ips": [
- "37.143.129.152"
+ "37.143.129.237"
]
},
{
"vpn": "openvpn",
- "country": "finland",
+ "country": "Finland",
+ "city": "Helsinki",
"hostname": "fi2.jumptoserver.com",
"tcp": true,
"udp": true,
@@ -23866,7 +23903,8 @@
},
{
"vpn": "openvpn",
- "country": "france",
+ "country": "France",
+ "city": "Paris",
"hostname": "fr.jumptoserver.com",
"tcp": true,
"udp": true,
@@ -23876,7 +23914,8 @@
},
{
"vpn": "openvpn",
- "country": "france-via-uk",
+ "country": "France",
+ "city": "Paris",
"hostname": "uk-dbl.jumptoserver.com",
"tcp": true,
"udp": true,
@@ -23886,7 +23925,30 @@
},
{
"vpn": "openvpn",
- "country": "germany",
+ "country": "Germany",
+ "city": "Dusseldorf",
+ "hostname": "de-dus1.jumptoserver.com",
+ "tcp": true,
+ "udp": true,
+ "ips": [
+ "89.163.157.7"
+ ]
+ },
+ {
+ "vpn": "openvpn",
+ "country": "Germany",
+ "city": "Dusseldorf",
+ "hostname": "de-dus2.jumptoserver.com",
+ "tcp": true,
+ "udp": true,
+ "ips": [
+ "213.202.218.2"
+ ]
+ },
+ {
+ "vpn": "openvpn",
+ "country": "Germany",
+ "city": "Frankfurt",
"hostname": "de-01.jumptoserver.com",
"tcp": true,
"udp": true,
@@ -23896,17 +23958,8 @@
},
{
"vpn": "openvpn",
- "country": "germany-dus",
- "hostname": "de-dus2.jumptoserver.com",
- "tcp": true,
- "udp": true,
- "ips": [
- "89.163.225.245"
- ]
- },
- {
- "vpn": "openvpn",
- "country": "greece",
+ "country": "Greece",
+ "city": "Athina",
"hostname": "grc.jumptoserver.com",
"tcp": true,
"udp": true,
@@ -23916,97 +23969,52 @@
},
{
"vpn": "openvpn",
- "country": "hongkong",
+ "country": "Hong Kong",
+ "city": "HK",
"hostname": "hk.jumptoserver.com",
"tcp": true,
"udp": true,
"ips": [
- "64.120.88.110"
+ "193.239.86.3"
]
},
{
"vpn": "openvpn",
- "country": "hongkong",
- "hostname": "hk2.jumptoserver.com",
- "tcp": true,
- "udp": true,
- "ips": [
- "84.17.37.194"
- ]
- },
- {
- "vpn": "openvpn",
- "country": "hongkong",
- "hostname": "hk3.jumptoserver.com",
- "tcp": true,
- "udp": true,
- "ips": [
- "103.75.117.73"
- ]
- },
- {
- "vpn": "openvpn",
- "country": "hungary",
+ "country": "Hungary",
+ "city": "Budapest",
"hostname": "hng.jumptoserver.com",
"tcp": true,
"udp": true,
"ips": [
- "37.120.144.162"
+ "37.120.144.165"
]
},
{
"vpn": "openvpn",
- "country": "india",
- "hostname": "in1.jumptoserver.com",
+ "country": "India",
+ "city": "Mumbai",
+ "hostname": "in-vr.jumptoserver.com",
"tcp": true,
"udp": true,
"ips": [
- "194.195.115.169"
+ "45.84.241.2"
]
},
{
"vpn": "openvpn",
- "country": "india",
- "hostname": "in2.jumptoserver.com",
+ "country": "Ireland",
+ "city": "Dublin",
+ "hostname": "ie-stream.jumptoserver.com",
"tcp": true,
"udp": true,
"ips": [
- "45.84.241.3"
+ "146.70.130.218"
]
},
{
"vpn": "openvpn",
- "country": "india-stream",
- "hostname": "in4.jumptoserver.com",
- "tcp": true,
- "udp": true,
- "ips": [
- "192.46.215.192"
- ]
- },
- {
- "vpn": "openvpn",
- "country": "india-stream",
- "hostname": "in5.jumptoserver.com",
- "tcp": true,
- "udp": true,
- "ips": [
- "194.195.118.230"
- ]
- },
- {
- "vpn": "openvpn",
- "country": "india-stream",
- "hostname": "in6.jumptoserver.com",
- "tcp": true,
- "udp": true,
- "ips": [
- "192.46.215.50"
- ]
- },
- {
- "vpn": "openvpn",
- "country": "ireland",
+ "country": "Ireland",
+ "city": "Dublin",
"hostname": "ir.jumptoserver.com",
"tcp": true,
"udp": true,
@@ -24016,17 +24024,8 @@
},
{
"vpn": "openvpn",
- "country": "italy",
- "hostname": "it-01.jumptoserver.com",
- "tcp": true,
- "udp": true,
- "ips": [
- "95.174.64.122"
- ]
- },
- {
- "vpn": "openvpn",
- "country": "italy-stream",
+ "country": "Italy",
+ "city": "Assago",
"hostname": "it-stream.jumptoserver.com",
"tcp": true,
"udp": true,
@@ -24036,7 +24035,19 @@
},
{
"vpn": "openvpn",
- "country": "japan",
+ "country": "Italy",
+ "city": "milan",
+ "hostname": "it-01.jumptoserver.com",
+ "tcp": true,
+ "udp": true,
+ "ips": [
+ "95.174.64.122"
+ ]
+ },
+ {
+ "vpn": "openvpn",
+ "country": "Japan",
+ "city": "Tokyo",
"hostname": "jp-01.jumptoserver.com",
"tcp": true,
"udp": true,
@@ -24046,18 +24057,9 @@
},
{
"vpn": "openvpn",
- "country": "japan",
- "hostname": "jp-02.jumptoserver.com",
- "tcp": true,
- "udp": true,
- "ips": [
- "146.70.138.108"
- ]
- },
- {
- "vpn": "openvpn",
- "country": "japan-stream",
- "hostname": "jp-03.jumptoserver.com",
+ "country": "Japan",
+ "city": "Tokyo",
+ "hostname": "jp-stream.jumptoserver.com",
"tcp": true,
"udp": true,
"ips": [
@@ -24066,67 +24068,60 @@
},
{
"vpn": "openvpn",
- "country": "luxembourg",
+ "country": "Luxembourg",
"hostname": "lux1.jumptoserver.com",
"tcp": true,
"udp": true,
"ips": [
- "92.223.105.18"
+ "5.253.204.43"
]
},
{
"vpn": "openvpn",
- "country": "malaysia",
- "hostname": "my.jumptoserver.com",
+ "country": "Malaysia",
+ "hostname": "my-cf.jumptoserver.com",
"tcp": true,
"udp": true,
"ips": [
+ "103.75.116.27",
+ "103.75.116.141",
"103.75.116.180"
]
},
{
"vpn": "openvpn",
- "country": "malaysia",
- "hostname": "my2.jumptoserver.com",
+ "country": "Mexico",
+ "hostname": "mx-cf.jumptoserver.com",
"tcp": true,
"udp": true,
"ips": [
- "103.75.116.141"
+ "147.78.1.112"
]
},
{
"vpn": "openvpn",
- "country": "mexico",
- "hostname": "mx.jumptoserver.com",
+ "country": "Netherlands",
+ "hostname": "nl-01.jumptoserver.com",
"tcp": true,
"udp": true,
"ips": [
- "147.78.1.191"
+ "108.181.123.74"
]
},
{
"vpn": "openvpn",
- "country": "netherlands",
- "hostname": "nl.jumptoserver.com",
+ "country": "Netherlands",
+ "hostname": "nl-02.jumptoserver.com",
"tcp": true,
"udp": true,
"ips": [
- "172.107.95.122"
+ "108.181.123.75"
]
},
{
"vpn": "openvpn",
- "country": "netherlands",
- "hostname": "nl2.jumptoserver.com",
- "tcp": true,
- "udp": true,
- "ips": [
- "172.107.95.123"
- ]
- },
- {
- "vpn": "openvpn",
- "country": "norway",
+ "country": "Norway",
+ "city": "Oslo",
"hostname": "nr-01.jumptoserver.com",
"tcp": true,
"udp": true,
@@ -24136,7 +24131,8 @@
},
{
"vpn": "openvpn",
- "country": "poland",
+ "country": "Poland",
+ "city": "Gdansk",
"hostname": "pl.jumptoserver.com",
"tcp": true,
"udp": true,
@@ -24146,7 +24142,8 @@
},
{
"vpn": "openvpn",
- "country": "portugal",
+ "country": "Portugal",
+ "city": "Lisbon",
"hostname": "pt.jumptoserver.com",
"tcp": true,
"udp": true,
@@ -24156,7 +24153,8 @@
},
{
"vpn": "openvpn",
- "country": "romania",
+ "country": "Romania",
+ "city": "Bucharest",
"hostname": "ro-01.jumptoserver.com",
"tcp": true,
"udp": true,
@@ -24166,27 +24164,41 @@
},
{
"vpn": "openvpn",
- "country": "russia",
- "hostname": "ru3.jumptoserver.com",
+ "country": "Romania",
+ "city": "Bucharest",
+ "hostname": "sg-dvpn.jumptoserver.com",
"tcp": true,
"udp": true,
"ips": [
- "185.126.239.245"
+ "91.207.173.101"
]
},
{
"vpn": "openvpn",
- "country": "russia",
- "hostname": "ru4.jumptoserver.com",
+ "country": "Russia",
+ "city": "Moscow",
+ "hostname": "ru-vr.jumptoserver.com",
"tcp": true,
"udp": true,
"ips": [
- "185.126.239.252"
+ "91.226.58.5"
]
},
{
"vpn": "openvpn",
- "country": "serbia",
+ "country": "Serbia",
+ "city": "Belgrade",
+ "hostname": "jp-dvpn.jumptoserver.com",
+ "tcp": true,
+ "udp": true,
+ "ips": [
+ "146.70.138.108"
+ ]
+ },
+ {
+ "vpn": "openvpn",
+ "country": "Serbia",
+ "city": "Belgrade",
"hostname": "rs-01.jumptoserver.com",
"tcp": true,
"udp": true,
@@ -24196,7 +24208,8 @@
},
{
"vpn": "openvpn",
- "country": "singapore",
+ "country": "Singapore",
+ "city": "Singapore",
"hostname": "sg-01.jumptoserver.com",
"tcp": true,
"udp": true,
@@ -24206,7 +24219,8 @@
},
{
"vpn": "openvpn",
- "country": "slovakia",
+ "country": "Slovakia",
+ "city": "Bratislava",
"hostname": "svk.jumptoserver.com",
"tcp": true,
"udp": true,
@@ -24216,7 +24230,19 @@
},
{
"vpn": "openvpn",
- "country": "southkorea",
+ "country": "South Africa",
+ "city": "Johannesburg",
+ "hostname": "sa-jn.jumptoserver.com",
+ "tcp": true,
+ "udp": true,
+ "ips": [
+ "129.232.176.170"
+ ]
+ },
+ {
+ "vpn": "openvpn",
+ "country": "South Korea",
+ "city": "Seoul",
"hostname": "kr.jumptoserver.com",
"tcp": true,
"udp": true,
@@ -24226,17 +24252,19 @@
},
{
"vpn": "openvpn",
- "country": "spain",
+ "country": "Spain",
+ "city": "Barcelona",
"hostname": "es-01.jumptoserver.com",
"tcp": true,
"udp": true,
"ips": [
- "172.107.242.178"
+ "108.181.71.242"
]
},
{
"vpn": "openvpn",
- "country": "sweden",
+ "country": "Sweden",
+ "city": "Stockholm",
"hostname": "se-01.jumptoserver.com",
"tcp": true,
"udp": true,
@@ -24246,7 +24274,8 @@
},
{
"vpn": "openvpn",
- "country": "switzerland",
+ "country": "Switzerland",
+ "city": "Zurich",
"hostname": "ch-01.jumptoserver.com",
"tcp": true,
"udp": true,
@@ -24256,17 +24285,19 @@
},
{
"vpn": "openvpn",
- "country": "turkey",
+ "country": "Turkey",
+ "city": "Izmir",
"hostname": "tr.jumptoserver.com",
"tcp": true,
"udp": true,
"ips": [
- "185.123.102.57"
+ "185.123.101.43"
]
},
{
"vpn": "openvpn",
- "country": "uae-dubai",
+ "country": "UAE",
+ "city": "Dubai",
"hostname": "uae.jumptoserver.com",
"tcp": true,
"udp": true,
@@ -24276,7 +24307,8 @@
},
{
"vpn": "openvpn",
- "country": "uk",
+ "country": "UK",
+ "city": "London",
"hostname": "uk-01.jumptoserver.com",
"tcp": true,
"udp": true,
@@ -24286,7 +24318,8 @@
},
{
"vpn": "openvpn",
- "country": "uk",
+ "country": "UK",
+ "city": "London",
"hostname": "uk-02.jumptoserver.com",
"tcp": true,
"udp": true,
@@ -24296,77 +24329,7 @@
},
{
"vpn": "openvpn",
- "country": "uk-stream",
- "hostname": "uk-streaming.jumptoserver.com",
- "tcp": true,
- "udp": true,
- "ips": [
- "195.191.219.70"
- ]
- },
- {
- "vpn": "openvpn",
- "country": "usa-ashburn",
- "hostname": "us-ash.jumptoserver.com",
- "tcp": true,
- "udp": true,
- "ips": [
- "172.106.16.62"
- ]
- },
- {
- "vpn": "openvpn",
- "country": "usa-atlanta",
- "hostname": "us-at.jumptoserver.com",
- "tcp": true,
- "udp": true,
- "ips": [
- "167.160.88.250"
- ]
- },
- {
- "vpn": "openvpn",
- "country": "usa-dallas",
- "hostname": "us-dl.jumptoserver.com",
- "tcp": true,
- "udp": true,
- "ips": [
- "172.106.1.66"
- ]
- },
- {
- "vpn": "openvpn",
- "country": "usa-denver",
- "hostname": "us-dv1.jumptoserver.com",
- "tcp": true,
- "udp": true,
- "ips": [
- "139.28.179.82"
- ]
- },
- {
- "vpn": "openvpn",
- "country": "usa-losangeles",
- "hostname": "us-la.jumptoserver.com",
- "tcp": true,
- "udp": true,
- "ips": [
- "109.236.60.123"
- ]
- },
- {
- "vpn": "openvpn",
- "country": "usa-losangeles",
- "hostname": "us-la2.jumptoserver.com",
- "tcp": true,
- "udp": true,
- "ips": [
- "109.236.60.124"
- ]
- },
- {
- "vpn": "openvpn",
- "country": "usa-miami",
+ "country": "USA",
"hostname": "us-mia.jumptoserver.com",
"tcp": true,
"udp": true,
@@ -24376,72 +24339,143 @@
},
{
"vpn": "openvpn",
- "country": "usa-newyork",
- "hostname": "us-ny.jumptoserver.com",
+ "country": "USA",
+ "hostname": "us-wt1.jumptoserver.com",
"tcp": true,
"udp": true,
"ips": [
- "173.208.96.143"
+ "23.105.160.221"
]
},
{
"vpn": "openvpn",
- "country": "usa-seattle",
- "hostname": "us-se1.jumptoserver.com",
- "tcp": true,
- "udp": true,
- "ips": [
- "216.244.81.174"
- ]
- },
- {
- "vpn": "openvpn",
- "country": "usa-stream",
- "hostname": "us-stream.jumptoserver.com",
- "tcp": true,
- "udp": true,
- "ips": [
- "172.106.16.58"
- ]
- },
- {
- "vpn": "openvpn",
- "country": "usa-stream",
- "hostname": "us-stream2.jumptoserver.com",
- "tcp": true,
- "udp": true,
- "ips": [
- "172.106.1.67"
- ]
- },
- {
- "vpn": "openvpn",
- "country": "usa-via-netherlands",
+ "country": "USA",
+ "city": "Anchorage",
"hostname": "nl-dbl.jumptoserver.com",
"tcp": true,
"udp": true,
"ips": [
- "172.107.95.125"
+ "108.181.123.76"
]
},
{
"vpn": "openvpn",
- "country": "usa-washington",
- "hostname": "us-wt.jumptoserver.com",
+ "country": "USA",
+ "city": "Ashburn",
+ "hostname": "us-ash.jumptoserver.com",
"tcp": true,
"udp": true,
"ips": [
- "23.105.160.235"
+ "108.181.56.98"
]
},
{
"vpn": "openvpn",
- "country": "usa-washington",
- "hostname": "us-wt2.jumptoserver.com",
+ "country": "USA",
+ "city": "Atlanta",
+ "hostname": "us-at.jumptoserver.com",
"tcp": true,
"udp": true,
"ips": [
- "23.82.15.91"
+ "167.160.88.250"
+ ]
+ },
+ {
+ "vpn": "openvpn",
+ "country": "USA",
+ "city": "Chicago",
+ "hostname": "us-ch.jumptoserver.com",
+ "udp": true,
+ "ips": [
+ "88.216.97.2"
+ ]
+ },
+ {
+ "vpn": "openvpn",
+ "country": "USA",
+ "city": "Dallas",
+ "hostname": "us-dl.jumptoserver.com",
+ "tcp": true,
+ "udp": true,
+ "ips": [
+ "108.181.92.50"
+ ]
+ },
+ {
+ "vpn": "openvpn",
+ "country": "USA",
+ "city": "Denver",
+ "hostname": "us-dv1.jumptoserver.com",
+ "tcp": true,
+ "udp": true,
+ "ips": [
+ "139.28.179.82"
+ ]
+ },
+ {
+ "vpn": "openvpn",
+ "country": "USA",
+ "city": "Fargo",
+ "hostname": "us-ash-stream.jumptoserver.com",
+ "tcp": true,
+ "udp": true,
+ "ips": [
+ "108.181.56.100"
+ ]
+ },
+ {
+ "vpn": "openvpn",
+ "country": "USA",
+ "city": "New York",
+ "hostname": "us-ny.jumptoserver.com",
+ "tcp": true,
+ "udp": true,
+ "ips": [
+ "173.208.96.145"
+ ]
+ },
+ {
+ "vpn": "openvpn",
+ "country": "USA",
+ "city": "Seattle",
+ "hostname": "us-se.jumptoserver.com",
+ "tcp": true,
+ "udp": true,
+ "ips": [
+ "64.42.176.74"
+ ]
+ },
+ {
+ "vpn": "openvpn",
+ "country": "Ukraine",
+ "city": "Kyyiv",
+ "hostname": "ur-kv.jumptoserver.com",
+ "tcp": true,
+ "udp": true,
+ "ips": [
+ "176.103.54.79"
+ ]
+ },
+ {
+ "vpn": "openvpn",
+ "country": "United Kingdom",
+ "city": "London",
+ "hostname": "uk-streaming.jumptoserver.com",
+ "tcp": true,
+ "udp": true,
+ "ips": [
+ "195.191.219.70"
+ ]
+ },
+ {
+ "vpn": "openvpn",
+ "country": "United Kingdom",
+ "city": "London",
+ "hostname": "us-dbl1.jumptoserver.com",
+ "tcp": true,
+ "udp": true,
+ "ips": [
+ "139.28.179.83"
]
}
]